add support for specifying usable ciphers
authorJo-Philipp Wich <jo@mein.io>
Sat, 15 Feb 2020 20:25:26 +0000 (21:25 +0100)
committerJo-Philipp Wich <jo@mein.io>
Sat, 15 Feb 2020 22:30:24 +0000 (23:30 +0100)
Implement a new ustream_ssl_ops.context_set_ciphers() function which allows
to specify the usable ciphers for TLS context which is useful to restrict
the accepted cipher subset especially for ustream-ssl server applications.

For the OpenSSL backend, the given cipher string is passed as-is to the
SSL_CTX_set_cipher_list().

For mbedTLS, the given string is split on colons and each item of the list
is resolved through mbedtls_ssl_get_ciphersuite_id() to construct a numeric
list of allowed ciphers.

Note that OpenSSL and mbedTLS use different names for their ciphers but both
implementations simply ignore unknown names, so it is possible to specify
cipherstrings which are applicable to either library, e.g. `-ALL:ECDHE-
ECDSA-AES128-GCM-SHA256:TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256` would
enable ChaCha20/Poly1305 in both OpenSSL and mbedTLS.

Another crucial difference between the libraries is that the cipherstring
in mbedTLS is effectively a whitelist of allowed ciphers while, without
additional syntax elements, OpenSSL's cipherstring merely appends ciphers
to the default selection.

Ref: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_cipher_list.html
Ref: https://tls.mbed.org/api/ssl_8h.html#a9914cdf5533e813e1ea7ca52981aa006
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
ustream-internal.h
ustream-mbedtls.c
ustream-mbedtls.h
ustream-openssl.c
ustream-ssl.c
ustream-ssl.h

index 8d5d0dbb5aaeedcfc661b1cb15edbca7a0b9e3c9..147141ab5f05930fb4298c01b05c2c1230d3d139 100644 (file)
@@ -38,6 +38,7 @@ struct ustream_ssl_ctx *__ustream_ssl_context_new(bool server);
 int __ustream_ssl_add_ca_crt_file(struct ustream_ssl_ctx *ctx, const char *file);
 int __ustream_ssl_set_crt_file(struct ustream_ssl_ctx *ctx, const char *file);
 int __ustream_ssl_set_key_file(struct ustream_ssl_ctx *ctx, const char *file);
+int __ustream_ssl_set_ciphers(struct ustream_ssl_ctx *ctx, const char *ciphers);
 void __ustream_ssl_context_free(struct ustream_ssl_ctx *ctx);
 enum ssl_conn_status __ustream_ssl_connect(struct ustream_ssl *us);
 int __ustream_ssl_read(struct ustream_ssl *us, char *buf, int len);
index 74c27a51b8c67db2339de8fe49eb1b1502c75a50..9f73c58360348fc62534ad69d696a71ed7c636dd 100644 (file)
@@ -225,6 +225,71 @@ __hidden int __ustream_ssl_set_key_file(struct ustream_ssl_ctx *ctx, const char
        return 0;
 }
 
+__hidden int __ustream_ssl_set_ciphers(struct ustream_ssl_ctx *ctx, const char *ciphers)
+{
+       int *ciphersuites = NULL, *tmp, id;
+       char *cipherstr, *p, *last, c;
+       size_t len = 0;
+
+       if (ciphers == NULL)
+               return -1;
+
+       cipherstr = strdup(ciphers);
+
+       if (cipherstr == NULL)
+               return -1;
+
+       for (p = cipherstr, last = p;; p++) {
+               if (*p == ':' || *p == 0) {
+                       c = *p;
+                       *p = 0;
+
+                       id = mbedtls_ssl_get_ciphersuite_id(last);
+
+                       if (id != 0) {
+                               tmp = realloc(ciphersuites, (len + 2) * sizeof(int));
+
+                               if (tmp == NULL) {
+                                       free(ciphersuites);
+                                       free(cipherstr);
+
+                                       return -1;
+                               }
+
+                               ciphersuites = tmp;
+                               ciphersuites[len++] = id;
+                               ciphersuites[len] = 0;
+                       }
+
+                       if (c == 0)
+                               break;
+
+                       last = p + 1;
+               }
+
+               /*
+                * mbedTLS expects cipher names with dashes while many sources elsewhere
+                * like the Firefox wiki or Wireshark specify ciphers with underscores,
+                * so simply convert all underscores to dashes to accept both notations.
+                */
+               else if (*p == '_') {
+                       *p = '-';
+               }
+       }
+
+       free(cipherstr);
+
+       if (len == 0)
+               return -1;
+
+       mbedtls_ssl_conf_ciphersuites(&ctx->conf, ciphersuites);
+       free(ctx->ciphersuites);
+
+       ctx->ciphersuites = ciphersuites;
+
+       return 0;
+}
+
 __hidden void __ustream_ssl_context_free(struct ustream_ssl_ctx *ctx)
 {
 #if defined(MBEDTLS_SSL_CACHE_C)
@@ -234,6 +299,7 @@ __hidden void __ustream_ssl_context_free(struct ustream_ssl_ctx *ctx)
        mbedtls_x509_crt_free(&ctx->ca_cert);
        mbedtls_x509_crt_free(&ctx->cert);
        mbedtls_ssl_config_free(&ctx->conf);
+       free(ctx->ciphersuites);
        free(ctx);
 }
 
index 0e5988a57275b2fbc819eb686b631aa66c1fd097..e622e5e234a106ad06c8d8ef4b942c48e35e7148 100644 (file)
@@ -41,6 +41,7 @@ struct ustream_ssl_ctx {
        mbedtls_ssl_cache_context cache;
 #endif
        bool server;
+       int *ciphersuites;
 };
 
 static inline char *__ustream_ssl_strerror(int error, char *buffer, int len)
index 14694733fb2dcf4c89b2052fabd651a9cf8d8cec..049aa407168c2e3d283321e8f4fb778ca3d19cb6 100644 (file)
@@ -190,6 +190,16 @@ __hidden int __ustream_ssl_set_key_file(struct ustream_ssl_ctx *ctx, const char
        return 0;
 }
 
+__hidden int __ustream_ssl_set_ciphers(struct ustream_ssl_ctx *ctx, const char *ciphers)
+{
+       int ret = SSL_CTX_set_cipher_list((void *) ctx, ciphers);
+
+       if (ret == 0)
+               return -1;
+
+       return 0;
+}
+
 __hidden void __ustream_ssl_context_free(struct ustream_ssl_ctx *ctx)
 {
        SSL_CTX_free((void *) ctx);
@@ -306,4 +316,3 @@ __hidden int __ustream_ssl_read(struct ustream_ssl *us, char *buf, int len)
 
        return ret;
 }
-
index 47f66d6c3fcc7a0be4807118ac2fe3036fd4b990..dbc3bee00e27af3f0815236857ef2823fb266fb5 100644 (file)
@@ -231,6 +231,7 @@ const struct ustream_ssl_ops ustream_ssl_ops = {
        .context_set_crt_file = __ustream_ssl_set_crt_file,
        .context_set_key_file = __ustream_ssl_set_key_file,
        .context_add_ca_crt_file = __ustream_ssl_add_ca_crt_file,
+       .context_set_ciphers = __ustream_ssl_set_ciphers,
        .context_free = __ustream_ssl_context_free,
        .init = _ustream_ssl_init,
        .set_peer_cn = _ustream_ssl_set_peer_cn,
index 77877885e43103c0e86224e6f6b2a427cf79ef2c..db89297604a9c7da5581e81ca945b5eccbaf1441 100644 (file)
@@ -56,6 +56,8 @@ struct ustream_ssl_ops {
 
        int (*init)(struct ustream_ssl *us, struct ustream *conn, struct ustream_ssl_ctx *ctx, bool server);
        int (*set_peer_cn)(struct ustream_ssl *conn, const char *name);
+
+       int (*context_set_ciphers)(struct ustream_ssl_ctx *ctx, const char *ciphers);
 };
 
 extern const struct ustream_ssl_ops ustream_ssl_ops;
@@ -64,6 +66,7 @@ extern const struct ustream_ssl_ops ustream_ssl_ops;
 #define ustream_ssl_context_set_crt_file       ustream_ssl_ops.context_set_crt_file
 #define ustream_ssl_context_set_key_file       ustream_ssl_ops.context_set_key_file
 #define ustream_ssl_context_add_ca_crt_file    ustream_ssl_ops.context_add_ca_crt_file
+#define ustream_ssl_context_set_ciphers                ustream_ssl_ops.context_set_ciphers
 #define ustream_ssl_context_free               ustream_ssl_ops.context_free
 #define ustream_ssl_init                       ustream_ssl_ops.init
 #define ustream_ssl_set_peer_cn                        ustream_ssl_ops.set_peer_cn