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/03/14 04:43:32 UTC

[2/4] git commit: TS-462: Add ServerNameIndication support

TS-462: Add ServerNameIndication support

When we load a SSL certificate, parse out the subject CN and any
subjectAltNames. If the OpenSSL version supports it, use the SNI
extension to match the requested server name and swizzle the SSL_CTX
appropriately.


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

Branch: refs/heads/master
Commit: c5487c852a02c00067b6064ec08663f696f1cc62
Parents: cad0e9b
Author: James Peach <jp...@apache.org>
Authored: Mon Mar 5 22:19:29 2012 -0800
Committer: James Peach <jp...@apache.org>
Committed: Tue Mar 13 20:33:19 2012 -0700

----------------------------------------------------------------------
 iocore/net/SSLCertLookup.cc     |  133 +++++++++++++++++++++++++++++++++-
 iocore/net/SSLNetProcessor.cc   |    8 +-
 iocore/net/SSLNetVConnection.cc |   37 ++++++++++
 3 files changed, 170 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c5487c85/iocore/net/SSLCertLookup.cc
----------------------------------------------------------------------
diff --git a/iocore/net/SSLCertLookup.cc b/iocore/net/SSLCertLookup.cc
index 0fbd0ef..42f6681 100644
--- a/iocore/net/SSLCertLookup.cc
+++ b/iocore/net/SSLCertLookup.cc
@@ -1,6 +1,6 @@
 /** @file
 
-  A brief file description
+  SSL Context management
 
   @section license License
 
@@ -25,6 +25,13 @@
 
 #include "P_SSLCertLookup.h"
 #include "P_UnixNet.h"
+#include "I_Layout.h"
+
+#include <openssl/bio.h>
+#include <openssl/pem.h>
+#include <openssl/x509.h>
+#include <openssl/asn1.h>
+#include <openssl/ts.h>
 
 #if (OPENSSL_VERSION_NUMBER >= 0x10000000L) // openssl returns a const SSL_METHOD
 typedef const SSL_METHOD * ink_ssl_method_t;
@@ -34,6 +41,9 @@ typedef SSL_METHOD * ink_ssl_method_t;
 
 SSLCertLookup sslCertLookup;
 
+static void
+insert_ssl_certificate(InkHashTable *, SSL_CTX *, const char *);
+
 #define SSL_IP_TAG "dest_ip"
 #define SSL_CERT_TAG "ssl_cert_name"
 #define SSL_PRIVATE_KEY_TAG "ssl_key_name"
@@ -228,9 +238,21 @@ SSLCertLookup::addInfoToHash(
 //  if (serverPrivateKey == NULL)
 //      serverPrivateKey = cert;
 
-  ssl_NetProcessor.initSSLServerCTX(ctx, param, cert, caCert,  serverPrivateKey, false);
-  ink_hash_table_insert(SSLCertLookupHashTable, strAddr, (void *) ctx);
-  return (true);
+  if (ssl_NetProcessor.initSSLServerCTX(ctx, this->param, cert, caCert, serverPrivateKey, false) == 0) {
+    char * certpath = Layout::relative_to(this->param->getServerCertPathOnly(), cert);
+    ink_hash_table_insert(SSLCertLookupHashTable, strAddr, (void *) ctx);
+
+    // 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.
+    insert_ssl_certificate(SSLCertLookupHashTable, ctx, certpath);
+
+    ats_free(certpath);
+    return (true);
+  }
+
+  SSL_CTX_free(ctx);
+  return (false);
 }
 
 SSL_CTX *
@@ -247,5 +269,108 @@ SSLCertLookup::findInfoInHash(char *strAddr) const
 
 SSLCertLookup::~SSLCertLookup()
 {
+  // XXX This is completely broken. You can't use ats_free to free
+  // a SSL_CTX *, you have to use SSL_CTX_free(). It doesn't matter
+  // right now because sslCertLookup is a singleton and never destroyed.
   ink_hash_table_destroy_and_xfree_values(SSLCertLookupHashTable);
 }
+
+struct ats_x509_certificate
+{
+  explicit ats_x509_certificate(X509 * x) : x509(x) {}
+  ~ats_x509_certificate() { if (x509) X509_free(x509); }
+
+  X509 * x509;
+
+private:
+  ats_x509_certificate(const ats_x509_certificate&);
+  ats_x509_certificate& operator=(const ats_x509_certificate&);
+};
+
+struct ats_file_bio
+{
+    ats_file_bio(const char * path, const char * mode)
+      : bio(BIO_new_file(path, mode)) {
+    }
+
+    ~ats_file_bio() {
+        (void)BIO_set_close(bio, BIO_CLOSE);
+        BIO_free(bio);
+    }
+
+    operator bool() const {
+        return bio != NULL;
+    }
+
+    BIO * bio;
+
+private:
+    ats_file_bio(const ats_file_bio&);
+    ats_file_bio& operator=(const ats_file_bio&);
+};
+
+static char *
+asn1_strdup(ASN1_STRING * s)
+{
+    // Make sure we have an 8-bit encoding.
+    ink_assert(ASN1_STRING_type(s) == V_ASN1_IA5STRING ||
+      ASN1_STRING_type(s) == V_ASN1_UTF8STRING ||
+      ASN1_STRING_type(s) == V_ASN1_PRINTABLESTRING);
+
+    return ats_strndup((const char *)ASN1_STRING_data(s), ASN1_STRING_length(s));
+}
+
+// Given a certificate and it's corresponding SSL_CTX context, insert hash
+// table aliases for all of the subject and subjectAltNames. Note that we don't
+// deal with wildcards (yet).
+static void
+insert_ssl_certificate(InkHashTable * htable, SSL_CTX * ctx, const char * certfile)
+{
+  GENERAL_NAMES * names = NULL;
+  X509_NAME * subject = NULL;
+
+  ats_file_bio bio(certfile, "r");
+  ats_x509_certificate certificate(PEM_read_bio_X509_AUX(bio.bio, NULL, NULL, NULL));
+
+  // Insert a key for the subject CN.
+  subject = X509_get_subject_name(certificate.x509);
+  if (subject) {
+    int pos = -1;
+    for (;;) {
+      pos = X509_NAME_get_index_by_NID(subject, NID_commonName, pos);
+      if (pos == -1) {
+        break;
+      }
+
+      X509_NAME_ENTRY * e = X509_NAME_get_entry(subject, pos);
+      ASN1_STRING * cn = X509_NAME_ENTRY_get_data(e);
+      char * name = asn1_strdup(cn);
+
+      Debug("ssl", "mapping '%s' to certificate %s", name, certfile);
+      ink_hash_table_insert(htable, name, (void *)ctx);
+      ats_free(name);
+    }
+  }
+
+  // Traverse the subjectAltNames (if any) and insert additional keys for the SSL context.
+  names = (GENERAL_NAMES *)X509_get_ext_d2i(certificate.x509, NID_subject_alt_name, NULL, NULL);
+  if (names) {
+    unsigned count = sk_GENERAL_NAME_num(names);
+    for (unsigned i = 0; i < count; ++i) {
+      GENERAL_NAME * name;
+      char * dns;
+
+      name = sk_GENERAL_NAME_value(names, i);
+      switch (name->type) {
+      case GEN_DNS:
+        dns = asn1_strdup(name->d.dNSName);
+        Debug("ssl", "mapping '%s' to certificate %s", dns, certfile);
+        ink_hash_table_insert(htable, dns, (void *)ctx);
+        ats_free(dns);
+        break;
+      }
+    }
+
+    GENERAL_NAMES_free(names);
+  }
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c5487c85/iocore/net/SSLNetProcessor.cc
----------------------------------------------------------------------
diff --git a/iocore/net/SSLNetProcessor.cc b/iocore/net/SSLNetProcessor.cc
index 89c31b0..e219480 100644
--- a/iocore/net/SSLNetProcessor.cc
+++ b/iocore/net/SSLNetProcessor.cc
@@ -348,12 +348,12 @@ SSLNetProcessor::initSSLServerCTX(SSL_CTX * lCtx, const SslConfigParams * param,
       }
     }
 
-    if (param->clientCertLevel == 2)
+    if (param->clientCertLevel == 2) {
       server_verify_client = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE;
-    else if (param->clientCertLevel == 1)
+    } else if (param->clientCertLevel == 1) {
       server_verify_client = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
-    else                        // disable client cert support
-    {
+    } else {
+      // disable client cert support
       server_verify_client = SSL_VERIFY_NONE;
       Error("Illegal Client Certification Level in records.config\n");
     }

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c5487c85/iocore/net/SSLNetVConnection.cc
----------------------------------------------------------------------
diff --git a/iocore/net/SSLNetVConnection.cc b/iocore/net/SSLNetVConnection.cc
index e619344..727e170 100644
--- a/iocore/net/SSLNetVConnection.cc
+++ b/iocore/net/SSLNetVConnection.cc
@@ -24,6 +24,10 @@
 #include "P_Net.h"
 #include "P_SSLNextProtocolSet.h"
 
+#if HAVE_OPENSSL_TLS1_H
+#include <openssl/tls1.h>
+#endif
+
 #define SSL_READ_ERROR_NONE	  0
 #define SSL_READ_ERROR		  1
 #define SSL_READ_READY		  2
@@ -42,6 +46,33 @@ ClassAllocator<SSLNetVConnection> sslNetVCAllocator("sslNetVCAllocator");
 // Private
 //
 
+#if TS_USE_TLS_SNI
+
+static int
+ssl_servername_callback(SSL * ssl, int * ad, void * arg)
+{
+  SSL_CTX * ctx;
+  SSLCertLookup * lookup = (SSLCertLookup *) arg;
+  const char * servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+
+  Debug("ssl", "ssl=%p ad=%d lookup=%p server=%s", ssl, *ad, lookup, servername);
+
+  ctx = lookup->findInfoInHash((char *)servername);
+  if (ctx == NULL) {
+    return SSL_TLSEXT_ERR_NOACK;
+  }
+
+  Debug("ssl", "found SSL context %p for requested name '%s'", ctx, servername);
+  SSL_set_SSL_CTX(ssl, ctx);
+
+  // We need to return one of the SSL_TLSEXT_ERR constants. If we return an
+  // error, we can fill in *ad with an alert code to propgate to the
+  // client, see SSL_AD_*.
+  return SSL_TLSEXT_ERR_OK;
+}
+
+#endif /* TS_USE_TLS_SNI */
+
 static SSL *
 make_ssl_connection(SSL_CTX * ctx, SSLNetVConnection * netvc)
 {
@@ -458,6 +489,12 @@ SSLNetVConnection::sslStartHandShake(int event, int &err)
         if (ctx == NULL) {
           ctx = ssl_NetProcessor.ctx;
         }
+
+#if TS_USE_TLS_SNI
+        Debug("ssl", "setting SNI callbacks");
+        SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_callback);
+        SSL_CTX_set_tlsext_servername_arg(ctx, &sslCertLookup);
+#endif
       }
 
       ssl = make_ssl_connection(ctx, this);