diff options
author | Roman Arutyunyan <arut@nginx.com> | 2020-05-22 17:30:12 +0300 |
---|---|---|
committer | Roman Arutyunyan <arut@nginx.com> | 2020-05-22 17:30:12 +0300 |
commit | 60438ae395d83b0f8b21bf667a1e260d60c3f46a (patch) | |
tree | 040886d686aa1eeb2d290c039b29e608f2c6633e /src | |
parent | aa94ee82f6040c8e2cbde3ae4de931c23fade3f3 (diff) | |
download | nginx-60438ae395d83b0f8b21bf667a1e260d60c3f46a.tar.gz nginx-60438ae395d83b0f8b21bf667a1e260d60c3f46a.zip |
SSL: client certificate validation with OCSP (ticket #1534).
OCSP validation for client certificates is enabled by the "ssl_ocsp" directive.
OCSP responder can be optionally specified by "ssl_ocsp_responder".
When session is reused, peer chain is not available for validation.
If the verified chain contains certificates from the peer chain not available
at the server, validation will fail.
Diffstat (limited to 'src')
-rw-r--r-- | src/event/ngx_event_openssl.c | 58 | ||||
-rw-r--r-- | src/event/ngx_event_openssl.h | 14 | ||||
-rw-r--r-- | src/event/ngx_event_openssl_stapling.c | 550 | ||||
-rw-r--r-- | src/http/modules/ngx_http_ssl_module.c | 64 | ||||
-rw-r--r-- | src/http/modules/ngx_http_ssl_module.h | 3 | ||||
-rw-r--r-- | src/http/ngx_http_request.c | 12 |
6 files changed, 681 insertions, 20 deletions
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 91b415caa..264d4e7a4 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -130,6 +130,7 @@ int ngx_ssl_connection_index; int ngx_ssl_server_conf_index; int ngx_ssl_session_cache_index; int ngx_ssl_session_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; @@ -213,6 +214,13 @@ ngx_ssl_init(ngx_log_t *log) return NGX_ERROR; } + ngx_ssl_ocsp_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); + if (ngx_ssl_ocsp_index == -1) { + ngx_ssl_error(NGX_LOG_ALERT, log, 0, + "SSL_CTX_get_ex_new_index() failed"); + return NGX_ERROR; + } + ngx_ssl_certificate_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); if (ngx_ssl_certificate_index == -1) { @@ -1594,6 +1602,7 @@ ngx_ssl_handshake(ngx_connection_t *c) { int n, sslerr; ngx_err_t err; + ngx_int_t rc; #ifdef SSL_READ_EARLY_DATA_SUCCESS if (c->ssl->try_early_data) { @@ -1601,6 +1610,10 @@ ngx_ssl_handshake(ngx_connection_t *c) } #endif + if (c->ssl->in_ocsp) { + return ngx_ssl_ocsp_validate(c); + } + ngx_ssl_clear_error(c->log); n = SSL_do_handshake(c->ssl->connection); @@ -1621,8 +1634,6 @@ ngx_ssl_handshake(ngx_connection_t *c) ngx_ssl_handshake_log(c); #endif - c->ssl->handshaked = 1; - c->recv = ngx_ssl_recv; c->send = ngx_ssl_write; c->recv_chain = ngx_ssl_recv_chain; @@ -1641,6 +1652,20 @@ ngx_ssl_handshake(ngx_connection_t *c) #endif #endif + rc = ngx_ssl_ocsp_validate(c); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_AGAIN) { + c->read->handler = ngx_ssl_handshake_handler; + c->write->handler = ngx_ssl_handshake_handler; + return NGX_AGAIN; + } + + c->ssl->handshaked = 1; + return NGX_OK; } @@ -1710,6 +1735,7 @@ ngx_ssl_try_early_data(ngx_connection_t *c) u_char buf; size_t readbytes; ngx_err_t err; + ngx_int_t rc; ngx_ssl_clear_error(c->log); @@ -1744,7 +1770,6 @@ ngx_ssl_try_early_data(ngx_connection_t *c) c->ssl->early_buf = buf; c->ssl->early_preread = 1; - c->ssl->handshaked = 1; c->ssl->in_early = 1; c->recv = ngx_ssl_recv; @@ -1752,6 +1777,20 @@ ngx_ssl_try_early_data(ngx_connection_t *c) c->recv_chain = ngx_ssl_recv_chain; c->send_chain = ngx_ssl_send_chain; + rc = ngx_ssl_ocsp_validate(c); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_AGAIN) { + c->read->handler = ngx_ssl_handshake_handler; + c->write->handler = ngx_ssl_handshake_handler; + return NGX_AGAIN; + } + + c->ssl->handshaked = 1; + return NGX_OK; } @@ -2735,6 +2774,8 @@ ngx_ssl_shutdown(ngx_connection_t *c) int n, sslerr, mode; ngx_err_t err; + ngx_ssl_ocsp_cleanup(c); + if (SSL_in_init(c->ssl->connection)) { /* * OpenSSL 1.0.2f complains if SSL_shutdown() is called during @@ -4894,11 +4935,14 @@ ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) rc = SSL_get_verify_result(c->ssl->connection); if (rc == X509_V_OK) { - ngx_str_set(s, "SUCCESS"); - return NGX_OK; - } + if (ngx_ssl_ocsp_get_status(c, &str) == NGX_OK) { + ngx_str_set(s, "SUCCESS"); + return NGX_OK; + } - str = X509_verify_cert_error_string(rc); + } else { + str = X509_verify_cert_error_string(rc); + } s->data = ngx_pnalloc(pool, sizeof("FAILED:") - 1 + ngx_strlen(str)); if (s->data == NULL) { diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index 61da0c5db..2c05b74ba 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -64,6 +64,9 @@ #endif +typedef struct ngx_ssl_ocsp_s ngx_ssl_ocsp_t; + + struct ngx_ssl_s { SSL_CTX *ctx; ngx_log_t *log; @@ -87,6 +90,8 @@ struct ngx_ssl_connection_s { ngx_event_handler_pt saved_read_handler; ngx_event_handler_pt saved_write_handler; + ngx_ssl_ocsp_t *ocsp; + u_char early_buf; unsigned handshaked:1; @@ -97,6 +102,7 @@ struct ngx_ssl_connection_s { unsigned handshake_buffer_set:1; unsigned try_early_data:1; unsigned in_early:1; + unsigned in_ocsp:1; unsigned early_preread:1; unsigned write_blocked:1; }; @@ -180,6 +186,13 @@ 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); ngx_int_t ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_resolver_t *resolver, ngx_msec_t resolver_timeout); +ngx_int_t ngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder, + ngx_uint_t depth); +ngx_int_t ngx_ssl_ocsp_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl, + ngx_resolver_t *resolver, ngx_msec_t resolver_timeout); +ngx_int_t ngx_ssl_ocsp_validate(ngx_connection_t *c); +ngx_int_t ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s); +void ngx_ssl_ocsp_cleanup(ngx_connection_t *c); RSA *ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export, int key_length); ngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file); @@ -281,6 +294,7 @@ extern int ngx_ssl_connection_index; 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_ocsp_index; extern int ngx_ssl_certificate_index; extern int ngx_ssl_next_certificate_index; extern int ngx_ssl_certificate_name_index; diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c index 50ca06321..00b1863c2 100644 --- a/src/event/ngx_event_openssl_stapling.c +++ b/src/event/ngx_event_openssl_stapling.c @@ -43,8 +43,35 @@ typedef struct { } ngx_ssl_stapling_t; +typedef struct { + ngx_addr_t *addrs; + ngx_uint_t naddrs; + + ngx_str_t host; + ngx_str_t uri; + in_port_t port; + ngx_uint_t depth; + + ngx_resolver_t *resolver; + ngx_msec_t resolver_timeout; +} ngx_ssl_ocsp_conf_t; + + typedef struct ngx_ssl_ocsp_ctx_s ngx_ssl_ocsp_ctx_t; + +struct ngx_ssl_ocsp_s { + STACK_OF(X509) *certs; + ngx_uint_t ncert; + + int cert_status; + ngx_int_t status; + + ngx_ssl_ocsp_conf_t *conf; + ngx_ssl_ocsp_ctx_t *ctx; +}; + + struct ngx_ssl_ocsp_ctx_s { SSL_CTX *ssl_ctx; @@ -114,7 +141,12 @@ static time_t ngx_ssl_stapling_time(ASN1_GENERALIZEDTIME *asn1time); static void ngx_ssl_stapling_cleanup(void *data); -static ngx_ssl_ocsp_ctx_t *ngx_ssl_ocsp_start(void); +static void ngx_ssl_ocsp_validate_next(ngx_connection_t *c); +static void ngx_ssl_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx); +static ngx_int_t ngx_ssl_ocsp_responder(ngx_connection_t *c, + ngx_ssl_ocsp_ctx_t *ctx); + +static ngx_ssl_ocsp_ctx_t *ngx_ssl_ocsp_start(ngx_log_t *log); static void ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx); static void ngx_ssl_ocsp_next(ngx_ssl_ocsp_ctx_t *ctx); static void ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx); @@ -570,7 +602,7 @@ ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple) staple->loading = 1; - ctx = ngx_ssl_ocsp_start(); + ctx = ngx_ssl_ocsp_start(ngx_cycle->log); if (ctx == NULL) { return; } @@ -709,14 +741,467 @@ ngx_ssl_stapling_cleanup(void *data) } +ngx_int_t +ngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder, + ngx_uint_t depth) +{ + ngx_url_t u; + ngx_ssl_ocsp_conf_t *ocf; + + ocf = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_ocsp_conf_t)); + if (ocf == NULL) { + return NGX_ERROR; + } + + ocf->depth = depth; + + if (responder->len) { + ngx_memzero(&u, sizeof(ngx_url_t)); + + u.url = *responder; + u.default_port = 80; + u.uri_part = 1; + + if (u.url.len > 7 + && ngx_strncasecmp(u.url.data, (u_char *) "http://", 7) == 0) + { + u.url.len -= 7; + u.url.data += 7; + + } else { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "invalid URL prefix in OCSP responder \"%V\" " + "in \"ssl_ocsp_responder\"", &u.url); + return NGX_ERROR; + } + + if (ngx_parse_url(cf->pool, &u) != NGX_OK) { + if (u.err) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "%s in OCSP responder \"%V\" " + "in \"ssl_ocsp_responder\"", u.err, &u.url); + } + + return NGX_ERROR; + } + + ocf->addrs = u.addrs; + ocf->naddrs = u.naddrs; + ocf->host = u.host; + ocf->uri = u.uri; + ocf->port = u.port; + } + + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_ocsp_index, ocf) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_set_ex_data() failed"); + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_ssl_ocsp_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl, + ngx_resolver_t *resolver, ngx_msec_t resolver_timeout) +{ + ngx_ssl_ocsp_conf_t *ocf; + + ocf = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_ocsp_index); + ocf->resolver = resolver; + ocf->resolver_timeout = resolver_timeout; + + return NGX_OK; +} + + +ngx_int_t +ngx_ssl_ocsp_validate(ngx_connection_t *c) +{ + X509 *cert; + SSL_CTX *ssl_ctx; + ngx_int_t rc; + X509_STORE *store; + X509_STORE_CTX *store_ctx; + STACK_OF(X509) *chain; + ngx_ssl_ocsp_t *ocsp; + ngx_ssl_ocsp_conf_t *ocf; + + if (c->ssl->in_ocsp) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + return NGX_ERROR; + } + + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_AGAIN; + } + + ssl_ctx = SSL_get_SSL_CTX(c->ssl->connection); + + ocf = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_ocsp_index); + if (ocf == NULL) { + return NGX_OK; + } + + if (SSL_get_verify_result(c->ssl->connection) != X509_V_OK) { + return NGX_OK; + } + + cert = SSL_get_peer_certificate(c->ssl->connection); + if (cert == NULL) { + return NGX_OK; + } + + ocsp = ngx_pcalloc(c->pool, sizeof(ngx_ssl_ocsp_t)); + if (ocsp == NULL) { + return NGX_ERROR; + } + + c->ssl->ocsp = ocsp; + + ocsp->status = NGX_AGAIN; + ocsp->cert_status = V_OCSP_CERTSTATUS_GOOD; + ocsp->conf = ocf; + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined LIBRESSL_VERSION_NUMBER) + + ocsp->certs = SSL_get0_verified_chain(c->ssl->connection); + + if (ocsp->certs) { + ocsp->certs = X509_chain_up_ref(ocsp->certs); + if (ocsp->certs == NULL) { + return NGX_ERROR; + } + } + +#endif + + if (ocsp->certs == NULL) { + store = SSL_CTX_get_cert_store(ssl_ctx); + if (store == NULL) { + ngx_ssl_error(NGX_LOG_ERR, c->log, 0, + "SSL_CTX_get_cert_store() failed"); + return NGX_ERROR; + } + + store_ctx = X509_STORE_CTX_new(); + if (store_ctx == NULL) { + ngx_ssl_error(NGX_LOG_ERR, c->log, 0, + "X509_STORE_CTX_new() failed"); + return NGX_ERROR; + } + + chain = SSL_get_peer_cert_chain(c->ssl->connection); + + if (X509_STORE_CTX_init(store_ctx, store, cert, chain) == 0) { + ngx_ssl_error(NGX_LOG_ERR, c->log, 0, + "X509_STORE_CTX_init() failed"); + X509_STORE_CTX_free(store_ctx); + return NGX_ERROR; + } + + rc = X509_verify_cert(store_ctx); + if (rc <= 0) { + ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "X509_verify_cert() failed"); + X509_STORE_CTX_free(store_ctx); + return NGX_ERROR; + } + + ocsp->certs = X509_STORE_CTX_get1_chain(store_ctx); + if (ocsp->certs == NULL) { + ngx_ssl_error(NGX_LOG_ERR, c->log, 0, + "X509_STORE_CTX_get1_chain() failed"); + X509_STORE_CTX_free(store_ctx); + return NGX_ERROR; + } + + X509_STORE_CTX_free(store_ctx); + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "ssl ocsp validate, certs:%i", sk_X509_num(ocsp->certs)); + + ngx_ssl_ocsp_validate_next(c); + + if (ocsp->status == NGX_AGAIN) { + c->ssl->in_ocsp = 1; + return NGX_AGAIN; + } + + return NGX_OK; +} + + +static void +ngx_ssl_ocsp_validate_next(ngx_connection_t *c) +{ + ngx_uint_t n; + ngx_ssl_ocsp_t *ocsp; + ngx_ssl_ocsp_ctx_t *ctx; + ngx_ssl_ocsp_conf_t *ocf; + + ocsp = c->ssl->ocsp; + ocf = ocsp->conf; + + n = sk_X509_num(ocsp->certs); + + for ( ;; ) { + + if (ocsp->ncert == n - 1 || (ocf->depth == 2 && ocsp->ncert == 1)) { + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "ssl ocsp validated, certs:%ui", ocsp->ncert); + goto done; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "ssl ocsp validate cert:%ui", ocsp->ncert); + + ctx = ngx_ssl_ocsp_start(c->log); + if (ctx == NULL) { + goto failed; + } + + ocsp->ctx = ctx; + + ctx->ssl_ctx = SSL_get_SSL_CTX(c->ssl->connection); + ctx->cert = sk_X509_value(ocsp->certs, ocsp->ncert); + ctx->issuer = sk_X509_value(ocsp->certs, ocsp->ncert + 1); + ctx->chain = ocsp->certs; + + ctx->resolver = ocf->resolver; + ctx->resolver_timeout = ocf->resolver_timeout; + + ctx->handler = ngx_ssl_ocsp_handler; + ctx->data = c; + + ctx->addrs = ocf->addrs; + ctx->naddrs = ocf->naddrs; + ctx->host = ocf->host; + ctx->uri = ocf->uri; + ctx->port = ocf->port; + + if (ngx_ssl_ocsp_responder(c, ctx) != NGX_OK) { + goto failed; + } + + if (ctx->uri.len == 0) { + ngx_str_set(&ctx->uri, "/"); + } + + ocsp->ncert++; + + break; + } + + ngx_ssl_ocsp_request(ctx); + return; + +done: + + ocsp->status = NGX_OK; + return; + +failed: + + ocsp->status = NGX_ERROR; +} + + +static void +ngx_ssl_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx) +{ + ngx_int_t rc; + ngx_ssl_ocsp_t *ocsp; + ngx_connection_t *c; + + c = ctx->data; + ocsp = c->ssl->ocsp; + ocsp->ctx = NULL; + + rc = ngx_ssl_ocsp_verify(ctx); + if (rc != NGX_OK) { + ocsp->status = rc; + ngx_ssl_ocsp_done(ctx); + goto done; + } + + if (ctx->status != V_OCSP_CERTSTATUS_GOOD) { + ocsp->cert_status = ctx->status; + ocsp->status = NGX_OK; + ngx_ssl_ocsp_done(ctx); + goto done; + } + + ngx_ssl_ocsp_done(ctx); + + ngx_ssl_ocsp_validate_next(c); + +done: + + if (ocsp->status == NGX_AGAIN || !c->ssl->in_ocsp) { + return; + } + + c->ssl->handshaked = 1; + + c->ssl->handler(c); +} + + +static ngx_int_t +ngx_ssl_ocsp_responder(ngx_connection_t *c, ngx_ssl_ocsp_ctx_t *ctx) +{ + char *s; + ngx_str_t responder; + ngx_url_t u; + STACK_OF(OPENSSL_STRING) *aia; + + if (ctx->host.len) { + return NGX_OK; + } + + /* extract OCSP responder URL from certificate */ + + aia = X509_get1_ocsp(ctx->cert); + if (aia == NULL) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "no OCSP responder URL in certificate"); + return NGX_ERROR; + } + +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + s = sk_OPENSSL_STRING_value(aia, 0); +#else + s = sk_value(aia, 0); +#endif + if (s == NULL) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "no OCSP responder URL in certificate"); + X509_email_free(aia); + return NGX_ERROR; + } + + responder.len = ngx_strlen(s); + responder.data = ngx_palloc(ctx->pool, responder.len); + if (responder.data == NULL) { + X509_email_free(aia); + return NGX_ERROR; + } + + ngx_memcpy(responder.data, s, responder.len); + X509_email_free(aia); + + ngx_memzero(&u, sizeof(ngx_url_t)); + + u.url = responder; + u.default_port = 80; + u.uri_part = 1; + u.no_resolve = 1; + + if (u.url.len > 7 + && ngx_strncasecmp(u.url.data, (u_char *) "http://", 7) == 0) + { + u.url.len -= 7; + u.url.data += 7; + + } else { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "invalid URL prefix in OCSP responder \"%V\" " + "in certificate", &u.url); + return NGX_ERROR; + } + + if (ngx_parse_url(ctx->pool, &u) != NGX_OK) { + if (u.err) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "%s in OCSP responder \"%V\" in certificate", + u.err, &u.url); + } + + return NGX_ERROR; + } + + if (u.host.len == 0) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "empty host in OCSP responder in certificate"); + return NGX_ERROR; + } + + ctx->addrs = u.addrs; + ctx->naddrs = u.naddrs; + ctx->host = u.host; + ctx->uri = u.uri; + ctx->port = u.port; + + return NGX_OK; +} + + +ngx_int_t +ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s) +{ + ngx_ssl_ocsp_t *ocsp; + + ocsp = c->ssl->ocsp; + if (ocsp == NULL) { + return NGX_OK; + } + + if (ocsp->status == NGX_ERROR) { + *s = "certificate status request failed"; + return NGX_DECLINED; + } + + switch (ocsp->cert_status) { + + case V_OCSP_CERTSTATUS_GOOD: + return NGX_OK; + + case V_OCSP_CERTSTATUS_REVOKED: + *s = "certificate revoked"; + break; + + default: /* V_OCSP_CERTSTATUS_UNKNOWN */ + *s = "certificate status unknown"; + } + + return NGX_DECLINED; +} + + +void +ngx_ssl_ocsp_cleanup(ngx_connection_t *c) +{ + ngx_ssl_ocsp_t *ocsp; + + ocsp = c->ssl->ocsp; + if (ocsp == NULL) { + return; + } + + if (ocsp->ctx) { + ngx_ssl_ocsp_done(ocsp->ctx); + ocsp->ctx = NULL; + } + + if (ocsp->certs) { + sk_X509_pop_free(ocsp->certs, X509_free); + ocsp->certs = NULL; + } +} + + static ngx_ssl_ocsp_ctx_t * -ngx_ssl_ocsp_start(void) +ngx_ssl_ocsp_start(ngx_log_t *log) { - ngx_log_t *log; ngx_pool_t *pool; ngx_ssl_ocsp_ctx_t *ctx; - pool = ngx_create_pool(2048, ngx_cycle->log); + pool = ngx_create_pool(2048, log); if (pool == NULL) { return NULL; } @@ -828,6 +1313,14 @@ ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx) } if (resolve == NGX_NO_RESOLVER) { + if (ctx->naddrs == 0) { + ngx_log_error(NGX_LOG_ERR, ctx->log, 0, + "no resolver defined to resolve %V", &ctx->host); + + ngx_ssl_ocsp_error(ctx); + return; + } + ngx_log_error(NGX_LOG_WARN, ctx->log, 0, "no resolver defined to resolve %V", &ctx->host); goto connect; @@ -979,8 +1472,10 @@ ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx) ctx->process = ngx_ssl_ocsp_process_status_line; - ngx_add_timer(ctx->peer.connection->read, ctx->timeout); - ngx_add_timer(ctx->peer.connection->write, ctx->timeout); + if (ctx->timeout) { + ngx_add_timer(ctx->peer.connection->read, ctx->timeout); + ngx_add_timer(ctx->peer.connection->write, ctx->timeout); + } if (rc == NGX_OK) { ngx_ssl_ocsp_write_handler(ctx->peer.connection->write); @@ -1036,7 +1531,7 @@ ngx_ssl_ocsp_write_handler(ngx_event_t *wev) } } - if (!wev->timer_set) { + if (!wev->timer_set && ctx->timeout) { ngx_add_timer(wev, ctx->timeout); } } @@ -1939,4 +2434,43 @@ ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl, } +ngx_int_t +ngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder, + ngx_uint_t depth) +{ + ngx_log_error(NGX_LOG_EMERG, ssl->log, 0, + "\"ssl_ocsp\" is not supported on this platform"); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_ssl_ocsp_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl, + ngx_resolver_t *resolver, ngx_msec_t resolver_timeout) +{ + return NGX_OK; +} + + +ngx_int_t +ngx_ssl_ocsp_validate(ngx_connection_t *c) +{ + return NGX_OK; +} + + +ngx_int_t +ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s) +{ + return NGX_OK; +} + + +void +ngx_ssl_ocsp_cleanup(ngx_connection_t *c) +{ +} + + #endif diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 495e628d3..1c9accdd3 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -74,6 +74,14 @@ static ngx_conf_enum_t ngx_http_ssl_verify[] = { }; +static ngx_conf_enum_t ngx_http_ssl_ocsp[] = { + { ngx_string("off"), 0 }, + { ngx_string("on"), 1 }, + { ngx_string("leaf"), 2 }, + { ngx_null_string, 0 } +}; + + static ngx_conf_deprecated_t ngx_http_ssl_deprecated = { ngx_conf_deprecated, "ssl", "listen ... ssl" }; @@ -214,6 +222,20 @@ static ngx_command_t ngx_http_ssl_commands[] = { offsetof(ngx_http_ssl_srv_conf_t, crl), NULL }, + { ngx_string("ssl_ocsp"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_enum_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, ocsp), + &ngx_http_ssl_ocsp }, + + { ngx_string("ssl_ocsp_responder"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, ocsp_responder), + NULL }, + { ngx_string("ssl_stapling"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, @@ -561,6 +583,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf) * sscf->crl = { 0, NULL }; * sscf->ciphers = { 0, NULL }; * sscf->shm_zone = NULL; + * sscf->ocsp_responder = { 0, NULL }; * sscf->stapling_file = { 0, NULL }; * sscf->stapling_responder = { 0, NULL }; */ @@ -578,6 +601,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf) sscf->session_timeout = NGX_CONF_UNSET; sscf->session_tickets = NGX_CONF_UNSET; sscf->session_ticket_keys = NGX_CONF_UNSET_PTR; + sscf->ocsp = NGX_CONF_UNSET_UINT; sscf->stapling = NGX_CONF_UNSET; sscf->stapling_verify = NGX_CONF_UNSET; @@ -641,6 +665,9 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS); + ngx_conf_merge_uint_value(conf->ocsp, prev->ocsp, 0); + ngx_conf_merge_str_value(conf->ocsp_responder, prev->ocsp_responder, ""); + ngx_conf_merge_value(conf->stapling, prev->stapling, 0); ngx_conf_merge_value(conf->stapling_verify, prev->stapling_verify, 0); ngx_conf_merge_str_value(conf->stapling_file, prev->stapling_file, ""); @@ -802,6 +829,22 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } + if (conf->ocsp) { + + if (conf->verify == 3) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "\"ssl_ocsp\" is incompatible with " + "\"ssl_verify_client optional_no_ca\""); + return NGX_CONF_ERROR; + } + + if (ngx_ssl_ocsp(cf, &conf->ssl, &conf->ocsp_responder, conf->ocsp) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } + if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) { return NGX_CONF_ERROR; } @@ -1118,17 +1161,28 @@ ngx_http_ssl_init(ngx_conf_t *cf) sscf = cscfp[s]->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; - if (sscf->ssl.ctx == NULL || !sscf->stapling) { + if (sscf->ssl.ctx == NULL) { continue; } clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index]; - if (ngx_ssl_stapling_resolver(cf, &sscf->ssl, clcf->resolver, + if (sscf->stapling) { + if (ngx_ssl_stapling_resolver(cf, &sscf->ssl, clcf->resolver, + clcf->resolver_timeout) + != NGX_OK) + { + return NGX_ERROR; + } + } + + if (sscf->ocsp) { + if (ngx_ssl_ocsp_resolver(cf, &sscf->ssl, clcf->resolver, clcf->resolver_timeout) - != NGX_OK) - { - return NGX_ERROR; + != NGX_OK) + { + return NGX_ERROR; + } } } diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h index 26fdccfe4..92d459f60 100644 --- a/src/http/modules/ngx_http_ssl_module.h +++ b/src/http/modules/ngx_http_ssl_module.h @@ -54,6 +54,9 @@ typedef struct { ngx_flag_t session_tickets; ngx_array_t *session_ticket_keys; + ngx_uint_t ocsp; + ngx_str_t ocsp_responder; + ngx_flag_t stapling; ngx_flag_t stapling_verify; ngx_str_t stapling_file; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index eb53996b1..6feb6cc31 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1993,6 +1993,7 @@ ngx_http_process_request(ngx_http_request_t *r) if (r->http_connection->ssl) { long rc; X509 *cert; + const char *s; ngx_http_ssl_srv_conf_t *sscf; if (c->ssl == NULL) { @@ -2037,6 +2038,17 @@ ngx_http_process_request(ngx_http_request_t *r) X509_free(cert); } + + if (ngx_ssl_ocsp_get_status(c, &s) != NGX_OK) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client SSL certificate verify error: %s", s); + + ngx_ssl_remove_cached_session(c->ssl->session_ctx, + (SSL_get0_session(c->ssl->connection))); + + ngx_http_finalize_request(r, NGX_HTTPS_CERT_ERROR); + return; + } } } |