Commit 967d943b authored by Paul Querna's avatar Paul Querna
Browse files

Add support for RFC 5077 TLS Session tickets. This adds two new directives:

* SSLTicketKeyFile: To store the private information for the encryption of the ticket.
* SSLTicketKeyDefault To set the default, otherwise the first listed token is used.  This enables key rotation across servers.


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1200040 13f79535-47bb-0310-9956-ffa450edef68
parent cd66061d
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
                                                         -*- coding: utf-8 -*-
Changes with Apache 2.3.16

  *) mod_ssl: Add support for RFC 5077 TLS Session tickets.
     [Paul Querna]

  *) mod_usertrack: Use random value instead of remote IP address.
     [Stefan Fritsch]

+8 −0
Original line number Diff line number Diff line
@@ -79,6 +79,14 @@ static const command_rec ssl_config_cmds[] = {
    SSL_CMD_SRV(FIPS, FLAG,
                "Enable FIPS-140 mode "
                "(`on', `off')")
#ifdef HAVE_TLSEXT_TICKETS
    SSL_CMD_SRV(TicketKeyFile, TAKE2,
                "Key file to use for encrypting and decrypting the client ticket (RFC 5077) "
                "(keyname '/path/to/file')")
    SSL_CMD_SRV(TicketKeyDefault, TAKE1,
                "Set the key name used by default for new sessions "
               "(keyname)")
#endif
    SSL_CMD_ALL(CipherSuite, TAKE1,
                "Colon-delimited list of permitted SSL Ciphers "
                "('XXX:...:XXX' - see manual)")
+67 −0
Original line number Diff line number Diff line
@@ -200,6 +200,12 @@ static SSLSrvConfigRec *ssl_config_server_new(apr_pool_t *p)
    sc->fips                   = UNSET;
#endif

#ifdef HAVE_TLSEXT_TICKETS
    sc->default_ticket_name = NULL;
    sc->default_ticket = NULL;
    sc->tickets = apr_array_make(p, 4, sizeof(modssl_ticket_t*));
#endif

    modssl_ctx_init_proxy(sc, p);

    modssl_ctx_init_server(sc, p);
@@ -304,6 +310,11 @@ void *ssl_config_server_merge(apr_pool_t *p, void *basev, void *addv)

    cfgMerge(mc, NULL);
    cfgMerge(enabled, SSL_ENABLED_UNSET);
#ifdef HAVE_TLSEXT_TICKETS
    cfgMergeString(default_ticket_name);
    apr_array_cat(mrg->tickets, base->tickets);
    apr_array_cat(mrg->tickets, add->tickets);
#endif
    cfgMergeBool(proxy_enabled);
    cfgMergeInt(session_cache_timeout);
    cfgMergeBool(cipher_server_pref);
@@ -584,6 +595,62 @@ const char *ssl_cmd_SSLEngine(cmd_parms *cmd, void *dcfg, const char *arg)
    return "Argument must be On, Off, or Optional";
}

const char *ssl_cmd_SSLTicketKeyDefault(cmd_parms *cmd, void *dcfg, const char *name)
{
#ifdef HAVE_TLSEXT_TICKETS
    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);

    sc->default_ticket_name = name;

    return NULL;
#else
    return "TLS Ticket keys are not supported.";
#endif
}

const char *ssl_cmd_SSLTicketKeyFile(cmd_parms *cmd, void *dcfg, const char *name, const char *path)
{
#ifdef HAVE_TLSEXT_TICKETS
    apr_status_t rv;
    apr_file_t *fp;
    apr_size_t len;
    char buf[TLSEXT_TICKET_KEYLEN];
    modssl_ticket_t* ticket = NULL;
    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);

    rv = apr_file_open(&fp, path, APR_READ|APR_BINARY,
                       APR_OS_DEFAULT, cmd->temp_pool);

    if (rv != APR_SUCCESS) {
      return apr_psprintf(cmd->pool,
                          "Failed to open %s: (%d) %pm",
                          path, rv, &rv);
    }

    rv = apr_file_read_full(fp, &buf[0], TLSEXT_TICKET_KEYLEN, &len);

    if (rv != APR_SUCCESS) {
      return apr_psprintf(cmd->pool,
                          "Failed to read at least 48 bytes from %s: (%d) %pm",
                          path, rv, &rv);
    }

    ticket = apr_palloc(cmd->pool, sizeof(modssl_ticket_t));

    ticket->conf_name = name;

    memcpy(ticket->key_name, buf, 16);
    memcpy(ticket->hmac_secret, buf + 16, 16);
    memcpy(ticket->aes_key, buf + 32, 16);

    APR_ARRAY_PUSH(sc->tickets, modssl_ticket_t*) = ticket;

    return NULL;
#else
    return "TLS Ticket keys are not supported.";
#endif
}

const char *ssl_cmd_SSLFIPS(cmd_parms *cmd, void *dcfg, int flag)
{
#ifdef HAVE_FIPS
+37 −0
Original line number Diff line number Diff line
@@ -1143,6 +1143,43 @@ static void ssl_init_server_certs(server_rec *s,
#endif
        ssl_die();
    }

#ifdef HAVE_TLSEXT_TICKETS
    if (mctx->sc->tickets->nelts > 0) { 

        if (mctx->sc->default_ticket_name != NULL) {
            int i;
            modssl_ticket_t* ticket = NULL;
            mctx->sc->default_ticket = NULL;

            for (i = 0; i < mctx->sc->tickets->nelts; i++) {
                ticket = APR_ARRAY_IDX(mctx->sc->tickets, i, modssl_ticket_t*);
                if (strcmp(ticket->conf_name, mctx->sc->default_ticket_name) == 0) {
                    mctx->sc->default_ticket = ticket;
                }
            }

            if (mctx->sc->default_ticket == NULL) {
                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
                            "Misconfigured TLS Tickets.  Couldn't find key named '%s'",
                            mctx->sc->default_ticket_name);
                ssl_die();
            }
        }
        else {
            mctx->sc->default_ticket = APR_ARRAY_IDX(mctx->sc->tickets, 0, modssl_ticket_t*);
        }

        if (!SSL_CTX_set_tlsext_ticket_key_cb(mctx->ssl_ctx, ssl_callback_tlsext_tickets)) {
            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
                        "Unable to initialize TLS session ticket extension "
                        "(incompatible OpenSSL version?)");
            ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
            ssl_die();
        }
    }
#endif

}

static void ssl_init_proxy_certs(server_rec *s,
+91 −0
Original line number Diff line number Diff line
@@ -2067,3 +2067,94 @@ static int ssl_find_vhost(void *servername, conn_rec *c, server_rec *s)
    return 0;
}
#endif

#ifdef HAVE_TLSEXT_TICKETS

#ifndef tlsext_tick_md
#ifdef OPENSSL_NO_SHA256
#define tlsext_tick_md	EVP_sha1
#else
#define tlsext_tick_md	EVP_sha256
#endif
#endif

int ssl_callback_tlsext_tickets(SSL *ssl,
                                char *keyname,
                                char *iv,
                                EVP_CIPHER_CTX *cipher_ctx,
                                HMAC_CTX *hctx,
                                int mode)
{
    conn_rec *conn      = (conn_rec *)SSL_get_app_data(ssl);
    server_rec *s       = mySrvFromConn(conn);
    SSLSrvConfigRec *sc = mySrvConfig(s);

    if (mode == 1) {
        modssl_ticket_t* ticket = sc->default_ticket;

        /* Setting up the stuff for encrypting:
         *  - keyname contains at least 16 bytes we can write to.
         *  - iv contains at least EVP_MAX_IV_LENGTH (16) bytes we can write to.
         *  - hctx is already allocated, we just need to set the
         *    secret key via HMAC_Init_ex.
         *  - cipher_ctx is also allocated, and we need to configure
         *    the cipher and private key.
         */

        if (ticket == NULL) {
            /* this should not happen, we always set the default
             * ticket.
             */
            return -1;
        }

        memcpy(keyname, ticket->key_name, 16);

        RAND_pseudo_bytes(iv, EVP_MAX_IV_LENGTH);

        memcpy(iv, iv, EVP_MAX_IV_LENGTH);

        EVP_EncryptInit_ex(cipher_ctx, EVP_aes_128_cbc(), NULL,
                           ticket->aes_key, iv);

        HMAC_Init_ex(hctx, ticket->hmac_secret, 16, tlsext_tick_md(), NULL);

        return 0;
    }
    else if (mode == 0) {
        /* Setup contextes for decryption, based on the keyname input */
        int i;
        modssl_ticket_t* ticket = NULL;

        for (i = 0; i < sc->tickets->nelts; i++) {
            modssl_ticket_t* itticket = APR_ARRAY_IDX(sc->tickets, i, modssl_ticket_t*);
            if (memcmp(keyname, itticket->key_name, 16) == 0) {
                ticket = itticket;
                break;
            }
        }

        if (ticket == NULL) {
            /* Ticket key not found, but no error */
            return 0;
        }

        EVP_DecryptInit_ex(cipher_ctx, EVP_aes_128_cbc(), NULL, ticket->aes_key, iv);

        HMAC_Init_ex(hctx, ticket->hmac_secret, 16, tlsext_tick_md(), NULL);

        if (ticket != sc->default_ticket) {
            /* Ticket key found, we did our stuff, but didn't use the default,
             * re-issue a ticket with the default ticket */
            return 2;
        }
        else {
            return 1;
        }
    }

    /* TODO: log invalid use */
    return -1;
}

#endif
Loading