Updated Branches:
  refs/heads/master da9a84da8 -> 5deb1b5a9

TS-2239: support the ALPN TLS extension

Add support the for TLS Application Layer Protocol Negotiation
extension. This uses the same infrastructure as NPN, so a consistent
protocol advertisement is created. Any plugin that registers to
accept a named protocol will automatically gain NPN and ALPN support.

This requires a very recent OpenSSL, probably 1.2 or something like
that.


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

Branch: refs/heads/master
Commit: 9225250fa19a87082643b1d0f12f25e7adfd0da3
Parents: a49330c
Author: James Peach <[email protected]>
Authored: Mon Feb 10 16:46:05 2014 -0800
Committer: James Peach <[email protected]>
Committed: Tue Feb 11 16:35:13 2014 -0800

----------------------------------------------------------------------
 iocore/net/P_SSLNetVConnection.h  |  3 +-
 iocore/net/SSLNetVConnection.cc   | 60 ++++++++++++++++++++++++++++++----
 iocore/net/SSLUtils.cc            | 11 +++++++
 proxy/http/HttpProxyServerMain.cc |  4 ++-
 4 files changed, 69 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9225250f/iocore/net/P_SSLNetVConnection.h
----------------------------------------------------------------------
diff --git a/iocore/net/P_SSLNetVConnection.h b/iocore/net/P_SSLNetVConnection.h
index e4734b2..fcb0e8c 100644
--- a/iocore/net/P_SSLNetVConnection.h
+++ b/iocore/net/P_SSLNetVConnection.h
@@ -104,7 +104,8 @@ public:
   X509 *client_cert;
   X509 *server_cert;
 
-  static int advertise_next_protocol(SSL *ssl, const unsigned char **out, 
unsigned int *outlen, void *arg);
+  static int advertise_next_protocol(SSL * ssl, const unsigned char ** out, 
unsigned * outlen, void *);
+  static int select_next_protocol(SSL * ssl, const unsigned char ** out, 
unsigned char * outlen, const unsigned char * in, unsigned inlen, void *);
 
   Continuation * endpoint() const {
     return npnEndpoint;

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9225250f/iocore/net/SSLNetVConnection.cc
----------------------------------------------------------------------
diff --git a/iocore/net/SSLNetVConnection.cc b/iocore/net/SSLNetVConnection.cc
index b04de64..00e7fa0 100644
--- a/iocore/net/SSLNetVConnection.cc
+++ b/iocore/net/SSLNetVConnection.cc
@@ -545,23 +545,38 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)
     }
     sslHandShakeComplete = 1;
 
-#if TS_USE_TLS_NPN
     {
       const unsigned char * proto = NULL;
       unsigned len = 0;
 
-      SSL_get0_next_proto_negotiated(ssl, &proto, &len);
+      // If it's possible to negotiate both NPN and ALPN, then ALPN
+      // is preferred since it is the server's preference.  The server
+      // preference would not be meaningful if we let the client
+      // preference have priority.
+
+#if TS_USE_TLS_ALPN
+      SSL_get0_alpn_selected(ssl, &proto, &len);
+#endif /* TS_USE_TLS_ALPN */
+
+#if TS_USE_TLS_NPN
+      if (len == 0) {
+        SSL_get0_next_proto_negotiated(ssl, &proto, &len);
+      }
+#endif /* TS_USE_TLS_NPN */
+
       if (len) {
-        if (this->npnSet) {
-          this->npnEndpoint = this->npnSet->findEndpoint(proto, len);
-          this->npnSet = NULL;
-        }
+        // If there's no NPN set, we should not have done this negotiation.
+        ink_assert(this->npnSet != NULL);
+
+        this->npnEndpoint = this->npnSet->findEndpoint(proto, len);
+        this->npnSet = NULL;
+
+        ink_assert(this->npnEndpoint != NULL);
         Debug("ssl", "client selected next protocol %.*s", len, proto);
       } else {
         Debug("ssl", "client did not select a next protocol");
       }
     }
-#endif /* TS_USE_TLS_NPN */
 
     return EVENT_DONE;
 
@@ -661,6 +676,9 @@ SSLNetVConnection::registerNextProtocolSet(const 
SSLNextProtocolSet * s)
   this->npnSet = s;
 }
 
+// NextProtocolNegotiation TLS extension callback. The NPN extension
+// allows the client to select a preferred protocol, so all we have
+// to do here is tell them what out protocol set is.
 int
 SSLNetVConnection::advertise_next_protocol(SSL *ssl, const unsigned char 
**out, unsigned int *outlen,
                                            void * /*arg ATS_UNUSED */)
@@ -676,3 +694,31 @@ SSLNetVConnection::advertise_next_protocol(SSL *ssl, const 
unsigned char **out,
 
   return SSL_TLSEXT_ERR_NOACK;
 }
+
+// ALPN TLS extension callback. Given the client's set of offered
+// protocols, we have to select a protocol to use for this session.
+int
+SSLNetVConnection::select_next_protocol(SSL * ssl, const unsigned char ** out, 
unsigned char * outlen, const unsigned char * in, unsigned inlen, void *)
+{
+  SSLNetVConnection * netvc = (SSLNetVConnection *)SSL_get_app_data(ssl);
+  const unsigned char * npn = NULL;
+  unsigned npnsz = 0;
+
+  ink_release_assert(netvc != NULL);
+
+  if (netvc->npnSet && netvc->npnSet->advertiseProtocols(&npn, &npnsz)) {
+    // SSL_select_next_proto chooses the first server-offered protocol that 
appears in the clients protocol set, ie. the
+    // server selects the protocol. This is a n^2 search, so it's preferable 
to keep the protocol set short.
+
+#if HAVE_SSL_SELECT_NEXT_PROTO
+    if (SSL_select_next_proto((unsigned char **)out, outlen, npn, npnsz, in, 
inlen) == OPENSSL_NPN_NEGOTIATED) {
+      Debug("ssl", "selected ALPN protocol %.*s", (int)(*outlen), *out);
+      return SSL_TLSEXT_ERR_OK;
+    }
+#endif /* HAVE_SSL_SELECT_NEXT_PROTO */
+  }
+
+  *out = NULL;
+  *outlen = 0;
+  return SSL_TLSEXT_ERR_NOACK;
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9225250f/iocore/net/SSLUtils.cc
----------------------------------------------------------------------
diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index 4b1b646..474f456 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -718,6 +718,10 @@ ssl_store_ssl_context(
   SSL_CTX_set_next_protos_advertised_cb(ctx, 
SSLNetVConnection::advertise_next_protocol, NULL);
 #endif /* TS_USE_TLS_NPN */
 
+#if TS_USE_TLS_ALPN
+  SSL_CTX_set_alpn_select_cb(ctx, SSLNetVConnection::select_next_protocol, 
NULL);
+#endif /* TS_USE_TLS_ALPN */
+
   certpath = Layout::relative_to(params->serverCertPathOnly, cert);
 
   // Index this certificate by the specified IP(v6) address. If the address is 
"*", make it the default context.
@@ -896,6 +900,13 @@ SSLParseCertificateConfiguration(
   // context.
   if (lookup->ssl_default == NULL) {
     lookup->ssl_default = ssl_context_enable_sni(SSLDefaultServerContext(), 
lookup);
+
+    // The ALPN negotiation happens before certificate selection, so we need 
to install the ALPN callback
+    // on the default SSL context.
+#if TS_USE_TLS_ALPN
+  SSL_CTX_set_alpn_select_cb(lookup->ssl_default, 
SSLNetVConnection::select_next_protocol, NULL);
+#endif /* TS_USE_TLS_ALPN */
+
     lookup->insert(lookup->ssl_default, "*");
   }
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9225250f/proxy/http/HttpProxyServerMain.cc
----------------------------------------------------------------------
diff --git a/proxy/http/HttpProxyServerMain.cc 
b/proxy/http/HttpProxyServerMain.cc
index 7c9b49d..662856a 100644
--- a/proxy/http/HttpProxyServerMain.cc
+++ b/proxy/http/HttpProxyServerMain.cc
@@ -163,8 +163,10 @@ MakeHttpProxyAcceptor(HttpProxyAcceptor& acceptor, 
HttpProxyPort& port, unsigned
   if (port.isSSL()) {
     HttpAccept * accept = NEW(new HttpAccept(accept_opt));
     SSLNextProtocolAccept * ssl = NEW(new SSLNextProtocolAccept(accept));
-    ssl->registerEndpoint(TS_NPN_PROTOCOL_HTTP_1_0, accept);
+
+    // ALPN selects the first server-offered protocol, so make sure that we 
offer HTTP/1.1 first.
     ssl->registerEndpoint(TS_NPN_PROTOCOL_HTTP_1_1, accept);
+    ssl->registerEndpoint(TS_NPN_PROTOCOL_HTTP_1_0, accept);
 
     ink_scoped_mutex lock(ssl_plugin_mutex);
     ssl_plugin_acceptors.push(ssl);

Reply via email to