From: Kaspar Brand Date: Mon, 12 Dec 2011 19:51:05 +0000 (+0000) Subject: backport r1213380 from trunk: X-Git-Tag: 2.3.16~10 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f082f40cb7a96f27e1072c8d9c793cdca5a21260;p=apache backport r1213380 from trunk: Streamline TLS session ticket key handling (added in r1200040): - drop the SSLTicketKeyDefault directive, and only support a single ticket key per server/vhost - rename the SSLTicketKeyFile directive to SSLSessionTicketKeyFile, remove the keyname parameter - move ticket key parameters from SSLSrvConfigRec to modssl_ctx_t - configure the tlsext_ticket_key_cb only when in server mode - add documentation for SSLSessionTicketKeyFile git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1213395 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index 47ac8f7348..316f0aa59b 100644 --- a/CHANGES +++ b/CHANGES @@ -88,8 +88,9 @@ Changes with Apache 2.4.0 *) end-generation hook: Fix false notification of end-of-generation for temporary intervals with no active MPM children. [Jeff Trawick] - *) mod_ssl: Add support for RFC 5077 TLS Session tickets. - [Paul Querna] + *) mod_ssl: Add support for configuring persistent TLS session ticket + encryption/decryption keys (useful for clustered environments). + [Paul Querna, Kaspar Brand] *) mod_usertrack: Use random value instead of remote IP address. [Stefan Fritsch] diff --git a/docs/manual/mod/mod_ssl.xml b/docs/manual/mod/mod_ssl.xml index ba3e6ea0ef..5bf2438752 100644 --- a/docs/manual/mod/mod_ssl.xml +++ b/docs/manual/mod/mod_ssl.xml @@ -2265,4 +2265,40 @@ Of potential use when going through a proxy for retrieving OCSP queries.

+ +SSLSessionTicketKeyFile +Persistent encryption/decryption key for TLS session tickets +SSLSessionTicketKeyFile file-path +server config +virtual host +Available in httpd 2.4.0 and later, if using OpenSSL 0.9.8h or later + + +

Optionally configures a secret key for encrypting and decrypting +TLS session tickets, as defined in +RFC 5077. +Primarily suitable for clustered environments where TLS sessions information +should be shared between multiple nodes. For single-instance httpd setups, +it is recommended to not configure a ticket key file, but to +rely on (random) keys generated by mod_ssl at startup, instead.

+

The ticket key file must contain 48 bytes of random data, +preferrably created from a high-entropy source. On a Unix-based system, +a ticket key file can be created as follows:

+ + +dd if=/dev/random of=/path/to/file.tkey bs=1 count=48 + + +

Ticket keys should be rotated (replaced) on a frequent basis, +as this is the only way to invalidate an existing session ticket - +OpenSSL currently doesn't allow to specify a limit for ticket lifetimes.

+ + +

The ticket key file contains sensitive keying material and should +be protected with file permissions similar to those used for +SSLCertificateKeyFile.

+
+
+
+ diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c index ad8d06e980..57bf2d949b 100644 --- a/modules/ssl/mod_ssl.c +++ b/modules/ssl/mod_ssl.c @@ -79,14 +79,6 @@ 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)") @@ -102,6 +94,11 @@ static const command_rec ssl_config_cmds[] = { SSL_CMD_SRV(PKCS7CertificateFile, TAKE1, "PKCS#7 file containing server certificate and chain" " certificates ('/path/to/file' - PEM encoded)") +#ifdef HAVE_TLS_SESSION_TICKETS + SSL_CMD_SRV(SessionTicketKeyFile, TAKE1, + "TLS session ticket encryption/decryption key file (RFC 5077) " + "('/path/to/file' - file with 48 bytes of random data)") +#endif SSL_CMD_ALL(CACertificatePath, TAKE1, "SSL CA Certificate path " "('/path/to/dir' - contains PEM encoded files)") diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c index 9ba61f77e2..8b2d53ad17 100644 --- a/modules/ssl/ssl_engine_config.c +++ b/modules/ssl/ssl_engine_config.c @@ -109,6 +109,10 @@ static void modssl_ctx_init(modssl_ctx_t *mctx) mctx->pks = NULL; mctx->pkp = NULL; +#ifdef HAVE_TLS_SESSION_TICKETS + mctx->ticket_key = NULL; +#endif + mctx->protocol = SSL_PROTOCOL_ALL; mctx->pphrase_dialog_type = SSL_PPTYPE_UNSET; @@ -177,6 +181,10 @@ static void modssl_ctx_init_server(SSLSrvConfigRec *sc, mctx->pks = apr_pcalloc(p, sizeof(*mctx->pks)); /* mctx->pks->... certs/keys are set during module init */ + +#ifdef HAVE_TLS_SESSION_TICKETS + mctx->ticket_key = apr_pcalloc(p, sizeof(*mctx->ticket_key)); +#endif } static SSLSrvConfigRec *ssl_config_server_new(apr_pool_t *p) @@ -200,12 +208,6 @@ 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); @@ -297,6 +299,10 @@ static void modssl_ctx_cfg_merge_server(modssl_ctx_t *base, cfgMergeString(pks->ca_name_path); cfgMergeString(pks->ca_name_file); + +#ifdef HAVE_TLS_SESSION_TICKETS + cfgMergeString(ticket_key->file_path); +#endif } /* @@ -310,11 +316,6 @@ 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); @@ -595,64 +596,6 @@ 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); - - path = ap_server_root_relative(cmd->pool, path); - - 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 @@ -863,6 +806,24 @@ const char *ssl_cmd_SSLPKCS7CertificateFile(cmd_parms *cmd, return NULL; } +#ifdef HAVE_TLS_SESSION_TICKETS +const char *ssl_cmd_SSLSessionTicketKeyFile(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + const char *err; + + if ((err = ssl_cmd_check_file(cmd, &arg))) { + return err; + } + + sc->server->ticket_key->file_path = arg; + + return NULL; +} +#endif + #define NO_PER_DIR_SSL_CA \ "Your SSL library does not have support for per-directory CA" diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c index 637d35ca85..afab1fad22 100644 --- a/modules/ssl/ssl_engine_init.c +++ b/modules/ssl/ssl_engine_init.c @@ -1134,44 +1134,64 @@ static void ssl_init_server_certs(server_rec *s, #endif ssl_die(); } +} -#ifdef HAVE_TLSEXT_TICKETS - if (mctx->sc->tickets->nelts > 0) { +#ifdef HAVE_TLS_SESSION_TICKETS +static void ssl_init_ticket_key(server_rec *s, + apr_pool_t *p, + apr_pool_t *ptemp, + modssl_ctx_t *mctx) +{ + apr_status_t rv; + apr_file_t *fp; + apr_size_t len; + char buf[TLSEXT_TICKET_KEY_LEN]; + char *path; + modssl_ticket_key_t *ticket_key = mctx->ticket_key; + + if (!ticket_key->file_path) { + return; + } - if (mctx->sc->default_ticket_name != NULL) { - int i; - modssl_ticket_t* ticket = NULL; - mctx->sc->default_ticket = NULL; + path = ap_server_root_relative(p, ticket_key->file_path); - 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; - } - } + rv = apr_file_open(&fp, path, APR_READ|APR_BINARY, + APR_OS_DEFAULT, ptemp); - if (mctx->sc->default_ticket == NULL) { - ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01912) - "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 (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02286) + "Failed to open ticket key file %s: (%d) %pm", + path, rv, &rv); + ssl_die(); + } - if (!SSL_CTX_set_tlsext_ticket_key_cb(mctx->ssl_ctx, ssl_callback_tlsext_tickets)) { - ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01913) - "Unable to initialize TLS session ticket extension " - "(incompatible OpenSSL version?)"); - ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s); - ssl_die(); - } + rv = apr_file_read_full(fp, &buf[0], TLSEXT_TICKET_KEY_LEN, &len); + + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02287) + "Failed to read %d bytes from %s: (%d) %pm", + TLSEXT_TICKET_KEY_LEN, path, rv, &rv); + ssl_die(); } -#endif + memcpy(ticket_key->key_name, buf, 16); + memcpy(ticket_key->hmac_secret, buf + 16, 16); + memcpy(ticket_key->aes_key, buf + 32, 16); + + if (!SSL_CTX_set_tlsext_ticket_key_cb(mctx->ssl_ctx, + ssl_callback_SessionTicket)) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01913) + "Unable to initialize TLS session ticket key callback " + "(incompatible OpenSSL version?)"); + ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s); + ssl_die(); + } + + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(02288) + "TLS session ticket key for %s successfully loaded from %s", + (mySrvConfig(s))->vhost_id, path); } +#endif static void ssl_init_proxy_certs(server_rec *s, apr_pool_t *p, @@ -1322,6 +1342,10 @@ static void ssl_init_server_ctx(server_rec *s, ssl_init_ctx(s, p, ptemp, sc->server); ssl_init_server_certs(s, p, ptemp, sc->server); + +#ifdef HAVE_TLS_SESSION_TICKETS + ssl_init_ticket_key(s, p, ptemp, sc->server); +#endif } /* diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c index e3f48c7af9..3165da90d0 100644 --- a/modules/ssl/ssl_engine_kernel.c +++ b/modules/ssl/ssl_engine_kernel.c @@ -2088,91 +2088,72 @@ static int ssl_find_vhost(void *servername, conn_rec *c, server_rec *s) } #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, - unsigned char *keyname, - unsigned char *iv, - EVP_CIPHER_CTX *cipher_ctx, - HMAC_CTX *hctx, - int mode) +#ifdef HAVE_TLS_SESSION_TICKETS +/* + * This callback function is executed when OpenSSL needs a key for encrypting/ + * decrypting a TLS session ticket (RFC 5077) and a ticket key file has been + * configured through SSLSessionTicketKeyFile. + */ +int ssl_callback_SessionTicket(SSL *ssl, + unsigned char *keyname, + unsigned 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); + conn_rec *c = (conn_rec *)SSL_get_app_data(ssl); + server_rec *s = mySrvFromConn(c); SSLSrvConfigRec *sc = mySrvConfig(s); + SSLConnRec *sslconn = myConnConfig(c); + modssl_ctx_t *mctx = myCtxConfig(sslconn, sc); + modssl_ticket_key_t *ticket_key = mctx->ticket_key; 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. + /* + * OpenSSL is asking for a key for encrypting a ticket, + * see s3_srvr.c:ssl3_send_newsession_ticket() */ - if (ticket == NULL) { - /* this should not happen, we always set the default - * ticket. - */ + if (ticket_key == NULL) { + /* should never happen, but better safe than sorry */ return -1; } - memcpy(keyname, ticket->key_name, 16); - + memcpy(keyname, ticket_key->key_name, 16); RAND_pseudo_bytes(iv, EVP_MAX_IV_LENGTH); - EVP_EncryptInit_ex(cipher_ctx, EVP_aes_128_cbc(), NULL, - ticket->aes_key, iv); + ticket_key->aes_key, iv); + HMAC_Init_ex(hctx, ticket_key->hmac_secret, 16, tlsext_tick_md(), NULL); - HMAC_Init_ex(hctx, ticket->hmac_secret, 16, tlsext_tick_md(), NULL); + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02289) + "TLS session ticket key for %s successfully set, " + "creating new session ticket", sc->vhost_id); 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; - } - } + /* + * OpenSSL is asking for the decryption key, + * see t1_lib.c:tls_decrypt_ticket() + */ - if (ticket == NULL) { - /* Ticket key not found, but no error */ + /* check key name */ + if (ticket_key == NULL || memcmp(keyname, ticket_key->key_name, 16)) { return 0; } - EVP_DecryptInit_ex(cipher_ctx, EVP_aes_128_cbc(), NULL, ticket->aes_key, iv); + EVP_DecryptInit_ex(cipher_ctx, EVP_aes_128_cbc(), NULL, + ticket_key->aes_key, iv); + HMAC_Init_ex(hctx, ticket_key->hmac_secret, 16, tlsext_tick_md(), NULL); - HMAC_Init_ex(hctx, ticket->hmac_secret, 16, tlsext_tick_md(), NULL); + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02290) + "TLS session ticket key for %s successfully set, " + "decrypting existing session ticket", sc->vhost_id); - 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; - } + return 1; } - /* TODO: log invalid use */ + /* OpenSSL is not expected to call us with modes other than 1 or 0 */ return -1; } - #endif diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h index d95b5780ef..0ed6e254b0 100644 --- a/modules/ssl/ssl_private.h +++ b/modules/ssl/ssl_private.h @@ -158,8 +158,16 @@ #endif #ifndef OPENSSL_NO_TLSEXT -#ifdef SSL_CTX_set_tlsext_ticket_key_cb -#define HAVE_TLSEXT_TICKETS +#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB +#define HAVE_TLS_SESSION_TICKETS +#define TLSEXT_TICKET_KEY_LEN 48 +#ifndef tlsext_tick_md +#ifdef OPENSSL_NO_SHA256 +#define tlsext_tick_md EVP_sha1 +#else +#define tlsext_tick_md EVP_sha256 +#endif +#endif #endif #endif @@ -559,19 +567,13 @@ typedef struct { ssl_verify_t verify_mode; } modssl_auth_ctx_t; - -#ifdef HAVE_TLSEXT_TICKETS - -/* 48 bytes: 16 for keyname, 16 for HMAC secret, 16 for AES private key */ -#define TLSEXT_TICKET_KEYLEN (48) - +#ifdef HAVE_TLS_SESSION_TICKETS typedef struct { - /* Human readable name, used in the configuration */ - const char *conf_name; - char key_name[16]; + const char *file_path; + unsigned char key_name[16]; unsigned char hmac_secret[16]; unsigned char aes_key[16]; -} modssl_ticket_t; +} modssl_ticket_key_t; #endif typedef struct SSLSrvConfigRec SSLSrvConfigRec; @@ -584,6 +586,10 @@ typedef struct { modssl_pk_server_t *pks; modssl_pk_proxy_t *pkp; +#ifdef HAVE_TLS_SESSION_TICKETS + modssl_ticket_key_t *ticket_key; +#endif + ssl_proto_t protocol; /** config for handling encrypted keys */ @@ -642,11 +648,6 @@ struct SSLSrvConfigRec { #ifdef HAVE_FIPS BOOL fips; #endif -#ifdef HAVE_TLSEXT_TICKETS - const char *default_ticket_name; - modssl_ticket_t* default_ticket; - apr_array_header_t* tickets; -#endif }; /** @@ -727,6 +728,9 @@ const char *ssl_cmd_SSLProxyCARevocationCheck(cmd_parms *, void *, const char * const char *ssl_cmd_SSLProxyMachineCertificatePath(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLProxyMachineCertificateFile(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLProxyMachineCertificateChainFile(cmd_parms *, void *, const char *); +#ifdef HAVE_TLS_SESSION_TICKETS +const char *ssl_cmd_SSLSessionTicketKeyFile(cmd_parms *cmd, void *dcfg, const char *arg); +#endif const char *ssl_cmd_SSLProxyCheckPeerExpire(cmd_parms *cmd, void *dcfg, int flag); const char *ssl_cmd_SSLProxyCheckPeerCN(cmd_parms *cmd, void *dcfg, int flag); @@ -738,8 +742,6 @@ const char *ssl_cmd_SSLOCSPResponderTimeout(cmd_parms *cmd, void *dcfg, const ch const char *ssl_cmd_SSLOCSPEnable(cmd_parms *cmd, void *dcfg, int flag); const char *ssl_cmd_SSLFIPS(cmd_parms *cmd, void *dcfg, int flag); -const char *ssl_cmd_SSLTicketKeyDefault(cmd_parms *cmd, void *dcfg, const char *name); -const char *ssl_cmd_SSLTicketKeyFile(cmd_parms *cmd, void *dcfg, const char *name, const char *path); /** module initialization */ int ssl_init_Module(apr_pool_t *, apr_pool_t *, apr_pool_t *, server_rec *); @@ -780,14 +782,9 @@ void ssl_callback_Info(const SSL *, int, int); #ifndef OPENSSL_NO_TLSEXT int ssl_callback_ServerNameIndication(SSL *, int *, modssl_ctx_t *); #endif - -#ifdef HAVE_TLSEXT_TICKETS -int ssl_callback_tlsext_tickets(SSL *ssl, - unsigned char *keyname, - unsigned char *iv, - EVP_CIPHER_CTX *cipher_ctx, - HMAC_CTX *hctx, - int mode); +#ifdef HAVE_TLS_SESSION_TICKETS +int ssl_callback_SessionTicket(SSL *, unsigned char *, unsigned char *, + EVP_CIPHER_CTX *, HMAC_CTX *, int); #endif /** Session Cache Support */