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);