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