mirror of
https://github.com/nginx/nginx.git
synced 2024-11-27 15:39:01 +08:00
SSL: caching CA certificates.
This can potentially provide a large amount of savings, because CA certificates can be quite large. Based on previous work by Mini Hawthorne.
This commit is contained in:
parent
61314518de
commit
5917e9de5a
@ -22,6 +22,8 @@ static ngx_inline ngx_int_t ngx_ssl_cert_already_in_hash(void);
|
||||
static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store);
|
||||
static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where,
|
||||
int ret);
|
||||
static int ngx_ssl_cmp_x509_name(const X509_NAME *const *a,
|
||||
const X509_NAME *const *b);
|
||||
static void ngx_ssl_passwords_cleanup(void *data);
|
||||
static int ngx_ssl_new_client_session(ngx_ssl_conn_t *ssl_conn,
|
||||
ngx_ssl_session_t *sess);
|
||||
@ -656,6 +658,12 @@ ngx_int_t
|
||||
ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
|
||||
ngx_int_t depth)
|
||||
{
|
||||
int n, i;
|
||||
char *err;
|
||||
X509 *x509;
|
||||
X509_NAME *name;
|
||||
X509_STORE *store;
|
||||
STACK_OF(X509) *chain;
|
||||
STACK_OF(X509_NAME) *list;
|
||||
|
||||
SSL_CTX_set_verify(ssl->ctx, SSL_VERIFY_PEER, ngx_ssl_verify_callback);
|
||||
@ -666,34 +674,84 @@ ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL)
|
||||
== 0)
|
||||
{
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"SSL_CTX_load_verify_locations(\"%s\") failed",
|
||||
cert->data);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* SSL_CTX_load_verify_locations() may leave errors in the error queue
|
||||
* while returning success
|
||||
*/
|
||||
|
||||
ERR_clear_error();
|
||||
|
||||
list = SSL_load_client_CA_file((char *) cert->data);
|
||||
|
||||
list = sk_X509_NAME_new(ngx_ssl_cmp_x509_name);
|
||||
if (list == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"SSL_load_client_CA_file(\"%s\") failed", cert->data);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
store = SSL_CTX_get_cert_store(ssl->ctx);
|
||||
|
||||
if (store == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"SSL_CTX_get_cert_store() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CA, &err, cert, NULL);
|
||||
if (chain == NULL) {
|
||||
if (err != NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"cannot load certificate \"%s\": %s",
|
||||
cert->data, err);
|
||||
}
|
||||
|
||||
sk_X509_NAME_pop_free(list, X509_NAME_free);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
n = sk_X509_num(chain);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
x509 = sk_X509_value(chain, i);
|
||||
|
||||
if (X509_STORE_add_cert(store, x509) != 1) {
|
||||
|
||||
if (ngx_ssl_cert_already_in_hash()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"X509_STORE_add_cert(\"%s\") failed", cert->data);
|
||||
sk_X509_NAME_pop_free(list, X509_NAME_free);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
name = X509_get_subject_name(x509);
|
||||
if (name == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"X509_get_subject_name(\"%s\") failed", cert->data);
|
||||
sk_X509_NAME_pop_free(list, X509_NAME_free);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
name = X509_NAME_dup(name);
|
||||
if (name == NULL) {
|
||||
sk_X509_NAME_pop_free(list, X509_NAME_free);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
if (sk_X509_NAME_find(list, NULL, name) > 0) {
|
||||
#else
|
||||
if (sk_X509_NAME_find(list, name) >= 0) {
|
||||
#endif
|
||||
X509_NAME_free(name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sk_X509_NAME_push(list, name) == 0) {
|
||||
sk_X509_NAME_pop_free(list, X509_NAME_free);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
X509_NAME_free(name);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
|
||||
SSL_CTX_set_client_CA_list(ssl->ctx, list);
|
||||
|
||||
return NGX_OK;
|
||||
@ -704,6 +762,12 @@ ngx_int_t
|
||||
ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
|
||||
ngx_int_t depth)
|
||||
{
|
||||
int i, n;
|
||||
char *err;
|
||||
X509 *x509;
|
||||
X509_STORE *store;
|
||||
STACK_OF(X509) *chain;
|
||||
|
||||
SSL_CTX_set_verify(ssl->ctx, SSL_CTX_get_verify_mode(ssl->ctx),
|
||||
ngx_ssl_verify_callback);
|
||||
|
||||
@ -713,25 +777,44 @@ ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
store = SSL_CTX_get_cert_store(ssl->ctx);
|
||||
|
||||
if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL)
|
||||
== 0)
|
||||
{
|
||||
if (store == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"SSL_CTX_load_verify_locations(\"%s\") failed",
|
||||
cert->data);
|
||||
"SSL_CTX_get_cert_store() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* SSL_CTX_load_verify_locations() may leave errors in the error queue
|
||||
* while returning success
|
||||
*/
|
||||
chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CA, &err, cert, NULL);
|
||||
if (chain == NULL) {
|
||||
if (err != NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"cannot load certificate \"%s\": %s",
|
||||
cert->data, err);
|
||||
}
|
||||
|
||||
ERR_clear_error();
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
n = sk_X509_num(chain);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
x509 = sk_X509_value(chain, i);
|
||||
|
||||
if (X509_STORE_add_cert(store, x509) != 1) {
|
||||
|
||||
if (ngx_ssl_cert_already_in_hash()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"X509_STORE_add_cert(\"%s\") failed", cert->data);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
@ -987,6 +1070,13 @@ ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret)
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ngx_ssl_cmp_x509_name(const X509_NAME *const *a, const X509_NAME *const *b)
|
||||
{
|
||||
return (X509_NAME_cmp(*a, *b));
|
||||
}
|
||||
|
||||
|
||||
ngx_array_t *
|
||||
ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file)
|
||||
{
|
||||
|
@ -196,6 +196,7 @@ typedef struct {
|
||||
#define NGX_SSL_CACHE_CERT 0
|
||||
#define NGX_SSL_CACHE_PKEY 1
|
||||
#define NGX_SSL_CACHE_CRL 2
|
||||
#define NGX_SSL_CACHE_CA 3
|
||||
|
||||
|
||||
ngx_int_t ngx_ssl_init(ngx_log_t *log);
|
||||
|
@ -70,6 +70,9 @@ static void *ngx_ssl_cache_crl_create(ngx_ssl_cache_key_t *id, char **err,
|
||||
static void ngx_ssl_cache_crl_free(void *data);
|
||||
static void *ngx_ssl_cache_crl_ref(char **err, void *data);
|
||||
|
||||
static void *ngx_ssl_cache_ca_create(ngx_ssl_cache_key_t *id, char **err,
|
||||
void *data);
|
||||
|
||||
static BIO *ngx_ssl_cache_create_bio(ngx_ssl_cache_key_t *id, char **err);
|
||||
|
||||
static void *ngx_openssl_cache_create_conf(ngx_cycle_t *cycle);
|
||||
@ -117,6 +120,11 @@ static ngx_ssl_cache_type_t ngx_ssl_cache_types[] = {
|
||||
{ ngx_ssl_cache_crl_create,
|
||||
ngx_ssl_cache_crl_free,
|
||||
ngx_ssl_cache_crl_ref },
|
||||
|
||||
/* NGX_SSL_CACHE_CA */
|
||||
{ ngx_ssl_cache_ca_create,
|
||||
ngx_ssl_cache_cert_free,
|
||||
ngx_ssl_cache_cert_ref }
|
||||
};
|
||||
|
||||
|
||||
@ -623,6 +631,64 @@ ngx_ssl_cache_crl_ref(char **err, void *data)
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_ssl_cache_ca_create(ngx_ssl_cache_key_t *id, char **err, void *data)
|
||||
{
|
||||
BIO *bio;
|
||||
X509 *x509;
|
||||
u_long n;
|
||||
STACK_OF(X509) *chain;
|
||||
|
||||
chain = sk_X509_new_null();
|
||||
if (chain == NULL) {
|
||||
*err = "sk_X509_new_null() failed";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bio = ngx_ssl_cache_create_bio(id, err);
|
||||
if (bio == NULL) {
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
x509 = PEM_read_bio_X509_AUX(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
|
||||
&& sk_X509_num(chain) > 0)
|
||||
{
|
||||
/* end of file */
|
||||
ERR_clear_error();
|
||||
break;
|
||||
}
|
||||
|
||||
/* some real error */
|
||||
|
||||
*err = "PEM_read_bio_X509_AUX() failed";
|
||||
BIO_free(bio);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sk_X509_push(chain, x509) == 0) {
|
||||
*err = "sk_X509_push() failed";
|
||||
BIO_free(bio);
|
||||
X509_free(x509);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
BIO_free(bio);
|
||||
|
||||
return chain;
|
||||
}
|
||||
|
||||
|
||||
static BIO *
|
||||
ngx_ssl_cache_create_bio(ngx_ssl_cache_key_t *id, char **err)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user