[PATCH] SSL: added support for TLS Session Tickets (RFC5077).

Piotr Sikora piotr at cloudflare.com
Sat Sep 28 09:55:36 UTC 2013


# HG changeset patch
# User Piotr Sikora <piotr at cloudflare.com>
# Date 1380361691 25200
#      Sat Sep 28 02:48:11 2013 -0700
# Node ID 6d3710969a18e2d0d817e297c2e17f941a58cd40
# Parent  a720f0b0e08345ebb01353250f4031bb6e141385
SSL: added support for TLS Session Tickets (RFC5077).

Signed-off-by: Piotr Sikora <piotr at cloudflare.com>

diff -r a720f0b0e083 -r 6d3710969a18 src/event/ngx_event_openssl.c
--- a/src/event/ngx_event_openssl.c     Fri Sep 27 19:39:33 2013 +0400
+++ b/src/event/ngx_event_openssl.c     Sat Sep 28 02:48:11 2013 -0700
@@ -38,6 +38,12 @@ static void ngx_ssl_expire_sessions(ngx_
 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,
+    unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx,
+    HMAC_CTX *hctx, int enc);
+#endif
+
 static void *ngx_openssl_create_conf(ngx_cycle_t *cycle);
 static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
 static void ngx_openssl_exit(ngx_cycle_t *cycle);
@@ -84,6 +90,9 @@ int  ngx_ssl_server_conf_index;
 int  ngx_ssl_session_cache_index;
 int  ngx_ssl_certificate_index;
 int  ngx_ssl_stapling_index;
+#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
+int  ngx_ssl_session_ticket_keys_index;
+#endif


 ngx_int_t
@@ -155,6 +164,18 @@ ngx_ssl_init(ngx_log_t *log)
         return NGX_ERROR;
     }

+#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
+
+    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_error(NGX_LOG_ALERT, log, 0,
+                      "SSL_CTX_get_ex_new_index() failed");
+        return NGX_ERROR;
+    }
+
+#endif
+
     return NGX_OK;
 }

@@ -2240,6 +2261,122 @@ ngx_ssl_session_rbtree_insert_value(ngx_
 }


+#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
+
+ngx_int_t
+ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl,
+    ngx_ssl_session_ticket_keys_t *keys, time_t timeout)
+{
+    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;
+    }
+
+    SSL_CTX_set_timeout(ssl->ctx, (long) timeout);
+
+    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;
+}
+
+
+static int
+ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
+    unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx,
+    HMAC_CTX *hctx, int enc)
+{
+    int                             rc;
+    SSL_CTX                        *ssl_ctx;
+    ngx_uint_t                      i;
+    ngx_ssl_session_ticket_key_t   *key;
+    ngx_ssl_session_ticket_keys_t  *keys;
+#if (NGX_DEBUG)
+    ngx_connection_t               *c;
+#endif
+
+    ssl_ctx = SSL_get_SSL_CTX(ssl_conn);
+
+    keys = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_ticket_keys_index);
+    if (keys == NULL) {
+        return -1;
+    }
+
+#if (NGX_DEBUG)
+    c = ngx_ssl_get_connection(ssl_conn);
+#endif
+
+    if (enc == 1) {
+        /* encrypt session ticket */
+
+        key = keys->default_key;
+
+        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                       "ssl session ticket encrypt, key: \"%*s\" (%s session)",
+                       key->name_len, key->name,
+                       SSL_session_reused(ssl_conn) ? "reused" : "new");
+
+        RAND_pseudo_bytes(iv, 16);
+        EVP_EncryptInit_ex(ectx, EVP_aes_128_cbc(), NULL, key->aes_key, iv);
+        HMAC_Init_ex(hctx, key->hmac_key, 16, ngx_ssl_session_ticket_md(),
+                     NULL);
+        memcpy(name, key->name, 16);
+
+        return 0;
+
+    } else {
+        /* decrypt session ticket */
+
+        if (ngx_strncmp(name, keys->default_key->name, 16)) {
+
+            key = keys->keys.elts;
+            for (i = 0; i < keys->keys.nelts; i++) {
+                if (ngx_strncmp(name, key[i].name, 16) == 0) {
+                    break;
+                }
+            }
+
+            if (i == keys->keys.nelts) {
+                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                               "ssl session ticket decrypt, key: \"%*s\" "
+                               "not found", 16, name);
+                return 0;
+            }
+
+            key = &key[i];
+            rc = 2; /* success, renew */
+
+        } else {
+            key = keys->default_key;
+            rc = 1; /* success */
+        }
+
+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                       "ssl session ticket decrypt, key: \"%*s\"",
+                       key->name_len, key->name);
+
+        HMAC_Init_ex(hctx, key->hmac_key, 16, ngx_ssl_session_ticket_md(),
+                     NULL);
+        EVP_DecryptInit_ex(ectx, EVP_aes_128_cbc(), NULL, key->aes_key, iv);
+
+        return rc;
+    }
+}
+
+#endif
+
+
 void
 ngx_ssl_cleanup_ctx(void *data)
 {
diff -r a720f0b0e083 -r 6d3710969a18 src/event/ngx_event_openssl.h
--- a/src/event/ngx_event_openssl.h     Fri Sep 27 19:39:33 2013 +0400
+++ b/src/event/ngx_event_openssl.h     Sat Sep 28 02:48:11 2013 -0700
@@ -83,6 +83,24 @@ typedef struct {
 } ngx_ssl_session_cache_t;


+#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
+
+typedef struct {
+    size_t                         name_len;
+    u_char                         name[16];
+
+    u_char                         aes_key[16];
+    u_char                         hmac_key[16];
+} ngx_ssl_session_ticket_key_t;
+
+
+typedef struct {
+    ngx_ssl_session_ticket_key_t  *default_key;
+    ngx_array_t                    keys;
+} ngx_ssl_session_ticket_keys_t;
+
+#endif
+

 #define NGX_SSL_SSLv2    0x0002
 #define NGX_SSL_SSLv3    0x0004
@@ -136,6 +154,16 @@ ngx_int_t ngx_ssl_set_session(ngx_connec
      || n == X509_V_ERR_CERT_UNTRUSTED                                        \
      || n == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE)

+#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
+ngx_int_t ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl,
+    ngx_ssl_session_ticket_keys_t *keys, time_t timeout);
+
+#ifdef OPENSSL_NO_SHA256
+#define ngx_ssl_session_ticket_md  EVP_sha1
+#else
+#define ngx_ssl_session_ticket_md  EVP_sha256
+#endif
+#endif

 ngx_int_t ngx_ssl_get_protocol(ngx_connection_t *c, ngx_pool_t *pool,
     ngx_str_t *s);
@@ -175,6 +203,9 @@ 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;
+#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
+extern int  ngx_ssl_session_ticket_keys_index;
+#endif


 #endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */
diff -r a720f0b0e083 -r 6d3710969a18 src/http/modules/ngx_http_ssl_module.c
--- a/src/http/modules/ngx_http_ssl_module.c    Fri Sep 27 19:39:33 2013 +0400
+++ b/src/http/modules/ngx_http_ssl_module.c    Sat Sep 28 02:48:11 2013 -0700
@@ -38,6 +38,11 @@ static char *ngx_http_ssl_enable(ngx_con
 static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);

+#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
+static char *ngx_http_ssl_session_ticket_key(ngx_conf_t *cf,
ngx_command_t *cmd,
+    void *conf);
+#endif
+
 static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf);


@@ -153,6 +158,17 @@ static ngx_command_t  ngx_http_ssl_comma
       0,
       NULL },

+#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
+
+    { ngx_string("ssl_session_ticket_key"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE23,
+      ngx_http_ssl_session_ticket_key,
+      NGX_HTTP_SRV_CONF_OFFSET,
+      0,
+      NULL },
+
+#endif
+
     { ngx_string("ssl_session_timeout"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_sec_slot,
@@ -413,6 +429,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t
      *     sscf->shm_zone = NULL;
      *     sscf->stapling_file = { 0, NULL };
      *     sscf->stapling_responder = { 0, NULL };
+     *     sscf->session_ticket_keys = NULL;
      */

     sscf->enable = NGX_CONF_UNSET;
@@ -634,6 +651,30 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *

     }

+#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
+
+    if (conf->session_ticket_keys == NULL) {
+        conf->session_ticket_keys = prev->session_ticket_keys;
+    }
+
+    if (conf->session_ticket_keys) {
+        if (conf->session_ticket_keys->default_key == NULL) {
+            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                          "default \"ssl_session_ticket_key\" is not defined");
+            return NGX_CONF_ERROR;
+        }
+
+        if (ngx_ssl_session_ticket_keys(cf, &conf->ssl,
+                                        conf->session_ticket_keys,
+                                        conf->session_timeout)
+            != NGX_OK)
+        {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+#endif
+
     return NGX_CONF_OK;
 }

@@ -769,6 +810,146 @@ invalid:
     return NGX_CONF_ERROR;
 }

+#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
+
+static char *
+ngx_http_ssl_session_ticket_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_ssl_srv_conf_t *sscf = conf;
+
+    char                          *rc;
+    u_char                         buf[32];
+    ssize_t                        n;
+    ngx_str_t                     *value;
+    ngx_file_t                     file;
+    ngx_uint_t                     i;
+    ngx_file_info_t                fi;
+    ngx_ssl_session_ticket_key_t  *key, *k;
+
+    if (sscf->session_ticket_keys == NULL) {
+        sscf->session_ticket_keys = ngx_pcalloc(cf->pool,
+                                        sizeof(ngx_ssl_session_ticket_keys_t));
+        if (sscf->session_ticket_keys == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        if (ngx_array_init(&sscf->session_ticket_keys->keys, cf->pool, 4,
+                           sizeof(ngx_ssl_session_ticket_key_t))
+            != NGX_OK)
+        {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    key = ngx_array_push(&sscf->session_ticket_keys->keys);
+    if (key == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    value = cf->args->elts;
+
+    if (value[1].len > 16) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "\"ssl_session_ticket_key\" name \"%V\" too long, "
+                           "it cannot exceed 16 characters", &value[1]);
+        return NGX_CONF_ERROR;
+    }
+
+    if (cf->args->nelts == 4) {
+
+        if (ngx_strcmp(value[3].data, "default")) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "invalid parameter \"%V\"", &value[3]);
+            return NGX_CONF_ERROR;
+        }
+
+        if (sscf->session_ticket_keys->default_key) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "default \"ssl_session_ticket_key\" is already "
+                               "defined");
+            return NGX_CONF_ERROR;
+        }
+
+        sscf->session_ticket_keys->default_key = key;
+    }
+
+    ngx_memzero(key->name, 16);
+    key->name_len = ngx_cpymem(key->name, value[1].data, value[1].len)
+                    - key->name;
+
+    k = sscf->session_ticket_keys->keys.elts;
+    for (i = 0; i < sscf->session_ticket_keys->keys.nelts - 1; i++) {
+        if (ngx_strncmp(key->name, k[i].name, 16) == 0) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "\"ssl_session_ticket_key\" named \"%V\" "
+                               "is already defined", &value[1]);
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    if (ngx_conf_full_name(cf->cycle, &value[2], 1) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    ngx_memzero(&file, sizeof(ngx_file_t));
+    file.name = value[2];
+    file.log = cf->log;
+
+    file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, 0, 0);
+    if (file.fd == NGX_INVALID_FILE) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
+                           ngx_open_file_n " \"%V\" failed", &file.name);
+
+        return NGX_CONF_ERROR;
+    }
+
+    rc = NGX_CONF_ERROR;
+
+    if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
+        ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
+                           ngx_fd_info_n " \"%V\" failed", &file.name);
+        goto failed;
+    }
+
+    if (ngx_file_size(&fi) != 32) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "\"%V\" must be 32 bytes", &file.name);
+        goto failed;
+    }
+
+    n = ngx_read_file(&file, buf, 32, 0);
+
+    if (n == NGX_ERROR) {
+        ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
+                           ngx_read_file_n " \"%V\" failed", &file.name);
+        goto failed;
+    }
+
+    if (n != 32) {
+        ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
+                           ngx_read_file_n " \"%V\" returned only %z bytes "
+                           "instead of 32", &file.name, n);
+        goto failed;
+    }
+
+    ngx_memcpy(key->aes_key, buf, 16);
+    ngx_memcpy(key->hmac_key, buf + 16, 16);
+
+    rc = NGX_CONF_OK;
+
+failed:
+
+    if (file.fd != NGX_INVALID_FILE) {
+        if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+                          ngx_close_file_n " \"%V\" failed", &file.name);
+        }
+    }
+
+    return rc;
+}
+
+#endif

 static ngx_int_t
 ngx_http_ssl_init(ngx_conf_t *cf)
diff -r a720f0b0e083 -r 6d3710969a18 src/http/modules/ngx_http_ssl_module.h
--- a/src/http/modules/ngx_http_ssl_module.h    Fri Sep 27 19:39:33 2013 +0400
+++ b/src/http/modules/ngx_http_ssl_module.h    Sat Sep 28 02:48:11 2013 -0700
@@ -49,6 +49,10 @@ typedef struct {

     u_char                         *file;
     ngx_uint_t                      line;
+
+#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
+    ngx_ssl_session_ticket_keys_t  *session_ticket_keys;
+#endif
 } ngx_http_ssl_srv_conf_t;



More information about the nginx-devel mailing list