# HG changeset patch # User Sergey Kandaurov <pluk...@nginx.com> # Date 1721762810 0 # Tue Jul 23 19:26:50 2024 +0000 # Node ID 6baaa6efe6f0a2e8b95374717cd5f73db8a3a862 # Parent 8796dfbe7177cb0be2a53bcdb4d25cc64a58d2a7 SSL: moved certificate storage out of exdata.
Instead of cross-linking the objects using exdata, pointers to configured certificates are now stored in ngx_ssl_t, and certificate names and OCSP staples are now accessed with ngx_rbtree_t. This allows sharing these objects between SSL contexts. Based on previous work by Mini Hawthorne. 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 @@ -131,10 +131,7 @@ int ngx_ssl_server_conf_index; int ngx_ssl_session_cache_index; int ngx_ssl_ticket_keys_index; int ngx_ssl_ocsp_index; -int ngx_ssl_certificate_index; -int ngx_ssl_next_certificate_index; -int ngx_ssl_certificate_name_index; -int ngx_ssl_stapling_index; +int ngx_ssl_index; ngx_int_t @@ -258,36 +255,13 @@ ngx_ssl_init(ngx_log_t *log) return NGX_ERROR; } - ngx_ssl_certificate_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, - NULL); - if (ngx_ssl_certificate_index == -1) { + ngx_ssl_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); + if (ngx_ssl_index == -1) { ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_CTX_get_ex_new_index() failed"); 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_certificate_name_index = X509_get_ex_new_index(0, NULL, NULL, NULL, - NULL); - - if (ngx_ssl_certificate_name_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) { - ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed"); - return NGX_ERROR; - } - return NGX_OK; } @@ -308,12 +282,18 @@ 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) { + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_index, ssl) == 0) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "SSL_CTX_set_ex_data() failed"); return NGX_ERROR; } + ngx_rbtree_init(&ssl->name_rbtree, &ssl->name_sentinel, + ngx_rbtree_insert_value); + + ngx_rbtree_init(&ssl->staple_rbtree, &ssl->staple_sentinel, + ngx_rbtree_insert_value); + ssl->buffer_size = NGX_SSL_BUFSIZE; /* client side options */ @@ -458,9 +438,10 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ ngx_str_t *key, ngx_array_t *passwords) { char *err; - X509 *x509; + X509 *x509, **elm; EVP_PKEY *pkey; STACK_OF(X509) *chain; + ngx_ssl_name_t *name; x509 = ngx_ssl_load_certificate(cf->pool, &err, cert, &chain); if (x509 == NULL) { @@ -481,38 +462,42 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ return NGX_ERROR; } - if (X509_set_ex_data(x509, ngx_ssl_certificate_name_index, cert->data) - == 0) - { - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed"); + name = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_name_t)); + if (name == NULL) { X509_free(x509); sk_X509_pop_free(chain, X509_free); 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"); + name->node.key = (ngx_rbtree_key_t) x509; + name->name.len = cert->len; + name->name.data = cert->data; + + ngx_rbtree_insert(&ssl->name_rbtree, &name->node); + + if (ssl->certs.elts == NULL) { + if (ngx_array_init(&ssl->certs, cf->pool, 1, sizeof(X509 *)) + != NGX_OK) + { + X509_free(x509); + sk_X509_pop_free(chain, X509_free); + return NGX_ERROR; + } + } + + elm = ngx_array_push(&ssl->certs); + if (elm == NULL) { X509_free(x509); sk_X509_pop_free(chain, X509_free); return NGX_ERROR; } - if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509) == 0) { - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, - "SSL_CTX_set_ex_data() failed"); - X509_free(x509); - sk_X509_pop_free(chain, X509_free); - return NGX_ERROR; - } + *elm = x509; /* * Note that x509 is not freed here, but will be instead freed in * ngx_ssl_cleanup_ctx(). This is because we need to preserve all - * certificates to be able to iterate all of them through exdata - * (ngx_ssl_certificate_index, ngx_ssl_next_certificate_index), + * certificates to be able to iterate all of them through ssl->certs, * while OpenSSL can free a certificate if it is replaced with another * certificate of the same type. */ @@ -3820,10 +3805,9 @@ ngx_ssl_session_id_context(ngx_ssl_t *ss 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)) - { + for (k = 0; k < ssl->certs.nelts; k++) { + cert = ((X509 **) ssl->certs.elts)[k]; + if (X509_digest(cert, EVP_sha1(), buf, &len) == 0) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_digest() failed"); @@ -3837,9 +3821,7 @@ ngx_ssl_session_id_context(ngx_ssl_t *ss } } - if (SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index) == NULL - && certificates != NULL) - { + if (ssl->certs.nelts == 0 && certificates != NULL) { /* * If certificates are loaded dynamically, we use certificate * names as specified in the configuration (with variables). @@ -4851,14 +4833,12 @@ 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 *cert; + ngx_uint_t i; + + for (i = 0; i < ssl->certs.nelts; i++) { + cert = ((X509 **) ssl->certs.elts)[i]; 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 @@ -86,10 +86,24 @@ typedef struct ngx_ssl_ocsp_s ngx_ssl_ocsp_t; +typedef struct { + ngx_rbtree_node_t node; + ngx_str_t name; +} ngx_ssl_name_t; + + struct ngx_ssl_s { SSL_CTX *ctx; ngx_log_t *log; size_t buffer_size; + + ngx_array_t certs; + + ngx_rbtree_t name_rbtree; + ngx_rbtree_node_t name_sentinel; + + ngx_rbtree_t staple_rbtree; + ngx_rbtree_node_t staple_sentinel; }; @@ -330,10 +344,7 @@ extern int ngx_ssl_server_conf_index; extern int ngx_ssl_session_cache_index; extern int ngx_ssl_ticket_keys_index; extern int ngx_ssl_ocsp_index; -extern int ngx_ssl_certificate_index; -extern int ngx_ssl_next_certificate_index; -extern int ngx_ssl_certificate_name_index; -extern int ngx_ssl_stapling_index; +extern int ngx_ssl_index; #endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */ 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 @@ -15,6 +15,8 @@ typedef struct { + ngx_rbtree_node_t node; + ngx_str_t staple; ngx_msec_t timeout; @@ -157,6 +159,11 @@ static int ngx_ssl_certificate_status_ca static void ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple); static void ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx); +static ngx_ssl_name_t *ngx_ssl_stapling_lookup_name(ngx_ssl_t *ssl, + X509 *cert); +static ngx_ssl_stapling_t *ngx_ssl_stapling_lookup_staple(ngx_ssl_t *ssl, + X509 *cert); + static time_t ngx_ssl_stapling_time(ASN1_GENERALIZEDTIME *asn1time); static void ngx_ssl_stapling_cleanup(void *data); @@ -195,12 +202,12 @@ 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; - - 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)) - { + X509 *cert; + ngx_uint_t k; + + for (k = 0; k < ssl->certs.nelts; k++) { + cert = ((X509 **) ssl->certs.elts)[k]; + if (ngx_ssl_stapling_certificate(cf, ssl, cert, file, responder, verify) != NGX_OK) { @@ -219,6 +226,7 @@ ngx_ssl_stapling_certificate(ngx_conf_t ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify) { ngx_int_t rc; + ngx_ssl_name_t *name; ngx_pool_cleanup_t *cln; ngx_ssl_stapling_t *staple; @@ -235,10 +243,9 @@ ngx_ssl_stapling_certificate(ngx_conf_t cln->handler = ngx_ssl_stapling_cleanup; cln->data = staple; - 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->node.key = (ngx_rbtree_key_t) cert; + + ngx_rbtree_insert(&ssl->staple_rbtree, &staple->node); #ifdef SSL_CTRL_SELECT_CURRENT_CERT /* OpenSSL 1.0.2+ */ @@ -256,8 +263,9 @@ ngx_ssl_stapling_certificate(ngx_conf_t staple->timeout = 60000; staple->verify = verify; staple->cert = cert; - staple->name = X509_get_ex_data(staple->cert, - ngx_ssl_certificate_name_index); + + name = ngx_ssl_stapling_lookup_name(ssl, cert); + staple->name = name->name.data; if (file->len) { /* use OCSP response from the file */ @@ -545,14 +553,21 @@ 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_rbtree_t *tree; + ngx_rbtree_node_t *node; ngx_ssl_stapling_t *staple; - 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)) + tree = &ssl->staple_rbtree; + + if (tree->root == tree->sentinel) { + return NGX_OK; + } + + for (node = ngx_rbtree_min(tree->root, tree->sentinel); + node; + node = ngx_rbtree_next(tree, node)) { - staple = X509_get_ex_data(cert, ngx_ssl_stapling_index); + staple = (ngx_ssl_stapling_t *) node; staple->resolver = resolver; staple->resolver_timeout = resolver_timeout; } @@ -567,6 +582,8 @@ ngx_ssl_certificate_status_callback(ngx_ int rc; X509 *cert; u_char *p; + SSL_CTX *ssl_ctx; + ngx_ssl_t *ssl; ngx_connection_t *c; ngx_ssl_stapling_t *staple; @@ -583,7 +600,10 @@ ngx_ssl_certificate_status_callback(ngx_ return rc; } - staple = X509_get_ex_data(cert, ngx_ssl_stapling_index); + ssl_ctx = SSL_get_SSL_CTX(ssl_conn); + ssl = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_index); + + staple = ngx_ssl_stapling_lookup_staple(ssl, cert); if (staple == NULL) { return rc; @@ -716,6 +736,54 @@ error: } +static ngx_ssl_name_t * +ngx_ssl_stapling_lookup_name(ngx_ssl_t *ssl, X509 *cert) +{ + ngx_rbtree_key_t key; + ngx_rbtree_node_t *node, *sentinel; + + node = ssl->name_rbtree.root; + sentinel = ssl->name_rbtree.sentinel; + key = (ngx_rbtree_key_t) cert; + + while (node != sentinel) { + + if (key != node->key) { + node = (key < node->key) ? node->left : node->right; + continue; + } + + return ngx_rbtree_data(node, ngx_ssl_name_t, node); + } + + return NULL; +} + + +static ngx_ssl_stapling_t * +ngx_ssl_stapling_lookup_staple(ngx_ssl_t *ssl, X509 *cert) +{ + ngx_rbtree_key_t key; + ngx_rbtree_node_t *node, *sentinel; + + node = ssl->staple_rbtree.root; + sentinel = ssl->staple_rbtree.sentinel; + key = (ngx_rbtree_key_t) cert; + + while (node != sentinel) { + + if (key != node->key) { + node = (key < node->key) ? node->left : node->right; + continue; + } + + return ngx_rbtree_data(node, ngx_ssl_stapling_t, node); + } + + return NULL; +} + + static time_t ngx_ssl_stapling_time(ASN1_GENERALIZEDTIME *asn1time) { _______________________________________________ nginx-devel mailing list nginx-devel@nginx.org https://mailman.nginx.org/mailman/listinfo/nginx-devel