From 67626d49c91e2c043352571a32e1a0dfa0e9b279 Mon Sep 17 00:00:00 2001
From: Deomid Ryabkov <rojer@cesanta.com>
Date: Tue, 10 Jan 2017 00:44:44 +0100
Subject: [PATCH] Add ssl_cipher_suites to mg_{bind,connect}_opts

Optional specification of SSL/TLS cipher suites.

PUBLISHED_FROM=e8968c6c7a92f10514d3ee575c2fb9be862e6cf8
---
 docs/c-api/net.h/struct_mg_bind_opts.md    | 33 +++++++---
 docs/c-api/net.h/struct_mg_connect_opts.md | 36 ++++++++---
 mongoose.c                                 | 22 +++++--
 mongoose.h                                 | 70 ++++++++++++++++------
 4 files changed, 119 insertions(+), 42 deletions(-)

diff --git a/docs/c-api/net.h/struct_mg_bind_opts.md b/docs/c-api/net.h/struct_mg_bind_opts.md
index 18dfc3d4a..13217ec79 100644
--- a/docs/c-api/net.h/struct_mg_bind_opts.md
+++ b/docs/c-api/net.h/struct_mg_bind_opts.md
@@ -9,15 +9,30 @@ signature: |
     const char **error_string; /* Placeholder for the error string */
     struct mg_iface *iface;    /* Interface instance */
   #if MG_ENABLE_SSL
-    /* SSL settings. */
-    const char *ssl_cert;    /* Server certificate to present to clients
-                              * Or client certificate to present to tunnel
-                              * dispatcher. */
-    const char *ssl_key;     /* Private key corresponding to the certificate.
-                                If ssl_cert is set but ssl_key is not, ssl_cert
-                                is used. */
-    const char *ssl_ca_cert; /* CA bundle used to verify client certificates or
-                              * tunnel dispatchers. */
+    /*
+     * SSL settings.
+     *
+     * Server certificate to present to clients or client certificate to
+     * present to tunnel dispatcher (for tunneled connections).
+     */
+    const char *ssl_cert;
+    /* Private key corresponding to the certificate. If ssl_cert is set but
+     * ssl_key is not, ssl_cert is used. */
+    const char *ssl_key;
+    /* CA bundle used to verify client certificates or tunnel dispatchers. */
+    const char *ssl_ca_cert;
+    /* Colon-delimited list of acceptable cipher suites.
+     * Names depend on the library used, for example:
+     *
+     * ECDH-ECDSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256 (OpenSSL)
+     * TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256
+     *   (mbedTLS)
+     *
+     * For OpenSSL the list can be obtained by running "openssl ciphers".
+     * For mbedTLS, names can be found in library/ssl_ciphersuites.c
+     * If NULL, a reasonable default is used.
+     */
+    const char *ssl_cipher_suites;
   #endif
   };
 ---
diff --git a/docs/c-api/net.h/struct_mg_connect_opts.md b/docs/c-api/net.h/struct_mg_connect_opts.md
index 2d07a21f8..00ee8dd56 100644
--- a/docs/c-api/net.h/struct_mg_connect_opts.md
+++ b/docs/c-api/net.h/struct_mg_connect_opts.md
@@ -9,15 +9,33 @@ signature: |
     const char **error_string; /* Placeholder for the error string */
     struct mg_iface *iface;    /* Interface instance */
   #if MG_ENABLE_SSL
-    /* SSL settings. */
-    const char *ssl_cert;    /* Client certificate to present to the server */
-    const char *ssl_key;     /* Private key corresponding to the certificate.
-                                If ssl_cert is set but ssl_key is not, ssl_cert
-                                is used. */
-    const char *ssl_ca_cert; /* Verify server certificate using this CA bundle.
-                                If set to "*", then SSL is enabled but no cert
-                                verification is performed. */
-  
+    /*
+     * SSL settings.
+     * Client certificate to present to the server.
+     */
+    const char *ssl_cert;
+    /*
+     * Private key corresponding to the certificate.
+     * If ssl_cert is set but ssl_key is not, ssl_cert is used.
+     */
+    const char *ssl_key;
+    /*
+     * Verify server certificate using this CA bundle. If set to "*", then SSL
+     * is enabled but no cert verification is performed.
+     */
+    const char *ssl_ca_cert;
+    /* Colon-delimited list of acceptable cipher suites.
+     * Names depend on the library used, for example:
+     *
+     * ECDH-ECDSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256 (OpenSSL)
+     * TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256
+     *   (mbedTLS)
+     *
+     * For OpenSSL the list can be obtained by running "openssl ciphers".
+     * For mbedTLS, names can be found in library/ssl_ciphersuites.c
+     * If NULL, a reasonable default is used.
+     */
+    const char *ssl_cipher_suites;
     /*
      * Server name verification. If ssl_ca_cert is set and the certificate has
      * passed verification, its subject will be verified against this string.
diff --git a/mongoose.c b/mongoose.c
index ec452a8c9..8958a9c1a 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -2746,6 +2746,7 @@ struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address,
     params.cert = opts.ssl_cert;
     params.key = opts.ssl_key;
     params.ca_cert = opts.ssl_ca_cert;
+    params.cipher_suites = opts.ssl_cipher_suites;
     if (opts.ssl_ca_cert != NULL) {
       if (opts.ssl_server_name != NULL) {
         if (strcmp(opts.ssl_server_name, "*") != 0) {
@@ -2850,6 +2851,7 @@ struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address,
     params.cert = opts.ssl_cert;
     params.key = opts.ssl_key;
     params.ca_cert = opts.ssl_ca_cert;
+    params.cipher_suites = opts.ssl_cipher_suites;
     if (mg_ssl_if_conn_init(nc, &params, &err_msg) != MG_SSL_OK) {
       MG_SET_PTRPTR(opts.error_string, err_msg);
       mg_destroy_conn(nc, 1 /* destroy_if */);
@@ -4060,7 +4062,7 @@ enum mg_ssl_if_result mg_ssl_if_conn_accept(struct mg_connection *nc,
 static enum mg_ssl_if_result mg_use_cert(SSL_CTX *ctx, const char *cert,
                                          const char *key, const char **err_msg);
 static enum mg_ssl_if_result mg_use_ca_cert(SSL_CTX *ctx, const char *cert);
-static enum mg_ssl_if_result mg_set_cipher_list(SSL_CTX *ctx);
+static enum mg_ssl_if_result mg_set_cipher_list(SSL_CTX *ctx, const char *cl);
 
 enum mg_ssl_if_result mg_ssl_if_conn_init(
     struct mg_connection *nc, const struct mg_ssl_if_conn_params *params,
@@ -4105,7 +4107,10 @@ enum mg_ssl_if_result mg_ssl_if_conn_init(
 #endif
   }
 
-  mg_set_cipher_list(ctx->ssl_ctx);
+  if (mg_set_cipher_list(ctx->ssl_ctx, params->cipher_suites) != MG_SSL_OK) {
+    MG_SET_PTRPTR(err_msg, "Invalid cipher suite list");
+    return MG_SSL_ERROR;
+  }
 
   if (!(nc->flags & MG_F_LISTENING) &&
       (ctx->ssl = SSL_new(ctx->ssl_ctx)) == NULL) {
@@ -4287,9 +4292,10 @@ static enum mg_ssl_if_result mg_use_cert(SSL_CTX *ctx, const char *cert,
   return MG_SSL_OK;
 }
 
-static enum mg_ssl_if_result mg_set_cipher_list(SSL_CTX *ctx) {
-  return (SSL_CTX_set_cipher_list(ctx, mg_s_cipher_list) == 1 ? MG_SSL_OK
-                                                              : MG_SSL_ERROR);
+static enum mg_ssl_if_result mg_set_cipher_list(SSL_CTX *ctx, const char *cl) {
+  return (SSL_CTX_set_cipher_list(ctx, cl ? cl : mg_s_cipher_list) == 1
+              ? MG_SSL_OK
+              : MG_SSL_ERROR);
 }
 
 const char *mg_set_ssl(struct mg_connection *nc, const char *cert,
@@ -4418,7 +4424,10 @@ enum mg_ssl_if_result mg_ssl_if_conn_init(
     return MG_SSL_ERROR;
   }
 
-  mg_set_cipher_list(ctx, NULL);
+  if (mg_set_cipher_list(ctx, params->cipher_suites) != MG_SSL_OK) {
+    MG_SET_PTRPTR(err_msg, "Invalid cipher suite list");
+    return MG_SSL_ERROR;
+  }
 
   if (!(nc->flags & MG_F_LISTENING)) {
     ctx->ssl = MG_CALLOC(1, sizeof(*ctx->ssl));
@@ -13493,6 +13502,7 @@ enum mg_ssl_if_result mg_ssl_if_conn_init(
   if (params->ca_cert != NULL && strcmp(params->ca_cert, "*") != 0) {
     ctx->ssl_ca_cert = strdup(params->ca_cert);
   }
+  /* TODO(rojer): cipher_suites. */
   if (params->server_name != NULL) {
     ctx->ssl_server_name = strdup(params->server_name);
   }
diff --git a/mongoose.h b/mongoose.h
index bb27c3fb6..84e2fcfd5 100644
--- a/mongoose.h
+++ b/mongoose.h
@@ -3152,6 +3152,7 @@ struct mg_ssl_if_conn_params {
   const char *key;
   const char *ca_cert;
   const char *server_name;
+  const char *cipher_suites;
 };
 
 enum mg_ssl_if_result mg_ssl_if_conn_init(
@@ -3471,15 +3472,30 @@ struct mg_bind_opts {
   const char **error_string; /* Placeholder for the error string */
   struct mg_iface *iface;    /* Interface instance */
 #if MG_ENABLE_SSL
-  /* SSL settings. */
-  const char *ssl_cert;    /* Server certificate to present to clients
-                            * Or client certificate to present to tunnel
-                            * dispatcher. */
-  const char *ssl_key;     /* Private key corresponding to the certificate.
-                              If ssl_cert is set but ssl_key is not, ssl_cert
-                              is used. */
-  const char *ssl_ca_cert; /* CA bundle used to verify client certificates or
-                            * tunnel dispatchers. */
+  /*
+   * SSL settings.
+   *
+   * Server certificate to present to clients or client certificate to
+   * present to tunnel dispatcher (for tunneled connections).
+   */
+  const char *ssl_cert;
+  /* Private key corresponding to the certificate. If ssl_cert is set but
+   * ssl_key is not, ssl_cert is used. */
+  const char *ssl_key;
+  /* CA bundle used to verify client certificates or tunnel dispatchers. */
+  const char *ssl_ca_cert;
+  /* Colon-delimited list of acceptable cipher suites.
+   * Names depend on the library used, for example:
+   *
+   * ECDH-ECDSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256 (OpenSSL)
+   * TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256
+   *   (mbedTLS)
+   *
+   * For OpenSSL the list can be obtained by running "openssl ciphers".
+   * For mbedTLS, names can be found in library/ssl_ciphersuites.c
+   * If NULL, a reasonable default is used.
+   */
+  const char *ssl_cipher_suites;
 #endif
 };
 
@@ -3519,15 +3535,33 @@ struct mg_connect_opts {
   const char **error_string; /* Placeholder for the error string */
   struct mg_iface *iface;    /* Interface instance */
 #if MG_ENABLE_SSL
-  /* SSL settings. */
-  const char *ssl_cert;    /* Client certificate to present to the server */
-  const char *ssl_key;     /* Private key corresponding to the certificate.
-                              If ssl_cert is set but ssl_key is not, ssl_cert
-                              is used. */
-  const char *ssl_ca_cert; /* Verify server certificate using this CA bundle.
-                              If set to "*", then SSL is enabled but no cert
-                              verification is performed. */
-
+  /*
+   * SSL settings.
+   * Client certificate to present to the server.
+   */
+  const char *ssl_cert;
+  /*
+   * Private key corresponding to the certificate.
+   * If ssl_cert is set but ssl_key is not, ssl_cert is used.
+   */
+  const char *ssl_key;
+  /*
+   * Verify server certificate using this CA bundle. If set to "*", then SSL
+   * is enabled but no cert verification is performed.
+   */
+  const char *ssl_ca_cert;
+  /* Colon-delimited list of acceptable cipher suites.
+   * Names depend on the library used, for example:
+   *
+   * ECDH-ECDSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256 (OpenSSL)
+   * TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256
+   *   (mbedTLS)
+   *
+   * For OpenSSL the list can be obtained by running "openssl ciphers".
+   * For mbedTLS, names can be found in library/ssl_ciphersuites.c
+   * If NULL, a reasonable default is used.
+   */
+  const char *ssl_cipher_suites;
   /*
    * Server name verification. If ssl_ca_cert is set and the certificate has
    * passed verification, its subject will be verified against this string.
-- 
GitLab