You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by jp...@apache.org on 2013/12/06 19:21:10 UTC
git commit: TS-1146: RFC 5077 TLS session tickets
Updated Branches:
refs/heads/master 16882d822 -> 8dbf06bf3
TS-1146: RFC 5077 TLS session tickets
For supporting RFC 5077 TLS Session tickets across a ATS cluster,
all the machines need to have the same server ticket. Add
ssl_multicert.config support for specifying a common session ticket
key.
Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/8dbf06bf
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/8dbf06bf
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/8dbf06bf
Branch: refs/heads/master
Commit: 8dbf06bf30f618aac4c1fc5c87afe7aa38569d33
Parents: 16882d8
Author: Wei Sun <su...@yahoo-inc.com>
Authored: Fri Dec 6 10:18:39 2013 -0800
Committer: James Peach <jp...@apache.org>
Committed: Fri Dec 6 10:20:35 2013 -0800
----------------------------------------------------------------------
CHANGES | 3 +
.../configuration/ssl_multicert.config.en.rst | 57 +++++--
iocore/net/P_SSLUtils.h | 3 +
iocore/net/SSLCertLookup.cc | 2 +-
iocore/net/SSLUtils.cc | 164 ++++++++++++++++++-
iocore/net/test_certlookup.cc | 9 +
6 files changed, 221 insertions(+), 17 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/8dbf06bf/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index 309edcf..429ac04 100644
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,9 @@
Changes with Apache Traffic Server 4.2.0
+ *) [TS-1146] Add RFC 5077 TLS session ticket support.
+ Author: Wei Sun <su...@yahoo-inc.com>
+
*) [TS-2401] Use Layout instead of global install path directories.
*) [TS-2420] Remove STAT_SYNC, CONF_SYNC, and REM_SYNC threads and schedule those
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/8dbf06bf/doc/reference/configuration/ssl_multicert.config.en.rst
----------------------------------------------------------------------
diff --git a/doc/reference/configuration/ssl_multicert.config.en.rst b/doc/reference/configuration/ssl_multicert.config.en.rst
index 9098fc7..39fda6f 100644
--- a/doc/reference/configuration/ssl_multicert.config.en.rst
+++ b/doc/reference/configuration/ssl_multicert.config.en.rst
@@ -5,9 +5,9 @@
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
-
+
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -46,8 +46,8 @@ Each :file:`ssl_multicert.config` line consists of a sequence of
`key=value` fields that specify how Traffic Server should use a
particular SSL certificate.
-ssl_cert_name=PATH
- The name of the file containing the TLS certificate. `PATH` is
+ssl_cert_name=FILENAME
+ The name of the file containing the TLS certificate. `FILENAME` is
located relative to the directory specified by the
:ts:cv:`proxy.config.ssl.server.cert.path` configuration variable.
This is the only field that is required to be present.
@@ -55,26 +55,45 @@ ssl_cert_name=PATH
dest_ip=ADDRESS
The IP (v4 or v6) address that the certificate should be presented
on. This is now only used as a fallback in the case that the TLS
- SubjectNameIndication extension is not supported. If `ADDRESS`
- is `*`, the corresponding certificate will be used as the global
+ SubjectNameIndication extension is not supported. If `ADDRESS` is
+ `*`, the corresponding certificate will be used as the global
default fallback if no other match can be made. The address may
contain a port specifier, in which case the corresponding certificate
will only match for connections accepted on the specified port.
IPv6 addresses must be enclosed by square brackets if they have
a port, eg, [::1]:80.
-ssl_key_name=PATH
+ssl_key_name=FILENAME
The name of the file containing the private key for this certificate.
If the key is contained in the certificate file, this field can
- be omitted, otherwise `PATH` is resolved relative to the
+ be omitted, otherwise `FILENAME` is resolved relative to the
:ts:cv:`proxy.config.ssl.server.private_key.path` configuration variable.
ssl_ca_name=FILENAME
If the certificate is issued by an authority that is not in the
system CA bundle, additional certificates may be needed to validate
- the certificate chain. `PATH` is resolved relative to the
+ the certificate chain. `FILENAME` is resolved relative to the
:ts:cv:`proxy.config.ssl.CA.cert.path` configuration variable.
+ssl_ticket_enabled=1|0
+ Enable :rfc:`5077` stateless TLS session tickets. To support this,
+ OpenSSL should be upgraded to version 0.9.8f or higher. This
+ option must be set to `0` to disable session ticket support.
+
+ticket_key_name=FILENAME
+ The name of session ticket key file which contains a secret for
+ encrypting and decrypting TLS session tickets. If `FILENAME` is
+ not an absolute path, it is resolved relative to the
+ :ts:cv:`proxy.config.ssl.server.cert.path` configuration variable.
+ This option has no effect if session tickets are disabled by the
+ ``ssl_ticket_enabled`` option. The contents of the key file should
+ be 48 random bytes.
+
+ Session ticket support is enabled by default. If neither of the
+ ``ssl_ticket_enabled`` and ``ticket_key_name`` options are
+ specified, and internal session tiucket key is generated. This
+ key will be different each time Traffic Server is started.
+
Certificate Selection
=====================
@@ -98,7 +117,6 @@ take precedence over a specification that does not contain a port
number. A specific certificate subject will take precedence over a
wildcard certificate.
-
Examples
========
@@ -123,6 +141,21 @@ for all requests to port 8443 on IP address 111.11.11.1. The
::
- dest_ip=111.11.11.1:8443 ssl_cert_name=server.pem ssl_key_name=serverKey.pem
- ssl_cert_name=general.pem
+ dest_ip=111.11.11.1:8443 ssl_cert_name=server.pem ssl_key_name=serverKey.pem ssl_cert_name=general.pem
+
+The following example configures Traffic Server to use the SSL
+certificate ``server.pem`` for all requests to the IP address
+111.11.11.1. Session tickets are enabled with a persistent ticket
+key.
+
+::
+
+ dest_ip=111.11.11.1 ssl_cert_name=server.pem ssl_ticket_enabled=1 ticket_key_name=ticket.key
+
+The following example configures Traffic Server to use the SSL
+certificate ``server.pem`` and disable sessiont ticket for all
+requests to the IP address 111.11.11.1.
+
+::
+ dest_ip=111.11.11.1 ssl_cert_name=server.pem ssl_ticket_enabled=0
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/8dbf06bf/iocore/net/P_SSLUtils.h
----------------------------------------------------------------------
diff --git a/iocore/net/P_SSLUtils.h b/iocore/net/P_SSLUtils.h
index 4f1ec96..289ff99 100644
--- a/iocore/net/P_SSLUtils.h
+++ b/iocore/net/P_SSLUtils.h
@@ -53,6 +53,9 @@ SSL_CTX * SSLInitClientContext(const SSLConfigParams * param);
// Initialize the SSL library.
void SSLInitializeLibrary();
+// Release SSL_CTX and the associated data
+void SSLReleaseContext(SSL_CTX* ctx);
+
// Log an SSL error.
#define SSLError(fmt, ...) SSLDiagnostic(DiagsMakeLocation(), false, fmt, ##__VA_ARGS__)
// Log a SSL diagnostic using the "ssl" diagnostic tag.
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/8dbf06bf/iocore/net/SSLCertLookup.cc
----------------------------------------------------------------------
diff --git a/iocore/net/SSLCertLookup.cc b/iocore/net/SSLCertLookup.cc
index 5715cdf..fe14369 100644
--- a/iocore/net/SSLCertLookup.cc
+++ b/iocore/net/SSLCertLookup.cc
@@ -206,7 +206,7 @@ SSLContextStorage::SSLContextStorage()
SSLContextStorage::~SSLContextStorage()
{
for (unsigned i = 0; i < this->references.count(); ++i) {
- SSL_CTX_free(this->references[i]);
+ SSLReleaseContext(this->references[i]);
}
ink_hash_table_destroy(this->hostnames);
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/8dbf06bf/iocore/net/SSLUtils.cc
----------------------------------------------------------------------
diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index 020c16b..a958b4a 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -29,6 +29,8 @@
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/asn1.h>
+#include <openssl/rand.h>
+#include <openssl/evp.h>
#if HAVE_OPENSSL_TS_H
#include <openssl/ts.h>
@@ -43,6 +45,16 @@
#define SSL_CERT_TAG "ssl_cert_name"
#define SSL_PRIVATE_KEY_TAG "ssl_key_name"
#define SSL_CA_TAG "ssl_ca_name"
+#define SSL_SESSION_TICKET_ENABLED "ssl_ticket_enabled"
+#define SSL_SESSION_TICKET_KEY_FILE_TAG "ticket_key_name"
+
+#ifndef evp_md_func
+#ifdef OPENSSL_NO_SHA256
+ #define evp_md_func EVP_sha1()
+#else
+ #define evp_md_func EVP_sha256()
+#endif
+#endif
#if (OPENSSL_VERSION_NUMBER >= 0x10000000L) // openssl returns a const SSL_METHOD
typedef const SSL_METHOD * ink_ssl_method_t;
@@ -50,8 +62,17 @@ typedef const SSL_METHOD * ink_ssl_method_t;
typedef SSL_METHOD * ink_ssl_method_t;
#endif
+struct ssl_ticket_key_t
+{
+ unsigned char key_name[16];
+ unsigned char hmac_secret[16];
+ unsigned char aes_key[16];
+};
+
static ProxyMutex ** sslMutexArray;
static bool open_ssl_initialized = false;
+static int ssl_callback_session_ticket(SSL *, unsigned char *, unsigned char *, EVP_CIPHER_CTX *, HMAC_CTX *, int);
+static int ssl_session_ticket_index = 0;
struct ats_file_bio
{
@@ -208,6 +229,50 @@ ssl_context_enable_ecdh(SSL_CTX * ctx)
return ctx;
}
+static SSL_CTX *
+ssl_context_enable_tickets(SSL_CTX * ctx, const char * ticket_key_path)
+{
+ xptr<char> ticket_key_data;
+ int ticket_key_len;
+ ssl_ticket_key_t * ticket_key = NULL;
+
+ ticket_key_data = readIntoBuffer(ticket_key_path, __func__, &ticket_key_len);
+ if (!ticket_key_data) {
+ Error("failed to read SSL session ticket key from %s", (const char *)ticket_key_path);
+ goto fail;
+ }
+
+ if (ticket_key_len < 48) {
+ Error("SSL session ticket key from %s is too short (48 bytes are required)", (const char *)ticket_key_path);
+ goto fail;
+ }
+
+ ticket_key = NEW(new ssl_ticket_key_t());
+ memcpy(ticket_key->key_name, (const char *)ticket_key_data, 16);
+ memcpy(ticket_key->hmac_secret, (const char *)ticket_key_data + 16, 16);
+ memcpy(ticket_key->aes_key, (const char *)ticket_key_data + 32, 16);
+
+ // Setting the callback can only fail if OpenSSL does not recognize the
+ // SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB constant. we set the callback first
+ // so that we don't leave a ticket_key pointer attached if it fails.
+ if (SSL_CTX_set_tlsext_ticket_key_cb(ctx, ssl_callback_session_ticket) == 0) {
+ Error("failed to set session ticket callback");
+ goto fail;
+ }
+
+ if (SSL_CTX_set_ex_data(ctx, ssl_session_ticket_index, ticket_key) == 0) {
+ Error ("failed to set session ticket data to ctx");
+ goto fail;
+ }
+
+ SSL_CTX_clear_options(ctx, SSL_OP_NO_TICKET);
+ return ctx;
+
+fail:
+ delete ticket_key;
+ return ctx;
+}
+
void
SSLInitializeLibrary()
{
@@ -227,6 +292,12 @@ SSLInitializeLibrary()
CRYPTO_set_id_callback(SSL_pthreads_thread_id);
}
+ int iRet = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+ if (iRet == -1) {
+ SSLError("failed to create session ticket index");
+ }
+ ssl_session_ticket_index = (iRet == -1 ? 0 : iRet);
+
open_ssl_initialized = true;
}
@@ -577,10 +648,13 @@ ssl_store_ssl_context(
xptr<char>& addr,
xptr<char>& cert,
xptr<char>& ca,
- xptr<char>& key)
+ xptr<char>& key,
+ const int session_ticket_enabled,
+ xptr<char>& ticket_key_filename)
{
SSL_CTX * ctx;
xptr<char> certpath;
+ xptr<char> session_key_path;
ctx = ssl_context_enable_sni(SSLInitServerContext(params, cert, ca, key), lookup);
if (!ctx) {
@@ -610,10 +684,23 @@ ssl_store_ssl_context(
}
}
+ // Session tickets are enabled by default. Disable if explicitly requested.
+ if (session_ticket_enabled == 0) {
+ SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET);
+ Debug("ssl", "ssl session ticket is disabled");
+ }
+
+ // Load the session ticket key if session tickets are not disabled and we have key name.
+ if (session_ticket_enabled != 0 && ticket_key_filename) {
+ xptr<char> ticket_key_path(Layout::relative_to(params->serverCertPathOnly, ticket_key_filename));
+ ssl_context_enable_tickets(ctx, ticket_key_path);
+ }
+
// Insert additional mappings. Note that this maps multiple keys to the same value, so when
// this code is updated to reconfigure the SSL certificates, it will need some sort of
// refcounting or alternate way of avoiding double frees.
ssl_index_certificate(lookup, ctx, certpath);
+
return true;
}
@@ -623,7 +710,9 @@ ssl_extract_certificate(
xptr<char>& addr, // IPv[64] address to match
xptr<char>& cert, // certificate
xptr<char>& ca, // CA public certificate
- xptr<char>& key) // Private key
+ xptr<char>& key, // Private key
+ int& session_ticket_enabled, // session ticket enabled
+ xptr<char>& ticket_key_filename) // session key file. [key_name (16Byte) + HMAC_secret (16Byte) + AES_key (16Byte)]
{
for (int i = 0; i < MATCHER_MAX_TOKENS; ++i) {
const char * label;
@@ -651,6 +740,14 @@ ssl_extract_certificate(
if (strcasecmp(label, SSL_PRIVATE_KEY_TAG) == 0) {
key = ats_strdup(value);
}
+
+ if (strcasecmp(label, SSL_SESSION_TICKET_ENABLED) == 0) {
+ session_ticket_enabled = atoi(value);
+ }
+
+ if (strcasecmp(label, SSL_SESSION_TICKET_KEY_FILE_TAG) == 0) {
+ ticket_key_filename = ats_strdup(value);
+ }
}
if (!cert) {
@@ -705,6 +802,8 @@ SSLParseCertificateConfiguration(
xptr<char> cert;
xptr<char> ca;
xptr<char> key;
+ int session_ticket_enabled = -1;
+ xptr<char> ticket_key_filename;
const char * errPtr;
errPtr = parseConfigLine(line, &line_info, &sslCertTags);
@@ -714,8 +813,8 @@ SSLParseCertificateConfiguration(
__func__, params->configFilePath, line_num, errPtr);
REC_SignalError(errBuf, alarmAlready);
} else {
- if (ssl_extract_certificate(&line_info, addr, cert, ca, key)) {
- if (!ssl_store_ssl_context(params, lookup, addr, cert, ca, key)) {
+ if (ssl_extract_certificate(&line_info, addr, cert, ca, key, session_ticket_enabled, ticket_key_filename)) {
+ if (!ssl_store_ssl_context(params, lookup, addr, cert, ca, key, session_ticket_enabled, ticket_key_filename)) {
Error("failed to load SSL certificate specification from %s line %u",
params->configFilePath, line_num);
}
@@ -741,3 +840,60 @@ SSLParseCertificateConfiguration(
return true;
}
+
+/*
+ * RFC 5077. Create session ticket to resume SSL session without requiring session-specific state at the TLS server.
+ * Specifically, it distributes the encrypted session-state information to the client in the form of a ticket and
+ * a mechanism to present the ticket back to the server.
+ * */
+int ssl_callback_session_ticket(SSL *ssl,
+ unsigned char *keyname,
+ unsigned char *iv,
+ EVP_CIPHER_CTX *cipher_ctx,
+ HMAC_CTX *hctx,
+ int enc)
+{
+ ssl_ticket_key_t* ssl_ticket_key = (ssl_ticket_key_t*) SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl), ssl_session_ticket_index);
+ if (NULL == ssl_ticket_key) {
+ Error("ssl ticket key is null.");
+ return -1;
+ }
+
+ if (enc == 1) {
+ memcpy(keyname, ssl_ticket_key->key_name, 16);
+ RAND_pseudo_bytes(iv, EVP_MAX_IV_LENGTH);
+ EVP_EncryptInit_ex(cipher_ctx, EVP_aes_128_cbc(), NULL,
+ ssl_ticket_key->aes_key, iv);
+ HMAC_Init_ex(hctx, ssl_ticket_key->hmac_secret, 16, evp_md_func, NULL);
+ Note("create ticket for a new session");
+
+ return 0;
+ } else if (enc == 0) {
+ if (memcmp(keyname, ssl_ticket_key->key_name, 16)) {
+ Error("keyname is not consistent.");
+ return 0;
+ }
+
+ EVP_DecryptInit_ex(cipher_ctx, EVP_aes_128_cbc(), NULL,
+ ssl_ticket_key->aes_key, iv);
+ HMAC_Init_ex(hctx, ssl_ticket_key->hmac_secret, 16, evp_md_func, NULL);
+
+ Note("verify the ticket for an existing session." );
+ return 1;
+ }
+
+ return -1;
+}
+
+void
+SSLReleaseContext(SSL_CTX * ctx)
+{
+ ssl_ticket_key_t * ssl_ticket_key = (ssl_ticket_key_t*)SSL_CTX_get_ex_data(ctx, ssl_session_ticket_index);
+
+ // Free the ticket if this is the last reference.
+ if (ctx->references == 1 && ssl_ticket_key) {
+ delete ssl_ticket_key;
+ }
+
+ SSL_CTX_free(ctx);
+}
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/8dbf06bf/iocore/net/test_certlookup.cc
----------------------------------------------------------------------
diff --git a/iocore/net/test_certlookup.cc b/iocore/net/test_certlookup.cc
index 9327258..e0ba053 100644
--- a/iocore/net/test_certlookup.cc
+++ b/iocore/net/test_certlookup.cc
@@ -175,6 +175,15 @@ load_hostnames_csv(const char * fname, SSLCertLookup& lookup)
return count;
}
+// This stub version of SSLReleaseContext saves us from having to drag in a lot
+// of binary dependencies. We don't have session tickets in this test environment
+// so it's safe to do this; just a bit ugly.
+void
+SSLReleaseContext(SSL_CTX * ctx)
+{
+ SSL_CTX_free(ctx);
+}
+
int main(int argc, const char ** argv)
{
res_track_memory = 1;
Re: git commit: TS-1146: RFC 5077 TLS session tickets
Posted by Leif Hedstrom <zw...@apache.org>.
On Dec 6, 2013, at 11:21 AM, jpeach@apache.org wrote:
> Updated Branches:
> refs/heads/master 16882d822 -> 8dbf06bf3
>
>
> TS-1146: RFC 5077 TLS session tickets
>
> For supporting RFC 5077 TLS Session tickets across a ATS cluster,
> all the machines need to have the same server ticket. Add
> ssl_multicert.config support for specifying a common session ticket
> key.
Indentation in this commit does not adhere to our standards. Please correct this is a subsequent patch. I’m reopening the bug for this. In particular, our indentation is 2 white spaces, not 4.
Thanks!
— Leif
https://cwiki.apache.org/confluence/display/TS/Coding+Style
Re: git commit: TS-1146: RFC 5077 TLS session tickets
Posted by Leif Hedstrom <zw...@apache.org>.
On Dec 6, 2013, at 11:21 AM, jpeach@apache.org wrote:
> Updated Branches:
> refs/heads/master 16882d822 -> 8dbf06bf3
>
>
> TS-1146: RFC 5077 TLS session tickets
>
> For supporting RFC 5077 TLS Session tickets across a ATS cluster,
> all the machines need to have the same server ticket. Add
> ssl_multicert.config support for specifying a common session ticket
> key.
Indentation in this commit does not adhere to our standards. Please correct this is a subsequent patch. I’m reopening the bug for this. In particular, our indentation is 2 white spaces, not 4.
Thanks!
— Leif
https://cwiki.apache.org/confluence/display/TS/Coding+Style