OCSP stapling: avoid sending expired responses (ticket #425).

This commit is contained in:
Maxim Dounin 2015-06-11 20:42:39 +03:00
parent 9984f3053f
commit cb3dcbb81e

View File

@ -32,6 +32,7 @@ typedef struct {
X509 *issuer; X509 *issuer;
time_t valid; time_t valid;
time_t refresh;
unsigned verify:1; unsigned verify:1;
unsigned loading:1; unsigned loading:1;
@ -93,6 +94,8 @@ static int ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn,
static void ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple); 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 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 void ngx_ssl_stapling_cleanup(void *data);
static ngx_ssl_ocsp_ctx_t *ngx_ssl_ocsp_start(void); static ngx_ssl_ocsp_ctx_t *ngx_ssl_ocsp_start(void);
@ -462,7 +465,9 @@ ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data)
staple = data; staple = data;
rc = SSL_TLSEXT_ERR_NOACK; 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 */ /* we have to copy ocsp response as OpenSSL will free it by itself */
p = OPENSSL_malloc(staple->staple.len); p = OPENSSL_malloc(staple->staple.len);
@ -490,7 +495,7 @@ ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple)
ngx_ssl_ocsp_ctx_t *ctx; ngx_ssl_ocsp_ctx_t *ctx;
if (staple->host.len == 0 if (staple->host.len == 0
|| staple->loading || staple->valid >= ngx_time()) || staple->loading || staple->refresh >= ngx_time())
{ {
return; return;
} }
@ -532,6 +537,7 @@ ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)
u_char *p; u_char *p;
int n; int n;
size_t len; size_t len;
time_t now, valid;
ngx_str_t response; ngx_str_t response;
X509_STORE *store; X509_STORE *store;
STACK_OF(X509) *chain; STACK_OF(X509) *chain;
@ -542,6 +548,7 @@ ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)
ASN1_GENERALIZEDTIME *thisupdate, *nextupdate; ASN1_GENERALIZEDTIME *thisupdate, *nextupdate;
staple = ctx->data; staple = ctx->data;
now = ngx_time();
ocsp = NULL; ocsp = NULL;
basic = NULL; basic = NULL;
id = NULL; id = NULL;
@ -629,17 +636,28 @@ ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)
goto error; 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_CERTID_free(id);
OCSP_BASICRESP_free(basic); OCSP_BASICRESP_free(basic);
OCSP_RESPONSE_free(ocsp); OCSP_RESPONSE_free(ocsp);
id = NULL;
basic = NULL;
ocsp = NULL;
/* copy the response to memory not in ctx->pool */ /* copy the response to memory not in ctx->pool */
response.len = len; response.len = len;
response.data = ngx_alloc(response.len, ctx->log); response.data = ngx_alloc(response.len, ctx->log);
if (response.data == NULL) { if (response.data == NULL) {
goto done; goto error;
} }
ngx_memcpy(response.data, ctx->response->pos, response.len); ngx_memcpy(response.data, ctx->response->pos, response.len);
@ -653,11 +671,15 @@ ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)
} }
staple->staple = response; 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->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); ngx_ssl_ocsp_done(ctx);
return; return;
@ -665,7 +687,7 @@ done:
error: error:
staple->loading = 0; staple->loading = 0;
staple->valid = ngx_time() + 300; /* ssl_stapling_err_valid */ staple->refresh = now + 300;
if (id) { if (id) {
OCSP_CERTID_free(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 static void
ngx_ssl_stapling_cleanup(void *data) ngx_ssl_stapling_cleanup(void *data)
{ {