# HG changeset patch # User Maxim Dounin # Date 1434044559 -10800 # Node ID 6893a1007a7c7e91e0afeb9c537c5bcecf937faa # Parent 8b6fa4842133eeabff9857f006d02971a0ba8b45 OCSP stapling: avoid sending expired responses (ticket #425). 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 @@ -32,6 +32,7 @@ typedef struct { X509 *issuer; time_t valid; + time_t refresh; unsigned verify:1; unsigned loading:1; @@ -93,6 +94,8 @@ 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 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); @@ -462,7 +465,9 @@ ngx_ssl_certificate_status_callback(ngx_ staple = data; rc = SSL_TLSEXT_ERR_NOACK; - if (staple->staple.len) { + if (staple->staple.len + && staple->valid >= ngx_time()) + { /* we have to copy ocsp response as OpenSSL will free it by itself */ p = OPENSSL_malloc(staple->staple.len); @@ -490,7 +495,7 @@ ngx_ssl_stapling_update(ngx_ssl_stapling ngx_ssl_ocsp_ctx_t *ctx; if (staple->host.len == 0 - || staple->loading || staple->valid >= ngx_time()) + || staple->loading || staple->refresh >= ngx_time()) { return; } @@ -532,6 +537,7 @@ ngx_ssl_stapling_ocsp_handler(ngx_ssl_oc u_char *p; int n; size_t len; + time_t now, valid; ngx_str_t response; X509_STORE *store; STACK_OF(X509) *chain; @@ -542,6 +548,7 @@ ngx_ssl_stapling_ocsp_handler(ngx_ssl_oc ASN1_GENERALIZEDTIME *thisupdate, *nextupdate; staple = ctx->data; + now = ngx_time(); ocsp = NULL; basic = NULL; id = NULL; @@ -629,17 +636,28 @@ ngx_ssl_stapling_ocsp_handler(ngx_ssl_oc goto error; } + valid = ngx_ssl_stapling_time(nextupdate); + if (valid == (time_t) NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, ctx->log, 0, + "invalid nextUpdate time in certificate status"); + goto error; + } + OCSP_CERTID_free(id); OCSP_BASICRESP_free(basic); OCSP_RESPONSE_free(ocsp); + id = NULL; + basic = NULL; + ocsp = NULL; + /* copy the response to memory not in ctx->pool */ response.len = len; response.data = ngx_alloc(response.len, ctx->log); if (response.data == NULL) { - goto done; + goto error; } ngx_memcpy(response.data, ctx->response->pos, response.len); @@ -653,11 +671,15 @@ ngx_ssl_stapling_ocsp_handler(ngx_ssl_oc } staple->staple = response; + staple->valid = valid; -done: + /* + * refresh before the response expires, + * but not earlier than in 5 minutes, and at least in an hour + */ staple->loading = 0; - staple->valid = ngx_time() + 3600; /* ssl_stapling_valid */ + staple->refresh = ngx_max(ngx_min(valid - 300, now + 3600), now + 300); ngx_ssl_ocsp_done(ctx); return; @@ -665,7 +687,7 @@ done: error: staple->loading = 0; - staple->valid = ngx_time() + 300; /* ssl_stapling_err_valid */ + staple->refresh = now + 300; if (id) { OCSP_CERTID_free(id); @@ -683,6 +705,40 @@ error: } +static time_t +ngx_ssl_stapling_time(ASN1_GENERALIZEDTIME *asn1time) +{ + u_char *value; + size_t len; + time_t time; + BIO *bio; + + /* + * OpenSSL doesn't provide a way to convert ASN1_GENERALIZEDTIME + * into time_t. To do this, we use ASN1_GENERALIZEDTIME_print(), + * which uses the "MMM DD HH:MM:SS YYYY [GMT]" format (e.g., + * "Feb 3 00:55:52 2015 GMT"), and parse the result. + */ + + bio = BIO_new(BIO_s_mem()); + if (bio == NULL) { + return NGX_ERROR; + } + + /* fake weekday prepended to match C asctime() format */ + + BIO_write(bio, "Tue ", sizeof("Tue ") - 1); + ASN1_GENERALIZEDTIME_print(bio, asn1time); + len = BIO_get_mem_data(bio, &value); + + time = ngx_parse_http_time(value, len); + + BIO_free(bio); + + return time; +} + + static void ngx_ssl_stapling_cleanup(void *data) {