OCSP stapling: loading OCSP responses.

This includes the ssl_stapling_responder directive (defaults to OCSP
responder set in certificate's AIA extension).

OCSP response for a given certificate is requested once we get at least
one connection with certificate_status extension in ClientHello, and
certificate status won't be sent in the connection in question.  This due
to limitations in the OpenSSL API (certificate status callback is blocking).

Note: SSL_CTX_use_certificate_chain_file() was reimplemented as it doesn't
allow to access the certificate loaded via SSL_CTX.
This commit is contained in:
Maxim Dounin 2012-10-01 12:47:55 +00:00
parent f7ec295fb4
commit 74ad4494a6
6 changed files with 1761 additions and 40 deletions

View File

@ -69,12 +69,12 @@ typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c);
#include <ngx_slab.h>
#include <ngx_inet.h>
#include <ngx_cycle.h>
#include <ngx_resolver.h>
#if (NGX_OPENSSL)
#include <ngx_event_openssl.h>
#endif
#include <ngx_process_cycle.h>
#include <ngx_conf_file.h>
#include <ngx_resolver.h>
#include <ngx_open_file_cache.h>
#include <ngx_os.h>
#include <ngx_connection.h>

View File

@ -82,6 +82,8 @@ ngx_module_t ngx_openssl_module = {
int ngx_ssl_connection_index;
int ngx_ssl_server_conf_index;
int ngx_ssl_session_cache_index;
int ngx_ssl_certificate_index;
int ngx_ssl_stapling_index;
ngx_int_t
@ -137,6 +139,22 @@ ngx_ssl_init(ngx_log_t *log)
return NGX_ERROR;
}
ngx_ssl_certificate_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
NULL);
if (ngx_ssl_certificate_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0,
"SSL_CTX_get_ex_new_index() failed");
return NGX_ERROR;
}
ngx_ssl_stapling_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
NULL);
if (ngx_ssl_stapling_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0,
"SSL_CTX_get_ex_new_index() failed");
return NGX_ERROR;
}
return NGX_OK;
}
@ -218,19 +236,89 @@ ngx_int_t
ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
ngx_str_t *key)
{
BIO *bio;
X509 *x509;
u_long n;
if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
return NGX_ERROR;
}
if (SSL_CTX_use_certificate_chain_file(ssl->ctx, (char *) cert->data)
/*
* we can't use SSL_CTX_use_certificate_chain_file() as it doesn't
* allow to access certificate later from SSL_CTX, so we reimplement
* it here
*/
bio = BIO_new_file((char *) cert->data, "r");
if (bio == NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"BIO_new_file(\"%s\") failed", cert->data);
return NGX_ERROR;
}
x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
if (x509 == NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"PEM_read_bio_X509_AUX(\"%s\") failed", cert->data);
BIO_free(bio);
return NGX_ERROR;
}
if (SSL_CTX_use_certificate(ssl->ctx, x509) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_use_certificate(\"%s\") failed", cert->data);
X509_free(x509);
BIO_free(bio);
return NGX_ERROR;
}
if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509)
== 0)
{
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_use_certificate_chain_file(\"%s\") failed",
cert->data);
"SSL_CTX_set_ex_data() failed");
return NGX_ERROR;
}
X509_free(x509);
/* read rest of the chain */
for ( ;; ) {
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
if (x509 == NULL) {
n = ERR_peek_last_error();
if (ERR_GET_LIB(n) == ERR_LIB_PEM
&& ERR_GET_REASON(n) == PEM_R_NO_START_LINE)
{
/* end of file */
ERR_clear_error();
break;
}
/* some real error */
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"PEM_read_bio_X509(\"%s\") failed", cert->data);
BIO_free(bio);
return NGX_ERROR;
}
if (SSL_CTX_add_extra_chain_cert(ssl->ctx, x509) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_add_extra_chain_cert(\"%s\") failed",
cert->data);
X509_free(x509);
BIO_free(bio);
return NGX_ERROR;
}
}
BIO_free(bio);
if (ngx_conf_full_name(cf->cycle, key, 1) != NGX_OK) {
return NGX_ERROR;
}

View File

@ -105,7 +105,10 @@ ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_int_t ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_str_t *cert, ngx_int_t depth);
ngx_int_t ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl);
ngx_int_t ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file);
ngx_int_t ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_str_t *responder, ngx_str_t *file);
ngx_int_t ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_resolver_t *resolver, ngx_msec_t resolver_timeout);
RSA *ngx_ssl_rsa512_key_callback(SSL *ssl, int is_export, int key_length);
ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file);
ngx_int_t ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name);
@ -161,6 +164,8 @@ void ngx_ssl_cleanup_ctx(void *data);
extern int ngx_ssl_connection_index;
extern int ngx_ssl_server_conf_index;
extern int ngx_ssl_session_cache_index;
extern int ngx_ssl_certificate_index;
extern int ngx_ssl_stapling_index;
#endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,8 @@ static char *ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd,
static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf);
static ngx_conf_bitmask_t ngx_http_ssl_protocols[] = {
{ ngx_string("SSLv2"), NGX_SSL_SSLv2 },
@ -173,13 +175,20 @@ static ngx_command_t ngx_http_ssl_commands[] = {
offsetof(ngx_http_ssl_srv_conf_t, stapling_file),
NULL },
{ ngx_string("ssl_stapling_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, stapling_responder),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_ssl_module_ctx = {
ngx_http_ssl_add_variables, /* preconfiguration */
NULL, /* postconfiguration */
ngx_http_ssl_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
@ -351,6 +360,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
* sscf->ciphers = { 0, NULL };
* sscf->shm_zone = NULL;
* sscf->stapling_file = { 0, NULL };
* sscf->stapling_responder = { 0, NULL };
*/
sscf->enable = NGX_CONF_UNSET;
@ -415,6 +425,8 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->stapling, prev->stapling, 0);
ngx_conf_merge_str_value(conf->stapling_file, prev->stapling_file, "");
ngx_conf_merge_str_value(conf->stapling_responder,
prev->stapling_responder, "");
conf->ssl.log = cf->log;
@ -551,10 +563,15 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
return NGX_CONF_ERROR;
}
if (conf->stapling
&& ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_file) != NGX_OK)
{
return NGX_CONF_ERROR;
if (conf->stapling) {
if (ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_responder,
&conf->stapling_file)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
}
return NGX_CONF_OK;
@ -692,3 +709,37 @@ invalid:
return NGX_CONF_ERROR;
}
static ngx_int_t
ngx_http_ssl_init(ngx_conf_t *cf)
{
ngx_uint_t s;
ngx_http_ssl_srv_conf_t *sscf;
ngx_http_core_loc_conf_t *clcf;
ngx_http_core_srv_conf_t **cscfp;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
cscfp = cmcf->servers.elts;
for (s = 0; s < cmcf->servers.nelts; s++) {
sscf = cscfp[s]->ctx->srv_conf[ngx_http_ssl_module.ctx_index];
if (!sscf->stapling) {
continue;
}
clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];
if (ngx_ssl_stapling_resolver(cf, &sscf->ssl, clcf->resolver,
clcf->resolver_timeout)
!= NGX_OK)
{
return NGX_ERROR;
}
}
return NGX_OK;
}

View File

@ -44,6 +44,7 @@ typedef struct {
ngx_flag_t stapling;
ngx_str_t stapling_file;
ngx_str_t stapling_responder;
u_char *file;
ngx_uint_t line;