Welcome! Log In Create A New Profile

Advanced

[nginx] SSL: client certificate validation with OCSP (ticket #1534).

Roman Arutyunyan
May 23, 2020 06:38AM
details: https://hg.nginx.org/nginx/rev/8409f9df6219
branches:
changeset: 7653:8409f9df6219
user: Roman Arutyunyan <arut@nginx.com>
date: Fri May 22 17:30:12 2020 +0300
description:
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:

src/event/ngx_event_openssl.c | 60 +++-
src/event/ngx_event_openssl.h | 14 +
src/event/ngx_event_openssl_stapling.c | 550 ++++++++++++++++++++++++++++++++-
src/http/modules/ngx_http_ssl_module.c | 64 +++-
src/http/modules/ngx_http_ssl_module.h | 3 +
src/http/ngx_http_request.c | 12 +
6 files changed, 682 insertions(+), 21 deletions(-)

diffs (970 lines):

diff -r 7cffd81015e7 -r 8409f9df6219 src/event/ngx_event_openssl.c
--- a/src/event/ngx_event_openssl.c Fri May 22 20:35:05 2020 +0300
+++ b/src/event/ngx_event_openssl.c Fri May 22 17:30:12 2020 +0300
@@ -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
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->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->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
rc = SSL_get_verify_result(c->ssl->connection);

if (rc == X509_V_OK) {
- ngx_str_set(s, "SUCCESS");
- return NGX_OK;
- }
-
- str = X509_verify_cert_error_string(rc);
+ if (ngx_ssl_ocsp_get_status(c, &str) == NGX_OK) {
+ ngx_str_set(s, "SUCCESS");
+ return NGX_OK;
+ }
+
+ } else {
+ str = X509_verify_cert_error_string(rc);
+ }

s->data = ngx_pnalloc(pool, sizeof("FAILED:") - 1 + ngx_strlen(str));
if (s->data == NULL) {
diff -r 7cffd81015e7 -r 8409f9df6219 src/event/ngx_event_openssl.h
--- a/src/event/ngx_event_openssl.h Fri May 22 20:35:05 2020 +0300
+++ b/src/event/ngx_event_openssl.h Fri May 22 17:30:12 2020 +0300
@@ -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 *c
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 -r 7cffd81015e7 -r 8409f9df6219 src/event/ngx_event_openssl_stapling.c
--- a/src/event/ngx_event_openssl_stapling.c Fri May 22 20:35:05 2020 +0300
+++ b/src/event/ngx_event_openssl_stapling.c Fri May 22 17:30:12 2020 +0300
@@ -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

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

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
}

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->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 *
}
}

- 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_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 -r 7cffd81015e7 -r 8409f9df6219 src/http/modules/ngx_http_ssl_module.c
--- a/src/http/modules/ngx_http_ssl_module.c Fri May 22 20:35:05 2020 +0300
+++ b/src/http/modules/ngx_http_ssl_module.c Fri May 22 17:30:12 2020 +0300
@@ -74,6 +74,14 @@ static ngx_conf_enum_t ngx_http_ssl_ver
};


+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_comma
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
* 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
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 *

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 *
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 -r 7cffd81015e7 -r 8409f9df6219 src/http/modules/ngx_http_ssl_module.h
--- a/src/http/modules/ngx_http_ssl_module.h Fri May 22 20:35:05 2020 +0300
+++ b/src/http/modules/ngx_http_ssl_module.h Fri May 22 17:30:12 2020 +0300
@@ -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 -r 7cffd81015e7 -r 8409f9df6219 src/http/ngx_http_request.c
--- a/src/http/ngx_http_request.c Fri May 22 20:35:05 2020 +0300
+++ b/src/http/ngx_http_request.c Fri May 22 17:30:12 2020 +0300
@@ -1993,6 +1993,7 @@ ngx_http_process_request(ngx_http_reques
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_reques

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;
+ }
}
}

_______________________________________________
nginx-devel mailing list
nginx-devel@nginx.org
http://mailman.nginx.org/mailman/listinfo/nginx-devel
Subject Author Views Posted

[nginx] SSL: client certificate validation with OCSP (ticket #1534).

Roman Arutyunyan 77 May 23, 2020 06:38AM



Sorry, you do not have permission to post/reply in this forum.

Online Users

Guests: 102
Record Number of Users: 6 on February 13, 2018
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready