summaryrefslogtreecommitdiffhomepage
path: root/src/event/ngx_event_openssl.c
diff options
context:
space:
mode:
authorSergey Kandaurov <pluknet@nginx.com>2022-10-20 16:41:36 +0400
committerSergey Kandaurov <pluknet@nginx.com>2022-10-20 16:41:36 +0400
commit3123fac3e764b4706881ffcb8f8b554c1628c5e0 (patch)
tree0a8345c637b00eae13b983af54d9ab51426a6c72 /src/event/ngx_event_openssl.c
parent3cd06a3a9a1f815cbd5391211947d7aed4feb061 (diff)
parent09d5305592c3ff8ff30e54dc3e9ce3dbac72cf59 (diff)
downloadnginx-3123fac3e764b4706881ffcb8f8b554c1628c5e0.tar.gz
nginx-3123fac3e764b4706881ffcb8f8b554c1628c5e0.tar.bz2
Merged with the default branch.
Diffstat (limited to 'src/event/ngx_event_openssl.c')
-rw-r--r--src/event/ngx_event_openssl.c445
1 files changed, 331 insertions, 114 deletions
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
index ee0089030..568c34828 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -71,10 +71,11 @@ static void ngx_ssl_session_rbtree_insert_value(ngx_rbtree_node_t *temp,
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
-static int ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
+static int ngx_ssl_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx,
HMAC_CTX *hctx, int enc);
-static void ngx_ssl_session_ticket_keys_cleanup(void *data);
+static ngx_int_t ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ctx, ngx_log_t *log);
+static void ngx_ssl_ticket_keys_cleanup(void *data);
#endif
#ifndef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT
@@ -131,7 +132,7 @@ 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_session_ticket_keys_index;
+int ngx_ssl_ticket_keys_index;
int ngx_ssl_ocsp_index;
int ngx_ssl_certificate_index;
int ngx_ssl_next_certificate_index;
@@ -208,9 +209,9 @@ ngx_ssl_init(ngx_log_t *log)
return NGX_ERROR;
}
- ngx_ssl_session_ticket_keys_index = SSL_CTX_get_ex_new_index(0, NULL, NULL,
- NULL, NULL);
- if (ngx_ssl_session_ticket_keys_index == -1) {
+ ngx_ssl_ticket_keys_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
+ NULL);
+ if (ngx_ssl_ticket_keys_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0,
"SSL_CTX_get_ex_new_index() failed");
return NGX_ERROR;
@@ -1085,6 +1086,53 @@ ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret)
#endif
+#ifdef TLS1_3_VERSION
+
+ if ((where & SSL_CB_ACCEPT_LOOP) == SSL_CB_ACCEPT_LOOP
+ && SSL_version(ssl_conn) == TLS1_3_VERSION)
+ {
+ time_t now, time, timeout, conf_timeout;
+ SSL_SESSION *sess;
+
+ /*
+ * OpenSSL with TLSv1.3 updates the session creation time on
+ * session resumption and keeps the session timeout unmodified,
+ * making it possible to maintain the session forever, bypassing
+ * client certificate expiration and revocation. To make sure
+ * session timeouts are actually used, we now update the session
+ * creation time and reduce the session timeout accordingly.
+ *
+ * BoringSSL with TLSv1.3 ignores configured session timeouts
+ * and uses a hardcoded timeout instead, 7 days. So we update
+ * session timeout to the configured value as soon as a session
+ * is created.
+ */
+
+ c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
+ sess = SSL_get0_session(ssl_conn);
+
+ if (!c->ssl->session_timeout_set && sess) {
+ c->ssl->session_timeout_set = 1;
+
+ now = ngx_time();
+ time = SSL_SESSION_get_time(sess);
+ timeout = SSL_SESSION_get_timeout(sess);
+ conf_timeout = SSL_CTX_get_timeout(c->ssl->session_ctx);
+
+ timeout = ngx_min(timeout, conf_timeout);
+
+ if (now - time >= timeout) {
+ SSL_SESSION_set1_id_context(sess, (unsigned char *) "", 0);
+
+ } else {
+ SSL_SESSION_set_time(sess, now);
+ SSL_SESSION_set_timeout(sess, timeout - (now - time));
+ }
+ }
+ }
+
+#endif
+
if ((where & SSL_CB_ACCEPT_LOOP) == SSL_CB_ACCEPT_LOOP) {
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
@@ -1426,9 +1474,9 @@ ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name)
SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_ECDH_USE);
-#if SSL_CTRL_SET_ECDH_AUTO
+#ifdef SSL_CTRL_SET_ECDH_AUTO
/* not needed in OpenSSL 1.1.0+ */
- SSL_CTX_set_ecdh_auto(ssl->ctx, 1);
+ (void) SSL_CTX_set_ecdh_auto(ssl->ctx, 1);
#endif
if (ngx_strcmp(name->data, "auto") == 0) {
@@ -1769,7 +1817,7 @@ ngx_ssl_handshake(ngx_connection_t *c)
#endif
#endif
-#ifdef BIO_get_ktls_send
+#if (defined BIO_get_ktls_send && !NGX_WIN32)
if (BIO_get_ktls_send(SSL_get_wbio(c->ssl->connection)) == 1) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
@@ -1914,7 +1962,7 @@ ngx_ssl_try_early_data(ngx_connection_t *c)
c->read->ready = 1;
c->write->ready = 1;
-#ifdef BIO_get_ktls_send
+#if (defined BIO_get_ktls_send && !NGX_WIN32)
if (BIO_get_ktls_send(SSL_get_wbio(c->ssl->connection)) == 1) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
@@ -2943,7 +2991,7 @@ ngx_ssl_write_early(ngx_connection_t *c, u_char *data, size_t size)
static ssize_t
ngx_ssl_sendfile(ngx_connection_t *c, ngx_buf_t *file, size_t size)
{
-#ifdef BIO_get_ktls_send
+#if (defined BIO_get_ktls_send && !NGX_WIN32)
int sslerr, flags;
ssize_t n;
@@ -3430,6 +3478,9 @@ ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err,
#ifdef SSL_R_VERSION_TOO_LOW
|| n == SSL_R_VERSION_TOO_LOW /* 396 */
#endif
+#ifdef SSL_R_BAD_RECORD_TYPE
+ || n == SSL_R_BAD_RECORD_TYPE /* 443 */
+#endif
|| n == 1000 /* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */
#ifdef SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE
|| n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE /* 1010 */
@@ -3774,6 +3825,12 @@ ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data)
ngx_queue_init(&cache->expire_queue);
+ cache->ticket_keys[0].expire = 0;
+ cache->ticket_keys[1].expire = 0;
+ cache->ticket_keys[2].expire = 0;
+
+ cache->fail_time = 0;
+
len = sizeof(" in SSL session shared cache \"\"") + shm_zone->shm.name.len;
shpool->log_ctx = ngx_slab_alloc(shpool, len);
@@ -3792,16 +3849,16 @@ ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data)
/*
* The length of the session id is 16 bytes for SSLv2 sessions and
- * between 1 and 32 bytes for SSLv3/TLSv1, typically 32 bytes.
- * It seems that the typical length of the external ASN1 representation
- * of a session is 118 or 119 bytes for SSLv3/TSLv1.
+ * between 1 and 32 bytes for SSLv3 and TLS, typically 32 bytes.
+ * Typical length of the external ASN1 representation of a session
+ * is about 150 bytes plus SNI server name.
*
- * Thus on 32-bit platforms we allocate separately an rbtree node,
- * a session id, and an ASN1 representation, they take accordingly
- * 64, 32, and 128 bytes.
+ * On 32-bit platforms we allocate an rbtree node, a session id, and
+ * an ASN1 representation in a single allocation, it typically takes
+ * 256 bytes.
*
* On 64-bit platforms we allocate separately an rbtree node + session_id,
- * and an ASN1 representation, they take accordingly 128 and 128 bytes.
+ * and an ASN1 representation, they take accordingly 128 and 256 bytes.
*
* OpenSSL's i2d_SSL_SESSION() and d2i_SSL_SESSION are slow,
* so they are outside the code locked by shared pool mutex
@@ -3811,7 +3868,8 @@ static int
ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
{
int len;
- u_char *p, *id, *cached_sess, *session_id;
+ u_char *p, *session_id;
+ size_t n;
uint32_t hash;
SSL_CTX *ssl_ctx;
unsigned int session_id_length;
@@ -3822,17 +3880,42 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
ngx_ssl_session_cache_t *cache;
u_char buf[NGX_SSL_MAX_SESSION_SIZE];
+#ifdef TLS1_3_VERSION
+
+ /*
+ * OpenSSL tries to save TLSv1.3 sessions into session cache
+ * even when using tickets for stateless session resumption,
+ * "because some applications just want to know about the creation
+ * of a session"; do not cache such sessions
+ */
+
+ if (SSL_version(ssl_conn) == TLS1_3_VERSION
+ && (SSL_get_options(ssl_conn) & SSL_OP_NO_TICKET) == 0)
+ {
+ return 0;
+ }
+
+#endif
+
len = i2d_SSL_SESSION(sess, NULL);
/* do not cache too big session */
- if (len > (int) NGX_SSL_MAX_SESSION_SIZE) {
+ if (len > NGX_SSL_MAX_SESSION_SIZE) {
return 0;
}
p = buf;
i2d_SSL_SESSION(sess, &p);
+ session_id = (u_char *) SSL_SESSION_get_id(sess, &session_id_length);
+
+ /* do not cache sessions with too long session id */
+
+ if (session_id_length > 32) {
+ return 0;
+ }
+
c = ngx_ssl_get_connection(ssl_conn);
ssl_ctx = c->ssl->session_ctx;
@@ -3846,23 +3929,13 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
/* drop one or two expired sessions */
ngx_ssl_expire_sessions(cache, shpool, 1);
- cached_sess = ngx_slab_alloc_locked(shpool, len);
-
- if (cached_sess == NULL) {
-
- /* drop the oldest non-expired session and try once more */
-
- ngx_ssl_expire_sessions(cache, shpool, 0);
-
- cached_sess = ngx_slab_alloc_locked(shpool, len);
-
- if (cached_sess == NULL) {
- sess_id = NULL;
- goto failed;
- }
- }
+#if (NGX_PTR_SIZE == 8)
+ n = sizeof(ngx_ssl_sess_id_t);
+#else
+ n = offsetof(ngx_ssl_sess_id_t, session) + len;
+#endif
- sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_ssl_sess_id_t));
+ sess_id = ngx_slab_alloc_locked(shpool, n);
if (sess_id == NULL) {
@@ -3870,41 +3943,34 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
ngx_ssl_expire_sessions(cache, shpool, 0);
- sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_ssl_sess_id_t));
+ sess_id = ngx_slab_alloc_locked(shpool, n);
if (sess_id == NULL) {
goto failed;
}
}
- session_id = (u_char *) SSL_SESSION_get_id(sess, &session_id_length);
-
#if (NGX_PTR_SIZE == 8)
- id = sess_id->sess_id;
-
-#else
-
- id = ngx_slab_alloc_locked(shpool, session_id_length);
+ sess_id->session = ngx_slab_alloc_locked(shpool, len);
- if (id == NULL) {
+ if (sess_id->session == NULL) {
/* drop the oldest non-expired session and try once more */
ngx_ssl_expire_sessions(cache, shpool, 0);
- id = ngx_slab_alloc_locked(shpool, session_id_length);
+ sess_id->session = ngx_slab_alloc_locked(shpool, len);
- if (id == NULL) {
+ if (sess_id->session == NULL) {
goto failed;
}
}
#endif
- ngx_memcpy(cached_sess, buf, len);
-
- ngx_memcpy(id, session_id, session_id_length);
+ ngx_memcpy(sess_id->session, buf, len);
+ ngx_memcpy(sess_id->id, session_id, session_id_length);
hash = ngx_crc32_short(session_id, session_id_length);
@@ -3914,9 +3980,7 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
sess_id->node.key = hash;
sess_id->node.data = (u_char) session_id_length;
- sess_id->id = id;
sess_id->len = len;
- sess_id->session = cached_sess;
sess_id->expire = ngx_time() + SSL_CTX_get_timeout(ssl_ctx);
@@ -3930,18 +3994,17 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
failed:
- if (cached_sess) {
- ngx_slab_free_locked(shpool, cached_sess);
- }
-
if (sess_id) {
ngx_slab_free_locked(shpool, sess_id);
}
ngx_shmtx_unlock(&shpool->mutex);
- ngx_log_error(NGX_LOG_ALERT, c->log, 0,
- "could not allocate new session%s", shpool->log_ctx);
+ if (cache->fail_time != ngx_time()) {
+ cache->fail_time = ngx_time();
+ ngx_log_error(NGX_LOG_WARN, c->log, 0,
+ "could not allocate new session%s", shpool->log_ctx);
+ }
return 0;
}
@@ -4027,9 +4090,10 @@ ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn,
ngx_rbtree_delete(&cache->session_rbtree, node);
+ ngx_explicit_memzero(sess_id->session, sess_id->len);
+
+#if (NGX_PTR_SIZE == 8)
ngx_slab_free_locked(shpool, sess_id->session);
-#if (NGX_PTR_SIZE == 4)
- ngx_slab_free_locked(shpool, sess_id->id);
#endif
ngx_slab_free_locked(shpool, sess_id);
@@ -4117,9 +4181,10 @@ ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)
ngx_rbtree_delete(&cache->session_rbtree, node);
+ ngx_explicit_memzero(sess_id->session, sess_id->len);
+
+#if (NGX_PTR_SIZE == 8)
ngx_slab_free_locked(shpool, sess_id->session);
-#if (NGX_PTR_SIZE == 4)
- ngx_slab_free_locked(shpool, sess_id->id);
#endif
ngx_slab_free_locked(shpool, sess_id);
@@ -4166,9 +4231,10 @@ ngx_ssl_expire_sessions(ngx_ssl_session_cache_t *cache,
ngx_rbtree_delete(&cache->session_rbtree, &sess_id->node);
+ ngx_explicit_memzero(sess_id->session, sess_id->len);
+
+#if (NGX_PTR_SIZE == 8)
ngx_slab_free_locked(shpool, sess_id->session);
-#if (NGX_PTR_SIZE == 4)
- ngx_slab_free_locked(shpool, sess_id->id);
#endif
ngx_slab_free_locked(shpool, sess_id);
}
@@ -4222,23 +4288,25 @@ ngx_ssl_session_rbtree_insert_value(ngx_rbtree_node_t *temp,
ngx_int_t
ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *paths)
{
- u_char buf[80];
- size_t size;
- ssize_t n;
- ngx_str_t *path;
- ngx_file_t file;
- ngx_uint_t i;
- ngx_array_t *keys;
- ngx_file_info_t fi;
- ngx_pool_cleanup_t *cln;
- ngx_ssl_session_ticket_key_t *key;
-
- if (paths == NULL) {
+ u_char buf[80];
+ size_t size;
+ ssize_t n;
+ ngx_str_t *path;
+ ngx_file_t file;
+ ngx_uint_t i;
+ ngx_array_t *keys;
+ ngx_file_info_t fi;
+ ngx_pool_cleanup_t *cln;
+ ngx_ssl_ticket_key_t *key;
+
+ if (paths == NULL
+ && SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_session_cache_index) == NULL)
+ {
return NGX_OK;
}
- keys = ngx_array_create(cf->pool, paths->nelts,
- sizeof(ngx_ssl_session_ticket_key_t));
+ keys = ngx_array_create(cf->pool, paths ? paths->nelts : 3,
+ sizeof(ngx_ssl_ticket_key_t));
if (keys == NULL) {
return NGX_ERROR;
}
@@ -4248,9 +4316,41 @@ ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *paths)
return NGX_ERROR;
}
- cln->handler = ngx_ssl_session_ticket_keys_cleanup;
+ cln->handler = ngx_ssl_ticket_keys_cleanup;
cln->data = keys;
+ if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_ticket_keys_index, keys) == 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_set_ex_data() failed");
+ return NGX_ERROR;
+ }
+
+ if (SSL_CTX_set_tlsext_ticket_key_cb(ssl->ctx, ngx_ssl_ticket_key_callback)
+ == 0)
+ {
+ ngx_log_error(NGX_LOG_WARN, cf->log, 0,
+ "nginx was built with Session Tickets support, however, "
+ "now it is linked dynamically to an OpenSSL library "
+ "which has no tlsext support, therefore Session Tickets "
+ "are not available");
+ return NGX_OK;
+ }
+
+ if (paths == NULL) {
+
+ /* placeholder for keys in shared memory */
+
+ key = ngx_array_push_n(keys, 3);
+ key[0].shared = 1;
+ key[0].expire = 0;
+ key[1].shared = 1;
+ key[1].expire = 0;
+ key[2].shared = 1;
+ key[2].expire = 0;
+
+ return NGX_OK;
+ }
+
path = paths->elts;
for (i = 0; i < paths->nelts; i++) {
@@ -4305,6 +4405,9 @@ ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *paths)
goto failed;
}
+ key->shared = 0;
+ key->expire = 1;
+
if (size == 48) {
key->size = 48;
ngx_memcpy(key->name, buf, 16);
@@ -4326,25 +4429,6 @@ ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *paths)
ngx_explicit_memzero(&buf, 80);
}
- if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_session_ticket_keys_index, keys)
- == 0)
- {
- ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
- "SSL_CTX_set_ex_data() failed");
- return NGX_ERROR;
- }
-
- if (SSL_CTX_set_tlsext_ticket_key_cb(ssl->ctx,
- ngx_ssl_session_ticket_key_callback)
- == 0)
- {
- ngx_log_error(NGX_LOG_WARN, cf->log, 0,
- "nginx was built with Session Tickets support, however, "
- "now it is linked dynamically to an OpenSSL library "
- "which has no tlsext support, therefore Session Tickets "
- "are not available");
- }
-
return NGX_OK;
failed:
@@ -4361,29 +4445,33 @@ failed:
static int
-ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
+ngx_ssl_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx,
HMAC_CTX *hctx, int enc)
{
- size_t size;
- SSL_CTX *ssl_ctx;
- ngx_uint_t i;
- ngx_array_t *keys;
- ngx_connection_t *c;
- ngx_ssl_session_ticket_key_t *key;
- const EVP_MD *digest;
- const EVP_CIPHER *cipher;
+ size_t size;
+ SSL_CTX *ssl_ctx;
+ ngx_uint_t i;
+ ngx_array_t *keys;
+ ngx_connection_t *c;
+ ngx_ssl_ticket_key_t *key;
+ const EVP_MD *digest;
+ const EVP_CIPHER *cipher;
c = ngx_ssl_get_connection(ssl_conn);
ssl_ctx = c->ssl->session_ctx;
+ if (ngx_ssl_rotate_ticket_keys(ssl_ctx, c->log) != NGX_OK) {
+ return -1;
+ }
+
#ifdef OPENSSL_NO_SHA256
digest = EVP_sha1();
#else
digest = EVP_sha256();
#endif
- keys = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_ticket_keys_index);
+ keys = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_ticket_keys_index);
if (keys == NULL) {
return -1;
}
@@ -4394,7 +4482,7 @@ ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
/* encrypt session ticket */
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "ssl session ticket encrypt, key: \"%*xs\" (%s session)",
+ "ssl ticket encrypt, key: \"%*xs\" (%s session)",
(size_t) 16, key[0].name,
SSL_session_reused(ssl_conn) ? "reused" : "new");
@@ -4441,7 +4529,7 @@ ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
}
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "ssl session ticket decrypt, key: \"%*xs\" not found",
+ "ssl ticket decrypt, key: \"%*xs\" not found",
(size_t) 16, name);
return 0;
@@ -4449,7 +4537,7 @@ ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
found:
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "ssl session ticket decrypt, key: \"%*xs\"%s",
+ "ssl ticket decrypt, key: \"%*xs\"%s",
(size_t) 16, key[i].name, (i == 0) ? " (default)" : "");
if (key[i].size == 48) {
@@ -4486,7 +4574,7 @@ ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
/* renew if non-default key */
- if (i != 0) {
+ if (i != 0 && key[i].expire) {
return 2;
}
@@ -4495,13 +4583,142 @@ ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
}
+static ngx_int_t
+ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ctx, ngx_log_t *log)
+{
+ time_t now, expire;
+ ngx_array_t *keys;
+ ngx_shm_zone_t *shm_zone;
+ ngx_slab_pool_t *shpool;
+ ngx_ssl_ticket_key_t *key;
+ ngx_ssl_session_cache_t *cache;
+ u_char buf[80];
+
+ keys = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_ticket_keys_index);
+ if (keys == NULL) {
+ return NGX_OK;
+ }
+
+ key = keys->elts;
+
+ if (!key[0].shared) {
+ return NGX_OK;
+ }
+
+ /*
+ * if we don't need to update expiration of the current key
+ * and the previous key is still needed, don't sync with shared
+ * memory to save some work; in the worst case other worker process
+ * will switch to the next key, but this process will still be able
+ * to decrypt tickets encrypted with it
+ */
+
+ now = ngx_time();
+ expire = now + SSL_CTX_get_timeout(ssl_ctx);
+
+ if (key[0].expire >= expire && key[1].expire >= now) {
+ return NGX_OK;
+ }
+
+ shm_zone = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_cache_index);
+
+ cache = shm_zone->data;
+ shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+ ngx_shmtx_lock(&shpool->mutex);
+
+ key = cache->ticket_keys;
+
+ if (key[0].expire == 0) {
+
+ /* initialize the current key */
+
+ if (RAND_bytes(buf, 80) != 1) {
+ ngx_ssl_error(NGX_LOG_ALERT, log, 0, "RAND_bytes() failed");
+ ngx_shmtx_unlock(&shpool->mutex);
+ return NGX_ERROR;
+ }
+
+ key[0].shared = 1;
+ key[0].expire = expire;
+ key[0].size = 80;
+ ngx_memcpy(key[0].name, buf, 16);
+ ngx_memcpy(key[0].hmac_key, buf + 16, 32);
+ ngx_memcpy(key[0].aes_key, buf + 48, 32);
+
+ ngx_explicit_memzero(&buf, 80);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0,
+ "ssl ticket key: \"%*xs\"",
+ (size_t) 16, key[0].name);
+
+ /*
+ * copy the current key to the next key, as initialization of
+ * the previous key will replace the current key with the next
+ * key
+ */
+
+ key[2] = key[0];
+ }
+
+ if (key[1].expire < now) {
+
+ /*
+ * if the previous key is no longer needed (or not initialized),
+ * replace it with the current key, replace the current key with
+ * the next key, and generate new next key
+ */
+
+ key[1] = key[0];
+ key[0] = key[2];
+
+ if (RAND_bytes(buf, 80) != 1) {
+ ngx_ssl_error(NGX_LOG_ALERT, log, 0, "RAND_bytes() failed");
+ ngx_shmtx_unlock(&shpool->mutex);
+ return NGX_ERROR;
+ }
+
+ key[2].shared = 1;
+ key[2].expire = 0;
+ key[2].size = 80;
+ ngx_memcpy(key[2].name, buf, 16);
+ ngx_memcpy(key[2].hmac_key, buf + 16, 32);
+ ngx_memcpy(key[2].aes_key, buf + 48, 32);
+
+ ngx_explicit_memzero(&buf, 80);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0,
+ "ssl ticket key: \"%*xs\"",
+ (size_t) 16, key[2].name);
+ }
+
+ /*
+ * update expiration of the current key: it is going to be needed
+ * at least till the session being created expires
+ */
+
+ if (expire > key[0].expire) {
+ key[0].expire = expire;
+ }
+
+ /* sync keys to the worker process memory */
+
+ ngx_memcpy(keys->elts, cache->ticket_keys,
+ 2 * sizeof(ngx_ssl_ticket_key_t));
+
+ ngx_shmtx_unlock(&shpool->mutex);
+
+ return NGX_OK;
+}
+
+
static void
-ngx_ssl_session_ticket_keys_cleanup(void *data)
+ngx_ssl_ticket_keys_cleanup(void *data)
{
ngx_array_t *keys = data;
ngx_explicit_memzero(keys->elts,
- keys->nelts * sizeof(ngx_ssl_session_ticket_key_t));
+ keys->nelts * sizeof(ngx_ssl_ticket_key_t));
}
#else