From 300766f367529912d43cedd85561ad5d8fa87478 Mon Sep 17 00:00:00 2001 From: Patrick McManus Date: Fri, 15 Aug 2014 09:39:53 -0400 Subject: [PATCH] bug 1050063 - consider tls client hello version in alpn/npn offer list r=hurley r=keeler --- netwerk/protocol/http/ASpdySession.cpp | 15 ++++++--- netwerk/protocol/http/ASpdySession.h | 23 +++++++++----- netwerk/protocol/http/Http2Session.cpp | 15 +++++++++ netwerk/protocol/http/Http2Session.h | 1 + netwerk/protocol/http/nsHttpConnection.cpp | 36 ++++++++++++---------- netwerk/socket/nsISSLSocketControl.idl | 3 +- security/manager/ssl/src/nsNSSIOLayer.cpp | 7 +++++ 7 files changed, 72 insertions(+), 28 deletions(-) diff --git a/netwerk/protocol/http/ASpdySession.cpp b/netwerk/protocol/http/ASpdySession.cpp index ba3b373f7151..5b1d2592e66a 100644 --- a/netwerk/protocol/http/ASpdySession.cpp +++ b/netwerk/protocol/http/ASpdySession.cpp @@ -64,6 +64,10 @@ ASpdySession::NewSpdySession(uint32_t version, return nullptr; } +static bool SpdySessionTrue(nsISupports *securityInfo) +{ + return true; +} SpdyInformation::SpdyInformation() { @@ -71,16 +75,19 @@ SpdyInformation::SpdyInformation() // most preferred for ALPN negotiaton Version[0] = SPDY_VERSION_3; VersionString[0] = NS_LITERAL_CSTRING("spdy/3"); + ALPNCallbacks[0] = SpdySessionTrue; Version[1] = SPDY_VERSION_31; VersionString[1] = NS_LITERAL_CSTRING("spdy/3.1"); + ALPNCallbacks[1] = SpdySessionTrue; Version[2] = NS_HTTP2_DRAFT_VERSION; VersionString[2] = NS_LITERAL_CSTRING(NS_HTTP2_DRAFT_TOKEN); + ALPNCallbacks[2] = Http2Session::ALPNCallback; } bool -SpdyInformation::ProtocolEnabled(uint32_t index) +SpdyInformation::ProtocolEnabled(uint32_t index) const { MOZ_ASSERT(index < kCount, "index out of range"); @@ -96,15 +103,15 @@ SpdyInformation::ProtocolEnabled(uint32_t index) } nsresult -SpdyInformation::GetNPNVersionIndex(const nsACString &npnString, - uint8_t *result) +SpdyInformation::GetNPNIndex(const nsACString &npnString, + uint32_t *result) const { if (npnString.IsEmpty()) return NS_ERROR_FAILURE; for (uint32_t index = 0; index < kCount; ++index) { if (npnString.Equals(VersionString[index])) { - *result = Version[index]; + *result = index; return NS_OK; } } diff --git a/netwerk/protocol/http/ASpdySession.h b/netwerk/protocol/http/ASpdySession.h index e9b264d7302b..b1ce648c86d5 100644 --- a/netwerk/protocol/http/ASpdySession.h +++ b/netwerk/protocol/http/ASpdySession.h @@ -61,6 +61,8 @@ public: } }; +typedef bool (*ALPNCallback) (nsISupports *); // nsISSLSocketControl is typical + // this is essentially a single instantiation as a member of nsHttpHandler. // It could be all static except using static ctors of XPCOM objects is a // bad idea. @@ -72,15 +74,22 @@ public: static const uint32_t kCount = 3; - // determine if a version of the protocol is enabled for index <= kCount - bool ProtocolEnabled(uint32_t index); + // determine the index (0..kCount-1) of the spdy information that + // correlates to the npn string. NS_FAILED() if no match is found. + nsresult GetNPNIndex(const nsACString &npnString, uint32_t *result) const; - // lookup a version enum based on an npn string. returns NS_OK if - // string was known. - nsresult GetNPNVersionIndex(const nsACString &npnString, uint8_t *result); + // determine if a version of the protocol is enabled for index < kCount + bool ProtocolEnabled(uint32_t index) const; - uint8_t Version[kCount]; - nsCString VersionString[kCount]; + uint8_t Version[kCount]; // telemetry enum e.g. SPDY_VERSION_31 + nsCString VersionString[kCount]; // npn string e.g. "spdy/3.1" + + // the ALPNCallback function allows the protocol stack to decide whether or + // not to offer a particular protocol based on the known TLS information + // that we will offer in the client hello (such as version). There has + // not been a Server Hello received yet, so not much else can be considered. + // Stacks without restrictions can just use SpdySessionTrue() + ALPNCallback ALPNCallbacks[kCount]; }; }} // namespace mozilla::net diff --git a/netwerk/protocol/http/Http2Session.cpp b/netwerk/protocol/http/Http2Session.cpp index 6bc5e694bf64..79051c7a1e86 100644 --- a/netwerk/protocol/http/Http2Session.cpp +++ b/netwerk/protocol/http/Http2Session.cpp @@ -2947,6 +2947,21 @@ Http2Session::BufferOutput(const char *buf, return rv; } +bool // static +Http2Session::ALPNCallback(nsISupports *securityInfo) +{ + nsCOMPtr ssl = do_QueryInterface(securityInfo); + LOG3(("Http2Session::ALPNCallback sslsocketcontrol=%p\n", ssl.get())); + if (ssl) { + int16_t version = ssl->GetSSLVersionOffered(); + LOG3(("Http2Session::ALPNCallback version=%x\n", version)); + if (version >= nsISSLSocketControl::TLS_VERSION_1_2) { + return true; + } + } + return false; +} + nsresult Http2Session::ConfirmTLSProfile() { diff --git a/netwerk/protocol/http/Http2Session.h b/netwerk/protocol/http/Http2Session.h index 79b951fea528..a62b7822626a 100644 --- a/netwerk/protocol/http/Http2Session.h +++ b/netwerk/protocol/http/Http2Session.h @@ -198,6 +198,7 @@ public: void MaybeDecrementConcurrent(Http2Stream *stream); nsresult ConfirmTLSProfile(); + static bool ALPNCallback(nsISupports *securityInfo); uint64_t Serial() { return mSerial; } diff --git a/netwerk/protocol/http/nsHttpConnection.cpp b/netwerk/protocol/http/nsHttpConnection.cpp index b4b6ba701ab6..04036bfd100f 100644 --- a/netwerk/protocol/http/nsHttpConnection.cpp +++ b/netwerk/protocol/http/nsHttpConnection.cpp @@ -310,21 +310,19 @@ nsHttpConnection::EnsureNPNComplete() return false; } - if (NS_FAILED(rv)) { - goto npnComplete; - } - LOG(("nsHttpConnection::EnsureNPNComplete %p [%s] negotiated to '%s'%s\n", - this, mConnInfo->HashKey().get(), negotiatedNPN.get(), - mTLSFilter ? " [Double Tunnel]" : "")); - - uint8_t spdyVersion; - rv = gHttpHandler->SpdyInfo()->GetNPNVersionIndex(negotiatedNPN, - &spdyVersion); if (NS_SUCCEEDED(rv)) { - StartSpdy(spdyVersion); - } + LOG(("nsHttpConnection::EnsureNPNComplete %p [%s] negotiated to '%s'%s\n", + this, mConnInfo->HashKey().get(), negotiatedNPN.get(), + mTLSFilter ? " [Double Tunnel]" : "")); - Telemetry::Accumulate(Telemetry::SPDY_NPN_CONNECT, UsingSpdy()); + uint32_t infoIndex; + const SpdyInformation *info = gHttpHandler->SpdyInfo(); + if (NS_SUCCEEDED(info->GetNPNIndex(negotiatedNPN, &infoIndex))) { + StartSpdy(info->Version[infoIndex]); + } + + Telemetry::Accumulate(Telemetry::SPDY_NPN_CONNECT, UsingSpdy()); + } npnComplete: LOG(("nsHttpConnection::EnsureNPNComplete setting complete to true")); @@ -475,6 +473,10 @@ nsHttpConnection::SetupSSL() } } +// The naming of NPN is historical - this function creates the basic +// offer list for both NPN and ALPN. ALPN validation callbacks are made +// now before the handshake is complete, and NPN validation callbacks +// are made during the handshake. nsresult nsHttpConnection::SetupNPNList(nsISSLSocketControl *ssl, uint32_t caps) { @@ -492,10 +494,12 @@ nsHttpConnection::SetupNPNList(nsISSLSocketControl *ssl, uint32_t caps) if (gHttpHandler->IsSpdyEnabled() && !(caps & NS_HTTP_DISALLOW_SPDY)) { LOG(("nsHttpConnection::SetupSSL Allow SPDY NPN selection")); + const SpdyInformation *info = gHttpHandler->SpdyInfo(); for (uint32_t index = SpdyInformation::kCount; index > 0; --index) { - if (gHttpHandler->SpdyInfo()->ProtocolEnabled(index - 1)) - protocolArray.AppendElement( - gHttpHandler->SpdyInfo()->VersionString[index - 1]); + if (info->ProtocolEnabled(index - 1) && + info->ALPNCallbacks[index - 1](ssl)) { + protocolArray.AppendElement(info->VersionString[index - 1]); + } } } diff --git a/netwerk/socket/nsISSLSocketControl.idl b/netwerk/socket/nsISSLSocketControl.idl index e9c3d746c794..0288172dd0fc 100644 --- a/netwerk/socket/nsISSLSocketControl.idl +++ b/netwerk/socket/nsISSLSocketControl.idl @@ -15,7 +15,7 @@ class nsCString; %} [ref] native nsCStringTArrayRef(nsTArray); -[scriptable, builtinclass, uuid(7836a872-e50c-4e43-8224-fd08a8d09699)] +[scriptable, builtinclass, uuid(89b819dc-31b0-4d09-915a-66f8a3703483)] interface nsISSLSocketControl : nsISupports { attribute nsIInterfaceRequestor notificationCallbacks; @@ -83,6 +83,7 @@ interface nsISSLSocketControl : nsISupports { const short SSL_VERSION_UNKNOWN = -1; [infallible] readonly attribute short SSLVersionUsed; + [infallible] readonly attribute short SSLVersionOffered; /* These values match the NSS defined values in sslt.h */ const short SSL_MAC_UNKNOWN = -1; diff --git a/security/manager/ssl/src/nsNSSIOLayer.cpp b/security/manager/ssl/src/nsNSSIOLayer.cpp index b688d3814f0f..3f5ca423a0f2 100644 --- a/security/manager/ssl/src/nsNSSIOLayer.cpp +++ b/security/manager/ssl/src/nsNSSIOLayer.cpp @@ -197,6 +197,13 @@ nsNSSSocketInfo::GetSSLVersionUsed(int16_t* aSSLVersionUsed) return NS_OK; } +NS_IMETHODIMP +nsNSSSocketInfo::GetSSLVersionOffered(int16_t* aSSLVersionOffered) +{ + *aSSLVersionOffered = mTLSVersionRange.max; + return NS_OK; +} + NS_IMETHODIMP nsNSSSocketInfo::GetMACAlgorithmUsed(int16_t* aMac) {