bug 1050063 - consider tls client hello version in alpn/npn offer list r=hurley r=keeler

This commit is contained in:
Patrick McManus 2014-08-15 09:39:53 -04:00
parent f7c74c5ba8
commit 300766f367
7 changed files with 72 additions and 28 deletions

View File

@ -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;
}
}

View File

@ -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

View File

@ -2947,6 +2947,21 @@ Http2Session::BufferOutput(const char *buf,
return rv;
}
bool // static
Http2Session::ALPNCallback(nsISupports *securityInfo)
{
nsCOMPtr<nsISSLSocketControl> 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()
{

View File

@ -198,6 +198,7 @@ public:
void MaybeDecrementConcurrent(Http2Stream *stream);
nsresult ConfirmTLSProfile();
static bool ALPNCallback(nsISupports *securityInfo);
uint64_t Serial() { return mSerial; }

View File

@ -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]);
}
}
}

View File

@ -15,7 +15,7 @@ class nsCString;
%}
[ref] native nsCStringTArrayRef(nsTArray<nsCString>);
[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;

View File

@ -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)
{