# HG changeset patch # User Maxim Dounin # Date 1463658392 -10800 # Node ID 8a34e92d8ab52480e439c8778fc507b5df1570c3 # Parent e222a97d46c152682aa88898858f3a4b829e0b12 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 to iterate though all certificates configured in the SSL context. This is now used to configure OCSP stapling for all certificates, and in ngx_ssl_session_id_context(). As SSL_CTX_use_certificate() frees previously loaded certificate of the same type, and we have no way to find out if it's the case, X509_free() calls are now posponed till ngx_ssl_cleanup_ctx(). 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) { @@ -360,8 +384,6 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ return NGX_ERROR; } - X509_free(x509); - /* read rest of the chain */ for ( ;; ) { @@ -2163,7 +2185,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 +2205,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); @@ -2950,6 +2975,16 @@ ngx_ssl_cleanup_ctx(void *data) { ngx_ssl_t *ssl = data; + X509 *cert, *next; + + cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index); + + while (cert) { + next = X509_get_ex_data(cert, ngx_ssl_next_certificate_index); + X509_free(cert); + cert = next; + } + SSL_CTX_free(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; }