aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIgor Sysoev <igor@sysoev.ru>2007-01-02 23:55:05 +0000
committerIgor Sysoev <igor@sysoev.ru>2007-01-02 23:55:05 +0000
commit528cdb7c2cfa53012da651e4c16b9bb7ba8ddcd2 (patch)
treebaf19b833ce6f560cb1c9449f260468bc0603708 /src
parent28c7f76635909a219078d2e1bc2ec7a45b408554 (diff)
downloadnginx-528cdb7c2cfa53012da651e4c16b9bb7ba8ddcd2.tar.gz
nginx-528cdb7c2cfa53012da651e4c16b9bb7ba8ddcd2.zip
ssl_session_cache
Diffstat (limited to 'src')
-rw-r--r--src/http/modules/ngx_http_ssl_module.c540
-rw-r--r--src/http/modules/ngx_http_ssl_module.h53
2 files changed, 579 insertions, 14 deletions
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
index 881404692..412aa47fb 100644
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -18,6 +18,17 @@ typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c,
#define NGX_DEFLAUT_CIPHERS "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP"
+#define NGX_HTTP_SSL_MAX_SESSION_SIZE \
+ (4096 - offsetof(ngx_http_ssl_cached_sess_t, asn1))
+
+
+#define NGX_HTTP_SSL_DFLT_BUILTIN_SCACHE -2
+#define NGX_HTTP_SSL_NO_BUILTIN_SCACHE -3
+
+
+static void ngx_http_ssl_expire_sessions(ngx_http_ssl_sesssion_cache_t *cache,
+ ngx_slab_pool_t *shpool, ngx_uint_t expire);
+
static ngx_int_t ngx_http_ssl_static_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_ssl_variable(ngx_http_request_t *r,
@@ -28,6 +39,9 @@ static void *ngx_http_ssl_create_srv_conf(ngx_conf_t *cf);
static char *ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf,
void *parent, void *child);
+static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
#if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE)
static char *ngx_http_ssl_nosupported(ngx_conf_t *cf, ngx_command_t *cmd,
@@ -115,6 +129,13 @@ static ngx_command_t ngx_http_ssl_commands[] = {
ngx_http_ssl_nosupported, 0, 0, ngx_http_ssl_openssl097 },
#endif
+ { ngx_string("ssl_session_cache"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE12,
+ ngx_http_ssl_session_cache,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
{ ngx_string("ssl_session_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_sec_slot,
@@ -182,6 +203,384 @@ static u_char ngx_http_session_id_ctx[] = "HTTP";
static ngx_int_t
+ngx_http_ssl_session_cache_init(ngx_shm_zone_t *shm_zone)
+{
+ ngx_slab_pool_t *shpool;
+ ngx_rbtree_node_t *sentinel;
+ ngx_http_ssl_sesssion_cache_t *cache;
+
+ shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+ cache = ngx_slab_alloc(shpool, sizeof(ngx_http_ssl_sesssion_cache_t));
+ if (cache == NULL) {
+ return NGX_ERROR;
+ }
+
+ cache->session_cache_head.prev = NULL;
+ cache->session_cache_head.next = &cache->session_cache_tail;
+
+ cache->session_cache_tail.prev = &cache->session_cache_head;
+ cache->session_cache_tail.next = NULL;
+
+ cache->session_rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t));
+ if (cache->session_rbtree == NULL) {
+ return NGX_ERROR;
+ }
+
+ sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t));
+ if (sentinel == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_rbtree_sentinel_init(sentinel);
+
+ cache->session_rbtree->root = sentinel;
+ cache->session_rbtree->sentinel = sentinel;
+ cache->session_rbtree->insert = ngx_rbtree_insert_value;
+
+ shm_zone->data = cache;
+
+ return NGX_OK;
+}
+
+
+/*
+ * OpenSSL's i2d_SSL_SESSION() and d2i_SSL_SESSION are slow,
+ * so they are outside the code locked by shared pool mutex
+ */
+
+static int
+ngx_http_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
+{
+ int len;
+ u_char *p, *id;
+ uint32_t hash;
+ ngx_time_t *tp;
+ ngx_slab_pool_t *shpool;
+ ngx_connection_t *c;
+ ngx_http_request_t *r;
+ ngx_http_ssl_sess_id_t *sess_id;
+ ngx_http_ssl_srv_conf_t *sscf;
+ ngx_http_ssl_cached_sess_t *cached_sess;
+ ngx_http_ssl_sesssion_cache_t *cache;
+ u_char buf[NGX_HTTP_SSL_MAX_SESSION_SIZE];
+
+ len = i2d_SSL_SESSION(sess, NULL);
+
+ /* do not cache too big session */
+
+ if (len > (int) NGX_HTTP_SSL_MAX_SESSION_SIZE) {
+ return 0;
+ }
+
+ c = ngx_ssl_get_connection(ssl_conn);
+ r = c->data;
+
+ p = buf;
+ i2d_SSL_SESSION(sess, &p);
+
+ sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
+
+ cache = sscf->shm_zone->data;
+ shpool = (ngx_slab_pool_t *) sscf->shm_zone->shm.addr;
+
+ ngx_shmtx_lock(&shpool->mutex);
+
+ /* drop one or two expired sessions */
+ ngx_http_ssl_expire_sessions(cache, shpool, 1);
+
+ cached_sess = ngx_slab_alloc_locked(shpool,
+ offsetof(ngx_http_ssl_cached_sess_t, asn1) + len);
+
+ if (cached_sess == NULL) {
+
+ /* drop the oldest non-expired session and try once more */
+
+ ngx_http_ssl_expire_sessions(cache, shpool, 0);
+
+ cached_sess = ngx_slab_alloc_locked(shpool,
+ offsetof(ngx_http_ssl_cached_sess_t, asn1) + len);
+
+ if (cached_sess == NULL) {
+ id = NULL;
+ goto failed;
+ }
+ }
+
+ id = ngx_slab_alloc_locked(shpool, sess->session_id_length);
+ if (id == NULL) {
+ goto failed;
+ }
+
+ sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_http_ssl_sess_id_t));
+ if (sess_id == NULL) {
+ goto failed;
+ }
+
+ ngx_memcpy(&cached_sess->asn1[0], buf, len);
+
+ ngx_memcpy(id, sess->session_id, sess->session_id_length);
+
+ hash = ngx_crc32_short(sess->session_id, sess->session_id_length);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http ssl new session: %08XD:%d:%d",
+ hash, sess->session_id_length, len);
+
+ sess_id->node.key = hash;
+ sess_id->node.data = (u_char) sess->session_id_length;
+ sess_id->id = id;
+ sess_id->len = len;
+ sess_id->session = cached_sess;
+
+ tp = ngx_timeofday();
+
+ cached_sess->expire = tp->sec + sscf->session_timeout;
+ cached_sess->sess_id = sess_id;
+
+ cached_sess->next = cache->session_cache_head.next;
+ cached_sess->next->prev = cached_sess;
+ cached_sess->prev = &cache->session_cache_head;
+ cache->session_cache_head.next = cached_sess;
+
+ ngx_rbtree_insert(cache->session_rbtree, &sess_id->node);
+
+ ngx_shmtx_unlock(&shpool->mutex);
+
+ return 0;
+
+failed:
+
+ if (cached_sess) {
+ ngx_slab_free_locked(shpool, cached_sess);
+ }
+
+ if (id) {
+ ngx_slab_free_locked(shpool, id);
+ }
+
+ ngx_shmtx_unlock(&shpool->mutex);
+
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "could not add new SSL session to the session cache");
+
+ return 0;
+}
+
+
+static ngx_ssl_session_t *
+ngx_http_ssl_get_session(ngx_ssl_conn_t *ssl_conn, u_char *id, int len,
+ int *copy)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x00908000
+ const
+#endif
+ u_char *p;
+ uint32_t hash;
+ ngx_time_t *tp;
+ ngx_slab_pool_t *shpool;
+ ngx_connection_t *c;
+ ngx_rbtree_node_t *node, *sentinel;
+ ngx_ssl_session_t *sess;
+ ngx_http_request_t *r;
+ ngx_http_ssl_sess_id_t *sess_id;
+ ngx_http_ssl_srv_conf_t *sscf;
+ ngx_http_ssl_cached_sess_t *cached_sess;
+ ngx_http_ssl_sesssion_cache_t *cache;
+ u_char buf[NGX_HTTP_SSL_MAX_SESSION_SIZE];
+
+ c = ngx_ssl_get_connection(ssl_conn);
+ r = c->data;
+
+ sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
+
+ hash = ngx_crc32_short(id, len);
+ *copy = 0;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http ssl get session: %08XD:%d", hash, len);
+
+ cache = sscf->shm_zone->data;
+
+ if (cache->session_rbtree == NULL) {
+ return NULL;
+ }
+
+ sess = NULL;
+
+ shpool = (ngx_slab_pool_t *) sscf->shm_zone->shm.addr;
+
+ ngx_shmtx_lock(&shpool->mutex);
+
+ node = cache->session_rbtree->root;
+ sentinel = cache->session_rbtree->sentinel;
+
+ while (node != sentinel) {
+
+ if (hash < node->key) {
+ node = node->left;
+ continue;
+ }
+
+ if (hash > node->key) {
+ node = node->right;
+ continue;
+ }
+
+ if (hash == node->key && (u_char) len == node->data) {
+ sess_id = (ngx_http_ssl_sess_id_t *) node;
+
+ if (ngx_strncmp(id, sess_id->id, len) == 0) {
+
+ cached_sess = sess_id->session;
+
+ tp = ngx_timeofday();
+
+ if (cached_sess->expire > tp->sec) {
+ ngx_memcpy(buf, &cached_sess->asn1[0], sess_id->len);
+
+ ngx_shmtx_unlock(&shpool->mutex);
+
+ p = buf;
+ sess = d2i_SSL_SESSION(NULL, &p, sess_id->len);
+
+ return sess;
+ }
+
+ cached_sess->next->prev = cached_sess->prev;
+ cached_sess->prev->next = cached_sess->next;
+
+ ngx_rbtree_delete(cache->session_rbtree, node);
+
+ ngx_slab_free_locked(shpool, cached_sess);
+ ngx_slab_free_locked(shpool, sess_id->id);
+ ngx_slab_free_locked(shpool, sess_id);
+
+ sess = NULL;
+
+ break;
+ }
+ }
+
+ node = node->right;
+ }
+
+ ngx_shmtx_unlock(&shpool->mutex);
+
+ return sess;
+}
+
+
+static void
+ngx_http_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)
+{
+ u_char *id, len;
+ uint32_t hash;
+ ngx_slab_pool_t *shpool;
+ ngx_rbtree_node_t *node, *sentinel;
+ ngx_http_ssl_sess_id_t *sess_id;
+ ngx_http_ssl_srv_conf_t *sscf;
+ ngx_http_ssl_cached_sess_t *cached_sess;
+ ngx_http_ssl_sesssion_cache_t *cache;
+
+ sscf = ngx_ssl_get_server_conf(ssl);
+
+ cache = sscf->shm_zone->data;
+
+ id = sess->session_id;
+ len = (u_char) sess->session_id_length;
+
+ hash = ngx_crc32_short(id, (size_t) len);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+ "http ssl remove session: %08XD:%d", hash, len);
+
+ shpool = (ngx_slab_pool_t *) sscf->shm_zone->shm.addr;
+
+ ngx_shmtx_lock(&shpool->mutex);
+
+ node = cache->session_rbtree->root;
+ sentinel = cache->session_rbtree->sentinel;
+
+ while (node != sentinel) {
+
+ if (hash < node->key) {
+ node = node->left;
+ continue;
+ }
+
+ if (hash > node->key) {
+ node = node->right;
+ continue;
+ }
+
+ if (hash == node->key && len == node->data) {
+ sess_id = (ngx_http_ssl_sess_id_t *) node;
+
+ if (ngx_strncmp(id, sess_id->id, (size_t) len) == 0) {
+
+ cached_sess = sess_id->session;
+
+ cached_sess->next->prev = cached_sess->prev;
+ cached_sess->prev->next = cached_sess->next;
+
+ ngx_rbtree_delete(cache->session_rbtree, node);
+
+ ngx_slab_free_locked(shpool, cached_sess);
+ ngx_slab_free_locked(shpool, sess_id->id);
+ ngx_slab_free_locked(shpool, sess_id);
+
+ break;
+ }
+ }
+
+ node = node->right;
+ }
+
+ ngx_shmtx_unlock(&shpool->mutex);
+}
+
+
+static void
+ngx_http_ssl_expire_sessions(ngx_http_ssl_sesssion_cache_t *cache,
+ ngx_slab_pool_t *shpool, ngx_uint_t n)
+{
+ ngx_time_t *tp;
+ ngx_http_ssl_sess_id_t *sess_id;
+ ngx_http_ssl_cached_sess_t *sess;
+
+ tp = ngx_timeofday();
+
+ while (n < 3) {
+
+ sess = cache->session_cache_tail.prev;
+
+ if (sess == &cache->session_cache_head) {
+ return;
+ }
+
+ if (n++ != 0 && sess->expire > tp->sec) {
+ break;
+ }
+
+ sess->next->prev = sess->prev;
+ sess->prev->next = sess->next;
+
+ sess_id = sess->sess_id;
+
+ ngx_rbtree_delete(cache->session_rbtree, &sess_id->node);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+ "expire session: %08Xi", sess_id->node.key);
+
+ ngx_slab_free_locked(shpool, sess);
+ ngx_slab_free_locked(shpool, sess_id->id);
+ ngx_slab_free_locked(shpool, sess_id);
+ }
+}
+
+
+static ngx_int_t
ngx_http_ssl_static_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
@@ -276,13 +675,15 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
* sscf->client_certificate.data = NULL;
* sscf->ciphers.len = 0;
* sscf->ciphers.data = NULL;
+ * sscf->shm_zone = NULL;
*/
sscf->enable = NGX_CONF_UNSET;
- sscf->session_timeout = NGX_CONF_UNSET;
sscf->verify = NGX_CONF_UNSET;
sscf->verify_depth = NGX_CONF_UNSET;
sscf->prefer_server_ciphers = NGX_CONF_UNSET;
+ sscf->builtin_session_cache = NGX_CONF_UNSET;
+ sscf->session_timeout = NGX_CONF_UNSET;
return sscf;
}
@@ -294,6 +695,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_http_ssl_srv_conf_t *prev = parent;
ngx_http_ssl_srv_conf_t *conf = child;
+ long cache_mode;
ngx_pool_cleanup_t *cln;
ngx_conf_merge_value(conf->enable, prev->enable, 0);
@@ -380,14 +782,146 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
return NGX_CONF_ERROR;
}
- SSL_CTX_set_session_cache_mode(conf->ssl.ctx, SSL_SESS_CACHE_SERVER);
+ ngx_conf_merge_value(conf->builtin_session_cache,
+ prev->builtin_session_cache,
+ NGX_HTTP_SSL_DFLT_BUILTIN_SCACHE);
+
+ if (conf->shm_zone == NULL) {
+ conf->shm_zone = prev->shm_zone;
+ }
+
+ cache_mode = SSL_SESS_CACHE_SERVER;
+
+ if (conf->shm_zone
+ && conf->builtin_session_cache == NGX_HTTP_SSL_NO_BUILTIN_SCACHE)
+ {
+ cache_mode |= SSL_SESS_CACHE_NO_INTERNAL;
+ }
+
+ SSL_CTX_set_session_cache_mode(conf->ssl.ctx, cache_mode);
SSL_CTX_set_session_id_context(conf->ssl.ctx, ngx_http_session_id_ctx,
sizeof(ngx_http_session_id_ctx) - 1);
- SSL_CTX_set_timeout(conf->ssl.ctx, conf->session_timeout);
+ if (conf->builtin_session_cache != NGX_HTTP_SSL_NO_BUILTIN_SCACHE) {
+
+ if (conf->builtin_session_cache != NGX_HTTP_SSL_DFLT_BUILTIN_SCACHE) {
+ SSL_CTX_sess_set_cache_size(conf->ssl.ctx,
+ conf->builtin_session_cache);
+ }
+
+ SSL_CTX_set_timeout(conf->ssl.ctx, conf->session_timeout);
+ }
+
+ if (conf->shm_zone) {
+ SSL_CTX_sess_set_new_cb(conf->ssl.ctx, ngx_http_ssl_new_session);
+ SSL_CTX_sess_set_get_cb(conf->ssl.ctx, ngx_http_ssl_get_session);
+ SSL_CTX_sess_set_remove_cb(conf->ssl.ctx, ngx_http_ssl_remove_session);
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_ssl_srv_conf_t *sscf = conf;
+
+ size_t len;
+ ngx_str_t *value, name, size;
+ ngx_int_t n;
+ ngx_uint_t i, j;
+
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+
+ if (ngx_strcmp(value[i].data, "builtin") == 0) {
+ sscf->builtin_session_cache = NGX_HTTP_SSL_DFLT_BUILTIN_SCACHE;
+ continue;
+ }
+
+ if (value[i].len > sizeof("builtin:") - 1
+ && ngx_strncmp(value[i].data, "builtin:", sizeof("builtin:") - 1)
+ == 0)
+ {
+ n = ngx_atoi(value[i].data + sizeof("builtin:") - 1,
+ value[i].len - (sizeof("builtin:") - 1));
+
+ if (n == NGX_ERROR) {
+ goto invalid;
+ }
+
+ sscf->builtin_session_cache = n;
+
+ continue;
+ }
+
+ if (value[i].len > sizeof("shared:") - 1
+ && ngx_strncmp(value[i].data, "shared:", sizeof("shared:") - 1)
+ == 0)
+ {
+ len = 0;
+
+ for (j = sizeof("shared:") - 1; j < value[i].len; j++) {
+ if (value[i].data[j] == ':') {
+ break;
+ }
+
+ len++;
+ }
+
+ if (len == 0) {
+ goto invalid;
+ }
+
+ name.len = len;
+ name.data = value[i].data + sizeof("shared:") - 1;
+
+ size.len = value[i].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,
+ "session cache \"%V\" to small",
+ &value[i]);
+
+ return NGX_CONF_ERROR;
+ }
+
+ sscf->shm_zone = ngx_shared_memory_add(cf, &name, n,
+ &ngx_http_ssl_module);
+ if (sscf->shm_zone == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ sscf->shm_zone->init = ngx_http_ssl_session_cache_init;
+
+ continue;
+ }
+
+ goto invalid;
+ }
+
+ if (sscf->shm_zone && sscf->builtin_session_cache == NGX_CONF_UNSET) {
+ sscf->builtin_session_cache = NGX_HTTP_SSL_NO_BUILTIN_SCACHE;
+ }
return NGX_CONF_OK;
+
+invalid:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid session cache \"%V\"", &value[i]);
+
+ return NGX_CONF_ERROR;
}
diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h
index 85803096e..7a5c6d5ad 100644
--- a/src/http/modules/ngx_http_ssl_module.h
+++ b/src/http/modules/ngx_http_ssl_module.h
@@ -13,25 +13,56 @@
#include <ngx_http.h>
+typedef struct ngx_http_ssl_cached_sess_s ngx_http_ssl_cached_sess_t;
+
+
+typedef struct {
+ ngx_rbtree_node_t node;
+ u_char *id;
+ size_t len;
+ ngx_http_ssl_cached_sess_t *session;
+} ngx_http_ssl_sess_id_t;
+
+
+struct ngx_http_ssl_cached_sess_s {
+ ngx_http_ssl_cached_sess_t *prev;
+ ngx_http_ssl_cached_sess_t *next;
+ time_t expire;
+ ngx_http_ssl_sess_id_t *sess_id;
+ u_char asn1[1];
+};
+
+
+typedef struct {
+ ngx_rbtree_t *session_rbtree;
+ ngx_http_ssl_cached_sess_t session_cache_head;
+ ngx_http_ssl_cached_sess_t session_cache_tail;
+} ngx_http_ssl_sesssion_cache_t;
+
+
typedef struct {
- ngx_flag_t enable;
+ ngx_flag_t enable;
+
+ ngx_ssl_t ssl;
+
+ ngx_flag_t prefer_server_ciphers;
- ngx_ssl_t ssl;
+ ngx_uint_t protocols;
- ngx_flag_t prefer_server_ciphers;
+ ngx_int_t verify;
+ ngx_int_t verify_depth;
- ngx_uint_t protocols;
+ ssize_t builtin_session_cache;
- ngx_int_t verify;
- ngx_int_t verify_depth;
+ time_t session_timeout;
- time_t session_timeout;
+ ngx_str_t certificate;
+ ngx_str_t certificate_key;
+ ngx_str_t client_certificate;
- ngx_str_t certificate;
- ngx_str_t certificate_key;
- ngx_str_t client_certificate;
+ ngx_str_t ciphers;
- ngx_str_t ciphers;
+ ngx_shm_zone_t *shm_zone;
} ngx_http_ssl_srv_conf_t;