Ticket #120: nginx-rfc5077.patch

File nginx-rfc5077.patch, 30.5 KB (added by launchpad.net/~daniel-black, 14 years ago)

the patch implementing rfc5077

  • src/core/ngx_string.c

    diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c
    index 3b392f7..a6f6a27 100644
    a b ngx_pstrdup(ngx_pool_t *pool, ngx_str_t *src)  
    7979 *    %[0][width][u][x|X]L      int64_t/uint64_t
    8080 *    %[0][width|m][u][x|X]A    ngx_atomic_int_t/ngx_atomic_uint_t
    8181 *    %[0][width][.width]f      double, max valid number fits to %18.15f
     82 *    %[x|X]*s                  length and string
    8283 *    %P                        ngx_pid_t
    8384 *    %M                        ngx_msec_t
    8485 *    %r                        rlim_t
    ngx_pstrdup(ngx_pool_t *pool, ngx_str_t *src)  
    8687 *    %V                        ngx_str_t *
    8788 *    %v                        ngx_variable_value_t *
    8889 *    %s                        null-terminated string
    89  *    %*s                       length and string
    9090 *    %Z                        '\0'
    9191 *    %N                        '\n'
    9292 *    %c                        char
    ngx_slprintf(u_char *buf, u_char *last, const char *fmt, ...)  
    141141}
    142142
    143143
     144static u_char   ngx_hex[] = "0123456789abcdef";
     145static u_char   ngx_HEX[] = "0123456789ABCDEF";
     146
    144147u_char *
    145148ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args)
    146149{
    147     u_char                *p, zero;
     150    u_char                *p, zero, *hex_p;
    148151    int                    d;
    149152    double                 f;
    150153    size_t                 len, slen;
    ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args)  
    250253            case 's':
    251254                p = va_arg(args, u_char *);
    252255
    253                 if (slen == (size_t) -1) {
    254                     while (*p && buf < last) {
    255                         *buf++ = *p++;
     256                if (hex == 0) {
     257                    if (slen == (size_t) -1) {
     258                        while (*p && buf < last) {
     259                            *buf++ = *p++;
     260                        }
     261                    } else {
     262                        len = ngx_min(((size_t) (last - buf)), slen);
     263                        buf = ngx_cpymem(buf, p, len);
    256264                    }
    257 
    258265                } else {
    259                     len = ngx_min(((size_t) (last - buf)), slen);
    260                     buf = ngx_cpymem(buf, p, len);
     266                    if ( hex == 1 ) {
     267                        hex_p = ngx_hex;
     268                    } else {
     269                        hex_p = ngx_HEX;
     270                    }
     271                    while (buf < (last-1)) {
     272                        if ( slen == (size_t) -1) {
     273                            if (!*p) break;
     274                        } else {
     275                            if ( slen == 0 ) {
     276                                break;
     277                            } else {
     278                                --slen;
     279                            }
     280                        }
     281                        *buf++ = hex_p[ ((uint32_t) *p & 0xf0) >> 4];
     282                        *buf++ = hex_p[ ((uint32_t) *p) & 0xf];
     283                        ++p;
     284                    }
    261285                }
    262286
    263287                fmt++;
    ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, u_char zero,  
    479503                        */
    480504    size_t          len;
    481505    uint32_t        ui32;
    482     static u_char   hex[] = "0123456789abcdef";
    483     static u_char   HEX[] = "0123456789ABCDEF";
    484506
    485507    p = temp + NGX_INT64_LEN;
    486508
    ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, u_char zero,  
    520542        do {
    521543
    522544            /* the "(uint32_t)" cast disables the BCC's warning */
    523             *--p = hex[(uint32_t) (ui64 & 0xf)];
     545            *--p = ngx_hex[(uint32_t) (ui64 & 0xf)];
    524546
    525547        } while (ui64 >>= 4);
    526548
    ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, u_char zero,  
    529551        do {
    530552
    531553            /* the "(uint32_t)" cast disables the BCC's warning */
    532             *--p = HEX[(uint32_t) (ui64 & 0xf)];
     554            *--p = ngx_HEX[(uint32_t) (ui64 & 0xf)];
    533555
    534556        } while (ui64 >>= 4);
    535557    }
  • src/event/ngx_event_openssl.c

    diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
    index 4356a05..073ef22 100644
    a b static void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr,  
    2727    ngx_err_t err, char *text);
    2828static void ngx_ssl_clear_error(ngx_log_t *log);
    2929
    30 ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data);
     30ngx_int_t ngx_ssl_cache_init(ngx_shm_zone_t *shm_zone, void *data);
    3131static int ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn,
    3232    ngx_ssl_session_t *sess);
    3333static ngx_ssl_session_t *ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn,
    static void ngx_ssl_expire_sessions(ngx_ssl_session_cache_t *cache,  
    3838static void ngx_ssl_session_rbtree_insert_value(ngx_rbtree_node_t *temp,
    3939    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
    4040
     41static int ngx_ssl_tlsext_ticket_key_cb(SSL *s, unsigned char *key_name,
     42    unsigned char *iv, EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc);
     43static ngx_int_t ngx_ssl_ticket_cache_init(ngx_ssl_ticket_cache_t *cache, ngx_log_t *log);
     44static ngx_int_t ngx_ssl_ticket_new(ngx_ssl_ticket_cache_t *cache, unsigned i, long expire);
     45
    4146static void *ngx_openssl_create_conf(ngx_cycle_t *cycle);
    4247static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
    4348static void ngx_openssl_exit(ngx_cycle_t *cycle);
    ngx_module_t ngx_openssl_module = {  
    8085
    8186
    8287int  ngx_ssl_connection_index;
    83 int  ngx_ssl_server_conf_index;
    8488int  ngx_ssl_session_cache_index;
     89int  ngx_ssl_ticket_timeout_index;
    8590
    8691
    8792ngx_int_t
    ngx_ssl_init(ngx_log_t *log)  
    115120    ngx_ssl_connection_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
    116121
    117122    if (ngx_ssl_connection_index == -1) {
    118         ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_get_ex_new_index() failed");
    119         return NGX_ERROR;
    120     }
    121 
    122     ngx_ssl_server_conf_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
    123                                                          NULL);
    124     if (ngx_ssl_server_conf_index == -1) {
    125123        ngx_ssl_error(NGX_LOG_ALERT, log, 0,
    126                       "SSL_CTX_get_ex_new_index() failed");
     124                      "SSL_CTX_get_ex_new_index() for ssl_connection failed");
    127125        return NGX_ERROR;
    128126    }
    129127
    ngx_ssl_init(ngx_log_t *log)  
    131129                                                           NULL);
    132130    if (ngx_ssl_session_cache_index == -1) {
    133131        ngx_ssl_error(NGX_LOG_ALERT, log, 0,
    134                       "SSL_CTX_get_ex_new_index() failed");
     132                      "SSL_CTX_get_ex_new_index() for session cache failed");
    135133        return NGX_ERROR;
    136134    }
    137135
     136    ngx_ssl_ticket_timeout_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
     137                                                           NULL);
     138    if (ngx_ssl_ticket_timeout_index == -1) {
     139        ngx_ssl_error(NGX_LOG_ALERT, log, 0,
     140                      "SSL_CTX_get_ex_new_index() for ticket_timeout failed");
     141        return NGX_ERROR;
     142    }
    138143    return NGX_OK;
    139144}
    140145
    ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data)  
    149154        return NGX_ERROR;
    150155    }
    151156
    152     if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_server_conf_index, data) == 0) {
    153         ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
    154                       "SSL_CTX_set_ex_data() failed");
    155         return NGX_ERROR;
    156     }
    157 
    158157    /* client side options */
    159158
    160159    SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_SESS_ID_BUG);
    ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...)  
    15131512
    15141513ngx_int_t
    15151514ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,
    1516     ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout)
     1515    ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone,
     1516    time_t session_timeout, time_t *ticket_timeout)
    15171517{
    15181518    long  cache_mode;
    15191519
    ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,  
    15621562        }
    15631563    }
    15641564
    1565     SSL_CTX_set_timeout(ssl->ctx, (long) timeout);
     1565    SSL_CTX_set_timeout(ssl->ctx, (long) session_timeout);
    15661566
    15671567    if (shm_zone) {
    15681568        SSL_CTX_sess_set_new_cb(ssl->ctx, ngx_ssl_new_session);
    15691569        SSL_CTX_sess_set_get_cb(ssl->ctx, ngx_ssl_get_cached_session);
    15701570        SSL_CTX_sess_set_remove_cb(ssl->ctx, ngx_ssl_remove_session);
    15711571
     1572        SSL_CTX_set_tlsext_ticket_key_cb(ssl->ctx, ngx_ssl_tlsext_ticket_key_cb);
     1573
     1574        if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_ticket_timeout_index,
     1575                                ticket_timeout) == 0)
     1576        {
     1577            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
     1578                          "SSL_CTX_set_ex_data() for ticket timeout failed");
     1579            return NGX_ERROR;
     1580        }
     1581
    15721582        if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_session_cache_index, shm_zone)
    15731583            == 0)
    15741584        {
    15751585            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
    1576                           "SSL_CTX_set_ex_data() failed");
     1586                          "SSL_CTX_set_ex_data() for session cache failed");
    15771587            return NGX_ERROR;
    15781588        }
    15791589    }
    ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,  
    15831593
    15841594
    15851595ngx_int_t
    1586 ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data)
     1596ngx_ssl_cache_init(ngx_shm_zone_t *shm_zone, void *data)
    15871597{
    15881598    size_t                    len;
    15891599    ngx_slab_pool_t          *shpool;
    1590     ngx_ssl_session_cache_t  *cache;
     1600    ngx_ssl_cache_t          *cache;
    15911601
    15921602    if (data) {
    15931603        shm_zone->data = data;
    ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data)  
    16011611
    16021612    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
    16031613
    1604     cache = ngx_slab_alloc(shpool, sizeof(ngx_ssl_session_cache_t));
     1614    cache = ngx_slab_alloc(shpool, sizeof(ngx_ssl_cache_t));
    16051615    if (cache == NULL) {
    16061616        return NGX_ERROR;
    16071617    }
    ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data)  
    16091619    shpool->data = cache;
    16101620    shm_zone->data = cache;
    16111621
    1612     ngx_rbtree_init(&cache->session_rbtree, &cache->sentinel,
     1622    ngx_rbtree_init(&cache->session.session_rbtree, &cache->session.sentinel,
    16131623                    ngx_ssl_session_rbtree_insert_value);
    16141624
    1615     ngx_queue_init(&cache->expire_queue);
     1625    ngx_queue_init(&cache->session.expire_queue);
    16161626
    1617     len = sizeof(" in SSL session shared cache \"\"") + shm_zone->shm.name.len;
     1627    len = sizeof(" in SSL shared cache \"\"") + shm_zone->shm.name.len;
    16181628
    16191629    shpool->log_ctx = ngx_slab_alloc(shpool, len);
    16201630    if (shpool->log_ctx == NULL) {
    16211631        return NGX_ERROR;
    16221632    }
    16231633
    1624     ngx_sprintf(shpool->log_ctx, " in SSL session shared cache \"%V\"%Z",
     1634    ngx_sprintf(shpool->log_ctx, " in SSL shared cache \"%V\"%Z",
    16251635                &shm_zone->shm.name);
    16261636
     1637    ngx_ssl_ticket_cache_init(&cache->ticket, shm_zone->shm.log);
     1638
     1639    return NGX_OK;
     1640}
     1641
     1642/* ssl tickets are RFC5077 tickets where the server
     1643 * doesn't maintain a session crypto state for each client
     1644 * Like session IDs it provides a mechanism for the client
     1645 * to short cut the cryptographic exchange. It does this by
     1646 * encrypting the crypto state information in the ticket
     1647 * and providing it to the client.
     1648 *
     1649 * The server size cyptographic parameters that encrypt the
     1650 * session expire for the same time as the session cache.
     1651 * They are however renewed in 1/2 that time so clients can
     1652 * progress to the new state without doing a full cryptographic
     1653 * exchange. See note below about poor client support.
     1654 */
     1655
     1656static ngx_int_t
     1657ngx_ssl_ticket_cache_init(ngx_ssl_ticket_cache_t *cache, ngx_log_t *log)
     1658{
     1659    int i;
     1660
     1661    for (i = 0; i< NGX_SSL_ACTIVE_TICKETS; i++) {
     1662        cache->keystore[i].keys.expire = (unsigned long) -1;
     1663    }
     1664    cache->keystore_i = 0;
     1665#if (NGX_THREADS)
     1666    cache->keyrefresh = ngx_mutex_init(log, 0);
     1667    if (cache->keyrefresh == NULL) {
     1668        ngx_log_error(NGX_LOG_ERR, log, 0,
     1669            "key refresh mutex initialisation failure");
     1670        return NGX_ERROR;
     1671    }
     1672#endif
     1673    return NGX_OK;
     1674}
     1675
     1676
     1677#define NGX_RENEW_SSL_TICKET
     1678/* Suspect that some clients aren't handling renewals properly
     1679 * despite being in rfc5077 3.3 paragraph 2.
     1680 * Tested clients:
     1681 * nss 3.13.5
     1682 * gnutls 2.12.17
     1683 * openssl 1.0.0j
     1684 *
     1685 * returns 0 if expired, 1 if early and valid, 2 if renew */
     1686static ngx_int_t ngx_ssl_ticket_valid(time_t now, time_t ref, long expire) {
     1687#ifdef NGX_RENEW_SSL_TICKET
     1688   long renew = expire / 2;
     1689#endif
     1690    if ( now <= ref ) {
     1691#ifdef NGX_RENEW_SSL_TICKET
     1692      if ( now < (ref - renew) ) {
     1693        return 1;
     1694      }  else {
     1695        return 2;
     1696      }
     1697#else
     1698      return 1;
     1699#endif
     1700    } else {
     1701      return 0;
     1702    }
     1703}
     1704
     1705static ngx_int_t
     1706ngx_ssl_ticket_new(ngx_ssl_ticket_cache_t *cache,unsigned i, long expire)
     1707{
     1708    ngx_ssl_ticket_id_t *keys = &cache->keystore[i];
     1709    if ((RAND_bytes(keys->keys.hmac_key, 16) <= 0)
     1710        || (RAND_bytes(keys->keys.aes_key, 16) <= 0)
     1711        || (RAND_pseudo_bytes(keys->key_name, 16) <= 0))
     1712        return NGX_ERROR;
     1713    keys->keys.expire = ngx_time() + expire;
    16271714    return NGX_OK;
    16281715}
    16291716
    16301717
     1718static int
     1719ngx_ssl_tlsext_ticket_key_create(ngx_ssl_ticket_cache_t *cache, ngx_log_t *log,
     1720    unsigned char *key_name, unsigned char *iv, EVP_CIPHER_CTX *ctx,
     1721    HMAC_CTX *hctx, long expiretime)
     1722{
     1723    time_t                   t;
     1724    unsigned                 newi;
     1725    ngx_ssl_ticket_id_t      *k;
     1726
     1727    t = ngx_time();
     1728
     1729    /* create new session */
     1730    if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) <= 0) {
     1731        ngx_log_error(NGX_LOG_WARN, log, 0,
     1732            "RAND_bytes entropy empty on iv initialisation");
     1733        return -1;
     1734    }
     1735    ngx_log_debug5(NGX_LOG_DEBUG_EVENT, log, 0,
     1736            "New iv %*Xs time %T current key expire %T expire interval %l",
     1737            (size_t) EVP_MAX_IV_LENGTH, iv,
     1738            t, cache->keystore[cache->keystore_i].keys.expire,
     1739            expiretime);
     1740    switch ( ngx_ssl_ticket_valid(t, cache->keystore[cache->keystore_i].keys.expire,
     1741             expiretime)) {
     1742    case 1:
     1743        ngx_log_error(NGX_LOG_WARN, log, 0,
     1744                  "current ticket sufficient");
     1745        break;
     1746    case 2:
     1747        ngx_log_debug(NGX_LOG_DEBUG_EVENT, log, 0,
     1748              "session ticket key valid but in renew period - use new key");
     1749        /* time for a new key */
     1750#if (NGX_THREADS)
     1751        ngx_shmtx_lock(cache->keyrefresh);
     1752#endif
     1753        /* check the next entry is expired inside the mutex to avoid a race
     1754         * condition if another worker already updated this */
     1755        newi = ( cache->keystore_i + 1) % NGX_SSL_ACTIVE_TICKETS;
     1756        if ( ngx_ssl_ticket_valid(t, cache->keystore[newi].keys.expire,
     1757             expiretime) == 0) {
     1758            if (ngx_ssl_ticket_new(cache,newi,expiretime) == NGX_OK) {
     1759                ngx_log_error(NGX_LOG_INFO, log, 0,
     1760                              "New ssl session ticket generated");
     1761            } else {
     1762                /* insufficent randomness */
     1763                ngx_log_error(NGX_LOG_WARN, log, 0,
     1764                    "RAND_bytes entropy empty on new key material");
     1765#if (NGX_THREADS)
     1766                ngx_shmtx_unlock(cache->keyrefresh);
     1767#endif
     1768                return -1;
     1769            }
     1770            cache->keystore_i = newi;
     1771            ngx_log_debug7(NGX_LOG_DEBUG_EVENT, log, 0,
     1772                   "New key/hmac for SSL session ticket key %d name %*Xs"
     1773                   "aes_key %*Xs hmac_key %*Xs",
     1774                   newi,
     1775                   (size_t)16, cache->keystore[newi].key_name,
     1776                   (size_t)16, cache->keystore[newi].keys.aes_key,
     1777                   (size_t)16, cache->keystore[newi].keys.hmac_key);
     1778        } else {
     1779            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0, "Was tasked to SSL"
     1780                "session renew ticket %d but it wasn't expired. Assume another"
     1781                "thread/process fixed it for us", newi);
     1782        }
     1783#if (NGX_THREADS)
     1784        ngx_shmtx_unlock(cache->keyrefresh);
     1785#endif
     1786        break;
     1787    case 0:
     1788        /* primary key expired */
     1789        ngx_log_debug(NGX_LOG_DEBUG_EVENT, log, 0,
     1790              "SSL session ticket current key expired - generating");
     1791#if (NGX_THREADS)
     1792        ngx_shmtx_lock(cache->keyrefresh);
     1793#endif
     1794        /* race condition prevention inside mutex */
     1795        if ( ngx_ssl_ticket_valid(t,
     1796            cache->keystore[cache->keystore_i].keys.expire, expiretime) == 0) {
     1797            if (ngx_ssl_ticket_new(cache,cache->keystore_i, expiretime)
     1798                == NGX_OK) {
     1799                ngx_log_error(NGX_LOG_INFO, log, 0,
     1800                              "New ssl session ticket generated");
     1801            } else {
     1802                /* insufficent randomness */
     1803                ngx_log_error(NGX_LOG_WARN, log, 0,
     1804                    "RAND_bytes entropy empty on new key material");
     1805#if (NGX_THREADS)
     1806                ngx_shmtx_unlock(&(cache->keyrefresh));
     1807#endif
     1808                return -1;
     1809            }
     1810        } else {
     1811            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0,
     1812               "SSL session ticket was to renew current ticket %d but it wasn't"
     1813               " expired. Assume another thread/process fixed it for us"
     1814               , cache->keystore_i);
     1815        }
     1816        newi = cache->keystore_i;     
     1817#if (NGX_THREADS)
     1818        ngx_shmtx_unlock(&(cache->keyrefresh));
     1819#endif
     1820        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, log, 0,
     1821            "New key/hmac for SSL session ticket key %d name %*Xs aes_key %*Xs"
     1822            " hmac_key %*Xs",
     1823            newi,
     1824            (size_t) 16, cache->keystore[newi].key_name,
     1825            (size_t) 16, cache->keystore[newi].keys.aes_key,
     1826            (size_t) 16, cache->keystore[newi].keys.hmac_key);
     1827    }
     1828    k = &cache->keystore[cache->keystore_i];
     1829    ngx_memcpy(key_name, k->key_name, 16);
     1830   
     1831    EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, k->keys.aes_key, iv);
     1832    HMAC_Init_ex(hctx, k->keys.hmac_key, 16, EVP_sha256(), NULL);
     1833    ngx_log_debug7(NGX_LOG_DEBUG_EVENT, log, 0,
     1834         "New SSL session ticket %d with name %*Xs aes_key %*Xs hmac_key %*Xs",
     1835         cache->keystore_i,
     1836         (size_t) 16, k->key_name,
     1837         (size_t) 16, k->keys.aes_key,
     1838         (size_t) 16, k->keys.hmac_key);
     1839    return 1;
     1840}
     1841
     1842/* tip copied off the Apache httpd source - assumed tlsext_tick_md didn't exist
     1843 * at some version of openssl */
     1844#ifndef tlsext_tick_md
     1845#ifdef OPENSSL_NO_SHA256
     1846#define tlsext_tick_md EVP_sha1
     1847#else
     1848#define tlsext_tick_md EVP_sha256
     1849#endif
     1850#endif
     1851
     1852static int
     1853ngx_ssl_tlsext_ticket_key_cb(ngx_ssl_conn_t *s, unsigned char *key_name,
     1854    unsigned char *iv, EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc)
     1855{
     1856    SSL_CTX                  *ssl_ctx;
     1857    ngx_shm_zone_t           *shm_zone;
     1858    ngx_ssl_cache_t          *bigcache;
     1859    ngx_ssl_ticket_cache_t   *cache;
     1860    ngx_connection_t         *c;
     1861    ngx_log_t                *log;
     1862    time_t                   t;
     1863    ngx_ssl_ticket_keys_t    *keys;
     1864    unsigned                 a;
     1865    unsigned                 i;
     1866    int                      cmp;
     1867    int                      renew;
     1868    long                     expiretime;
     1869
     1870    ssl_ctx = SSL_get_SSL_CTX(s);
     1871    shm_zone = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_cache_index);
     1872    expiretime = *((long *) SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_ticket_timeout_index));
     1873    bigcache = shm_zone->data;
     1874    cache = &bigcache->ticket;
     1875
     1876    c = ngx_ssl_get_connection(s);
     1877    log = c->log;
     1878    t = ngx_time();
     1879
     1880    ngx_log_debug5(NGX_LOG_DEBUG_EVENT, log, 0,
     1881                   "tlsext_ticket_key_cb (in): key_name: %*Xs iv: %*Xs enc %d",
     1882                    (size_t) 16, key_name,
     1883                    (size_t) EVP_MAX_IV_LENGTH, iv,
     1884                    enc);
     1885    if (enc) {
     1886        return ngx_ssl_tlsext_ticket_key_create(cache, log, key_name, iv, ctx,
     1887            hctx,expiretime);
     1888    } else {
     1889        /* retrieve session */
     1890        a = 0;
     1891        cmp = -1;
     1892        for (i = a = cache->keystore_i;
     1893                i < NGX_SSL_ACTIVE_TICKETS &&
     1894                (cmp = ngx_memcmp(key_name, cache->keystore[a].key_name,16)!=0);
     1895             ++i, a = (a + 1) % NGX_SSL_ACTIVE_TICKETS);
     1896        if (cmp == 0) {
     1897            keys = &cache->keystore[a].keys;
     1898        } else {
     1899            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, "SSL session ticket key "
     1900                "%*Xs not found", (size_t) 16, key_name);
     1901            return 0;
     1902        }
     1903        renew = ngx_ssl_ticket_valid(t, cache->keystore[a].keys.expire,
     1904            expiretime);
     1905        if (renew == 0) {
     1906            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, log, 0, "retreiving key %d "
     1907                "expired %T < now %T", a, keys->expire, t);
     1908            return 0;
     1909        }
     1910
     1911        HMAC_Init_ex(hctx, keys->hmac_key, 16, tlsext_tick_md(), NULL);
     1912        EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, keys->aes_key, iv);
     1913        ngx_log_debug8(NGX_LOG_DEBUG_EVENT, log, 0,
     1914            "Resumed SSL session ticket %d with iv %*Xs aes_key %*Xs "
     1915            "hmac_key %*Xs renew %s",
     1916            a,
     1917            (size_t) 16, iv,
     1918            (size_t) 16, keys->aes_key,
     1919            (size_t) 16, keys->hmac_key,
     1920            ((renew == 2) ? "yes" : "no"));
     1921        if  (renew == 2) {
     1922            /* this sesssion will get a new ticket. This is handled in the ssl
     1923             * library and will call this function again with a enc=0 */
     1924            ngx_log_debug5(NGX_LOG_DEBUG_EVENT, log, 0,
     1925                           "tlsext_ticket_key_cb (out): key_name: %*Xs iv: %*Xs"
     1926                           " enc %d ret 2",
     1927                           (size_t) 16, key_name,
     1928                           (size_t) EVP_MAX_IV_LENGTH, iv,
     1929                           enc);
     1930            return 2;
     1931        }
     1932    }
     1933    ngx_log_debug5(NGX_LOG_DEBUG_EVENT, log, 0,
     1934                   "tlsext_ticket_key_cb (out): key_name: %*Xs iv: %*Xs enc %d - ret 1",
     1935                   (size_t) 16, key_name,
     1936                   (size_t) EVP_MAX_IV_LENGTH, iv,
     1937                   enc);
     1938    return 1;
     1939}
     1940
     1941
     1942
     1943
     1944
    16311945/*
    16321946 * The length of the session id is 16 bytes for SSLv2 sessions and
    16331947 * between 1 and 32 bytes for SSLv3/TLSv1, typically 32 bytes.
    ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)  
    16611975
    16621976    len = i2d_SSL_SESSION(sess, NULL);
    16631977
     1978
     1979    /* This isn't called if a session ticket is created
     1980     * see rfc5077 3.4
     1981     */
     1982
    16641983    /* do not cache too big session */
    16651984
    16661985    if (len > (int) NGX_SSL_MAX_SESSION_SIZE) {
    ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)  
    16751994    ssl_ctx = SSL_get_SSL_CTX(ssl_conn);
    16761995    shm_zone = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_cache_index);
    16771996
    1678     cache = shm_zone->data;
     1997    cache = &((ngx_ssl_cache_t  *) shm_zone->data)->session;
    16791998    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
    16801999
    16812000    ngx_shmtx_lock(&shpool->mutex);
    ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn, u_char *id, int len,  
    17962115    shm_zone = SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl_conn),
    17972116                                   ngx_ssl_session_cache_index);
    17982117
    1799     cache = shm_zone->data;
     2118    cache = &((ngx_ssl_cache_t  *) shm_zone->data)->session;
    18002119
    18012120    sess = NULL;
    18022121
    ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)  
    18922211        return;
    18932212    }
    18942213
    1895     cache = shm_zone->data;
     2214    cache = &((ngx_ssl_cache_t  *) shm_zone->data)->session;
    18962215
    18972216    id = sess->session_id;
    18982217    len = (size_t) sess->session_id_length;
  • src/event/ngx_event_openssl.h

    diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
    index cd6d885..8850459 100644
    a b typedef struct {  
    8181} ngx_ssl_session_cache_t;
    8282
    8383
     84/* RFC 5077 */
     85typedef struct {
     86        u_char    hmac_key[16];
     87        u_char    aes_key[16];
     88        time_t    expire;
     89} ngx_ssl_ticket_keys_t;
     90
     91
     92typedef struct {
     93        u_char                    key_name[16];
     94        ngx_ssl_ticket_keys_t     keys;
     95} ngx_ssl_ticket_id_t;
     96
     97
     98#define NGX_SSL_ACTIVE_TICKETS  2
     99typedef struct {
     100    volatile unsigned      keystore_i;
     101    ngx_ssl_ticket_id_t    keystore[NGX_SSL_ACTIVE_TICKETS];
     102#if (NGX_THREADS)
     103    ngx_shmtx_t            *keyrefresh;
     104#endif
     105} ngx_ssl_ticket_cache_t;
     106
     107
     108typedef struct {
     109    ngx_ssl_session_cache_t session;
     110    ngx_ssl_ticket_cache_t ticket;
     111} ngx_ssl_cache_t;
     112
    84113
    85114#define NGX_SSL_SSLv2    0x0002
    86115#define NGX_SSL_SSLv3    0x0004
    RSA *ngx_ssl_rsa512_key_callback(SSL *ssl, int is_export, int key_length);  
    106135ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file);
    107136ngx_int_t ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name);
    108137ngx_int_t ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,
    109     ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout);
    110 ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data);
     138    ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone,
     139    time_t session_timeout, long *ticket_timeout);
     140ngx_int_t ngx_ssl_cache_init(ngx_shm_zone_t *shm_zone, void *data);
    111141ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c,
    112142    ngx_uint_t flags);
    113143
    ngx_int_t ngx_ssl_set_session(ngx_connection_t *c, ngx_ssl_session_t *session);  
    117147#define ngx_ssl_free_session        SSL_SESSION_free
    118148#define ngx_ssl_get_connection(ssl_conn)                                      \
    119149    SSL_get_ex_data(ssl_conn, ngx_ssl_connection_index)
    120 #define ngx_ssl_get_server_conf(ssl_ctx)                                      \
    121     SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_server_conf_index)
    122150
    123151
    124152ngx_int_t ngx_ssl_get_protocol(ngx_connection_t *c, ngx_pool_t *pool,
    void ngx_ssl_cleanup_ctx(void *data);  
    155183
    156184
    157185extern int  ngx_ssl_connection_index;
    158 extern int  ngx_ssl_server_conf_index;
    159186extern int  ngx_ssl_session_cache_index;
    160187
    161188
  • src/http/modules/ngx_http_ssl_module.c

    diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
    index d759489..2be0988 100644
    a b static ngx_command_t ngx_http_ssl_commands[] = {  
    145145      offsetof(ngx_http_ssl_srv_conf_t, session_timeout),
    146146      NULL },
    147147
     148    { ngx_string("ssl_ticket_timeout"),
     149      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
     150      ngx_conf_set_sec_slot,
     151      NGX_HTTP_SRV_CONF_OFFSET,
     152      offsetof(ngx_http_ssl_srv_conf_t, ticket_timeout),
     153      NULL },
     154
    148155    { ngx_string("ssl_crl"),
    149156      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
    150157      ngx_conf_set_str_slot,
    ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)  
    336343    sscf->verify_depth = NGX_CONF_UNSET_UINT;
    337344    sscf->builtin_session_cache = NGX_CONF_UNSET;
    338345    sscf->session_timeout = NGX_CONF_UNSET;
     346    sscf->ticket_timeout = NGX_CONF_UNSET;
    339347
    340348    return sscf;
    341349}
    ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)  
    363371    ngx_conf_merge_value(conf->session_timeout,
    364372                         prev->session_timeout, 300);
    365373
     374    ngx_conf_merge_value(conf->ticket_timeout,
     375                         prev->ticket_timeout, 60*60*24*7); /* 1 week */
     376
    366377    ngx_conf_merge_value(conf->prefer_server_ciphers,
    367378                         prev->prefer_server_ciphers, 0);
    368379
    ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)  
    509520
    510521    if (ngx_ssl_session_cache(&conf->ssl, &ngx_http_ssl_sess_id_ctx,
    511522                              conf->builtin_session_cache,
    512                               conf->shm_zone, conf->session_timeout)
     523                              conf->shm_zone, conf->session_timeout,
     524                              &conf->ticket_timeout)
    513525        != NGX_OK)
    514526    {
    515527        return NGX_CONF_ERROR;
    ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)  
    629641                return NGX_CONF_ERROR;
    630642            }
    631643
    632             sscf->shm_zone->init = ngx_ssl_session_cache_init;
     644            sscf->shm_zone->init = ngx_ssl_cache_init;
    633645
    634646            continue;
    635647        }
  • src/http/modules/ngx_http_ssl_module.h

    diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h
    index 58659ab..3fe79f0 100644
    a b typedef struct {  
    2929    ssize_t                         builtin_session_cache;
    3030
    3131    time_t                          session_timeout;
     32    long                            ticket_timeout;
    3233
    3334    ngx_str_t                       certificate;
    3435    ngx_str_t                       certificate_key;
  • src/mail/ngx_mail_ssl_module.c

    diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c
    index dd6f2ac..93a4070 100644
    a b static ngx_command_t ngx_mail_ssl_commands[] = {  
    123123      offsetof(ngx_mail_ssl_conf_t, session_timeout),
    124124      NULL },
    125125
     126    { ngx_string("ssl_ticket_session_timeout"),
     127      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
     128      ngx_conf_set_sec_slot,
     129      NGX_MAIL_SRV_CONF_OFFSET,
     130      offsetof(ngx_mail_ssl_conf_t, ticket_timeout),
     131      NULL },
     132
    126133      ngx_null_command
    127134};
    128135
    ngx_mail_ssl_create_conf(ngx_conf_t *cf)  
    184191    scf->prefer_server_ciphers = NGX_CONF_UNSET;
    185192    scf->builtin_session_cache = NGX_CONF_UNSET;
    186193    scf->session_timeout = NGX_CONF_UNSET;
     194    scf->ticket_timeout = NGX_CONF_UNSET;
    187195
    188196    return scf;
    189197}
    ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)  
    205213    ngx_conf_merge_value(conf->session_timeout,
    206214                         prev->session_timeout, 300);
    207215
     216    ngx_conf_merge_value(conf->ticket_timeout,
     217                         prev->ticket_timeout, 60*60*24*7); /* 1 week */
     218
    208219    ngx_conf_merge_value(conf->prefer_server_ciphers,
    209220                         prev->prefer_server_ciphers, 0);
    210221
    ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)  
    317328
    318329    if (ngx_ssl_session_cache(&conf->ssl, &ngx_mail_ssl_sess_id_ctx,
    319330                              conf->builtin_session_cache,
    320                               conf->shm_zone, conf->session_timeout)
     331                              conf->shm_zone, conf->session_timeout,
     332                              &conf->ticket_timeout)
    321333        != NGX_OK)
    322334    {
    323335        return NGX_CONF_ERROR;
    ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)  
    468480                return NGX_CONF_ERROR;
    469481            }
    470482
    471             scf->shm_zone->init = ngx_ssl_session_cache_init;
     483            scf->shm_zone->init = ngx_ssl_cache_init;
    472484
    473485            continue;
    474486        }
  • src/mail/ngx_mail_ssl_module.h

    diff --git a/src/mail/ngx_mail_ssl_module.h b/src/mail/ngx_mail_ssl_module.h
    index 7f59b38..45b8214 100644
    a b typedef struct {  
    3131    ssize_t          builtin_session_cache;
    3232
    3333    time_t           session_timeout;
     34    long             ticket_timeout;
    3435
    3536    ngx_str_t        certificate;
    3637    ngx_str_t        certificate_key;