# HG changeset patch # User Maxim Dounin # Date 1551102125 -10800 # Node ID be2af41d3620a662e82277e93e240b7df96bbfab # Parent a68799465b192d8c9bdaa248d496336fa478bda7 SSL: variables support in ssl_certificate and ssl_certificate_key. To evaluate variables, a request is created in the certificate callback, and then freed. To do this without side effects on the stub_status counters and connection state, an additional function was introduced, ngx_http_alloc_request(). Only works with OpenSSL 1.0.2+, since there is no SSL_CTX_set_cert_cb() in older versions. 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 @@ -41,6 +41,9 @@ static void *ngx_http_ssl_create_srv_con static char *ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child); +static ngx_int_t ngx_http_ssl_compile_certificates(ngx_conf_t *cf, + ngx_http_ssl_srv_conf_t *conf); + static char *ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, @@ -550,6 +553,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t * set by ngx_pcalloc(): * * sscf->protocols = 0; + * sscf->certificate_values = NULL; * sscf->dhparam = { 0, NULL }; * sscf->ecdh_curve = { 0, NULL }; * sscf->client_certificate = { 0, NULL }; @@ -727,13 +731,38 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t * cln->handler = ngx_ssl_cleanup_ctx; cln->data = &conf->ssl; - if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates, - conf->certificate_keys, conf->passwords) - != NGX_OK) - { + if (ngx_http_ssl_compile_certificates(cf, conf) != NGX_OK) { return NGX_CONF_ERROR; } + if (conf->certificate_values) { + +#ifdef SSL_R_CERT_CB_ERROR + + /* install callback to lookup certificates */ + + SSL_CTX_set_cert_cb(conf->ssl.ctx, ngx_http_ssl_certificate, NULL); + +#else + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "variables in " + "\"ssl_certificate\" and \"ssl_certificate_key\" " + "directives are not supported on this platform"); + return NGX_CONF_ERROR; +#endif + + } else { + + /* configure certificates */ + + if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates, + conf->certificate_keys, conf->passwords) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } + if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers, conf->prefer_server_ciphers) != NGX_OK) @@ -831,6 +860,85 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t * } +static ngx_int_t +ngx_http_ssl_compile_certificates(ngx_conf_t *cf, + ngx_http_ssl_srv_conf_t *conf) +{ + ngx_str_t *cert, *key; + ngx_uint_t i, nelts; + ngx_http_complex_value_t *cv; + ngx_http_compile_complex_value_t ccv; + + cert = conf->certificates->elts; + key = conf->certificate_keys->elts; + nelts = conf->certificates->nelts; + + for (i = 0; i < nelts; i++) { + + if (ngx_http_script_variables_count(&cert[i])) { + goto found; + } + + if (ngx_http_script_variables_count(&key[i])) { + goto found; + } + } + + return NGX_OK; + +found: + + conf->certificate_values = ngx_array_create(cf->pool, nelts, + sizeof(ngx_http_complex_value_t)); + if (conf->certificate_values == NULL) { + return NGX_ERROR; + } + + conf->certificate_key_values = ngx_array_create(cf->pool, nelts, + sizeof(ngx_http_complex_value_t)); + if (conf->certificate_key_values == NULL) { + return NGX_ERROR; + } + + for (i = 0; i < nelts; i++) { + + cv = ngx_array_push(conf->certificate_values); + if (cv == NULL) { + return NGX_ERROR; + } + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &cert[i]; + ccv.complex_value = cv; + ccv.zero = 1; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_ERROR; + } + + cv = ngx_array_push(conf->certificate_key_values); + if (cv == NULL) { + return NGX_ERROR; + } + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &key[i]; + ccv.complex_value = cv; + ccv.zero = 1; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_ERROR; + } + } + + return NGX_OK; +} + + static char * ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { 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 @@ -36,6 +36,9 @@ typedef struct { ngx_array_t *certificates; ngx_array_t *certificate_keys; + ngx_array_t *certificate_values; + ngx_array_t *certificate_key_values; + ngx_str_t dhparam; ngx_str_t ecdh_curve; ngx_str_t client_certificate; diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -88,6 +88,10 @@ void ngx_http_close_connection(ngx_conne #if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME) int ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg); #endif +#if (NGX_HTTP_SSL && defined SSL_R_CERT_CB_ERROR) +int ngx_http_ssl_certificate(ngx_ssl_conn_t *ssl_conn, void *arg); +#endif + ngx_int_t ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b); ngx_int_t ngx_http_parse_uri(ngx_http_request_t *r); diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -11,6 +11,7 @@ static void ngx_http_wait_request_handler(ngx_event_t *ev); +static ngx_http_request_t *ngx_http_alloc_request(ngx_connection_t *c); static void ngx_http_process_request_line(ngx_event_t *rev); static void ngx_http_process_request_headers(ngx_event_t *rev); static ssize_t ngx_http_read_request_header(ngx_http_request_t *r); @@ -503,17 +504,45 @@ ngx_http_wait_request_handler(ngx_event_ ngx_http_request_t * ngx_http_create_request(ngx_connection_t *c) { + ngx_http_request_t *r; + ngx_http_log_ctx_t *ctx; + ngx_http_core_loc_conf_t *clcf; + + r = ngx_http_alloc_request(c); + if (r == NULL) { + return NULL; + } + + c->requests++; + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + ngx_set_connection_log(c, clcf->error_log); + + ctx = c->log->data; + ctx->request = r; + ctx->current_request = r; + +#if (NGX_STAT_STUB) + (void) ngx_atomic_fetch_add(ngx_stat_reading, 1); + r->stat_reading = 1; + (void) ngx_atomic_fetch_add(ngx_stat_requests, 1); +#endif + + return r; +} + + +static ngx_http_request_t * +ngx_http_alloc_request(ngx_connection_t *c) +{ ngx_pool_t *pool; ngx_time_t *tp; ngx_http_request_t *r; - ngx_http_log_ctx_t *ctx; ngx_http_connection_t *hc; ngx_http_core_srv_conf_t *cscf; - ngx_http_core_loc_conf_t *clcf; ngx_http_core_main_conf_t *cmcf; - c->requests++; - hc = c->data; cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); @@ -541,10 +570,6 @@ ngx_http_create_request(ngx_connection_t r->read_event_handler = ngx_http_block_reading; - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - ngx_set_connection_log(r->connection, clcf->error_log); - r->header_in = hc->busy ? hc->busy->buf : c->buffer; if (ngx_list_init(&r->headers_out.headers, r->pool, 20, @@ -604,17 +629,8 @@ ngx_http_create_request(ngx_connection_t r->http_state = NGX_HTTP_READING_REQUEST_STATE; - ctx = c->log->data; - ctx->request = r; - ctx->current_request = r; r->log_handler = ngx_http_log_error_handler; -#if (NGX_STAT_STUB) - (void) ngx_atomic_fetch_add(ngx_stat_reading, 1); - r->stat_reading = 1; - (void) ngx_atomic_fetch_add(ngx_stat_requests, 1); -#endif - return r; } @@ -931,6 +947,74 @@ ngx_http_ssl_servername(ngx_ssl_conn_t * #endif + +#ifdef SSL_R_CERT_CB_ERROR + +int +ngx_http_ssl_certificate(ngx_ssl_conn_t *ssl_conn, void *arg) +{ + ngx_str_t cert, key; + ngx_uint_t i, nelts; + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_ssl_srv_conf_t *sscf; + ngx_http_complex_value_t *certs, *keys; + + c = ngx_ssl_get_connection(ssl_conn); + + if (c->ssl->handshaked) { + return 0; + } + + r = ngx_http_alloc_request(c); + if (r == NULL) { + return 0; + } + + r->logged = 1; + + sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); + + nelts = sscf->certificate_values->nelts; + certs = sscf->certificate_values->elts; + keys = sscf->certificate_key_values->elts; + + for (i = 0; i < nelts; i++) { + + if (ngx_http_complex_value(r, &certs[i], &cert) != NGX_OK) { + goto failed; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ssl cert: \"%s\"", cert.data); + + if (ngx_http_complex_value(r, &keys[i], &key) != NGX_OK) { + goto failed; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ssl key: \"%s\"", key.data); + + if (ngx_ssl_connection_certificate(c, r->pool, &cert, &key, NULL) + != NGX_OK) + { + goto failed; + } + } + + ngx_http_free_request(r, 0); + c->destroyed = 0; + return 1; + +failed: + + ngx_http_free_request(r, 0); + c->destroyed = 0; + return 0; +} + +#endif + #endif @@ -3514,9 +3598,11 @@ ngx_http_free_request(ngx_http_request_t r->headers_out.status = rc; } - log->action = "logging request"; - - ngx_http_log_request(r); + if (!r->logged) { + log->action = "logging request"; + + ngx_http_log_request(r); + } log->action = "closing request";