# HG changeset patch # User Maxim Dounin # Date 1410789587 -14400 # Node ID 1ee1db30c9b96e9e43e85ab0bfba42140af24966 # Parent 60d2cb03faee4201ed29bce61d84c79401e6a337 SSL: session id context now includes certificate hash. This prevents inappropriate session reuse in unrelated server{} blocks, while preserving ability to restore sessions on other servers when using TLS Session Tickets. Additionally, session context is now set even if there is no session cache configured. This is needed as it's also used for TLS Session Tickets. Thanks to Antoine Delignat-Lavaud and Piotr Sikora. diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -27,6 +27,8 @@ static void ngx_ssl_connection_error(ngx ngx_err_t err, char *text); static void ngx_ssl_clear_error(ngx_log_t *log); +static ngx_int_t ngx_ssl_session_id_context(ngx_ssl_t *ssl, + ngx_str_t *sess_ctx); ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data); static int ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess); @@ -1729,13 +1731,15 @@ ngx_ssl_session_cache(ngx_ssl_t *ssl, ng SSL_CTX_set_timeout(ssl->ctx, (long) timeout); + if (ngx_ssl_session_id_context(ssl, sess_ctx) != NGX_OK) { + return NGX_ERROR; + } + if (builtin_session_cache == NGX_SSL_NO_SCACHE) { SSL_CTX_set_session_cache_mode(ssl->ctx, SSL_SESS_CACHE_OFF); return NGX_OK; } - SSL_CTX_set_session_id_context(ssl->ctx, sess_ctx->data, sess_ctx->len); - if (builtin_session_cache == NGX_SSL_NONE_SCACHE) { /* @@ -1792,6 +1796,96 @@ ngx_ssl_session_cache(ngx_ssl_t *ssl, ng } +static ngx_int_t +ngx_ssl_session_id_context(ngx_ssl_t *ssl, ngx_str_t *sess_ctx) +{ + int n, i; + X509 *cert; + X509_NAME *name; + EVP_MD_CTX md; + unsigned int len; + STACK_OF(X509_NAME) *list; + u_char buf[EVP_MAX_MD_SIZE]; + + /* + * Session ID context is set based on the string provided, + * the server certificate, and the client CA list. + */ + + EVP_MD_CTX_init(&md); + + if (EVP_DigestInit_ex(&md, EVP_sha1(), NULL) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "EVP_DigestInit_ex() failed"); + goto failed; + } + + if (EVP_DigestUpdate(&md, sess_ctx->data, sess_ctx->len) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "EVP_DigestUpdate() failed"); + goto failed; + } + + cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index); + + if (X509_digest(cert, EVP_sha1(), buf, &len) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "X509_digest() failed"); + goto failed; + } + + if (EVP_DigestUpdate(&md, buf, len) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "EVP_DigestUpdate() failed"); + goto failed; + } + + list = SSL_CTX_get_client_CA_list(ssl->ctx); + + if (list != NULL) { + n = sk_X509_NAME_num(list); + + for (i = 0; i < n; i++) { + name = sk_X509_NAME_value(list, i); + + if (X509_NAME_digest(name, EVP_sha1(), buf, &len) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "X509_NAME_digest() failed"); + goto failed; + } + + if (EVP_DigestUpdate(&md, buf, len) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "EVP_DigestUpdate() failed"); + goto failed; + } + } + } + + if (EVP_DigestFinal_ex(&md, buf, &len) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "EVP_DigestUpdate() failed"); + goto failed; + } + + EVP_MD_CTX_cleanup(&md); + + if (SSL_CTX_set_session_id_context(ssl->ctx, buf, len) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_set_session_id_context() failed"); + return NGX_ERROR; + } + + return NGX_OK; + +failed: + + EVP_MD_CTX_cleanup(&md); + + return NGX_ERROR; +} + + ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data) {