r1200040 | pquerna | 2011-11-10 00:37:37 +0100 (Thu, 10 Nov 2011) | 5 lines
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.
r1200372 | pquerna | 2011-11-10 16:17:18 +0100 (Thu, 10 Nov 2011) | 4 lines
Apply ap_server_root_relative to the path used for the ticket secrets file.
Suggested by: Rüdiger Plüm
r1200374 | pquerna | 2011-11-10 16:19:15 +0100 (Thu, 10 Nov 2011) | 4 lines
Remove unneeded memcpy.
Spotted by: Rüdiger Plüm
r1213380 | kbrand | 2011-12-12 20:21:35 +0100 (Mon, 12 Dec 2011) | 9 lines
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
Reviewed by: ylavic, wrowe, rjung
Backported by: ylavic
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x@
1680905 13f79535-47bb-0310-9956-
ffa450edef68
-*- coding: utf-8 -*-
Changes with Apache 2.2.30
+ *) mod_ssl: Add support for configuring persistent TLS session ticket
+ encryption/decryption keys (useful for clustered environments).
+ [Paul Querna, Kaspar Brand]
+
*) SSLProtocol and SSLCipherSuite recommendations in the example/default
conf/extra/httpd-ssl.conf file are now global in scope, affecting all
VirtualHosts (matching 2.4 default configuration). [William Rowe]
ylavic: trunk/2.4.x not concerned, 2.2.x only.
+1: ylavic, jkaluza, wrowe
- * mod_ssl: Add support for configuring persistent TLS session ticket
- encryption/decryption keys (useful for clustered environments).
- [Paul Querna, Kaspar Brand]
- trunk patch: http://svn.apache.org/r1200040
- http://svn.apache.org/r1200372
- http://svn.apache.org/r1200374
- http://svn.apache.org/r1213380
- 2.2.x patch: http://people.apache.org/~ylavic/httpd-2.2.x-SSLSessionTicketKeyFile.patch
- +1: ylavic, wrowe, rjung
- rjung: Minor nits you can IMHO apply as CTR:
- - in mod_ssl.c the info string for SessionTicketKeyFile contains
- '/path/to/file', whereas existing directives use `/path/to/file'.
- The first quotation mark is of different style.
- - enhance docs note about frequent key file rotation by info that one also needs
- to restart the web server in order for the changed file to take effect
- (either gracefully or not). Would be useful for 2.4/trunk as well
- - mention RFC 5077 in CHANGES
-
* mod_ssl: Improve handling of ephemeral DH and ECDH keys by
allowing custom parameters to be configured via SSLCertificateFile,
and by adding standardized DH parameters for 1024/2048/3072/4096 bits.
</usage>
</directivesynopsis>
+<directivesynopsis>
+<name>SSLSessionTicketKeyFile</name>
+<description>Persistent encryption/decryption key for TLS session tickets</description>
+<syntax>SSLSessionTicketKeyFile <em>file-path</em></syntax>
+<contextlist><context>server config</context>
+<context>virtual host</context></contextlist>
+<compatibility>Available in httpd 2.2.30 and later, if using OpenSSL 0.9.8h or later</compatibility>
+
+<usage>
+<p>Optionally configures a secret key for encrypting and decrypting
+TLS session tickets, as defined in
+<a href="http://www.ietf.org/rfc/rfc5077.txt">RFC 5077</a>.
+Primarily suitable for clustered environments where TLS sessions information
+should be shared between multiple nodes. For single-instance httpd setups,
+it is recommended to <em>not</em> configure a ticket key file, but to
+rely on (random) keys generated by mod_ssl at startup, instead.</p>
+<p>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:</p>
+
+<example>
+dd if=/dev/random of=/path/to/file.tkey bs=1 count=48
+</example>
+
+<p>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.</p>
+
+<note type="warning">
+<p>The ticket key file contains sensitive keying material and should
+be protected with file permissions similar to those used for
+<directive module="mod_ssl">SSLCertificateKeyFile</directive>.</p>
+</note>
+</usage>
+</directivesynopsis>
+
</modulesynopsis>
SSL_CMD_SRV(CertificateChainFile, TAKE1,
"SSL Server CA Certificate Chain file "
"(`/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)")
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;
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)
cfgMergeString(pks->ca_name_path);
cfgMergeString(pks->ca_name_file);
+
+#ifdef HAVE_TLS_SESSION_TICKETS
+ cfgMergeString(ticket_key->file_path);
+#endif
}
/*
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"
}
}
+#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;
+ }
+
+ path = ap_server_root_relative(p, ticket_key->file_path);
+
+ rv = apr_file_open(&fp, path, APR_READ|APR_BINARY,
+ APR_OS_DEFAULT, ptemp);
+
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
+ "Failed to open ticket key file %s: (%d) %pm",
+ path, rv, &rv);
+ 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,
+ "Failed to read %d bytes from %s: (%d) %pm",
+ TLSEXT_TICKET_KEY_LEN, path, rv, &rv);
+ ssl_die();
+ }
+
+ 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,
+ "Unable to initialize TLS session ticket key callback "
+ "(incompatible OpenSSL version?)");
+ ssl_log_ssl_error(APLOG_MARK, APLOG_EMERG, s);
+ ssl_die();
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
+ "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,
apr_pool_t *ptemp,
ssl_init_server_certs(s, p, ptemp, sc->server);
+#ifdef HAVE_TLS_SESSION_TICKETS
+ ssl_init_ticket_key(s, p, ptemp, sc->server);
+#endif
+
SSL_CTX_set_timeout(sc->server->ssl_ctx,
sc->session_cache_timeout == UNSET ?
SSL_SESSION_CACHE_TIMEOUT : sc->session_cache_timeout);
return 0;
}
#endif
+
+#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 *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) {
+ /*
+ * OpenSSL is asking for a key for encrypting a ticket,
+ * see s3_srvr.c:ssl3_send_newsession_ticket()
+ */
+
+ if (ticket_key == NULL) {
+ /* should never happen, but better safe than sorry */
+ return -1;
+ }
+
+ 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_key->aes_key, iv);
+ HMAC_Init_ex(hctx, ticket_key->hmac_secret, 16, tlsext_tick_md(), NULL);
+
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
+ "TLS session ticket key for %s successfully set, "
+ "creating new session ticket", sc->vhost_id);
+
+ return 0;
+ }
+ else if (mode == 0) {
+ /*
+ * OpenSSL is asking for the decryption key,
+ * see t1_lib.c:tls_decrypt_ticket()
+ */
+
+ /* 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_key->aes_key, iv);
+ HMAC_Init_ex(hctx, ticket_key->hmac_secret, 16, tlsext_tick_md(), NULL);
+
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
+ "TLS session ticket key for %s successfully set, "
+ "decrypting existing session ticket", sc->vhost_id);
+
+ return 1;
+ }
+
+ /* OpenSSL is not expected to call us with modes other than 1 or 0 */
+ return -1;
+}
+#endif
#define OPENSSL_NO_COMP
#endif
+#ifndef OPENSSL_NO_TLSEXT
+#ifdef SSL_CTX_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
+
#include "ssl_util_ssl.h"
/** The #ifdef macros are only defined AFTER including the above
ssl_verify_t verify_mode;
} modssl_auth_ctx_t;
+#ifdef HAVE_TLS_SESSION_TICKETS
+typedef struct {
+ const char *file_path;
+ unsigned char key_name[16];
+ unsigned char hmac_secret[16];
+ unsigned char aes_key[16];
+} modssl_ticket_key_t;
+#endif
+
typedef struct SSLSrvConfigRec SSLSrvConfigRec;
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 */
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);
#ifndef OPENSSL_NO_TLSEXT
int ssl_callback_ServerNameIndication(SSL *, int *, modssl_ctx_t *);
#endif
+#ifdef HAVE_TLS_SESSION_TICKETS
+int ssl_callback_SessionTicket(SSL *, unsigned char *, unsigned char *,
+ EVP_CIPHER_CTX *, HMAC_CTX *, int);
+#endif
/** Session Cache Support */
void ssl_scache_init(server_rec *, apr_pool_t *);