Latest update - 7655
This commit is contained in:
@@ -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) {
|
||||
@@ -1603,6 +1611,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) {
|
||||
@@ -1610,6 +1619,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);
|
||||
@@ -1630,8 +1643,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;
|
||||
@@ -1650,6 +1661,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;
|
||||
}
|
||||
|
||||
@@ -1719,6 +1744,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);
|
||||
|
||||
@@ -1753,7 +1779,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;
|
||||
@@ -1761,6 +1786,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;
|
||||
}
|
||||
|
||||
@@ -2782,6 +2821,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
|
||||
@@ -4963,11 +5004,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) {
|
||||
|
||||
@@ -72,6 +72,9 @@ typedef struct {
|
||||
} ngx_ssl_dyn_rec_t;
|
||||
|
||||
|
||||
typedef struct ngx_ssl_ocsp_s ngx_ssl_ocsp_t;
|
||||
|
||||
|
||||
struct ngx_ssl_s {
|
||||
SSL_CTX *ctx;
|
||||
ngx_log_t *log;
|
||||
@@ -96,6 +99,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;
|
||||
@@ -106,6 +111,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;
|
||||
|
||||
@@ -193,6 +199,14 @@ 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_shm_zone_t *shm_zone);
|
||||
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);
|
||||
ngx_int_t ngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data);
|
||||
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);
|
||||
@@ -294,6 +308,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;
|
||||
|
||||
+1057
-173
@@ -22,6 +22,7 @@ typedef struct {
|
||||
ngx_msec_t resolver_timeout;
|
||||
|
||||
ngx_addr_t *addrs;
|
||||
ngx_uint_t naddrs;
|
||||
ngx_str_t host;
|
||||
ngx_str_t uri;
|
||||
in_port_t port;
|
||||
@@ -30,6 +31,7 @@ typedef struct {
|
||||
|
||||
X509 *cert;
|
||||
X509 *issuer;
|
||||
STACK_OF(X509) *chain;
|
||||
|
||||
u_char *name;
|
||||
|
||||
@@ -41,15 +43,66 @@ 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_shm_zone_t *shm_zone;
|
||||
|
||||
ngx_resolver_t *resolver;
|
||||
ngx_msec_t resolver_timeout;
|
||||
} ngx_ssl_ocsp_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_rbtree_t rbtree;
|
||||
ngx_rbtree_node_t sentinel;
|
||||
ngx_queue_t expire_queue;
|
||||
} ngx_ssl_ocsp_cache_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_node_t node;
|
||||
ngx_queue_t queue;
|
||||
int status;
|
||||
time_t valid;
|
||||
} ngx_ssl_ocsp_cache_node_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;
|
||||
|
||||
X509 *cert;
|
||||
X509 *issuer;
|
||||
STACK_OF(X509) *chain;
|
||||
|
||||
int status;
|
||||
time_t valid;
|
||||
|
||||
u_char *name;
|
||||
|
||||
ngx_uint_t naddrs;
|
||||
ngx_uint_t naddr;
|
||||
|
||||
ngx_addr_t *addrs;
|
||||
ngx_str_t host;
|
||||
@@ -64,17 +117,20 @@ struct ngx_ssl_ocsp_ctx_s {
|
||||
void (*handler)(ngx_ssl_ocsp_ctx_t *ctx);
|
||||
void *data;
|
||||
|
||||
ngx_str_t key;
|
||||
ngx_buf_t *request;
|
||||
ngx_buf_t *response;
|
||||
ngx_peer_connection_t peer;
|
||||
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
|
||||
ngx_int_t (*process)(ngx_ssl_ocsp_ctx_t *ctx);
|
||||
|
||||
ngx_uint_t state;
|
||||
|
||||
ngx_uint_t code;
|
||||
ngx_uint_t count;
|
||||
|
||||
ngx_uint_t flags;
|
||||
ngx_uint_t done;
|
||||
|
||||
u_char *header_name_start;
|
||||
@@ -105,8 +161,14 @@ 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);
|
||||
static void ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve);
|
||||
static void ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx);
|
||||
@@ -120,6 +182,11 @@ static ngx_int_t ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx);
|
||||
static ngx_int_t ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx);
|
||||
static ngx_int_t ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx);
|
||||
static ngx_int_t ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx);
|
||||
static ngx_int_t ngx_ssl_ocsp_verify(ngx_ssl_ocsp_ctx_t *ctx);
|
||||
|
||||
static ngx_int_t ngx_ssl_ocsp_cache_lookup(ngx_ssl_ocsp_ctx_t *ctx);
|
||||
static ngx_int_t ngx_ssl_ocsp_cache_store(ngx_ssl_ocsp_ctx_t *ctx);
|
||||
static ngx_int_t ngx_ssl_ocsp_create_key(ngx_ssl_ocsp_ctx_t *ctx);
|
||||
|
||||
static u_char *ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len);
|
||||
|
||||
@@ -173,6 +240,18 @@ ngx_ssl_stapling_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, X509 *cert,
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
#ifdef SSL_CTRL_SELECT_CURRENT_CERT
|
||||
/* OpenSSL 1.0.2+ */
|
||||
SSL_CTX_select_current_cert(ssl->ctx, cert);
|
||||
#endif
|
||||
|
||||
#ifdef SSL_CTRL_GET_EXTRA_CHAIN_CERTS
|
||||
/* OpenSSL 1.0.1+ */
|
||||
SSL_CTX_get_extra_chain_certs(ssl->ctx, &staple->chain);
|
||||
#else
|
||||
staple->chain = ssl->ctx->extra_certs;
|
||||
#endif
|
||||
|
||||
staple->ssl_ctx = ssl->ctx;
|
||||
staple->timeout = 60000;
|
||||
staple->verify = verify;
|
||||
@@ -289,29 +368,16 @@ ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl,
|
||||
X509 *cert, *issuer;
|
||||
X509_STORE *store;
|
||||
X509_STORE_CTX *store_ctx;
|
||||
STACK_OF(X509) *chain;
|
||||
|
||||
cert = staple->cert;
|
||||
|
||||
#ifdef SSL_CTRL_SELECT_CURRENT_CERT
|
||||
/* OpenSSL 1.0.2+ */
|
||||
SSL_CTX_select_current_cert(ssl->ctx, cert);
|
||||
#endif
|
||||
|
||||
#ifdef SSL_CTRL_GET_EXTRA_CHAIN_CERTS
|
||||
/* OpenSSL 1.0.1+ */
|
||||
SSL_CTX_get_extra_chain_certs(ssl->ctx, &chain);
|
||||
#else
|
||||
chain = ssl->ctx->extra_certs;
|
||||
#endif
|
||||
|
||||
n = sk_X509_num(chain);
|
||||
n = sk_X509_num(staple->chain);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,
|
||||
"SSL get issuer: %d extra certs", n);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
issuer = sk_X509_value(chain, i);
|
||||
issuer = sk_X509_value(staple->chain, i);
|
||||
if (X509_check_issued(issuer, cert) == X509_V_OK) {
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100001L
|
||||
X509_up_ref(issuer);
|
||||
@@ -462,6 +528,7 @@ ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl,
|
||||
}
|
||||
|
||||
staple->addrs = u.addrs;
|
||||
staple->naddrs = u.naddrs;
|
||||
staple->host = u.host;
|
||||
staple->uri = u.uri;
|
||||
staple->port = u.port;
|
||||
@@ -559,16 +626,20 @@ 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;
|
||||
}
|
||||
|
||||
ctx->ssl_ctx = staple->ssl_ctx;
|
||||
ctx->cert = staple->cert;
|
||||
ctx->issuer = staple->issuer;
|
||||
ctx->chain = staple->chain;
|
||||
ctx->name = staple->name;
|
||||
ctx->flags = (staple->verify ? OCSP_TRUSTOTHER : OCSP_NOVERIFY);
|
||||
|
||||
ctx->addrs = staple->addrs;
|
||||
ctx->naddrs = staple->naddrs;
|
||||
ctx->host = staple->host;
|
||||
ctx->uri = staple->uri;
|
||||
ctx->port = staple->port;
|
||||
@@ -589,137 +660,27 @@ ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple)
|
||||
static void
|
||||
ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)
|
||||
{
|
||||
int n;
|
||||
size_t len;
|
||||
time_t now, valid;
|
||||
ngx_str_t response;
|
||||
X509_STORE *store;
|
||||
const u_char *p;
|
||||
STACK_OF(X509) *chain;
|
||||
OCSP_CERTID *id;
|
||||
OCSP_RESPONSE *ocsp;
|
||||
OCSP_BASICRESP *basic;
|
||||
ngx_ssl_stapling_t *staple;
|
||||
ASN1_GENERALIZEDTIME *thisupdate, *nextupdate;
|
||||
time_t now;
|
||||
ngx_str_t response;
|
||||
ngx_ssl_stapling_t *staple;
|
||||
|
||||
staple = ctx->data;
|
||||
now = ngx_time();
|
||||
ocsp = NULL;
|
||||
basic = NULL;
|
||||
id = NULL;
|
||||
|
||||
if (ctx->code != 200) {
|
||||
if (ngx_ssl_ocsp_verify(ctx) != NGX_OK) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* check the response */
|
||||
|
||||
len = ctx->response->last - ctx->response->pos;
|
||||
p = ctx->response->pos;
|
||||
|
||||
ocsp = d2i_OCSP_RESPONSE(NULL, &p, len);
|
||||
if (ocsp == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
|
||||
"d2i_OCSP_RESPONSE() failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
n = OCSP_response_status(ocsp);
|
||||
|
||||
if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
|
||||
ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
|
||||
"OCSP response not successful (%d: %s)",
|
||||
n, OCSP_response_status_str(n));
|
||||
goto error;
|
||||
}
|
||||
|
||||
basic = OCSP_response_get1_basic(ocsp);
|
||||
if (basic == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
|
||||
"OCSP_response_get1_basic() failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
store = SSL_CTX_get_cert_store(staple->ssl_ctx);
|
||||
if (store == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
|
||||
"SSL_CTX_get_cert_store() failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
#ifdef SSL_CTRL_SELECT_CURRENT_CERT
|
||||
/* OpenSSL 1.0.2+ */
|
||||
SSL_CTX_select_current_cert(staple->ssl_ctx, ctx->cert);
|
||||
#endif
|
||||
|
||||
#ifdef SSL_CTRL_GET_EXTRA_CHAIN_CERTS
|
||||
/* OpenSSL 1.0.1+ */
|
||||
SSL_CTX_get_extra_chain_certs(staple->ssl_ctx, &chain);
|
||||
#else
|
||||
chain = staple->ssl_ctx->extra_certs;
|
||||
#endif
|
||||
|
||||
if (OCSP_basic_verify(basic, chain, store,
|
||||
staple->verify ? OCSP_TRUSTOTHER : OCSP_NOVERIFY)
|
||||
!= 1)
|
||||
{
|
||||
ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
|
||||
"OCSP_basic_verify() failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer);
|
||||
if (id == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
|
||||
"OCSP_cert_to_id() failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (OCSP_resp_find_status(basic, id, &n, NULL, NULL,
|
||||
&thisupdate, &nextupdate)
|
||||
!= 1)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
|
||||
"certificate status not found in the OCSP response");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (n != V_OCSP_CERTSTATUS_GOOD) {
|
||||
if (ctx->status != V_OCSP_CERTSTATUS_GOOD) {
|
||||
ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
|
||||
"certificate status \"%s\" in the OCSP response",
|
||||
OCSP_cert_status_str(n));
|
||||
OCSP_cert_status_str(ctx->status));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) {
|
||||
ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
|
||||
"OCSP_check_validity() failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (nextupdate) {
|
||||
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;
|
||||
}
|
||||
|
||||
} else {
|
||||
valid = NGX_MAX_TIME_T_VALUE;
|
||||
}
|
||||
|
||||
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.len = ctx->response->last - ctx->response->pos;
|
||||
response.data = ngx_alloc(response.len, ctx->log);
|
||||
|
||||
if (response.data == NULL) {
|
||||
@@ -728,16 +689,12 @@ ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)
|
||||
|
||||
ngx_memcpy(response.data, ctx->response->pos, response.len);
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
|
||||
"ssl ocsp response, %s, %uz",
|
||||
OCSP_cert_status_str(n), response.len);
|
||||
|
||||
if (staple->staple.data) {
|
||||
ngx_free(staple->staple.data);
|
||||
}
|
||||
|
||||
staple->staple = response;
|
||||
staple->valid = valid;
|
||||
staple->valid = ctx->valid;
|
||||
|
||||
/*
|
||||
* refresh before the response expires,
|
||||
@@ -745,7 +702,7 @@ ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)
|
||||
*/
|
||||
|
||||
staple->loading = 0;
|
||||
staple->refresh = ngx_max(ngx_min(valid - 300, now + 3600), now + 300);
|
||||
staple->refresh = ngx_max(ngx_min(ctx->valid - 300, now + 3600), now + 300);
|
||||
|
||||
ngx_ssl_ocsp_done(ctx);
|
||||
return;
|
||||
@@ -755,18 +712,6 @@ error:
|
||||
staple->loading = 0;
|
||||
staple->refresh = now + 300;
|
||||
|
||||
if (id) {
|
||||
OCSP_CERTID_free(id);
|
||||
}
|
||||
|
||||
if (basic) {
|
||||
OCSP_BASICRESP_free(basic);
|
||||
}
|
||||
|
||||
if (ocsp) {
|
||||
OCSP_RESPONSE_free(ocsp);
|
||||
}
|
||||
|
||||
ngx_ssl_ocsp_done(ctx);
|
||||
}
|
||||
|
||||
@@ -820,14 +765,499 @@ ngx_ssl_stapling_cleanup(void *data)
|
||||
}
|
||||
|
||||
|
||||
static ngx_ssl_ocsp_ctx_t *
|
||||
ngx_ssl_ocsp_start(void)
|
||||
ngx_int_t
|
||||
ngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder,
|
||||
ngx_uint_t depth, ngx_shm_zone_t *shm_zone)
|
||||
{
|
||||
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;
|
||||
ocf->shm_zone = shm_zone;
|
||||
|
||||
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:%d", 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_int_t rc;
|
||||
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->shm_zone = ocf->shm_zone;
|
||||
|
||||
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++;
|
||||
|
||||
rc = ngx_ssl_ocsp_cache_lookup(ctx);
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (rc == NGX_DECLINED) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* rc == NGX_OK */
|
||||
|
||||
if (ctx->status != V_OCSP_CERTSTATUS_GOOD) {
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
|
||||
"ssl ocsp cached status \"%s\"",
|
||||
OCSP_cert_status_str(ctx->status));
|
||||
ocsp->cert_status = ctx->status;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ocsp->ctx = NULL;
|
||||
ngx_ssl_ocsp_done(ctx);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
rc = ngx_ssl_ocsp_cache_store(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(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;
|
||||
}
|
||||
@@ -884,6 +1314,36 @@ ngx_ssl_ocsp_error(ngx_ssl_ocsp_ctx_t *ctx)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_ssl_ocsp_next(ngx_ssl_ocsp_ctx_t *ctx)
|
||||
{
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
|
||||
"ssl ocsp next");
|
||||
|
||||
if (++ctx->naddr >= ctx->naddrs) {
|
||||
ngx_ssl_ocsp_error(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->request->pos = ctx->request->start;
|
||||
|
||||
if (ctx->response) {
|
||||
ctx->response->last = ctx->response->pos;
|
||||
}
|
||||
|
||||
if (ctx->peer.connection) {
|
||||
ngx_close_connection(ctx->peer.connection);
|
||||
ctx->peer.connection = NULL;
|
||||
}
|
||||
|
||||
ctx->state = 0;
|
||||
ctx->count = 0;
|
||||
ctx->done = 0;
|
||||
|
||||
ngx_ssl_ocsp_connect(ctx);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx)
|
||||
{
|
||||
@@ -909,6 +1369,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;
|
||||
@@ -1022,16 +1490,17 @@ failed:
|
||||
static void
|
||||
ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_int_t rc;
|
||||
ngx_addr_t *addr;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
|
||||
"ssl ocsp connect");
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
|
||||
"ssl ocsp connect %ui/%ui", ctx->naddr, ctx->naddrs);
|
||||
|
||||
/* TODO: use all ip addresses */
|
||||
addr = &ctx->addrs[ctx->naddr];
|
||||
|
||||
ctx->peer.sockaddr = ctx->addrs[0].sockaddr;
|
||||
ctx->peer.socklen = ctx->addrs[0].socklen;
|
||||
ctx->peer.name = &ctx->addrs[0].name;
|
||||
ctx->peer.sockaddr = addr->sockaddr;
|
||||
ctx->peer.socklen = addr->socklen;
|
||||
ctx->peer.name = &addr->name;
|
||||
ctx->peer.get = ngx_event_get_peer;
|
||||
ctx->peer.log = ctx->log;
|
||||
ctx->peer.log_error = NGX_ERROR_ERR;
|
||||
@@ -1041,11 +1510,16 @@ ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx)
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
|
||||
"ssl ocsp connect peer done");
|
||||
|
||||
if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
|
||||
if (rc == NGX_ERROR) {
|
||||
ngx_ssl_ocsp_error(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rc == NGX_BUSY || rc == NGX_DECLINED) {
|
||||
ngx_ssl_ocsp_next(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->peer.connection->data = ctx;
|
||||
ctx->peer.connection->pool = ctx->pool;
|
||||
|
||||
@@ -1054,8 +1528,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);
|
||||
@@ -1080,7 +1556,7 @@ ngx_ssl_ocsp_write_handler(ngx_event_t *wev)
|
||||
if (wev->timedout) {
|
||||
ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT,
|
||||
"OCSP responder timed out");
|
||||
ngx_ssl_ocsp_error(ctx);
|
||||
ngx_ssl_ocsp_next(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1089,7 +1565,7 @@ ngx_ssl_ocsp_write_handler(ngx_event_t *wev)
|
||||
n = ngx_send(c, ctx->request->pos, size);
|
||||
|
||||
if (n == NGX_ERROR) {
|
||||
ngx_ssl_ocsp_error(ctx);
|
||||
ngx_ssl_ocsp_next(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1111,7 +1587,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);
|
||||
}
|
||||
}
|
||||
@@ -1134,7 +1610,7 @@ ngx_ssl_ocsp_read_handler(ngx_event_t *rev)
|
||||
if (rev->timedout) {
|
||||
ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,
|
||||
"OCSP responder timed out");
|
||||
ngx_ssl_ocsp_error(ctx);
|
||||
ngx_ssl_ocsp_next(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1158,7 +1634,7 @@ ngx_ssl_ocsp_read_handler(ngx_event_t *rev)
|
||||
rc = ctx->process(ctx);
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
ngx_ssl_ocsp_error(ctx);
|
||||
ngx_ssl_ocsp_next(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1189,7 +1665,7 @@ ngx_ssl_ocsp_read_handler(ngx_event_t *rev)
|
||||
ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
|
||||
"OCSP responder prematurely closed connection");
|
||||
|
||||
ngx_ssl_ocsp_error(ctx);
|
||||
ngx_ssl_ocsp_next(ctx);
|
||||
}
|
||||
|
||||
|
||||
@@ -1831,6 +2307,368 @@ ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx)
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_ssl_ocsp_verify(ngx_ssl_ocsp_ctx_t *ctx)
|
||||
{
|
||||
int n;
|
||||
size_t len;
|
||||
X509_STORE *store;
|
||||
const u_char *p;
|
||||
OCSP_CERTID *id;
|
||||
OCSP_RESPONSE *ocsp;
|
||||
OCSP_BASICRESP *basic;
|
||||
ASN1_GENERALIZEDTIME *thisupdate, *nextupdate;
|
||||
|
||||
ocsp = NULL;
|
||||
basic = NULL;
|
||||
id = NULL;
|
||||
|
||||
if (ctx->code != 200) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* check the response */
|
||||
|
||||
len = ctx->response->last - ctx->response->pos;
|
||||
p = ctx->response->pos;
|
||||
|
||||
ocsp = d2i_OCSP_RESPONSE(NULL, &p, len);
|
||||
if (ocsp == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
|
||||
"d2i_OCSP_RESPONSE() failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
n = OCSP_response_status(ocsp);
|
||||
|
||||
if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
|
||||
ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
|
||||
"OCSP response not successful (%d: %s)",
|
||||
n, OCSP_response_status_str(n));
|
||||
goto error;
|
||||
}
|
||||
|
||||
basic = OCSP_response_get1_basic(ocsp);
|
||||
if (basic == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
|
||||
"OCSP_response_get1_basic() failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
store = SSL_CTX_get_cert_store(ctx->ssl_ctx);
|
||||
if (store == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
|
||||
"SSL_CTX_get_cert_store() failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (OCSP_basic_verify(basic, ctx->chain, store, ctx->flags) != 1) {
|
||||
ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
|
||||
"OCSP_basic_verify() failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer);
|
||||
if (id == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
|
||||
"OCSP_cert_to_id() failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (OCSP_resp_find_status(basic, id, &ctx->status, NULL, NULL,
|
||||
&thisupdate, &nextupdate)
|
||||
!= 1)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
|
||||
"certificate status not found in the OCSP response");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) {
|
||||
ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
|
||||
"OCSP_check_validity() failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (nextupdate) {
|
||||
ctx->valid = ngx_ssl_stapling_time(nextupdate);
|
||||
if (ctx->valid == (time_t) NGX_ERROR) {
|
||||
ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
|
||||
"invalid nextUpdate time in certificate status");
|
||||
goto error;
|
||||
}
|
||||
|
||||
} else {
|
||||
ctx->valid = NGX_MAX_TIME_T_VALUE;
|
||||
}
|
||||
|
||||
OCSP_CERTID_free(id);
|
||||
OCSP_BASICRESP_free(basic);
|
||||
OCSP_RESPONSE_free(ocsp);
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
|
||||
"ssl ocsp response, %s, %uz",
|
||||
OCSP_cert_status_str(ctx->status), len);
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
error:
|
||||
|
||||
if (id) {
|
||||
OCSP_CERTID_free(id);
|
||||
}
|
||||
|
||||
if (basic) {
|
||||
OCSP_BASICRESP_free(basic);
|
||||
}
|
||||
|
||||
if (ocsp) {
|
||||
OCSP_RESPONSE_free(ocsp);
|
||||
}
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data)
|
||||
{
|
||||
size_t len;
|
||||
ngx_slab_pool_t *shpool;
|
||||
ngx_ssl_ocsp_cache_t *cache;
|
||||
|
||||
if (data) {
|
||||
shm_zone->data = data;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
|
||||
|
||||
if (shm_zone->shm.exists) {
|
||||
shm_zone->data = shpool->data;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
cache = ngx_slab_alloc(shpool, sizeof(ngx_ssl_ocsp_cache_t));
|
||||
if (cache == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
shpool->data = cache;
|
||||
shm_zone->data = cache;
|
||||
|
||||
ngx_rbtree_init(&cache->rbtree, &cache->sentinel,
|
||||
ngx_str_rbtree_insert_value);
|
||||
|
||||
ngx_queue_init(&cache->expire_queue);
|
||||
|
||||
len = sizeof(" in OCSP cache \"\"") + shm_zone->shm.name.len;
|
||||
|
||||
shpool->log_ctx = ngx_slab_alloc(shpool, len);
|
||||
if (shpool->log_ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_sprintf(shpool->log_ctx, " in OCSP cache \"%V\"%Z",
|
||||
&shm_zone->shm.name);
|
||||
|
||||
shpool->log_nomem = 0;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_ssl_ocsp_cache_lookup(ngx_ssl_ocsp_ctx_t *ctx)
|
||||
{
|
||||
uint32_t hash;
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
ngx_slab_pool_t *shpool;
|
||||
ngx_ssl_ocsp_cache_t *cache;
|
||||
ngx_ssl_ocsp_cache_node_t *node;
|
||||
|
||||
shm_zone = ctx->shm_zone;
|
||||
|
||||
if (shm_zone == NULL) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
if (ngx_ssl_ocsp_create_key(ctx) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, "ssl ocsp cache lookup");
|
||||
|
||||
cache = shm_zone->data;
|
||||
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
|
||||
hash = ngx_hash_key(ctx->key.data, ctx->key.len);
|
||||
|
||||
ngx_shmtx_lock(&shpool->mutex);
|
||||
|
||||
node = (ngx_ssl_ocsp_cache_node_t *)
|
||||
ngx_str_rbtree_lookup(&cache->rbtree, &ctx->key, hash);
|
||||
|
||||
if (node) {
|
||||
if (node->valid > ngx_time()) {
|
||||
ctx->status = node->status;
|
||||
ngx_shmtx_unlock(&shpool->mutex);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
|
||||
"ssl ocsp cache hit, %s",
|
||||
OCSP_cert_status_str(ctx->status));
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ngx_queue_remove(&node->queue);
|
||||
ngx_rbtree_delete(&cache->rbtree, &node->node.node);
|
||||
ngx_slab_free_locked(shpool, node);
|
||||
|
||||
ngx_shmtx_unlock(&shpool->mutex);
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
|
||||
"ssl ocsp cache expired");
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
ngx_shmtx_unlock(&shpool->mutex);
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, "ssl ocsp cache miss");
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_ssl_ocsp_cache_store(ngx_ssl_ocsp_ctx_t *ctx)
|
||||
{
|
||||
time_t now, valid;
|
||||
uint32_t hash;
|
||||
ngx_queue_t *q;
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
ngx_slab_pool_t *shpool;
|
||||
ngx_ssl_ocsp_cache_t *cache;
|
||||
ngx_ssl_ocsp_cache_node_t *node;
|
||||
|
||||
shm_zone = ctx->shm_zone;
|
||||
|
||||
if (shm_zone == NULL) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
valid = ctx->valid;
|
||||
|
||||
now = ngx_time();
|
||||
|
||||
if (valid < now) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (valid == NGX_MAX_TIME_T_VALUE) {
|
||||
valid = now + 3600;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
|
||||
"ssl ocsp cache store, valid:%T", valid - now);
|
||||
|
||||
cache = shm_zone->data;
|
||||
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
|
||||
hash = ngx_hash_key(ctx->key.data, ctx->key.len);
|
||||
|
||||
ngx_shmtx_lock(&shpool->mutex);
|
||||
|
||||
node = ngx_slab_calloc_locked(shpool,
|
||||
sizeof(ngx_ssl_ocsp_cache_node_t) + ctx->key.len);
|
||||
if (node == NULL) {
|
||||
|
||||
if (!ngx_queue_empty(&cache->expire_queue)) {
|
||||
q = ngx_queue_last(&cache->expire_queue);
|
||||
node = ngx_queue_data(q, ngx_ssl_ocsp_cache_node_t, queue);
|
||||
|
||||
ngx_rbtree_delete(&cache->rbtree, &node->node.node);
|
||||
ngx_queue_remove(q);
|
||||
ngx_slab_free_locked(shpool, node);
|
||||
|
||||
node = ngx_slab_alloc_locked(shpool,
|
||||
sizeof(ngx_ssl_ocsp_cache_node_t) + ctx->key.len);
|
||||
}
|
||||
|
||||
if (node == NULL) {
|
||||
ngx_shmtx_unlock(&shpool->mutex);
|
||||
ngx_log_error(NGX_LOG_ALERT, ctx->log, 0,
|
||||
"could not allocate new entry%s", shpool->log_ctx);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
node->node.str.len = ctx->key.len;
|
||||
node->node.str.data = (u_char *) node + sizeof(ngx_ssl_ocsp_cache_node_t);
|
||||
ngx_memcpy(node->node.str.data, ctx->key.data, ctx->key.len);
|
||||
node->node.node.key = hash;
|
||||
node->status = ctx->status;
|
||||
node->valid = valid;
|
||||
|
||||
ngx_rbtree_insert(&cache->rbtree, &node->node.node);
|
||||
ngx_queue_insert_head(&cache->expire_queue, &node->queue);
|
||||
|
||||
ngx_shmtx_unlock(&shpool->mutex);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_ssl_ocsp_create_key(ngx_ssl_ocsp_ctx_t *ctx)
|
||||
{
|
||||
u_char *p;
|
||||
X509_NAME *name;
|
||||
ASN1_INTEGER *serial;
|
||||
|
||||
p = ngx_pnalloc(ctx->pool, 60);
|
||||
if (p == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->key.data = p;
|
||||
ctx->key.len = 60;
|
||||
|
||||
name = X509_get_subject_name(ctx->issuer);
|
||||
if (X509_NAME_digest(name, EVP_sha1(), p, NULL) == 0) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
p += 20;
|
||||
|
||||
if (X509_pubkey_digest(ctx->issuer, EVP_sha1(), p, NULL) == 0) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
p += 20;
|
||||
|
||||
serial = X509_get_serialNumber(ctx->cert);
|
||||
if (serial->length > 20) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
p = ngx_cpymem(p, serial->data, serial->length);
|
||||
ngx_memzero(p, 20 - serial->length);
|
||||
|
||||
#if (NGX_DEBUG)
|
||||
{
|
||||
u_char buf[120];
|
||||
|
||||
ngx_hex_dump(buf, ctx->key.data, ctx->key.len);
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
|
||||
"ssl ocsp key %*s", sizeof(buf), buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static u_char *
|
||||
ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len)
|
||||
{
|
||||
@@ -1891,4 +2729,50 @@ 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_shm_zone_t *shm_zone)
|
||||
{
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data)
|
||||
{
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -50,6 +50,8 @@ static char *ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_http_ssl_ocsp_cache(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf);
|
||||
|
||||
@@ -74,6 +76,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"
|
||||
};
|
||||
@@ -221,6 +231,27 @@ 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_ocsp_cache"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_http_ssl_ocsp_cache,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_stapling"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
@@ -603,6 +634,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 };
|
||||
*/
|
||||
@@ -621,6 +653,8 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
|
||||
sscf->session_timeout_tls13 = NGX_CONF_UNSET;
|
||||
sscf->session_tickets = NGX_CONF_UNSET;
|
||||
sscf->session_ticket_keys = NGX_CONF_UNSET_PTR;
|
||||
sscf->ocsp = NGX_CONF_UNSET_UINT;
|
||||
sscf->ocsp_cache_zone = NGX_CONF_UNSET_PTR;
|
||||
sscf->stapling = NGX_CONF_UNSET;
|
||||
sscf->stapling_verify = NGX_CONF_UNSET;
|
||||
sscf->dyn_rec_enable = NGX_CONF_UNSET;
|
||||
@@ -691,6 +725,11 @@ 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_ptr_value(conf->ocsp_cache_zone,
|
||||
prev->ocsp_cache_zone, NULL);
|
||||
|
||||
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, "");
|
||||
@@ -866,6 +905,23 @@ 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,
|
||||
conf->ocsp_cache_zone)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
@@ -1186,6 +1242,85 @@ invalid:
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_ssl_ocsp_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_ssl_srv_conf_t *sscf = conf;
|
||||
|
||||
size_t len;
|
||||
ngx_int_t n;
|
||||
ngx_str_t *value, name, size;
|
||||
ngx_uint_t j;
|
||||
|
||||
if (sscf->ocsp_cache_zone != NGX_CONF_UNSET_PTR) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
if (ngx_strcmp(value[1].data, "off") == 0) {
|
||||
sscf->ocsp_cache_zone = NULL;
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
if (value[1].len <= sizeof("shared:") - 1
|
||||
|| ngx_strncmp(value[1].data, "shared:", sizeof("shared:") - 1) != 0)
|
||||
{
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
len = 0;
|
||||
|
||||
for (j = sizeof("shared:") - 1; j < value[1].len; j++) {
|
||||
if (value[1].data[j] == ':') {
|
||||
break;
|
||||
}
|
||||
|
||||
len++;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
name.len = len;
|
||||
name.data = value[1].data + sizeof("shared:") - 1;
|
||||
|
||||
size.len = value[1].len - j - 1;
|
||||
size.data = name.data + len + 1;
|
||||
|
||||
n = ngx_parse_size(&size);
|
||||
|
||||
if (n == NGX_ERROR) {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
if (n < (ngx_int_t) (8 * ngx_pagesize)) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"OCSP cache \"%V\" is too small", &value[1]);
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
sscf->ocsp_cache_zone = ngx_shared_memory_add(cf, &name, n,
|
||||
&ngx_http_ssl_module_ctx);
|
||||
if (sscf->ocsp_cache_zone == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
sscf->ocsp_cache_zone->init = ngx_ssl_ocsp_cache_init;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
|
||||
invalid:
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid OCSP cache \"%V\"", &value[1]);
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_ssl_init(ngx_conf_t *cf)
|
||||
{
|
||||
@@ -1204,17 +1339,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,10 @@ typedef struct {
|
||||
ngx_flag_t session_tickets;
|
||||
ngx_array_t *session_ticket_keys;
|
||||
|
||||
ngx_uint_t ocsp;
|
||||
ngx_str_t ocsp_responder;
|
||||
ngx_shm_zone_t *ocsp_cache_zone;
|
||||
|
||||
ngx_flag_t stapling;
|
||||
ngx_flag_t stapling_verify;
|
||||
ngx_str_t stapling_file;
|
||||
|
||||
@@ -2003,6 +2003,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) {
|
||||
@@ -2051,6 +2052,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user