You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by zw...@apache.org on 2013/12/14 22:51:21 UTC

[12/50] git commit: TS-1146: RFC 5077 TLS session tickets

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/5.0.x
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;