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 2012/12/14 06:46:24 UTC

git commit: TS-1500: Allow SSL certificates to be selected per-port

Updated Branches:
  refs/heads/master 3e6359ee5 -> 129d7719e


TS-1500: Allow SSL certificates to be selected per-port

Allow the dest_ip specifier in ssl_multicert.config to match on the
IP address and the port. We select the longest match.


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/129d7719
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/129d7719
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/129d7719

Branch: refs/heads/master
Commit: 129d7719ece8c1114ad629466e84bd3c40937516
Parents: 3e6359e
Author: James Peach <jp...@apache.org>
Authored: Sat Dec 8 10:54:44 2012 -0800
Committer: James Peach <jp...@apache.org>
Committed: Thu Dec 13 21:38:58 2012 -0800

----------------------------------------------------------------------
 CHANGES                                   |    3 +
 iocore/net/SSLCertLookup.cc               |   23 +++++++-
 iocore/net/SSLConfig.cc                   |    6 ++-
 iocore/net/SSLNetVConnection.cc           |   18 ++-----
 iocore/net/SSLUtils.cc                    |   21 +++++---
 iocore/net/test_certlookup.cc             |   67 +++++++++++++++++++++++-
 proxy/config/ssl_multicert.config.default |    6 ++
 7 files changed, 117 insertions(+), 27 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/129d7719/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index cd29051..144173e 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,9 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache Traffic Server 3.3.1
 
+
+  *) [TS-1500] let ssl_multicert.config specify sslcert per port
+
   *) [TS-1621] Adopt ConfigUpdateHandler pattern
      Author: Ethan Lai <yz...@yahoo.com>
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/129d7719/iocore/net/SSLCertLookup.cc
----------------------------------------------------------------------
diff --git a/iocore/net/SSLCertLookup.cc b/iocore/net/SSLCertLookup.cc
index bb22b3a..b804d51 100644
--- a/iocore/net/SSLCertLookup.cc
+++ b/iocore/net/SSLCertLookup.cc
@@ -35,7 +35,7 @@
 struct SSLAddressLookupKey
 {
   explicit
-  SSLAddressLookupKey(const IpEndpoint& ip)
+  SSLAddressLookupKey(const IpEndpoint& ip) : sep(0)
   {
     static const char hextab[16] = {
       '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
@@ -45,10 +45,11 @@ struct SSLAddressLookupKey
     uint16_t port = ntohs(ip.port());
 
     // For IP addresses, the cache key is the hex address with the port concatenated. This makes the lookup
-    // insensitive to address formatting and also allow the longest match semantic to product different matches
+    // insensitive to address formatting and also allow the longest match semantic to produce different matches
     // if there is a certificate on the port.
     nbytes = ats_ip_to_hex(&ip.sa, key, sizeof(key));
     if (port) {
+      sep = nbytes;
       key[nbytes++] = '.';
       key[nbytes++] = hextab[ (port >> 12) & 0x000F ];
       key[nbytes++] = hextab[ (port >>  8) & 0x000F ];
@@ -59,9 +60,12 @@ struct SSLAddressLookupKey
   }
 
   const char * get() const { return key; }
+  void split() { key[sep] = '\0'; }
+  void unsplit() { key[sep] = '.'; }
 
 private:
   char key[(TS_IP6_SIZE * 2) /* hex addr */ + 1 /* dot */ + 4 /* port */ + 1 /* NULL */];
+  unsigned char sep; // offset of address/port separator
 };
 
 struct SSLContextStorage
@@ -107,8 +111,21 @@ SSLCertLookup::findInfoInHash(const char * address) const
 SSL_CTX *
 SSLCertLookup::findInfoInHash(const IpEndpoint& address) const
 {
+  SSL_CTX * ctx;
   SSLAddressLookupKey key(address);
-  return this->ssl_storage->lookup(key.get());
+
+  // First try the full address.
+  if ((ctx = this->ssl_storage->lookup(key.get()))) {
+    return ctx;
+  }
+
+  // If that failed, try the address without the port.
+  if (address.port()) {
+    key.split();
+    return this->ssl_storage->lookup(key.get());
+  }
+
+  return NULL;
 }
 
 bool

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/129d7719/iocore/net/SSLConfig.cc
----------------------------------------------------------------------
diff --git a/iocore/net/SSLConfig.cc b/iocore/net/SSLConfig.cc
index c91860e..4549e73 100644
--- a/iocore/net/SSLConfig.cc
+++ b/iocore/net/SSLConfig.cc
@@ -110,7 +110,7 @@ set_paths_helper(const char *path, const char *filename, char **final_path, char
   }
 
   if (final_filename) {
-    *final_filename = filename ? ats_strdup(Layout::get()->relative_to(path, filename)) : NULL;
+    *final_filename = filename ? Layout::get()->relative_to(path, filename) : NULL;
   }
 
 }
@@ -167,7 +167,7 @@ SSLConfigParams::initialize()
 
   char *cert_chain = NULL;
   IOCORE_ReadConfigStringAlloc(cert_chain, "proxy.config.ssl.server.cert_chain.filename");
-  set_paths_helper(serverCertRelativePath, cert_chain, &serverCertPathOnly, &serverCertChainPath);
+  set_paths_helper(serverCertRelativePath, cert_chain, NULL, &serverCertChainPath);
   ats_free(cert_chain);
 
   IOCORE_ReadConfigStringAlloc(multicert_config_file, "proxy.config.ssl.server.multicert.filename");
@@ -259,6 +259,8 @@ SSLCertificateConfig::reconfigure()
 
   if (SSLParseCertificateConfiguration(params, lookup)) {
     configid = configProcessor.set(configid, lookup);
+  } else {
+    delete lookup;
   }
 }
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/129d7719/iocore/net/SSLNetVConnection.cc
----------------------------------------------------------------------
diff --git a/iocore/net/SSLNetVConnection.cc b/iocore/net/SSLNetVConnection.cc
index 6fe9f6c..1af06d4 100644
--- a/iocore/net/SSLNetVConnection.cc
+++ b/iocore/net/SSLNetVConnection.cc
@@ -450,24 +450,14 @@ SSLNetVConnection::free(EThread * t) {
 int
 SSLNetVConnection::sslStartHandShake(int event, int &err)
 {
-  IpEndpoint ip;
-
   if (event == SSL_EVENT_SERVER) {
     if (this->ssl == NULL) {
-      SSL_CTX * ctx;
-      int namelen = sizeof(ip);
       SSLCertificateConfig::scoped_config lookup;
 
-      safe_getsockname(get_socket(), &ip.sa, &namelen);
-
-      ip.port() = 0; // XXX certificate lookup can't check the port yet; TS-1500
-      ctx = lookup->findInfoInHash(ip);
-      Debug("ssl", "IP context is %p, default context %p", ctx, lookup->defaultContext());
-      if (ctx == NULL) {
-        ctx = lookup->defaultContext();
-      }
-
-      this->ssl = make_ssl_connection(ctx, this);
+      // Attach the default SSL_CTX to this SSL session. The default context is never going to be able
+      // to negotiate a SSL session, but it's enough to trampoline us into the SNI callback where we
+      // can select the right server certificate.
+      this->ssl = make_ssl_connection(lookup->defaultContext(), this);
       if (this->ssl == NULL) {
         Debug("ssl", "SSLNetVConnection::sslServerHandShakeEvent, ssl create failed");
         SSLError("SSL_StartHandShake");

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/129d7719/iocore/net/SSLUtils.cc
----------------------------------------------------------------------
diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index e7d8d50..030f170 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -22,10 +22,7 @@
 #include "ink_config.h"
 #include "libts.h"
 #include "I_Layout.h"
-#include "P_EventSystem.h"
-#include "P_SSLUtils.h"
-#include "P_SSLConfig.h"
-#include "P_SSLCertLookup.h"
+#include "P_Net.h"
 
 #include <openssl/err.h>
 #include <openssl/bio.h>
@@ -123,9 +120,10 @@ end:
 static int
 ssl_servername_callback(SSL * ssl, int * ad, void * arg)
 {
-  SSL_CTX *       ctx = NULL;
-  SSLCertLookup * lookup = (SSLCertLookup *) arg;
-  const char *    servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+  SSL_CTX *           ctx = NULL;
+  SSLCertLookup *     lookup = (SSLCertLookup *) arg;
+  const char *        servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+  SSLNetVConnection * netvc = (SSLNetVConnection *)SSL_get_app_data(ssl);
 
   Debug("ssl", "ssl=%p ad=%d lookup=%p server=%s", ssl, *ad, lookup, servername);
 
@@ -136,6 +134,15 @@ ssl_servername_callback(SSL * ssl, int * ad, void * arg)
     ctx = lookup->findInfoInHash((char *)servername);
   }
 
+  // If there's no match on the server name, try to match on the peer address.
+  if (ctx == NULL) {
+    IpEndpoint ip;
+    int namelen = sizeof(ip);
+
+    safe_getsockname(netvc->get_socket(), &ip.sa, &namelen);
+    ctx = lookup->findInfoInHash(ip);
+  }
+
   if (ctx != NULL) {
     SSL_set_SSL_CTX(ssl, ctx);
   }

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/129d7719/iocore/net/test_certlookup.cc
----------------------------------------------------------------------
diff --git a/iocore/net/test_certlookup.cc b/iocore/net/test_certlookup.cc
index bd0a1f8..c5b60ae 100644
--- a/iocore/net/test_certlookup.cc
+++ b/iocore/net/test_certlookup.cc
@@ -25,6 +25,15 @@
 #include "ts/TestBox.h"
 #include <fstream>
 
+static IpEndpoint
+make_endpoint(const char * address)
+{
+  IpEndpoint ip;
+
+  assert(ats_ip_pton(address, &ip) == 0);
+  return ip;
+}
+
 REGRESSION_TEST(SSLCertificateLookup)(RegressionTest* t, int atype, int * pstatus)
 {
   TestBox       box(t, pstatus);
@@ -69,12 +78,69 @@ REGRESSION_TEST(SSLCertificateLookup)(RegressionTest* t, int atype, int * pstatu
   box.check(lookup.findInfoInHash("www.bar.com") == NULL, "host lookup for www.bar.com");
 }
 
+REGRESSION_TEST(SSLAddressLookup)(RegressionTest* t, int atype, int * pstatus)
+{
+  TestBox       box(t, pstatus);
+  SSLCertLookup lookup;
+
+  struct {
+    SSL_CTX * ip6;
+    SSL_CTX * ip6p;
+    SSL_CTX * ip4;
+    SSL_CTX * ip4p;
+  } context;
+
+  struct {
+     IpEndpoint ip6;
+     IpEndpoint ip6p;
+     IpEndpoint ip4;
+     IpEndpoint ip4p;
+  } endpoint;
+
+  context.ip6 = SSL_CTX_new(SSLv23_server_method());
+  context.ip6p = SSL_CTX_new(SSLv23_server_method());
+  context.ip4 = SSL_CTX_new(SSLv23_server_method());
+  context.ip4p = SSL_CTX_new(SSLv23_server_method());
+
+  endpoint.ip6 = make_endpoint("fe80::7ed1:c3ff:fe90:2582");
+  endpoint.ip6p = make_endpoint("[fe80::7ed1:c3ff:fe90:2582]:80");
+  endpoint.ip4 = make_endpoint("10.0.0.5");
+  endpoint.ip4p = make_endpoint("10.0.0.5:80");
+
+  box = REGRESSION_TEST_PASSED;
+
+  // For each combination of address with port and address without port, make sure that we find the
+  // the most specific match (ie. find the context with the port if it is available) ...
+
+  box.check(lookup.insert(context.ip6, endpoint.ip6), "insert IPv6 address");
+  box.check(lookup.findInfoInHash(endpoint.ip6) == context.ip6, "IPv6 exact match lookup");
+  box.check(lookup.findInfoInHash(endpoint.ip6p) == context.ip6, "IPv6 exact match lookup w/ port");
+
+  box.check(lookup.insert(context.ip6p, endpoint.ip6p), "insert IPv6 address w/ port");
+  box.check(lookup.findInfoInHash(endpoint.ip6) == context.ip6, "IPv6 longest match lookup");
+  box.check(lookup.findInfoInHash(endpoint.ip6p) == context.ip6p, "IPv6 longest match lookup w/ port");
+
+  box.check(lookup.insert(context.ip4, endpoint.ip4), "insert IPv4 address");
+  box.check(lookup.findInfoInHash(endpoint.ip4) == context.ip4, "IPv4 exact match lookup");
+  box.check(lookup.findInfoInHash(endpoint.ip4p) == context.ip4, "IPv4 exact match lookup w/ port");
+
+  box.check(lookup.insert(context.ip4p, endpoint.ip4p), "insert IPv4 address w/ port");
+  box.check(lookup.findInfoInHash(endpoint.ip4) == context.ip4, "IPv4 longest match lookup");
+  box.check(lookup.findInfoInHash(endpoint.ip4p) == context.ip4p, "IPv4 longest match lookup w/ port");
+}
+
 static unsigned
 load_hostnames_csv(const char * fname, SSLCertLookup& lookup)
 {
   std::fstream infile(fname, std::ios_base::in);
   unsigned count = 0;
 
+  // SSLCertLookup correctly handles indexing the same certificate
+  // with multiple names, an it's way faster to load a lot of names
+  // if we don't need a new context every time.
+
+  SSL_CTX * ctx = SSL_CTX_new(SSLv23_server_method());
+
   // The input should have 2 comma-separated fields; this is the format that you get when
   // you download the top 1M sites from alexa.
   //
@@ -88,7 +154,6 @@ load_hostnames_csv(const char * fname, SSLCertLookup& lookup)
   while (!infile.eof()) {
     std::string line;
     std::string::size_type pos;
-    SSL_CTX * ctx = SSL_CTX_new(SSLv23_server_method());
 
     infile >> line;
     if (line.empty()) {

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/129d7719/proxy/config/ssl_multicert.config.default
----------------------------------------------------------------------
diff --git a/proxy/config/ssl_multicert.config.default b/proxy/config/ssl_multicert.config.default
index de05832..be60994 100644
--- a/proxy/config/ssl_multicert.config.default
+++ b/proxy/config/ssl_multicert.config.default
@@ -22,6 +22,11 @@
 #   '*', the certificate will be used as the default fallback if no
 #   other match can be made.
 #
+#   The address specified here can 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=FILENAME
 #   The name of the file containg the private key for this certificate.
 #   If the key is contained in the certificate file, this field can be
@@ -39,4 +44,5 @@
 #   ssl_cert_name=foo.pem
 #   dest_ip=*	ssl_cert_name=bar.pem ssl_key_name=barKey.pem
 #   dest_ip=209.131.48.79	ssl_cert_name=server.pem ssl_key_name=serverKey.pem
+#   dest_ip=10.0.0.1:99 ssl_cert_name=port99.pem