Multiple certificate support revisited

Maxim Dounin mdounin at mdounin.ru
Sun May 15 02:06:42 UTC 2016


Hello!

On Sat, May 14, 2016 at 03:22:33PM +0000, Brandon Black wrote:

> On Tue, Aug 18, 2015 at 2:31 PM, Brandon Black <bblack at wikimedia.org> wrote:
> > Hi all,
> >
> > The Wikimedia Foundation has been running nginx-1.9.3 patched for
> > multi-certificate support for all production TLS traffic for a few
> > weeks now without incident, for all inbound requests to Wikipedia and
> > other associated projects of the Foundation.
> 
> [... http://mailman.nginx.org/pipermail/nginx-devel/2015-August/007225.html
> for full text]
> 
> Bump!
> 
> We're still running these patches for all Wikimedia sites (including
> Wikipedia) to serve dual ECDSA+RSA certificates.  There was some
> feedback from some of the original author(s) privately back at the
> time of my last post on this in Aug 2015, but no real progress on
> making newer/better patches and no upstream feedback from nginx.org
> AFAIK so far.

Patch series to implement multiple certificates support is 
currently under review and is expected to be committed in a week 
or so.

Just in case you are interested, you can find it attached.

-- 
Maxim Dounin
http://nginx.org/
-------------- next part --------------
# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1463277750 -10800
#      Sun May 15 05:02:30 2016 +0300
# Node ID c88a894246015f7d7b65061785546eb368458722
# Parent  db699978a33fe19d786e95e00143eb2b6a1662c0
OCSP stapling: staple provided in arguments.

diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c
--- a/src/event/ngx_event_openssl_stapling.c
+++ b/src/event/ngx_event_openssl_stapling.c
@@ -84,10 +84,11 @@ struct ngx_ssl_ocsp_ctx_s {
 
 
 static ngx_int_t ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl,
-    ngx_str_t *file);
-static ngx_int_t ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl);
+    ngx_ssl_stapling_t *staple, ngx_str_t *file);
+static ngx_int_t ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl,
+    ngx_ssl_stapling_t *staple);
 static ngx_int_t ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl,
-    ngx_str_t *responder);
+    ngx_ssl_stapling_t *staple, ngx_str_t *responder);
 
 static int ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn,
     void *data);
@@ -153,14 +154,14 @@ ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl
     if (file->len) {
         /* use OCSP response from the file */
 
-        if (ngx_ssl_stapling_file(cf, ssl, file) != NGX_OK) {
+        if (ngx_ssl_stapling_file(cf, ssl, staple, file) != NGX_OK) {
             return NGX_ERROR;
         }
 
         goto done;
     }
 
-    rc = ngx_ssl_stapling_issuer(cf, ssl);
+    rc = ngx_ssl_stapling_issuer(cf, ssl, staple);
 
     if (rc == NGX_DECLINED) {
         return NGX_OK;
@@ -170,7 +171,7 @@ ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl
         return NGX_ERROR;
     }
 
-    rc = ngx_ssl_stapling_responder(cf, ssl, responder);
+    rc = ngx_ssl_stapling_responder(cf, ssl, staple, responder);
 
     if (rc == NGX_DECLINED) {
         return NGX_OK;
@@ -190,15 +191,13 @@ done:
 
 
 static ngx_int_t
-ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
+ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl,
+    ngx_ssl_stapling_t *staple, ngx_str_t *file)
 {
     BIO                 *bio;
     int                  len;
     u_char              *p, *buf;
     OCSP_RESPONSE       *response;
-    ngx_ssl_stapling_t  *staple;
-
-    staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);
 
     if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {
         return NGX_ERROR;
@@ -259,16 +258,15 @@ failed:
 
 
 static ngx_int_t
-ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl)
+ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl,
+    ngx_ssl_stapling_t *staple)
 {
     int                  i, n, rc;
     X509                *cert, *issuer;
     X509_STORE          *store;
     X509_STORE_CTX      *store_ctx;
     STACK_OF(X509)      *chain;
-    ngx_ssl_stapling_t  *staple;
 
-    staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);
     cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
 
 #if OPENSSL_VERSION_NUMBER >= 0x10001000L
@@ -351,15 +349,13 @@ ngx_ssl_stapling_issuer(ngx_conf_t *cf, 
 
 
 static ngx_int_t
-ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder)
+ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl,
+    ngx_ssl_stapling_t *staple, ngx_str_t *responder)
 {
     ngx_url_t                  u;
     char                      *s;
-    ngx_ssl_stapling_t        *staple;
     STACK_OF(OPENSSL_STRING)  *aia;
 
-    staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);
-
     if (responder->len == 0) {
 
         /* extract OCSP responder URL from certificate */
# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1463277750 -10800
#      Sun May 15 05:02:30 2016 +0300
# Node ID 0d2a0c36111ecb8744928f51f54a125f8e3f7776
# Parent  c88a894246015f7d7b65061785546eb368458722
OCSP stapling: staple now stored in certificate, not context.

diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -187,11 +187,10 @@ ngx_ssl_init(ngx_log_t *log)
         return NGX_ERROR;
     }
 
-    ngx_ssl_stapling_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
-                                                      NULL);
+    ngx_ssl_stapling_index = X509_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+
     if (ngx_ssl_stapling_index == -1) {
-        ngx_ssl_error(NGX_LOG_ALERT, log, 0,
-                      "SSL_CTX_get_ex_new_index() failed");
+        ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed");
         return NGX_ERROR;
     }
 
diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c
--- a/src/event/ngx_event_openssl_stapling.c
+++ b/src/event/ngx_event_openssl_stapling.c
@@ -122,6 +122,7 @@ ngx_int_t
 ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file,
     ngx_str_t *responder, ngx_uint_t verify)
 {
+    X509                      *cert;
     ngx_int_t                  rc;
     ngx_pool_cleanup_t        *cln;
     ngx_ssl_stapling_t        *staple;
@@ -139,17 +140,17 @@ ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl
     cln->handler = ngx_ssl_stapling_cleanup;
     cln->data = staple;
 
-    if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_stapling_index, staple)
-        == 0)
-    {
-        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                      "SSL_CTX_set_ex_data() failed");
+    cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
+
+    if (X509_set_ex_data(cert, ngx_ssl_stapling_index, staple) == 0) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed");
         return NGX_ERROR;
     }
 
     staple->ssl_ctx = ssl->ctx;
     staple->timeout = 60000;
     staple->verify = verify;
+    staple->cert = cert;
 
     if (file->len) {
         /* use OCSP response from the file */
@@ -267,7 +268,7 @@ ngx_ssl_stapling_issuer(ngx_conf_t *cf, 
     X509_STORE_CTX      *store_ctx;
     STACK_OF(X509)      *chain;
 
-    cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
+    cert = staple->cert;
 
 #if OPENSSL_VERSION_NUMBER >= 0x10001000L
     SSL_CTX_get_extra_chain_certs(ssl->ctx, &chain);
@@ -292,7 +293,6 @@ ngx_ssl_stapling_issuer(ngx_conf_t *cf, 
             ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,
                            "SSL get issuer: found %p in extra certs", issuer);
 
-            staple->cert = cert;
             staple->issuer = issuer;
 
             return NGX_OK;
@@ -341,7 +341,6 @@ ngx_ssl_stapling_issuer(ngx_conf_t *cf, 
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,
                    "SSL get issuer: found %p in cert store", issuer);
 
-    staple->cert = cert;
     staple->issuer = issuer;
 
     return NGX_OK;
@@ -439,9 +438,11 @@ ngx_int_t
 ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
     ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)
 {
+    X509                *cert;
     ngx_ssl_stapling_t  *staple;
 
-    staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);
+    cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
+    staple = X509_get_ex_data(cert, ngx_ssl_stapling_index);
 
     staple->resolver = resolver;
     staple->resolver_timeout = resolver_timeout;
# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1463277750 -10800
#      Sun May 15 05:02:30 2016 +0300
# Node ID 62bce4509292f04a8ddb24e4049cddc32996a57c
# Parent  0d2a0c36111ecb8744928f51f54a125f8e3f7776
OCSP stapling: staple now extracted via SSL_get_certificate().

This makes it possible to properly return OCSP staple with multiple
certificates configured.

Note that it only works properly in OpenSSL 1.0.1d+, 1.0.0k, 0.9.8y+.
In older versions SSL_get_certificate() fails to return correct certificate
when the certificate status callback is called.

diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c
--- a/src/event/ngx_event_openssl_stapling.c
+++ b/src/event/ngx_event_openssl_stapling.c
@@ -185,7 +185,6 @@ ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl
 done:
 
     SSL_CTX_set_tlsext_status_cb(ssl->ctx, ngx_ssl_certificate_status_callback);
-    SSL_CTX_set_tlsext_status_arg(ssl->ctx, staple);
 
     return NGX_OK;
 }
@@ -455,6 +454,7 @@ static int
 ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data)
 {
     int                  rc;
+    X509                *cert;
     u_char              *p;
     ngx_connection_t    *c;
     ngx_ssl_stapling_t  *staple;
@@ -464,9 +464,15 @@ ngx_ssl_certificate_status_callback(ngx_
     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
                    "SSL certificate status callback");
 
-    staple = data;
     rc = SSL_TLSEXT_ERR_NOACK;
 
+    cert = SSL_get_certificate(ssl_conn);
+    staple = X509_get_ex_data(cert, ngx_ssl_stapling_index);
+
+    if (staple == NULL) {
+        return rc;
+    }
+
     if (staple->staple.len
         && staple->valid >= ngx_time())
     {
# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1463277750 -10800
#      Sun May 15 05:02:30 2016 +0300
# Node ID f18ad0ff562839070121aa05544cec089715ba0e
# Parent  62bce4509292f04a8ddb24e4049cddc32996a57c
OCSP stapling: additional function to configure stapling on a cert.

diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c
--- a/src/event/ngx_event_openssl_stapling.c
+++ b/src/event/ngx_event_openssl_stapling.c
@@ -83,6 +83,8 @@ struct ngx_ssl_ocsp_ctx_s {
 };
 
 
+static ngx_int_t ngx_ssl_stapling_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
+    X509 *cert, ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify);
 static ngx_int_t ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl,
     ngx_ssl_stapling_t *staple, ngx_str_t *file);
 static ngx_int_t ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl,
@@ -122,10 +124,29 @@ ngx_int_t
 ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file,
     ngx_str_t *responder, ngx_uint_t verify)
 {
-    X509                      *cert;
-    ngx_int_t                  rc;
-    ngx_pool_cleanup_t        *cln;
-    ngx_ssl_stapling_t        *staple;
+    X509  *cert;
+
+    cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
+
+    if (ngx_ssl_stapling_certificate(cf, ssl, cert, file, responder, verify)
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+    SSL_CTX_set_tlsext_status_cb(ssl->ctx, ngx_ssl_certificate_status_callback);
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_ssl_stapling_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, X509 *cert,
+    ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify)
+{
+    ngx_int_t            rc;
+    ngx_pool_cleanup_t  *cln;
+    ngx_ssl_stapling_t  *staple;
 
     staple = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_stapling_t));
     if (staple == NULL) {
@@ -140,8 +161,6 @@ ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl
     cln->handler = ngx_ssl_stapling_cleanup;
     cln->data = staple;
 
-    cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
-
     if (X509_set_ex_data(cert, ngx_ssl_stapling_index, staple) == 0) {
         ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed");
         return NGX_ERROR;
@@ -159,7 +178,7 @@ ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl
             return NGX_ERROR;
         }
 
-        goto done;
+        return NGX_OK;
     }
 
     rc = ngx_ssl_stapling_issuer(cf, ssl, staple);
@@ -182,10 +201,6 @@ ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl
         return NGX_ERROR;
     }
 
-done:
-
-    SSL_CTX_set_tlsext_status_cb(ssl->ctx, ngx_ssl_certificate_status_callback);
-
     return NGX_OK;
 }
 
# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1463277750 -10800
#      Sun May 15 05:02:30 2016 +0300
# Node ID 61522cf7ee72270ad214df45c9802fa89ef14339
# Parent  f18ad0ff562839070121aa05544cec089715ba0e
SSL: made it possible to iterate though all certificates.

A pointer to a previously configured certificate now stored in a certificate,
this makes it possible iterate though all certificates configured in the SSL
context.  This is now used to configure OCSP stapling on all certificates,
and in ngx_ssl_session_id_context().

Note that in OpenSSL 1.0.2+ this can be done without storing things in exdata
using the SSL_CTX_set_current_cert() and SSL_CTX_get0_certificate() functions.
These are not yet available in all supported versions though, so it's easier
to continue to use exdata for now.

diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -105,6 +105,7 @@ int  ngx_ssl_server_conf_index;
 int  ngx_ssl_session_cache_index;
 int  ngx_ssl_session_ticket_keys_index;
 int  ngx_ssl_certificate_index;
+int  ngx_ssl_next_certificate_index;
 int  ngx_ssl_stapling_index;
 
 
@@ -187,6 +188,13 @@ ngx_ssl_init(ngx_log_t *log)
         return NGX_ERROR;
     }
 
+    ngx_ssl_next_certificate_index = X509_get_ex_new_index(0, NULL, NULL, NULL,
+                                                           NULL);
+    if (ngx_ssl_next_certificate_index == -1) {
+        ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed");
+        return NGX_ERROR;
+    }
+
     ngx_ssl_stapling_index = X509_get_ex_new_index(0, NULL, NULL, NULL, NULL);
 
     if (ngx_ssl_stapling_index == -1) {
@@ -214,6 +222,12 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_
         return NGX_ERROR;
     }
 
+    if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, NULL) == 0) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "SSL_CTX_set_ex_data() failed");
+        return NGX_ERROR;
+    }
+
     ssl->buffer_size = NGX_SSL_BUFSIZE;
 
     /* client side options */
@@ -350,6 +364,16 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_
         return NGX_ERROR;
     }
 
+    if (X509_set_ex_data(x509, ngx_ssl_next_certificate_index,
+                      SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index))
+        == 0)
+    {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed");
+        X509_free(x509);
+        BIO_free(bio);
+        return NGX_ERROR;
+    }
+
     if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509)
         == 0)
     {
@@ -2163,7 +2187,7 @@ ngx_ssl_session_id_context(ngx_ssl_t *ss
 
     /*
      * Session ID context is set based on the string provided,
-     * the server certificate, and the client CA list.
+     * the server certificates, and the client CA list.
      */
 
     md = EVP_MD_CTX_create();
@@ -2183,18 +2207,21 @@ ngx_ssl_session_id_context(ngx_ssl_t *ss
         goto failed;
     }
 
-    cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
-
-    if (X509_digest(cert, EVP_sha1(), buf, &len) == 0) {
-        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                      "X509_digest() failed");
-        goto failed;
-    }
-
-    if (EVP_DigestUpdate(md, buf, len) == 0) {
-        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                      "EVP_DigestUpdate() failed");
-        goto failed;
+    for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
+         cert;
+         cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index))
+    {
+        if (X509_digest(cert, EVP_sha1(), buf, &len) == 0) {
+            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                          "X509_digest() failed");
+            goto failed;
+        }
+
+        if (EVP_DigestUpdate(md, buf, len) == 0) {
+            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                          "EVP_DigestUpdate() failed");
+            goto failed;
+        }
     }
 
     list = SSL_CTX_get_client_CA_list(ssl->ctx);
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -227,6 +227,7 @@ extern int  ngx_ssl_server_conf_index;
 extern int  ngx_ssl_session_cache_index;
 extern int  ngx_ssl_session_ticket_keys_index;
 extern int  ngx_ssl_certificate_index;
+extern int  ngx_ssl_next_certificate_index;
 extern int  ngx_ssl_stapling_index;
 
 
diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c
--- a/src/event/ngx_event_openssl_stapling.c
+++ b/src/event/ngx_event_openssl_stapling.c
@@ -126,12 +126,15 @@ ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl
 {
     X509  *cert;
 
-    cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
-
-    if (ngx_ssl_stapling_certificate(cf, ssl, cert, file, responder, verify)
-        != NGX_OK)
+    for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
+         cert;
+         cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index))
     {
-        return NGX_ERROR;
+        if (ngx_ssl_stapling_certificate(cf, ssl, cert, file, responder, verify)
+            != NGX_OK)
+        {
+            return NGX_ERROR;
+        }
     }
 
     SSL_CTX_set_tlsext_status_cb(ssl->ctx, ngx_ssl_certificate_status_callback);
@@ -455,11 +458,14 @@ ngx_ssl_stapling_resolver(ngx_conf_t *cf
     X509                *cert;
     ngx_ssl_stapling_t  *staple;
 
-    cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
-    staple = X509_get_ex_data(cert, ngx_ssl_stapling_index);
-
-    staple->resolver = resolver;
-    staple->resolver_timeout = resolver_timeout;
+    for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
+         cert;
+         cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index))
+    {
+        staple = X509_get_ex_data(cert, ngx_ssl_stapling_index);
+        staple->resolver = resolver;
+        staple->resolver_timeout = resolver_timeout;
+    }
 
     return NGX_OK;
 }
# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1463277750 -10800
#      Sun May 15 05:02:30 2016 +0300
# Node ID 61948bdc9c9170fd3714d50f712c8496ae4b13f2
# Parent  61522cf7ee72270ad214df45c9802fa89ef14339
SSL: support for per-certificate chains.

The SSL_CTX_add0_chain_cert() function as introduced in OpenSSL 1.0.2 now
used instead of SSL_CTX_add_extra_chain_cert().

SSL_CTX_add_extra_chain_cert() adds extra certs for all certificates
in the context, while SSL_CTX_add0_chain_cert() only to a particular
certificate.  There is no difference unless multiple certificates are used,
though it is important when using multiple certificates.

Additionally, SSL_CTX_select_current_cert() is now called before using
a chain to make sure correct chain will be returned.

diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -410,6 +410,24 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_
             return NGX_ERROR;
         }
 
+#ifdef SSL_CTRL_CHAIN_CERT
+
+        /*
+         * SSL_CTX_add0_chain_cert() is needed to add chain to
+         * a parituclar certificate when multiple certificates are used;
+         * only available in OpenSSL 1.0.2+
+         */
+
+        if (SSL_CTX_add0_chain_cert(ssl->ctx, x509) == 0) {
+            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                          "SSL_CTX_add0_chain_cert(\"%s\") failed",
+                          cert->data);
+            X509_free(x509);
+            BIO_free(bio);
+            return NGX_ERROR;
+        }
+
+#else
         if (SSL_CTX_add_extra_chain_cert(ssl->ctx, x509) == 0) {
             ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
                           "SSL_CTX_add_extra_chain_cert(\"%s\") failed",
@@ -418,6 +436,7 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_
             BIO_free(bio);
             return NGX_ERROR;
         }
+#endif
     }
 
     BIO_free(bio);
diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c
--- a/src/event/ngx_event_openssl_stapling.c
+++ b/src/event/ngx_event_openssl_stapling.c
@@ -287,7 +287,13 @@ ngx_ssl_stapling_issuer(ngx_conf_t *cf, 
 
     cert = staple->cert;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+#ifdef SSL_CTRL_SELECT_CURRENT_CERT
+    /* OpenSSL 1.0.2+ */
+    SSL_CTX_select_current_cert(ssl->ctx, cert);
+#endif
+
+#ifdef SSL_CTRL_GET_EXTRA_CHAIN_CERTS
+    /* OpenSSL 1.0.1+ */
     SSL_CTX_get_extra_chain_certs(ssl->ctx, &chain);
 #else
     chain = ssl->ctx->extra_certs;
@@ -621,7 +627,13 @@ ngx_ssl_stapling_ocsp_handler(ngx_ssl_oc
         goto error;
     }
 
-#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+#ifdef SSL_CTRL_SELECT_CURRENT_CERT
+    /* OpenSSL 1.0.2+ */
+    SSL_CTX_select_current_cert(staple->ssl_ctx, ctx->cert);
+#endif
+
+#ifdef SSL_CTRL_GET_EXTRA_CHAIN_CERTS
+    /* OpenSSL 1.0.1+ */
     SSL_CTX_get_extra_chain_certs(staple->ssl_ctx, &chain);
 #else
     chain = staple->ssl_ctx->extra_certs;
# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1463277750 -10800
#      Sun May 15 05:02:30 2016 +0300
# Node ID 4c8bf7080cf15f2ff754818a48fe223f2ee5812b
# Parent  61948bdc9c9170fd3714d50f712c8496ae4b13f2
SSL: support for multiple certificates (ticket #814).

diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -322,6 +322,29 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_
 
 
 ngx_int_t
+ngx_ssl_certificates(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *certs,
+    ngx_array_t *keys, ngx_array_t *passwords)
+{
+    ngx_str_t   *cert, *key;
+    ngx_uint_t   i;
+
+    cert = certs->elts;
+    key = keys->elts;
+
+    for (i = 0; i < certs->nelts; i++) {
+
+        if (ngx_ssl_certificate(cf, ssl, &cert[i], &key[i], passwords)
+            != NGX_OK)
+        {
+            return NGX_ERROR;
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
 ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
     ngx_str_t *key, ngx_array_t *passwords)
 {
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -142,6 +142,8 @@ ngx_int_t ngx_ssl_init(ngx_log_t *log);
 ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data);
 ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
     ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords);
+ngx_int_t ngx_ssl_certificates(ngx_conf_t *cf, ngx_ssl_t *ssl,
+    ngx_array_t *certs, ngx_array_t *keys, ngx_array_t *passwords);
 ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
     ngx_str_t *cert, ngx_int_t depth);
 ngx_int_t ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -81,16 +81,16 @@ static ngx_command_t  ngx_http_ssl_comma
 
     { ngx_string("ssl_certificate"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_str_slot,
+      ngx_conf_set_str_array_slot,
       NGX_HTTP_SRV_CONF_OFFSET,
-      offsetof(ngx_http_ssl_srv_conf_t, certificate),
+      offsetof(ngx_http_ssl_srv_conf_t, certificates),
       NULL },
 
     { ngx_string("ssl_certificate_key"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_str_slot,
+      ngx_conf_set_str_array_slot,
       NGX_HTTP_SRV_CONF_OFFSET,
-      offsetof(ngx_http_ssl_srv_conf_t, certificate_key),
+      offsetof(ngx_http_ssl_srv_conf_t, certificate_keys),
       NULL },
 
     { ngx_string("ssl_password_file"),
@@ -508,8 +508,6 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t 
      * set by ngx_pcalloc():
      *
      *     sscf->protocols = 0;
-     *     sscf->certificate = { 0, NULL };
-     *     sscf->certificate_key = { 0, NULL };
      *     sscf->dhparam = { 0, NULL };
      *     sscf->ecdh_curve = { 0, NULL };
      *     sscf->client_certificate = { 0, NULL };
@@ -526,6 +524,8 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t 
     sscf->buffer_size = NGX_CONF_UNSET_SIZE;
     sscf->verify = NGX_CONF_UNSET_UINT;
     sscf->verify_depth = NGX_CONF_UNSET_UINT;
+    sscf->certificates = NGX_CONF_UNSET_PTR;
+    sscf->certificate_keys = NGX_CONF_UNSET_PTR;
     sscf->passwords = NGX_CONF_UNSET_PTR;
     sscf->builtin_session_cache = NGX_CONF_UNSET;
     sscf->session_timeout = NGX_CONF_UNSET;
@@ -573,8 +573,9 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
     ngx_conf_merge_uint_value(conf->verify, prev->verify, 0);
     ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);
 
-    ngx_conf_merge_str_value(conf->certificate, prev->certificate, "");
-    ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, "");
+    ngx_conf_merge_ptr_value(conf->certificates, prev->certificates, NULL);
+    ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys,
+                         NULL);
 
     ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);
 
@@ -601,7 +602,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
 
     if (conf->enable) {
 
-        if (conf->certificate.len == 0) {
+        if (conf->certificates == NULL) {
             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                           "no \"ssl_certificate\" is defined for "
                           "the \"ssl\" directive in %s:%ui",
@@ -609,7 +610,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
             return NGX_CONF_ERROR;
         }
 
-        if (conf->certificate_key.len == 0) {
+        if (conf->certificate_keys == NULL) {
             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                           "no \"ssl_certificate_key\" is defined for "
                           "the \"ssl\" directive in %s:%ui",
@@ -617,16 +618,31 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
             return NGX_CONF_ERROR;
         }
 
+        if (conf->certificate_keys->nelts < conf->certificates->nelts) {
+            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                          "no \"ssl_certificate_key\" is defined "
+                          "for certificate \"%V\" and "
+                          "the \"ssl\" directive in %s:%ui",
+                          ((ngx_str_t *) conf->certificates->elts)
+                          + conf->certificates->nelts - 1,
+                          conf->file, conf->line);
+            return NGX_CONF_ERROR;
+        }
+
     } else {
 
-        if (conf->certificate.len == 0) {
+        if (conf->certificates == NULL) {
             return NGX_CONF_OK;
         }
 
-        if (conf->certificate_key.len == 0) {
+        if (conf->certificate_keys == NULL
+            || conf->certificate_keys->nelts < conf->certificates->nelts)
+        {
             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                           "no \"ssl_certificate_key\" is defined "
-                          "for certificate \"%V\"", &conf->certificate);
+                          "for certificate \"%V\"",
+                          ((ngx_str_t *) conf->certificates->elts)
+                          + conf->certificates->nelts - 1);
             return NGX_CONF_ERROR;
         }
     }
@@ -666,8 +682,8 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
     cln->handler = ngx_ssl_cleanup_ctx;
     cln->data = &conf->ssl;
 
-    if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate,
-                            &conf->certificate_key, conf->passwords)
+    if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates,
+                             conf->certificate_keys, conf->passwords)
         != NGX_OK)
     {
         return NGX_CONF_ERROR;
diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h
--- a/src/http/modules/ngx_http_ssl_module.h
+++ b/src/http/modules/ngx_http_ssl_module.h
@@ -32,8 +32,9 @@ typedef struct {
 
     time_t                          session_timeout;
 
-    ngx_str_t                       certificate;
-    ngx_str_t                       certificate_key;
+    ngx_array_t                    *certificates;
+    ngx_array_t                    *certificate_keys;
+
     ngx_str_t                       dhparam;
     ngx_str_t                       ecdh_curve;
     ngx_str_t                       client_certificate;
diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c
--- a/src/mail/ngx_mail_ssl_module.c
+++ b/src/mail/ngx_mail_ssl_module.c
@@ -73,16 +73,16 @@ static ngx_command_t  ngx_mail_ssl_comma
 
     { ngx_string("ssl_certificate"),
       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_str_slot,
+      ngx_conf_set_str_array_slot,
       NGX_MAIL_SRV_CONF_OFFSET,
-      offsetof(ngx_mail_ssl_conf_t, certificate),
+      offsetof(ngx_mail_ssl_conf_t, certificates),
       NULL },
 
     { ngx_string("ssl_certificate_key"),
       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_str_slot,
+      ngx_conf_set_str_array_slot,
       NGX_MAIL_SRV_CONF_OFFSET,
-      offsetof(ngx_mail_ssl_conf_t, certificate_key),
+      offsetof(ngx_mail_ssl_conf_t, certificate_keys),
       NULL },
 
     { ngx_string("ssl_password_file"),
@@ -238,8 +238,6 @@ ngx_mail_ssl_create_conf(ngx_conf_t *cf)
      * set by ngx_pcalloc():
      *
      *     scf->protocols = 0;
-     *     scf->certificate = { 0, NULL };
-     *     scf->certificate_key = { 0, NULL };
      *     scf->dhparam = { 0, NULL };
      *     scf->ecdh_curve = { 0, NULL };
      *     scf->client_certificate = { 0, NULL };
@@ -251,6 +249,8 @@ ngx_mail_ssl_create_conf(ngx_conf_t *cf)
 
     scf->enable = NGX_CONF_UNSET;
     scf->starttls = NGX_CONF_UNSET_UINT;
+    scf->certificates = NGX_CONF_UNSET_PTR;
+    scf->certificate_keys = NGX_CONF_UNSET_PTR;
     scf->passwords = NGX_CONF_UNSET_PTR;
     scf->prefer_server_ciphers = NGX_CONF_UNSET;
     scf->verify = NGX_CONF_UNSET_UINT;
@@ -290,8 +290,9 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, 
     ngx_conf_merge_uint_value(conf->verify, prev->verify, 0);
     ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);
 
-    ngx_conf_merge_str_value(conf->certificate, prev->certificate, "");
-    ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, "");
+    ngx_conf_merge_ptr_value(conf->certificates, prev->certificates, NULL);
+    ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys,
+                         NULL);
 
     ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);
 
@@ -328,7 +329,7 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, 
 
     if (*mode) {
 
-        if (conf->certificate.len == 0) {
+        if (conf->certificates == NULL) {
             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                           "no \"ssl_certificate\" is defined for "
                           "the \"%s\" directive in %s:%ui",
@@ -336,7 +337,7 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, 
             return NGX_CONF_ERROR;
         }
 
-        if (conf->certificate_key.len == 0) {
+        if (conf->certificate_keys == NULL) {
             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                           "no \"ssl_certificate_key\" is defined for "
                           "the \"%s\" directive in %s:%ui",
@@ -344,17 +345,31 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, 
             return NGX_CONF_ERROR;
         }
 
+        if (conf->certificate_keys->nelts < conf->certificates->nelts) {
+            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                          "no \"ssl_certificate_key\" is defined "
+                          "for certificate \"%V\" and "
+                          "the \"ssl\" directive in %s:%ui",
+                          ((ngx_str_t *) conf->certificates->elts)
+                          + conf->certificates->nelts - 1,
+                          conf->file, conf->line);
+            return NGX_CONF_ERROR;
+        }
+
     } else {
 
-        if (conf->certificate.len == 0) {
+        if (conf->certificates == NULL) {
             return NGX_CONF_OK;
         }
 
-        if (conf->certificate_key.len == 0) {
+        if (conf->certificate_keys == NULL
+            || conf->certificate_keys->nelts < conf->certificates->nelts)
+        {
             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                           "no \"ssl_certificate_key\" is defined "
                           "for certificate \"%V\"",
-                          &conf->certificate);
+                          ((ngx_str_t *) conf->certificates->elts)
+                          + conf->certificates->nelts - 1);
             return NGX_CONF_ERROR;
         }
     }
@@ -371,8 +386,8 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, 
     cln->handler = ngx_ssl_cleanup_ctx;
     cln->data = &conf->ssl;
 
-    if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate,
-                            &conf->certificate_key, conf->passwords)
+    if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates,
+                             conf->certificate_keys, conf->passwords)
         != NGX_OK)
     {
         return NGX_CONF_ERROR;
diff --git a/src/mail/ngx_mail_ssl_module.h b/src/mail/ngx_mail_ssl_module.h
--- a/src/mail/ngx_mail_ssl_module.h
+++ b/src/mail/ngx_mail_ssl_module.h
@@ -35,8 +35,9 @@ typedef struct {
 
     time_t           session_timeout;
 
-    ngx_str_t        certificate;
-    ngx_str_t        certificate_key;
+    ngx_array_t     *certificates;
+    ngx_array_t     *certificate_keys;
+
     ngx_str_t        dhparam;
     ngx_str_t        ecdh_curve;
     ngx_str_t        client_certificate;
diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c
--- a/src/stream/ngx_stream_ssl_module.c
+++ b/src/stream/ngx_stream_ssl_module.c
@@ -45,16 +45,16 @@ static ngx_command_t  ngx_stream_ssl_com
 
     { ngx_string("ssl_certificate"),
       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_str_slot,
+      ngx_conf_set_str_array_slot,
       NGX_STREAM_SRV_CONF_OFFSET,
-      offsetof(ngx_stream_ssl_conf_t, certificate),
+      offsetof(ngx_stream_ssl_conf_t, certificates),
       NULL },
 
     { ngx_string("ssl_certificate_key"),
       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_str_slot,
+      ngx_conf_set_str_array_slot,
       NGX_STREAM_SRV_CONF_OFFSET,
-      offsetof(ngx_stream_ssl_conf_t, certificate_key),
+      offsetof(ngx_stream_ssl_conf_t, certificate_keys),
       NULL },
 
     { ngx_string("ssl_password_file"),
@@ -175,8 +175,6 @@ ngx_stream_ssl_create_conf(ngx_conf_t *c
      * set by ngx_pcalloc():
      *
      *     scf->protocols = 0;
-     *     scf->certificate = { 0, NULL };
-     *     scf->certificate_key = { 0, NULL };
      *     scf->dhparam = { 0, NULL };
      *     scf->ecdh_curve = { 0, NULL };
      *     scf->ciphers = { 0, NULL };
@@ -184,6 +182,8 @@ ngx_stream_ssl_create_conf(ngx_conf_t *c
      */
 
     scf->handshake_timeout = NGX_CONF_UNSET_MSEC;
+    scf->certificates = NGX_CONF_UNSET_PTR;
+    scf->certificate_keys = NGX_CONF_UNSET_PTR;
     scf->passwords = NGX_CONF_UNSET_PTR;
     scf->prefer_server_ciphers = NGX_CONF_UNSET;
     scf->builtin_session_cache = NGX_CONF_UNSET;
@@ -216,8 +216,9 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf
                          (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1
                           |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));
 
-    ngx_conf_merge_str_value(conf->certificate, prev->certificate, "");
-    ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, "");
+    ngx_conf_merge_ptr_value(conf->certificates, prev->certificates, NULL);
+    ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys,
+                         NULL);
 
     ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);
 
@@ -231,15 +232,18 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf
 
     conf->ssl.log = cf->log;
 
-    if (conf->certificate.len == 0) {
+    if (conf->certificates == NULL) {
         return NGX_CONF_OK;
     }
 
-    if (conf->certificate_key.len == 0) {
+    if (conf->certificate_keys == NULL
+        || conf->certificate_keys->nelts < conf->certificates->nelts)
+    {
         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                       "no \"ssl_certificate_key\" is defined "
                       "for certificate \"%V\"",
-                      &conf->certificate);
+                      ((ngx_str_t *) conf->certificates->elts)
+                      + conf->certificates->nelts - 1);
         return NGX_CONF_ERROR;
     }
 
@@ -255,8 +259,8 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf
     cln->handler = ngx_ssl_cleanup_ctx;
     cln->data = &conf->ssl;
 
-    if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate,
-                            &conf->certificate_key, conf->passwords)
+    if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates,
+                            conf->certificate_keys, conf->passwords)
         != NGX_OK)
     {
         return NGX_CONF_ERROR;
diff --git a/src/stream/ngx_stream_ssl_module.h b/src/stream/ngx_stream_ssl_module.h
--- a/src/stream/ngx_stream_ssl_module.h
+++ b/src/stream/ngx_stream_ssl_module.h
@@ -27,8 +27,9 @@ typedef struct {
 
     time_t           session_timeout;
 
-    ngx_str_t        certificate;
-    ngx_str_t        certificate_key;
+    ngx_array_t     *certificates;
+    ngx_array_t     *certificate_keys;
+
     ngx_str_t        dhparam;
     ngx_str_t        ecdh_curve;
 
# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1463277750 -10800
#      Sun May 15 05:02:30 2016 +0300
# Node ID cb14a92f9fe51051ae4f7b8972288f9f9cd3b816
# Parent  4c8bf7080cf15f2ff754818a48fe223f2ee5812b
SSL: error messages style.

diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -1078,14 +1078,14 @@ ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_s
     nid = OBJ_sn2nid((const char *) name->data);
     if (nid == 0) {
         ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                      "Unknown curve name \"%s\"", name->data);
+                      "OBJ_sn2nid(\"%s\") failed: unknown curve", name->data);
         return NGX_ERROR;
     }
 
     ecdh = EC_KEY_new_by_curve_name(nid);
     if (ecdh == NULL) {
         ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                      "Unable to create curve \"%s\"", name->data);
+                      "EC_KEY_new_by_curve_name(\"%s\") failed", name->data);
         return NGX_ERROR;
     }
 
# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1463277750 -10800
#      Sun May 15 05:02:30 2016 +0300
# Node ID ffc722edbba5bdb041b4e9a83f5b3576cb470a60
# Parent  cb14a92f9fe51051ae4f7b8972288f9f9cd3b816
SSL: style.

diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -1071,11 +1071,11 @@ ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_s
     /*
      * Elliptic-Curve Diffie-Hellman parameters are either "named curves"
      * from RFC 4492 section 5.1.1, or explicitly described curves over
-     * binary fields. OpenSSL only supports the "named curves", which provide
+     * binary fields.  OpenSSL only supports the "named curves", which provide
      * maximum interoperability.
      */
 
-    nid = OBJ_sn2nid((const char *) name->data);
+    nid = OBJ_sn2nid((char *) name->data);
     if (nid == 0) {
         ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
                       "OBJ_sn2nid(\"%s\") failed: unknown curve", name->data);
@@ -2167,7 +2167,7 @@ ngx_ssl_session_cache(ngx_ssl_t *ssl, ng
          * session reuse (see SSL_SESS_CACHE_OFF above), then
          * Outlook Express fails to upload a sent email to
          * the Sent Items folder on the IMAP server via a separate IMAP
-         * connection in the background. Therefore we have a special
+         * connection in the background.  Therefore we have a special
          * mode (SSL_SESS_CACHE_SERVER|SSL_SESS_CACHE_NO_INTERNAL_STORE)
          * where the server pretends that it supports session reuse,
          * but it does not actually store any session.
@@ -3594,7 +3594,7 @@ ngx_openssl_engine(ngx_conf_t *cf, ngx_c
 
     value = cf->args->elts;
 
-    engine = ENGINE_by_id((const char *) value[1].data);
+    engine = ENGINE_by_id((char *) value[1].data);
 
     if (engine == NULL) {
         ngx_ssl_error(NGX_LOG_WARN, cf->log, 0,
# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1463277750 -10800
#      Sun May 15 05:02:30 2016 +0300
# Node ID b2690d83792594990aa8ec0cda5010e2d2e743e0
# Parent  ffc722edbba5bdb041b4e9a83f5b3576cb470a60
SSL: support for multiple curves (ticket #885).

OpenSSL 1.0.2+ allows configuring a curve list instead of a single curve
previously supported.  This allows use of different curves depending on
what client support (as available via the elliptic_curves extension),
and also allows use of different curves in an ECDHE key exchange and
in the ECDSA certificate.

The special value "auto" was introduced (now the default for ssl_ecdh_curve),
which means "use an internal list of curves as available in the OpenSSL
library used".  For versions prior to OpenSSL 1.0.2 it maps to "prime256v1"
as previously used.  The default in 1.0.2b+ prefers prime256v1 as well
(and X25519 in OpenSSL 1.1.0+).

As client vs. server preference of curves is controlled by the
same option as used for ciphers (SSL_OP_CIPHER_SERVER_PREFERENCE),
the ssl_prefer_server_ciphers directive now controls both.

diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -1065,8 +1065,6 @@ ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_s
 {
 #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
 #ifndef OPENSSL_NO_ECDH
-    int      nid;
-    EC_KEY  *ecdh;
 
     /*
      * Elliptic-Curve Diffie-Hellman parameters are either "named curves"
@@ -1075,17 +1073,61 @@ ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_s
      * maximum interoperability.
      */
 
-    nid = OBJ_sn2nid((char *) name->data);
+#ifdef SSL_CTRL_SET_CURVES_LIST
+
+    /*
+     * OpenSSL 1.0.2+ allows configuring a curve list instead of a single
+     * curve previously supported.  By default an internal list is used,
+     * with prime256v1 being preferred by server in OpenSSL 1.0.2b+
+     * and X25519 in OpenSSL 1.1.0+.
+     *
+     * By default a curve preferred by the client will be used for
+     * key exchange.  The SSL_OP_CIPHER_SERVER_PREFERENCE option can
+     * be used to prefer server curves instead, similar to what it
+     * does for ciphers.
+     */
+
+    SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_ECDH_USE);
+
+#if SSL_CTRL_SET_ECDH_AUTO
+    /* not needed in OpenSSL 1.1.0+ */
+    SSL_CTX_set_ecdh_auto(ssl->ctx, 1);
+#endif
+
+    if (ngx_strcmp(name->data, "auto") == 0) {
+        return NGX_OK;
+    }
+
+    if (SSL_CTX_set1_curves_list(ssl->ctx, (char *) name->data) == 0) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "SSL_CTX_set1_curves_list(\"%s\") failed", name->data);
+        return NGX_ERROR;
+    }
+
+#else
+
+    int      nid;
+    char    *curve;
+    EC_KEY  *ecdh;
+
+    if (ngx_strcmp(name->data, "auto") == 0) {
+        curve = "prime256v1";
+
+    } else {
+        curve = (char *) name->data;
+    }
+
+    nid = OBJ_sn2nid(curve);
     if (nid == 0) {
         ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                      "OBJ_sn2nid(\"%s\") failed: unknown curve", name->data);
+                      "OBJ_sn2nid(\"%s\") failed: unknown curve", curve);
         return NGX_ERROR;
     }
 
     ecdh = EC_KEY_new_by_curve_name(nid);
     if (ecdh == NULL) {
         ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                      "EC_KEY_new_by_curve_name(\"%s\") failed", name->data);
+                      "EC_KEY_new_by_curve_name(\"%s\") failed", curve);
         return NGX_ERROR;
     }
 
@@ -1096,6 +1138,7 @@ ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_s
     EC_KEY_free(ecdh);
 #endif
 #endif
+#endif
 
     return NGX_OK;
 }
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -15,7 +15,7 @@ typedef ngx_int_t (*ngx_ssl_variable_han
 
 
 #define NGX_DEFAULT_CIPHERS     "HIGH:!aNULL:!MD5"
-#define NGX_DEFAULT_ECDH_CURVE  "prime256v1"
+#define NGX_DEFAULT_ECDH_CURVE  "auto"
 
 #define NGX_HTTP_NPN_ADVERTISE  "\x08http/1.1"
 
diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c
--- a/src/mail/ngx_mail_ssl_module.c
+++ b/src/mail/ngx_mail_ssl_module.c
@@ -11,7 +11,7 @@
 
 
 #define NGX_DEFAULT_CIPHERS     "HIGH:!aNULL:!MD5"
-#define NGX_DEFAULT_ECDH_CURVE  "prime256v1"
+#define NGX_DEFAULT_ECDH_CURVE  "auto"
 
 
 static void *ngx_mail_ssl_create_conf(ngx_conf_t *cf);
diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c
--- a/src/stream/ngx_stream_ssl_module.c
+++ b/src/stream/ngx_stream_ssl_module.c
@@ -11,7 +11,7 @@
 
 
 #define NGX_DEFAULT_CIPHERS     "HIGH:!aNULL:!MD5"
-#define NGX_DEFAULT_ECDH_CURVE  "prime256v1"
+#define NGX_DEFAULT_ECDH_CURVE  "auto"
 
 
 static void *ngx_stream_ssl_create_conf(ngx_conf_t *cf);
# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1463277750 -10800
#      Sun May 15 05:02:30 2016 +0300
# Node ID 8da0f1ffbe318142031ae0499e1a7630a7a42f06
# Parent  b2690d83792594990aa8ec0cda5010e2d2e743e0
SSL: removed default DH parameters.

Using the same DH parameters on multiple servers is believed to be subject
to precomputation attacks, see http://weakdh.org/.  Additionally, 1024 bits
are not enough in modern world as well.  Let users to provide their own
DH parameters with the ssl_dhparam directive if they want to use EDH ciphers.

Note that SSL_CTX_set_dh_auto() as provided by OpenSSL 1.1.0 uses fixed
DH parameters from RFC 5114 and RFC 3526, and therefore subject to the same
precomputation attacks.  We avoid using it as well.

This change also fixes compilation with OpenSSL 1.1.0-pre5 (aka Beta 2),
as OpenSSL developers changed their policy after releasing Beta 1 and
broke API once again by making the DH struct opaque (see ticket #860).

diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -983,52 +983,7 @@ ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_
     DH   *dh;
     BIO  *bio;
 
-    /*
-     * -----BEGIN DH PARAMETERS-----
-     * MIGHAoGBALu8LcrYRnSQfEP89YDpz9vZWKP1aLQtSwju1OsPs1BMbAMCducQgAxc
-     * y7qokiYUxb7spWWl/fHSh6K8BJvmd4Bg6RqSp1fjBI9osHb302zI8pul34HcLKcl
-     * 7OZicMyaUDXYzs7vnqAnSmOrHlj6/UmI0PZdFGdX2gcd8EXP4WubAgEC
-     * -----END DH PARAMETERS-----
-     */
-
-    static unsigned char dh1024_p[] = {
-        0xBB, 0xBC, 0x2D, 0xCA, 0xD8, 0x46, 0x74, 0x90, 0x7C, 0x43, 0xFC, 0xF5,
-        0x80, 0xE9, 0xCF, 0xDB, 0xD9, 0x58, 0xA3, 0xF5, 0x68, 0xB4, 0x2D, 0x4B,
-        0x08, 0xEE, 0xD4, 0xEB, 0x0F, 0xB3, 0x50, 0x4C, 0x6C, 0x03, 0x02, 0x76,
-        0xE7, 0x10, 0x80, 0x0C, 0x5C, 0xCB, 0xBA, 0xA8, 0x92, 0x26, 0x14, 0xC5,
-        0xBE, 0xEC, 0xA5, 0x65, 0xA5, 0xFD, 0xF1, 0xD2, 0x87, 0xA2, 0xBC, 0x04,
-        0x9B, 0xE6, 0x77, 0x80, 0x60, 0xE9, 0x1A, 0x92, 0xA7, 0x57, 0xE3, 0x04,
-        0x8F, 0x68, 0xB0, 0x76, 0xF7, 0xD3, 0x6C, 0xC8, 0xF2, 0x9B, 0xA5, 0xDF,
-        0x81, 0xDC, 0x2C, 0xA7, 0x25, 0xEC, 0xE6, 0x62, 0x70, 0xCC, 0x9A, 0x50,
-        0x35, 0xD8, 0xCE, 0xCE, 0xEF, 0x9E, 0xA0, 0x27, 0x4A, 0x63, 0xAB, 0x1E,
-        0x58, 0xFA, 0xFD, 0x49, 0x88, 0xD0, 0xF6, 0x5D, 0x14, 0x67, 0x57, 0xDA,
-        0x07, 0x1D, 0xF0, 0x45, 0xCF, 0xE1, 0x6B, 0x9B
-    };
-
-    static unsigned char dh1024_g[] = { 0x02 };
-
-
     if (file->len == 0) {
-
-        dh = DH_new();
-        if (dh == NULL) {
-            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "DH_new() failed");
-            return NGX_ERROR;
-        }
-
-        dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL);
-        dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL);
-
-        if (dh->p == NULL || dh->g == NULL) {
-            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "BN_bin2bn() failed");
-            DH_free(dh);
-            return NGX_ERROR;
-        }
-
-        SSL_CTX_set_tmp_dh(ssl->ctx, dh);
-
-        DH_free(dh);
-
         return NGX_OK;
     }
 


More information about the nginx-devel mailing list