diff options
author | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-18 18:56:34 +0000 |
---|---|---|
committer | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-18 18:56:34 +0000 |
commit | 5285d9763b1680e6344425ec29e3c92e8bfc9b3d (patch) | |
tree | fa0d67ea7578fb6c7ee34e644222b5d2cf768e55 /net | |
parent | c6e6617d80159e7c1dbf00ebf44f82b52f89f4ff (diff) | |
download | chromium_src-5285d9763b1680e6344425ec29e3c92e8bfc9b3d.zip chromium_src-5285d9763b1680e6344425ec29e3c92e8bfc9b3d.tar.gz chromium_src-5285d9763b1680e6344425ec29e3c92e8bfc9b3d.tar.bz2 |
net: rework the NPN patch.
This change moves the protocol selection logic out of NSS and into Chromium
code. This allows some things to be a little cleaner (no more wire-encoded NPN
strings) and also allows for some tricks that we have been considering for
SPDY+WebSockets.
As a consequence of this change, next protocols are now a
std::vector<std::string> rather than an encoded char*
BUG=none
TEST=SPDY still works with Google sites.
Review URL: http://codereview.chromium.org/8156001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@106093 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/base/ssl_config_service.h | 12 | ||||
-rw-r--r-- | net/http/http_network_layer.cc | 28 | ||||
-rw-r--r-- | net/http/http_network_transaction.cc | 4 | ||||
-rw-r--r-- | net/http/http_network_transaction_unittest.cc | 77 | ||||
-rw-r--r-- | net/http/http_stream_factory.cc | 2 | ||||
-rw-r--r-- | net/http/http_stream_factory.h | 21 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_nss.cc | 95 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_nss.h | 11 | ||||
-rw-r--r-- | net/spdy/spdy_network_transaction_unittest.cc | 15 | ||||
-rw-r--r-- | net/third_party/nss/patches/nextproto.patch | 308 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl.h | 45 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl3con.c | 2 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl3ext.c | 58 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslerr.h | 1 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslimpl.h | 17 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslsock.c | 87 |
16 files changed, 496 insertions, 287 deletions
diff --git a/net/base/ssl_config_service.h b/net/base/ssl_config_service.h index 10d143c..0746abf 100644 --- a/net/base/ssl_config_service.h +++ b/net/base/ssl_config_service.h @@ -97,12 +97,12 @@ struct NET_EXPORT SSLConfig { // needs to clear tls1_enabled). // The list of application level protocols supported. If set, this will - // enable Next Protocol Negotiation (if supported). This is a list of 8-bit - // length prefixed strings. The order of the protocols doesn't matter expect - // for one case: if the server supports Next Protocol Negotiation, but there - // is no overlap between the server's and client's protocol sets, then the - // first protocol in this list will be requested by the client. - std::string next_protos; + // enable Next Protocol Negotiation (if supported). The order of the + // protocols doesn't matter expect for one case: if the server supports Next + // Protocol Negotiation, but there is no overlap between the server's and + // client's protocol sets, then the first protocol in this list will be + // requested by the client. + std::vector<std::string> next_protos; scoped_refptr<X509Certificate> client_cert; diff --git a/net/http/http_network_layer.cc b/net/http/http_network_layer.cc index 63cd287..0ff32d6 100644 --- a/net/http/http_network_layer.cc +++ b/net/http/http_network_layer.cc @@ -66,18 +66,6 @@ void HttpNetworkLayer::EnableSpdy(const std::string& mode) { static const char kEnableNPN[] = "npn"; static const char kEnableNpnHttpOnly[] = "npn-http"; - // Except for the first element, the order is irrelevant. First element - // specifies the fallback in case nothing matches - // (SSLClientSocket::kNextProtoNoOverlap). Otherwise, the SSL library - // will choose the first overlapping protocol in the server's list, since - // it presumedly has a better understanding of which protocol we should - // use, therefore the rest of the ordering here is not important. - static const char kNpnProtosFull[] = "\x08http/1.1\x06spdy/2"; - // This is a temporary hack to pretend we support version 1. - static const char kNpnProtosFullV1[] = "\x08http/1.1\x06spdy/1"; - // No spdy specified. - static const char kNpnProtosHttpOnly[] = "\x08http/1.1\x07http1.1"; - static const char kInitialMaxConcurrentStreams[] = "init-max-streams"; std::vector<std::string> spdy_options; @@ -110,15 +98,25 @@ void HttpNetworkLayer::EnableSpdy(const std::string& mode) { spdy::SpdyFramer::set_enable_compression_default(false); } else if (option == kEnableNPN) { HttpStreamFactory::set_use_alternate_protocols(use_alt_protocols); - HttpStreamFactory::set_next_protos(kNpnProtosFull); + std::vector<std::string> next_protos; + next_protos.push_back("http/1.1"); + next_protos.push_back("spdy/2"); + HttpStreamFactory::set_next_protos(next_protos); } else if (option == kEnableNpnHttpOnly) { // Avoid alternate protocol in this case. Otherwise, browser will try SSL // and then fallback to http. This introduces extra load. HttpStreamFactory::set_use_alternate_protocols(false); - HttpStreamFactory::set_next_protos(kNpnProtosHttpOnly); + std::vector<std::string> next_protos; + next_protos.push_back("http/1.1"); + next_protos.push_back("http1.1"); + HttpStreamFactory::set_next_protos(next_protos); } else if (option == kEnableVersionOne) { spdy::SpdyFramer::set_protocol_version(1); - HttpStreamFactory::set_next_protos(kNpnProtosFullV1); + std::vector<std::string> next_protos; + // This is a temporary hack to pretend we support version 1. + next_protos.push_back("http/1.1"); + next_protos.push_back("spdy/1"); + HttpStreamFactory::set_next_protos(next_protos); } else if (option == kDisableAltProtocols) { use_alt_protocols = false; HttpStreamFactory::set_use_alternate_protocols(false); diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index 0169d96..84df782 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -109,9 +109,9 @@ HttpNetworkTransaction::HttpNetworkTransaction(HttpNetworkSession* session) next_state_(STATE_NONE), establishing_tunnel_(false) { session->ssl_config_service()->GetSSLConfig(&server_ssl_config_); - if (session->http_stream_factory()->next_protos()) { + if (session->http_stream_factory()->has_next_protos()) { server_ssl_config_.next_protos = - *session->http_stream_factory()->next_protos(); + session->http_stream_factory()->next_protos(); } proxy_ssl_config_ = server_ssl_config_; } diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index 307cf86..1de21e7 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -5,6 +5,8 @@ #include "net/http/http_network_transaction.h" #include <math.h> // ceil +#include <stdarg.h> +#include <string> #include <vector> #include "base/basictypes.h" @@ -68,6 +70,32 @@ const string16 kSecond(ASCIIToUTF16("second")); const string16 kTestingNTLM(ASCIIToUTF16("testing-ntlm")); const string16 kWrongPassword(ASCIIToUTF16("wrongpassword")); +// MakeNextProtos is a utility function that returns a vector of std::strings +// from its arguments. Don't forget to terminate the argument list with a NULL. +std::vector<std::string> MakeNextProtos(const char* a, ...) { + std::vector<std::string> ret; + ret.push_back(a); + + va_list args; + va_start(args, a); + + for (;;) { + const char* value = va_arg(args, const char*); + if (value == NULL) + break; + ret.push_back(value); + } + va_end(args); + + return ret; +} + +// SpdyNextProtos returns a vector of NPN protocol strings for negotiating +// SPDY. +std::vector<std::string> SpdyNextProtos() { + return MakeNextProtos("http/1.1", "spdy/2", NULL); +} + } // namespace namespace net { @@ -332,10 +360,6 @@ CaptureGroupNameSSLSocketPool::CaptureGroupNameSocketPool( //----------------------------------------------------------------------------- -// This is the expected list of advertised protocols from the browser's NPN -// list. -static const char kExpectedNPNString[] = "\x08http/1.1\x06spdy/2"; - // This is the expected return from a current server advertising SPDY. static const char kAlternateProtocolHttpHeader[] = "Alternate-Protocol: 443:npn-spdy/2\r\n\r\n"; @@ -6472,7 +6496,7 @@ TEST_F(HttpNetworkTransactionTest, ChangeAuthRealms) { } TEST_F(HttpNetworkTransactionTest, HonorAlternateProtocolHeader) { - HttpStreamFactory::set_next_protos("needs_to_be_set_for_this_test"); + HttpStreamFactory::set_next_protos(MakeNextProtos("foo", "bar", NULL)); HttpStreamFactory::set_use_alternate_protocols(true); SessionDependencies session_deps; @@ -6529,7 +6553,7 @@ TEST_F(HttpNetworkTransactionTest, HonorAlternateProtocolHeader) { EXPECT_TRUE(expected_alternate.Equals(alternate)); HttpStreamFactory::set_use_alternate_protocols(false); - HttpStreamFactory::set_next_protos(""); + HttpStreamFactory::set_next_protos(std::vector<std::string>()); } TEST_F(HttpNetworkTransactionTest, MarkBrokenAlternateProtocolAndFallback) { @@ -6785,7 +6809,7 @@ TEST_F(HttpNetworkTransactionTest, AlternateProtocolPortUnrestrictedAllowed2) { TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForNpnSpdy) { HttpStreamFactory::set_use_alternate_protocols(true); - HttpStreamFactory::set_next_protos(kExpectedNPNString); + HttpStreamFactory::set_next_protos(SpdyNextProtos()); SessionDependencies session_deps; HttpRequestInfo request; @@ -6870,13 +6894,13 @@ TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForNpnSpdy) { ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); EXPECT_EQ("hello!", response_data); - HttpStreamFactory::set_next_protos(""); + HttpStreamFactory::set_next_protos(std::vector<std::string>()); HttpStreamFactory::set_use_alternate_protocols(false); } TEST_F(HttpNetworkTransactionTest, AlternateProtocolWithSpdyLateBinding) { HttpStreamFactory::set_use_alternate_protocols(true); - HttpStreamFactory::set_next_protos(kExpectedNPNString); + HttpStreamFactory::set_next_protos(SpdyNextProtos()); SessionDependencies session_deps; HttpRequestInfo request; @@ -6988,13 +7012,13 @@ TEST_F(HttpNetworkTransactionTest, AlternateProtocolWithSpdyLateBinding) { ASSERT_EQ(OK, ReadTransaction(&trans3, &response_data)); EXPECT_EQ("hello!", response_data); - HttpStreamFactory::set_next_protos(""); + HttpStreamFactory::set_next_protos(std::vector<std::string>()); HttpStreamFactory::set_use_alternate_protocols(false); } TEST_F(HttpNetworkTransactionTest, StallAlternateProtocolForNpnSpdy) { HttpStreamFactory::set_use_alternate_protocols(true); - HttpStreamFactory::set_next_protos(kExpectedNPNString); + HttpStreamFactory::set_next_protos(SpdyNextProtos()); SessionDependencies session_deps; HttpRequestInfo request; @@ -7064,7 +7088,7 @@ TEST_F(HttpNetworkTransactionTest, StallAlternateProtocolForNpnSpdy) { ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); EXPECT_EQ("hello world", response_data); - HttpStreamFactory::set_next_protos(""); + HttpStreamFactory::set_next_protos(std::vector<std::string>()); HttpStreamFactory::set_use_alternate_protocols(false); } @@ -7108,7 +7132,7 @@ class CapturingProxyResolver : public ProxyResolver { TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForTunneledNpnSpdy) { HttpStreamFactory::set_use_alternate_protocols(true); - HttpStreamFactory::set_next_protos(kExpectedNPNString); + HttpStreamFactory::set_next_protos(SpdyNextProtos()); ProxyConfig proxy_config; proxy_config.set_auto_detect(true); @@ -7216,14 +7240,14 @@ TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForTunneledNpnSpdy) { EXPECT_EQ("https://www.google.com/", capturing_proxy_resolver->resolved()[1].spec()); - HttpStreamFactory::set_next_protos(""); + HttpStreamFactory::set_next_protos(std::vector<std::string>()); HttpStreamFactory::set_use_alternate_protocols(false); } TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForNpnSpdyWithExistingSpdySession) { HttpStreamFactory::set_use_alternate_protocols(true); - HttpStreamFactory::set_next_protos(kExpectedNPNString); + HttpStreamFactory::set_next_protos(SpdyNextProtos()); SessionDependencies session_deps; HttpRequestInfo request; @@ -7335,7 +7359,7 @@ TEST_F(HttpNetworkTransactionTest, ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); EXPECT_EQ("hello!", response_data); - HttpStreamFactory::set_next_protos(""); + HttpStreamFactory::set_next_protos(std::vector<std::string>()); HttpStreamFactory::set_use_alternate_protocols(false); } @@ -8051,7 +8075,8 @@ TEST_F(HttpNetworkTransactionTest, // npn is negotiated. TEST_F(HttpNetworkTransactionTest, NpnWithHttpOverSSL) { HttpStreamFactory::set_use_alternate_protocols(true); - HttpStreamFactory::set_next_protos("\x08http/1.1\x07http1.1"); + HttpStreamFactory::set_next_protos( + MakeNextProtos("http/1.1", "http1.1", NULL)); SessionDependencies session_deps; HttpRequestInfo request; request.method = "GET"; @@ -8103,7 +8128,7 @@ TEST_F(HttpNetworkTransactionTest, NpnWithHttpOverSSL) { EXPECT_FALSE(response->was_fetched_via_spdy); EXPECT_TRUE(response->was_npn_negotiated); - HttpStreamFactory::set_next_protos(""); + HttpStreamFactory::set_next_protos(std::vector<std::string>()); HttpStreamFactory::set_use_alternate_protocols(false); } @@ -8112,7 +8137,7 @@ TEST_F(HttpNetworkTransactionTest, SpdyPostNPNServerHangup) { // followed by an immediate server closing of the socket. // Fix crash: http://crbug.com/46369 HttpStreamFactory::set_use_alternate_protocols(true); - HttpStreamFactory::set_next_protos(kExpectedNPNString); + HttpStreamFactory::set_next_protos(SpdyNextProtos()); SessionDependencies session_deps; HttpRequestInfo request; @@ -8149,7 +8174,7 @@ TEST_F(HttpNetworkTransactionTest, SpdyPostNPNServerHangup) { EXPECT_EQ(ERR_IO_PENDING, rv); EXPECT_EQ(ERR_CONNECTION_CLOSED, callback.WaitForResult()); - HttpStreamFactory::set_next_protos(""); + HttpStreamFactory::set_next_protos(std::vector<std::string>()); HttpStreamFactory::set_use_alternate_protocols(false); } @@ -8158,7 +8183,7 @@ TEST_F(HttpNetworkTransactionTest, SpdyAlternateProtocolThroughProxy) { // to https when doing an Alternate Protocol upgrade. HttpStreamFactory::set_use_alternate_protocols(true); HttpStreamFactory::set_next_protos( - "\x08http/1.1\x07http1.1\x06spdy/2\x04spdy"); + MakeNextProtos("http/1.1", "http1.1", "spdy/2", "spdy", NULL)); SessionDependencies session_deps(ProxyService::CreateFixed("myproxy:70")); HttpAuthHandlerMock::Factory* auth_factory = @@ -8299,7 +8324,7 @@ TEST_F(HttpNetworkTransactionTest, SpdyAlternateProtocolThroughProxy) { EXPECT_EQ("https", request_url.scheme()); EXPECT_EQ("www.google.com", request_url.host()); - HttpStreamFactory::set_next_protos(""); + HttpStreamFactory::set_next_protos(std::vector<std::string>()); HttpStreamFactory::set_use_alternate_protocols(false); } @@ -8950,7 +8975,7 @@ void IPPoolingAddAlias(MockCachingHostResolver* host_resolver, TEST_F(HttpNetworkTransactionTest, UseIPConnectionPooling) { HttpStreamFactory::set_use_alternate_protocols(true); - HttpStreamFactory::set_next_protos(kExpectedNPNString); + HttpStreamFactory::set_next_protos(SpdyNextProtos()); // Set up a special HttpNetworkSession with a MockCachingHostResolver. SessionDependencies session_deps; @@ -9056,7 +9081,7 @@ TEST_F(HttpNetworkTransactionTest, UseIPConnectionPooling) { ASSERT_EQ(OK, ReadTransaction(&trans2, &response_data)); EXPECT_EQ("hello!", response_data); - HttpStreamFactory::set_next_protos(""); + HttpStreamFactory::set_next_protos(std::vector<std::string>()); HttpStreamFactory::set_use_alternate_protocols(false); } @@ -9111,7 +9136,7 @@ class OneTimeCachingHostResolver : public net::HostResolver { TEST_F(HttpNetworkTransactionTest, UseIPConnectionPoolingWithHostCacheExpiration) { HttpStreamFactory::set_use_alternate_protocols(true); - HttpStreamFactory::set_next_protos(kExpectedNPNString); + HttpStreamFactory::set_next_protos(SpdyNextProtos()); // Set up a special HttpNetworkSession with a OneTimeCachingHostResolver. SessionDependencies session_deps; @@ -9216,7 +9241,7 @@ TEST_F(HttpNetworkTransactionTest, ASSERT_EQ(OK, ReadTransaction(&trans2, &response_data)); EXPECT_EQ("hello!", response_data); - HttpStreamFactory::set_next_protos(""); + HttpStreamFactory::set_next_protos(std::vector<std::string>()); HttpStreamFactory::set_use_alternate_protocols(false); } diff --git a/net/http/http_stream_factory.cc b/net/http/http_stream_factory.cc index 9aacbdc..bcbd8d3 100644 --- a/net/http/http_stream_factory.cc +++ b/net/http/http_stream_factory.cc @@ -17,7 +17,7 @@ namespace net { // static const HostMappingRules* HttpStreamFactory::host_mapping_rules_ = NULL; // static -const std::string* HttpStreamFactory::next_protos_ = NULL; +std::vector<std::string>* HttpStreamFactory::next_protos_ = NULL; // static bool HttpStreamFactory::spdy_enabled_ = true; // static diff --git a/net/http/http_stream_factory.h b/net/http/http_stream_factory.h index ef78a70..f92c11e 100644 --- a/net/http/http_stream_factory.h +++ b/net/http/http_stream_factory.h @@ -7,6 +7,7 @@ #include <list> #include <string> +#include <vector> #include "base/memory/ref_counted.h" #include "base/string16.h" @@ -180,8 +181,10 @@ class NET_EXPORT HttpStreamFactory { // Turns spdy on or off. static void set_spdy_enabled(bool value) { spdy_enabled_ = value; - if (value == false) - set_next_protos(""); + if (!spdy_enabled_) { + delete next_protos_; + next_protos_ = NULL; + } } static bool spdy_enabled() { return spdy_enabled_; } @@ -211,11 +214,15 @@ class NET_EXPORT HttpStreamFactory { static bool HasSpdyExclusion(const HostPortPair& endpoint); // Sets the next protocol negotiation value used during the SSL handshake. - static void set_next_protos(const std::string& value) { - delete next_protos_; - next_protos_ = new std::string(value); + static void set_next_protos(const std::vector<std::string>& value) { + if (!next_protos_) + next_protos_ = new std::vector<std::string>; + *next_protos_ = value; + } + static bool has_next_protos() { return next_protos_ != NULL; } + static const std::vector<std::string>& next_protos() { + return *next_protos_; } - static const std::string* next_protos() { return next_protos_; } // Sets the HttpStreamFactoryImpl into a mode where it can ignore certificate // errors. This is for testing. @@ -235,7 +242,7 @@ class NET_EXPORT HttpStreamFactory { static const HostMappingRules& host_mapping_rules(); static const HostMappingRules* host_mapping_rules_; - static const std::string* next_protos_; + static std::vector<std::string>* next_protos_; static bool spdy_enabled_; static bool use_alternate_protocols_; static bool force_spdy_over_ssl_; diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc index a715d06..e0ed410 100644 --- a/net/socket/ssl_client_socket_nss.cc +++ b/net/socket/ssl_client_socket_nss.cc @@ -466,6 +466,7 @@ SSLClientSocketNSS::SSLClientSocketNSS(ClientSocketHandle* transport_socket, net_log_(transport_socket->socket()->NetLog()), ssl_host_info_(ssl_host_info), dns_cert_checker_(context.dns_cert_checker), + next_proto_status_(kNextProtoUnsupported), valid_thread_id_(base::kInvalidThreadId) { EnterFunction(""); } @@ -553,38 +554,8 @@ int SSLClientSocketNSS::ExportKeyingMaterial(const base::StringPiece& label, SSLClientSocket::NextProtoStatus SSLClientSocketNSS::GetNextProto(std::string* proto) { -#if defined(SSL_NEXT_PROTO_NEGOTIATED) - unsigned char buf[255]; - int state; - unsigned len; - SECStatus rv = SSL_GetNextProto(nss_fd_, &state, buf, &len, sizeof(buf)); - if (rv != SECSuccess) { - NOTREACHED() << "Error return from SSL_GetNextProto: " << rv; - proto->clear(); - return kNextProtoUnsupported; - } - // We don't check for truncation because sizeof(buf) is large enough to hold - // the maximum protocol size. - switch (state) { - case SSL_NEXT_PROTO_NO_SUPPORT: - proto->clear(); - return kNextProtoUnsupported; - case SSL_NEXT_PROTO_NEGOTIATED: - *proto = std::string(reinterpret_cast<char*>(buf), len); - return kNextProtoNegotiated; - case SSL_NEXT_PROTO_NO_OVERLAP: - *proto = std::string(reinterpret_cast<char*>(buf), len); - return kNextProtoNoOverlap; - default: - NOTREACHED() << "Unknown status from SSL_GetNextProto: " << state; - proto->clear(); - return kNextProtoUnsupported; - } -#else - // No NPN support in the libssl that we are building with. - proto->clear(); - return kNextProtoUnsupported; -#endif + *proto = next_proto_; + return next_proto_status_; } int SSLClientSocketNSS::Connect(OldCompletionCallback* callback) { @@ -966,12 +937,10 @@ int SSLClientSocketNSS::InitializeSSLOptions() { #ifdef SSL_NEXT_PROTO_NEGOTIATED if (!ssl_config_.next_protos.empty()) { - rv = SSL_SetNextProtoNego( - nss_fd_, - reinterpret_cast<const unsigned char *>(ssl_config_.next_protos.data()), - ssl_config_.next_protos.size()); + rv = SSL_SetNextProtoCallback( + nss_fd_, SSLClientSocketNSS::NextProtoCallback, this); if (rv != SECSuccess) - LogFailedNSSFunction(net_log_, "SSL_SetNextProtoNego", ""); + LogFailedNSSFunction(net_log_, "SSL_SetNextProtoCallback", ""); } #endif @@ -2564,6 +2533,58 @@ void SSLClientSocketNSS::HandshakeCallback(PRFileDesc* socket, that->UpdateConnectionStatus(); } +// NextProtoCallback is called by NSS during the handshake, if the server +// supports NPN, to select a protocol from the list that the server offered. +// See the comment in net/third_party/nss/ssl/ssl.h for the meanings of the +// arguments. +// static +SECStatus +SSLClientSocketNSS::NextProtoCallback(void* arg, + PRFileDesc* nss_fd, + const unsigned char* protos, + unsigned int protos_len, + unsigned char* proto_out, + unsigned int* proto_out_len) { + SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg); + + // For each protocol in server preference, see if we support it. + for (unsigned int i = 0; i < protos_len; ) { + const size_t len = protos[i]; + for (std::vector<std::string>::const_iterator + j = that->ssl_config_.next_protos.begin(); + j != that->ssl_config_.next_protos.end(); j++) { + // Having very long elements in the |next_protos| vector isn't a disaster + // because they'll never be selected, but it does indicate an error + // somewhere. + DCHECK_LT(j->size(), 256u); + + if (j->size() == len && + memcmp(&protos[i + 1], j->data(), len) == 0) { + that->next_proto_status_ = kNextProtoNegotiated; + that->next_proto_ = *j; + break; + } + } + + if (that->next_proto_status_ == kNextProtoNegotiated) + break; + + // NSS checks that the data in |protos| is well formed, so we know that + // this doesn't cause us to jump off the end of the buffer. + i += len + 1; + } + + // If we didn't find a protocol, we select the first one from our list. + if (that->next_proto_status_ != kNextProtoNegotiated) { + that->next_proto_status_ = kNextProtoNoOverlap; + that->next_proto_ = that->ssl_config_.next_protos[0]; + } + + memcpy(proto_out, that->next_proto_.data(), that->next_proto_.size()); + *proto_out_len = that->next_proto_.size(); + return SECSuccess; +} + void SSLClientSocketNSS::EnsureThreadIdAssigned() const { base::AutoLock auto_lock(lock_); if (valid_thread_id_ != base::kInvalidThreadId) diff --git a/net/socket/ssl_client_socket_nss.h b/net/socket/ssl_client_socket_nss.h index 4cc681a..648e249 100644 --- a/net/socket/ssl_client_socket_nss.h +++ b/net/socket/ssl_client_socket_nss.h @@ -188,6 +188,13 @@ class SSLClientSocketNSS : public SSLClientSocket { // argument. static void HandshakeCallback(PRFileDesc* socket, void* arg); + static SECStatus NextProtoCallback(void* arg, + PRFileDesc* fd, + const unsigned char* protos, + unsigned int protos_len, + unsigned char* proto_out, + unsigned int* proto_out_len); + // The following methods are for debugging bug 65948. Will remove this code // after fixing bug 65948. void EnsureThreadIdAssigned() const; @@ -285,6 +292,10 @@ class SSLClientSocketNSS : public SSLClientSocket { scoped_ptr<SSLHostInfo> ssl_host_info_; DnsCertProvenanceChecker* const dns_cert_checker_; + // next_proto_ is the protocol that we selected by NPN. + std::string next_proto_; + NextProtoStatus next_proto_status_; + // The following two variables are added for debugging bug 65948. Will // remove this code after fixing bug 65948. // Added the following code Debugging in release mode. diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc index b7ee325..e51c4a5 100644 --- a/net/spdy/spdy_network_transaction_unittest.cc +++ b/net/spdy/spdy_network_transaction_unittest.cc @@ -24,10 +24,6 @@ namespace net { -// This is the expected list of advertised protocols from the browser's NPN -// list. -static const char kExpectedNPNString[] = "\x08http/1.1\x06spdy/2"; - enum SpdyNetworkTransactionTestTypes { SPDYNPN, SPDYNOSSL, @@ -122,13 +118,18 @@ class SpdyNetworkTransactionTest HttpStreamFactory::set_use_alternate_protocols(false); HttpStreamFactory::set_force_spdy_over_ssl(false); HttpStreamFactory::set_force_spdy_always(false); + + std::vector<std::string> next_protos; + next_protos.push_back("http/1.1"); + next_protos.push_back("spdy/2"); + switch (test_type_) { case SPDYNPN: session_->http_server_properties()->SetAlternateProtocol( HostPortPair("www.google.com", 80), 443, NPN_SPDY_2); HttpStreamFactory::set_use_alternate_protocols(true); - HttpStreamFactory::set_next_protos(kExpectedNPNString); + HttpStreamFactory::set_next_protos(next_protos); break; case SPDYNOSSL: HttpStreamFactory::set_force_spdy_over_ssl(false); @@ -852,8 +853,8 @@ TEST_P(SpdyNetworkTransactionTest, TwoGetsLateBindingFromPreconnect) { helper.session()->ssl_config_service()->GetSSLConfig(&preconnect_ssl_config); HttpStreamFactory* http_stream_factory = helper.session()->http_stream_factory(); - if (http_stream_factory->next_protos()) { - preconnect_ssl_config.next_protos = *http_stream_factory->next_protos(); + if (http_stream_factory->has_next_protos()) { + preconnect_ssl_config.next_protos = http_stream_factory->next_protos(); } http_stream_factory->PreconnectStreams( diff --git a/net/third_party/nss/patches/nextproto.patch b/net/third_party/nss/patches/nextproto.patch index a01f240..46021b42 100644 --- a/net/third_party/nss/patches/nextproto.patch +++ b/net/third_party/nss/patches/nextproto.patch @@ -1,68 +1,89 @@ -From 6b594dc531e7a1d1d5bca2f0f78e7bc0ac3ff937 Mon Sep 17 00:00:00 2001 +From 3caa0f573d2637bfed99dcc0e5887fe3a52462ba Mon Sep 17 00:00:00 2001 From: Adam Langley <agl@chromium.org> Date: Mon, 3 Oct 2011 12:19:28 -0400 -Subject: [PATCH] nextproto.patch +Subject: [PATCH 01/15] nextproto.patch --- - mozilla/security/nss/cmd/tstclnt/tstclnt.c | 6 ++ - mozilla/security/nss/lib/ssl/ssl.def | 7 ++ - mozilla/security/nss/lib/ssl/ssl.h | 12 +++ - mozilla/security/nss/lib/ssl/ssl3con.c | 54 ++++++++++++ - mozilla/security/nss/lib/ssl/ssl3ext.c | 122 +++++++++++++++++++++++++++- - mozilla/security/nss/lib/ssl/ssl3prot.h | 3 +- - mozilla/security/nss/lib/ssl/sslimpl.h | 24 ++++++ - mozilla/security/nss/lib/ssl/sslsock.c | 74 +++++++++++++++++ - mozilla/security/nss/lib/ssl/sslt.h | 3 +- - 9 files changed, 302 insertions(+), 3 deletions(-) + mozilla/security/nss/lib/ssl/ssl.def | 6 ++ + mozilla/security/nss/lib/ssl/ssl.h | 51 ++++++++++++ + mozilla/security/nss/lib/ssl/ssl3con.c | 54 +++++++++++++ + mozilla/security/nss/lib/ssl/ssl3ext.c | 104 ++++++++++++++++++++++++- + mozilla/security/nss/lib/ssl/ssl3prot.h | 3 +- + mozilla/security/nss/lib/ssl/sslerr.h | 2 + + mozilla/security/nss/lib/ssl/sslimpl.h | 21 +++++ + mozilla/security/nss/lib/ssl/sslsock.c | 131 +++++++++++++++++++++++++++++++ + mozilla/security/nss/lib/ssl/sslt.h | 3 +- + 9 files changed, 372 insertions(+), 3 deletions(-) -diff --git a/mozilla/security/nss/cmd/tstclnt/tstclnt.c b/mozilla/security/nss/cmd/tstclnt/tstclnt.c -index 55684e6..d209a33 100644 ---- a/mozilla/security/nss/cmd/tstclnt/tstclnt.c -+++ b/mozilla/security/nss/cmd/tstclnt/tstclnt.c -@@ -868,6 +868,12 @@ int main(int argc, char **argv) - return 1; - } - -+ rv = SSL_SetNextProtoNego(s, "\004flip\004http1.1", 10); -+ if (rv != SECSuccess) { -+ SECU_PrintError(progName, "error enabling next protocol negotiation"); -+ return 1; -+ } -+ - /* enable false start. */ - rv = SSL_OptionSet(s, SSL_ENABLE_FALSE_START, enableFalseStart); - if (rv != SECSuccess) { diff --git a/mozilla/security/nss/lib/ssl/ssl.def b/mozilla/security/nss/lib/ssl/ssl.def -index d3f455c..a1f4b51 100644 +index d3f455c..5256ae2 100644 --- a/mozilla/security/nss/lib/ssl/ssl.def +++ b/mozilla/security/nss/lib/ssl/ssl.def -@@ -152,3 +152,10 @@ SSL_SNISocketConfigHook; +@@ -152,3 +152,9 @@ SSL_SNISocketConfigHook; ;+ local: ;+*; ;+}; +;+NSS_CHROMIUM { +;+ global: -+SSL_GetNextProto; -+SSL_SetNextProtoNego; ++SSL_SetNextProtoCallback; +;+ local: +;+*; +;+}; diff --git a/mozilla/security/nss/lib/ssl/ssl.h b/mozilla/security/nss/lib/ssl/ssl.h -index 4a9e89d..ffa973c 100644 +index 4a9e89d..2cf777d 100644 --- a/mozilla/security/nss/lib/ssl/ssl.h +++ b/mozilla/security/nss/lib/ssl/ssl.h -@@ -153,6 +153,18 @@ SSL_IMPORT SECStatus SSL_OptionSetDefault(PRInt32 option, PRBool on); +@@ -153,6 +153,57 @@ SSL_IMPORT SECStatus SSL_OptionSetDefault(PRInt32 option, PRBool on); SSL_IMPORT SECStatus SSL_OptionGetDefault(PRInt32 option, PRBool *on); SSL_IMPORT SECStatus SSL_CertDBHandleSet(PRFileDesc *fd, CERTCertDBHandle *dbHandle); ++/* SSLNextProtoCallback is called, during the handshake, when the server has ++ * sent a Next Protocol Negotiation extension. |protos| and |protosLen| define ++ * a buffer which contains the server's advertisement. This data is guaranteed ++ * to be well formed per the NPN spec. |protoOut| is a buffer provided by the ++ * caller, of length 255 (the maximum allowed by the protocol). ++ * On successful return, the protocol to be announced to the server will be in ++ * |protoOut| and its length in |protoOutLen|. */ ++typedef SECStatus (PR_CALLBACK *SSLNextProtoCallback)( ++ void *arg, ++ PRFileDesc *fd, ++ const unsigned char* protos, ++ unsigned int protosLen, ++ unsigned char* protoOut, ++ unsigned int* protoOutLen); ++ ++/* SSL_SetNextProtoCallback sets a callback function to handle Next Protocol ++ * Negotiation. It causes a client to advertise NPN. */ ++SSL_IMPORT SECStatus SSL_SetNextProtoCallback(PRFileDesc *fd, ++ SSLNextProtoCallback callback, ++ void *arg); ++ ++/* SSL_SetNextProtoNego can be used as an alternative to ++ * SSL_SetNextProtoCallback. It also causes a client to advertise NPN and ++ * installs a default callback function which selects the first supported ++ * protocol in server-preference order. If no matching protocol is found it ++ * selects the first supported protocol. ++ * ++ * The supported protocols are specified in |data| in wire-format (8-bit ++ * length-prefixed). For example: "\010http/1.1\006spdy/2". */ +SSL_IMPORT SECStatus SSL_SetNextProtoNego(PRFileDesc *fd, + const unsigned char *data, -+ unsigned short length); ++ unsigned int length); ++/* SSL_GetNextProto can be used after a handshake on a socket where ++ * SSL_SetNextProtoNego was called to retrieve the result of the Next Protocol ++ * negotiation. ++ * ++ * state is set to one of the SSL_NEXT_PROTO_* constants. The negotiated ++ * protocol, if any, is written into buf, which must be at least buf_len bytes ++ * long. If the negotiated protocol is longer than this, it is truncated. The ++ * number of bytes copied is written into *length. */ +SSL_IMPORT SECStatus SSL_GetNextProto(PRFileDesc *fd, + int *state, + unsigned char *buf, -+ unsigned *length, -+ unsigned buf_len); ++ unsigned int *length, ++ unsigned int buf_len); ++ ++// TODO(wtc): it may be a good idea to define these as an enum type. +#define SSL_NEXT_PROTO_NO_SUPPORT 0 /* No peer support */ +#define SSL_NEXT_PROTO_NEGOTIATED 1 /* Mutual agreement */ +#define SSL_NEXT_PROTO_NO_OVERLAP 2 /* No protocol overlap found */ @@ -71,7 +92,7 @@ index 4a9e89d..ffa973c 100644 ** Control ciphers that SSL uses. If on is non-zero then the named cipher ** is enabled, otherwise it is disabled. diff --git a/mozilla/security/nss/lib/ssl/ssl3con.c b/mozilla/security/nss/lib/ssl/ssl3con.c -index 8048913..e0cb4e9 100644 +index 8048913..8f860a9 100644 --- a/mozilla/security/nss/lib/ssl/ssl3con.c +++ b/mozilla/security/nss/lib/ssl/ssl3con.c @@ -81,6 +81,7 @@ static SECStatus ssl3_InitState( sslSocket *ss); @@ -107,7 +128,7 @@ index 8048913..e0cb4e9 100644 + int padding_len; + static const unsigned char padding[32] = {0}; + -+ if (ss->ssl3.nextProtoState == SSL_NEXT_PROTO_NO_SUPPORT) ++ if (ss->ssl3.nextProto.len == 0) + return SECSuccess; + + PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); @@ -164,7 +185,7 @@ index 8048913..e0cb4e9 100644 /* End of ssl3con.c */ diff --git a/mozilla/security/nss/lib/ssl/ssl3ext.c b/mozilla/security/nss/lib/ssl/ssl3ext.c -index becbfe9..fbd5a91 100644 +index becbfe9..36ac4de 100644 --- a/mozilla/security/nss/lib/ssl/ssl3ext.c +++ b/mozilla/security/nss/lib/ssl/ssl3ext.c @@ -235,6 +235,7 @@ static const ssl3HelloExtensionHandler clientHelloHandlers[] = { @@ -193,7 +214,7 @@ index becbfe9..fbd5a91 100644 /* any extra entries will appear as { 0, NULL } */ }; -@@ -534,6 +537,123 @@ ssl3_SendSessionTicketXtn( +@@ -534,6 +537,105 @@ ssl3_SendSessionTicketXtn( return -1; } @@ -206,12 +227,11 @@ index becbfe9..fbd5a91 100644 + return SECFailure; + } + -+ ss->ssl3.hs.nextProtoNego = PR_TRUE; + return SECSuccess; +} + +/* ssl3_ValidateNextProtoNego checks that the given block of data is valid: none -+ * of the length may be 0 and the sum of the lengths must equal the length of ++ * of the lengths may be 0 and the sum of the lengths must equal the length of + * the block. */ +SECStatus +ssl3_ValidateNextProtoNego(const unsigned char* data, unsigned short length) @@ -220,63 +240,46 @@ index becbfe9..fbd5a91 100644 + + while (offset < length) { + if (data[offset] == 0) { ++ PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); + return SECFailure; + } + offset += (unsigned int)data[offset] + 1; + } + -+ if (offset > length) ++ if (offset > length) { ++ PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); + return SECFailure; ++ } + + return SECSuccess; +} + +SECStatus +ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type, -+ SECItem *data) ++ SECItem *data) +{ -+ unsigned int i, j; + SECStatus rv; -+ unsigned char *result; -+ -+ if (data->len == 0) { -+ /* The server supports the extension, but doesn't have any -+ * protocols configured. In this case we request our favoured -+ * protocol. */ -+ goto pick_first; -+ } ++ unsigned char result[255]; ++ unsigned int result_len; + + rv = ssl3_ValidateNextProtoNego(data->data, data->len); + if (rv != SECSuccess) + return rv; + -+ /* For each protocol in server preference order, see if we support it. */ -+ for (i = 0; i < data->len; ) { -+ for (j = 0; j < ss->opt.nextProtoNego.len; ) { -+ if (data->data[i] == ss->opt.nextProtoNego.data[j] && -+ memcmp(&data->data[i+1], &ss->opt.nextProtoNego.data[j+1], -+ data->data[i]) == 0) { -+ /* We found a match */ -+ ss->ssl3.nextProtoState = SSL_NEXT_PROTO_NEGOTIATED; -+ result = &data->data[i]; -+ goto found; -+ } -+ j += (unsigned int)ss->opt.nextProtoNego.data[j] + 1; -+ } -+ -+ i += (unsigned int)data->data[i] + 1; -+ } -+ -+ pick_first: -+ ss->ssl3.nextProtoState = SSL_NEXT_PROTO_NO_OVERLAP; -+ result = ss->opt.nextProtoNego.data; ++ rv = ss->nextProtoCallback(ss->nextProtoArg, ss->fd, ++ data->data, data->len, ++ result, &result_len); ++ if (rv != SECSuccess) ++ return rv; ++ // If the callback wrote more than allowed to |result| it has corrupted our ++ // stack. ++ PORT_Assert(result_len <= sizeof(result)); + -+ found: + if (ss->ssl3.nextProto.data) + PORT_Free(ss->ssl3.nextProto.data); -+ ss->ssl3.nextProto.data = PORT_Alloc(result[0]); -+ PORT_Memcpy(ss->ssl3.nextProto.data, result + 1, result[0]); -+ ss->ssl3.nextProto.len = result[0]; ++ ss->ssl3.nextProto.data = PORT_Alloc(result_len); ++ PORT_Memcpy(ss->ssl3.nextProto.data, result, result_len); ++ ss->ssl3.nextProto.len = result_len; + return SECSuccess; +} + @@ -288,7 +291,7 @@ index becbfe9..fbd5a91 100644 + PRInt32 extension_length; + + /* Renegotiations do not send this extension. */ -+ if (ss->opt.nextProtoNego.len == 0 || ss->firstHsDone) { ++ if (!ss->nextProtoCallback || ss->firstHsDone) { + return 0; + } + @@ -331,48 +334,58 @@ index 4702fcc..f3c950e 100644 } SSL3HandshakeType; typedef struct { +diff --git a/mozilla/security/nss/lib/ssl/sslerr.h b/mozilla/security/nss/lib/ssl/sslerr.h +index a2f6524..c76ffa9 100644 +--- a/mozilla/security/nss/lib/ssl/sslerr.h ++++ b/mozilla/security/nss/lib/ssl/sslerr.h +@@ -203,6 +203,8 @@ SSL_ERROR_RX_UNEXPECTED_UNCOMPRESSED_RECORD = (SSL_ERROR_BASE + 114), + + SSL_ERROR_WEAK_SERVER_EPHEMERAL_DH_KEY = (SSL_ERROR_BASE + 115), + ++SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID = (SSL_ERROR_BASE + 117), ++ + SSL_ERROR_END_OF_LIST /* let the c compiler determine the value of this. */ + } SSLErrorCodes; + #endif /* NO_SECURITY_ERROR_ENUM */ diff --git a/mozilla/security/nss/lib/ssl/sslimpl.h b/mozilla/security/nss/lib/ssl/sslimpl.h -index 9af471d..d1c1181 100644 +index 9af471d..199c573 100644 --- a/mozilla/security/nss/lib/ssl/sslimpl.h +++ b/mozilla/security/nss/lib/ssl/sslimpl.h -@@ -313,6 +313,11 @@ typedef struct { +@@ -313,6 +313,10 @@ typedef struct { #endif /* NSS_ENABLE_ECC */ typedef struct sslOptionsStr { -+ /* For clients, this is a validated list of protocols in preference order -+ * and wire format. For servers, this is the list of support protocols, -+ * also in wire format. */ ++ /* If SSL_SetNextProtoNego has been called, then this contains the ++ * list of supported protocols. */ + SECItem nextProtoNego; + unsigned int useSecurity : 1; /* 1 */ unsigned int useSocks : 1; /* 2 */ unsigned int requestCertificate : 1; /* 3 */ -@@ -786,6 +791,7 @@ const ssl3CipherSuiteDef *suite_def; - #ifdef NSS_ENABLE_ECC - PRUint32 negotiatedECCurves; /* bit mask */ - #endif /* NSS_ENABLE_ECC */ -+ PRBool nextProtoNego;/* Our peer has sent this extension */ - } SSL3HandshakeState; - - -@@ -827,6 +833,16 @@ struct ssl3StateStr { +@@ -827,6 +831,13 @@ struct ssl3StateStr { PRBool initialized; SSL3HandshakeState hs; ssl3CipherSpec specs[2]; /* one is current, one is pending. */ + + /* In a client: if the server supports Next Protocol Negotiation, then -+ * this is the protocol that was requested. -+ * In a server: this is the protocol that the client requested via Next -+ * Protocol Negotiation. ++ * this is the protocol that was negotiated. + * -+ * In either case, if the data pointer is non-NULL, then it is malloced -+ * data. */ ++ * If the data pointer is non-NULL, then it is malloced data. */ + SECItem nextProto; -+ int nextProtoState; /* See SSL_NEXT_PROTO_* defines */ ++ int nextProtoState; /* See NEXT_PROTO_* defines */ }; typedef struct { -@@ -1494,8 +1510,12 @@ extern SECStatus ssl3_HandleSupportedPointFormatsXtn(sslSocket * ss, +@@ -1058,6 +1069,8 @@ const unsigned char * preferredCipher; + SSLHandshakeCallback handshakeCallback; + void *handshakeCallbackData; + void *pkcs11PinArg; ++ SSLNextProtoCallback nextProtoCallback; ++ void *nextProtoArg; + + PRIntervalTime rTimeout; /* timeout for NSPR I/O */ + PRIntervalTime wTimeout; /* timeout for NSPR I/O */ +@@ -1494,8 +1507,12 @@ extern SECStatus ssl3_HandleSupportedPointFormatsXtn(sslSocket * ss, PRUint16 ex_type, SECItem *data); extern SECStatus ssl3_ClientHandleSessionTicketXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data); @@ -385,7 +398,7 @@ index 9af471d..d1c1181 100644 /* ClientHello and ServerHello extension senders. * Note that not all extension senders are exposed here; only those that -@@ -1526,6 +1546,10 @@ extern PRInt32 ssl3_SendSupportedCurvesXtn(sslSocket *ss, +@@ -1526,6 +1543,10 @@ extern PRInt32 ssl3_SendSupportedCurvesXtn(sslSocket *ss, extern PRInt32 ssl3_SendSupportedPointFormatsXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes); #endif @@ -397,7 +410,7 @@ index 9af471d..d1c1181 100644 /* call the registered extension handlers. */ extern SECStatus ssl3_HandleHelloExtensions(sslSocket *ss, diff --git a/mozilla/security/nss/lib/ssl/sslsock.c b/mozilla/security/nss/lib/ssl/sslsock.c -index bc770a1..4c8fbfd 100644 +index bc770a1..769ea0a 100644 --- a/mozilla/security/nss/lib/ssl/sslsock.c +++ b/mozilla/security/nss/lib/ssl/sslsock.c @@ -163,6 +163,7 @@ static const sslSocketOps ssl_secure_ops = { /* SSL. */ @@ -419,16 +432,14 @@ index bc770a1..4c8fbfd 100644 PORT_Assert(!ss->xtnData.sniNameArr); if (ss->xtnData.sniNameArr) { PORT_Free(ss->xtnData.sniNameArr); -@@ -1266,6 +1271,75 @@ SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd) +@@ -1266,6 +1271,132 @@ SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd) return fd; } -+/* SSL_SetNextProtoNego sets the list of supported protocols for the given -+ * socket. The list is a series of 8-bit, length prefixed strings. */ +SECStatus -+SSL_SetNextProtoNego(PRFileDesc *fd, const unsigned char *data, -+ unsigned short length) -+{ ++SSL_SetNextProtoCallback(PRFileDesc *fd, ++ SSLNextProtoCallback callback, ++ void *arg) { + sslSocket *ss = ssl_FindSocket(fd); + + if (!ss) { @@ -437,6 +448,74 @@ index bc770a1..4c8fbfd 100644 + return SECFailure; + } + ++ ssl_GetSSL3HandshakeLock(ss); ++ ss->nextProtoCallback = callback; ++ ss->nextProtoArg = arg; ++ ssl_ReleaseSSL3HandshakeLock(ss); ++} ++ ++/* NextProtoStandardCallback is set as an NPN callback for the case when the ++ * user of the sockets wants the standard selection algorithm. */ ++static SECStatus ++NextProtoStandardCallback(void *arg, ++ PRFileDesc *fd, ++ const unsigned char *protos, ++ unsigned int protos_len, ++ unsigned char *protoOut, ++ unsigned int *protoOutLen) ++{ ++ unsigned int i, j; ++ const unsigned char *result; ++ ++ sslSocket *ss = ssl_FindSocket(fd); ++ PORT_Assert(ss); ++ ++ if (protos_len == 0) { ++ /* The server supports the extension, but doesn't have any protocols ++ * configured. In this case we request our favoured protocol. */ ++ goto pick_first; ++ } ++ ++ /* For each protocol in server preference, see if we support it. */ ++ for (i = 0; i < protos_len; ) { ++ for (j = 0; j < ss->opt.nextProtoNego.len; ) { ++ if (protos[i] == ss->opt.nextProtoNego.data[j] && ++ memcmp(&protos[i+1], &ss->opt.nextProtoNego.data[j+1], ++ protos[i]) == 0) { ++ /* We found a match. */ ++ ss->ssl3.nextProtoState = SSL_NEXT_PROTO_NEGOTIATED; ++ result = &protos[i]; ++ goto found; ++ } ++ j += (unsigned int)ss->opt.nextProtoNego.data[j] + 1; ++ } ++ i += (unsigned int)protos[i] + 1; ++ } ++ ++pick_first: ++ ss->ssl3.nextProtoState = SSL_NEXT_PROTO_NO_OVERLAP; ++ result = ss->opt.nextProtoNego.data; ++ ++found: ++ memcpy(protoOut, result + 1, result[0]); ++ *protoOutLen = result[0]; ++ return SECSuccess; ++} ++ ++SECStatus ++SSL_SetNextProtoNego(PRFileDesc *fd, const unsigned char *data, ++ unsigned int length) ++{ ++ SECStatus rv; ++ ++ sslSocket *ss = ssl_FindSocket(fd); ++ ++ if (!ss) { ++ SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetNextProtoNego", ++ SSL_GETPID(), fd)); ++ return SECFailure; ++ } ++ + if (ssl3_ValidateNextProtoNego(data, length) != SECSuccess) + return SECFailure; + @@ -453,18 +532,9 @@ index bc770a1..4c8fbfd 100644 + ss->opt.nextProtoNego.type = siBuffer; + ssl_ReleaseSSL3HandshakeLock(ss); + -+ return SECSuccess; ++ return SSL_SetNextProtoCallback(fd, NextProtoStandardCallback, NULL); +} + -+/* SSL_GetNextProto reads the resulting Next Protocol Negotiation result for -+ * the given socket. It's only valid to call this once the handshake has -+ * completed. -+ * -+ * state is set to one of the SSL_NEXT_PROTO_* constants. The negotiated -+ * protocol, if any, is written into buf, which must be at least buf_len -+ * bytes long. If the negotiated protocol is longer than this, it is truncated. -+ * The number of bytes copied is written into length. -+ */ +SECStatus +SSL_GetNextProto(PRFileDesc *fd, int *state, unsigned char *buf, + unsigned int *length, unsigned int buf_len) diff --git a/net/third_party/nss/ssl/ssl.h b/net/third_party/nss/ssl/ssl.h index 03535f3..debfbfb 100644 --- a/net/third_party/nss/ssl/ssl.h +++ b/net/third_party/nss/ssl/ssl.h @@ -157,14 +157,53 @@ SSL_IMPORT SECStatus SSL_OptionSetDefault(PRInt32 option, PRBool on); SSL_IMPORT SECStatus SSL_OptionGetDefault(PRInt32 option, PRBool *on); SSL_IMPORT SECStatus SSL_CertDBHandleSet(PRFileDesc *fd, CERTCertDBHandle *dbHandle); +/* SSLNextProtoCallback is called, during the handshake, when the server has + * sent a Next Protocol Negotiation extension. |protos| and |protosLen| define + * a buffer which contains the server's advertisement. This data is guaranteed + * to be well formed per the NPN spec. |protoOut| is a buffer provided by the + * caller, of length 255 (the maximum allowed by the protocol). + * On successful return, the protocol to be announced to the server will be in + * |protoOut| and its length in |protoOutLen|. */ +typedef SECStatus (PR_CALLBACK *SSLNextProtoCallback)( + void *arg, + PRFileDesc *fd, + const unsigned char* protos, + unsigned int protosLen, + unsigned char* protoOut, + unsigned int* protoOutLen); + +/* SSL_SetNextProtoCallback sets a callback function to handle Next Protocol + * Negotiation. It causes a client to advertise NPN. */ +SSL_IMPORT SECStatus SSL_SetNextProtoCallback(PRFileDesc *fd, + SSLNextProtoCallback callback, + void *arg); + +/* SSL_SetNextProtoNego can be used as an alternative to + * SSL_SetNextProtoCallback. It also causes a client to advertise NPN and + * installs a default callback function which selects the first supported + * protocol in server-preference order. If no matching protocol is found it + * selects the first supported protocol. + * + * The supported protocols are specified in |data| in wire-format (8-bit + * length-prefixed). For example: "\010http/1.1\006spdy/2". */ SSL_IMPORT SECStatus SSL_SetNextProtoNego(PRFileDesc *fd, const unsigned char *data, - unsigned short length); + unsigned int length); +/* SSL_GetNextProto can be used after a handshake on a socket where + * SSL_SetNextProtoNego was called to retrieve the result of the Next Protocol + * negotiation. + * + * state is set to one of the SSL_NEXT_PROTO_* constants. The negotiated + * protocol, if any, is written into buf, which must be at least buf_len bytes + * long. If the negotiated protocol is longer than this, it is truncated. The + * number of bytes copied is written into *length. */ SSL_IMPORT SECStatus SSL_GetNextProto(PRFileDesc *fd, int *state, unsigned char *buf, - unsigned *length, - unsigned buf_len); + unsigned int *length, + unsigned int buf_len); + +// TODO(wtc): it may be a good idea to define these as an enum type. #define SSL_NEXT_PROTO_NO_SUPPORT 0 /* No peer support */ #define SSL_NEXT_PROTO_NEGOTIATED 1 /* Mutual agreement */ #define SSL_NEXT_PROTO_NO_OVERLAP 2 /* No protocol overlap found */ diff --git a/net/third_party/nss/ssl/ssl3con.c b/net/third_party/nss/ssl/ssl3con.c index 577086d..9dbf399 100644 --- a/net/third_party/nss/ssl/ssl3con.c +++ b/net/third_party/nss/ssl/ssl3con.c @@ -8557,7 +8557,7 @@ ssl3_SendNextProto(sslSocket *ss) int padding_len; static const unsigned char padding[32] = {0}; - if (ss->ssl3.nextProtoState == SSL_NEXT_PROTO_NO_SUPPORT) + if (ss->ssl3.nextProto.len == 0) return SECSuccess; PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); diff --git a/net/third_party/nss/ssl/ssl3ext.c b/net/third_party/nss/ssl/ssl3ext.c index e54b4fd..0c6b8e6 100644 --- a/net/third_party/nss/ssl/ssl3ext.c +++ b/net/third_party/nss/ssl/ssl3ext.c @@ -554,12 +554,11 @@ ssl3_ServerHandleNextProtoNegoXtn(sslSocket * ss, PRUint16 ex_type, SECItem *dat return SECFailure; } - ss->ssl3.hs.nextProtoNego = PR_TRUE; return SECSuccess; } /* ssl3_ValidateNextProtoNego checks that the given block of data is valid: none - * of the length may be 0 and the sum of the lengths must equal the length of + * of the lengths may be 0 and the sum of the lengths must equal the length of * the block. */ SECStatus ssl3_ValidateNextProtoNego(const unsigned char* data, unsigned short length) @@ -568,63 +567,46 @@ ssl3_ValidateNextProtoNego(const unsigned char* data, unsigned short length) while (offset < length) { if (data[offset] == 0) { + PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); return SECFailure; } offset += (unsigned int)data[offset] + 1; } - if (offset > length) + if (offset > length) { + PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); return SECFailure; + } return SECSuccess; } SECStatus ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data) + SECItem *data) { - unsigned int i, j; SECStatus rv; - unsigned char *result; - - if (data->len == 0) { - /* The server supports the extension, but doesn't have any - * protocols configured. In this case we request our favoured - * protocol. */ - goto pick_first; - } + unsigned char result[255]; + unsigned int result_len; rv = ssl3_ValidateNextProtoNego(data->data, data->len); if (rv != SECSuccess) return rv; - /* For each protocol in server preference order, see if we support it. */ - for (i = 0; i < data->len; ) { - for (j = 0; j < ss->opt.nextProtoNego.len; ) { - if (data->data[i] == ss->opt.nextProtoNego.data[j] && - memcmp(&data->data[i+1], &ss->opt.nextProtoNego.data[j+1], - data->data[i]) == 0) { - /* We found a match */ - ss->ssl3.nextProtoState = SSL_NEXT_PROTO_NEGOTIATED; - result = &data->data[i]; - goto found; - } - j += (unsigned int)ss->opt.nextProtoNego.data[j] + 1; - } - - i += (unsigned int)data->data[i] + 1; - } - - pick_first: - ss->ssl3.nextProtoState = SSL_NEXT_PROTO_NO_OVERLAP; - result = ss->opt.nextProtoNego.data; + rv = ss->nextProtoCallback(ss->nextProtoArg, ss->fd, + data->data, data->len, + result, &result_len); + if (rv != SECSuccess) + return rv; + // If the callback wrote more than allowed to |result| it has corrupted our + // stack. + PORT_Assert(result_len <= sizeof(result)); - found: if (ss->ssl3.nextProto.data) PORT_Free(ss->ssl3.nextProto.data); - ss->ssl3.nextProto.data = PORT_Alloc(result[0]); - PORT_Memcpy(ss->ssl3.nextProto.data, result + 1, result[0]); - ss->ssl3.nextProto.len = result[0]; + ss->ssl3.nextProto.data = PORT_Alloc(result_len); + PORT_Memcpy(ss->ssl3.nextProto.data, result, result_len); + ss->ssl3.nextProto.len = result_len; return SECSuccess; } @@ -636,7 +618,7 @@ ssl3_ClientSendNextProtoNegoXtn(sslSocket * ss, PRInt32 extension_length; /* Renegotiations do not send this extension. */ - if (ss->opt.nextProtoNego.len == 0 || ss->firstHsDone) { + if (!ss->nextProtoCallback || ss->firstHsDone) { return 0; } diff --git a/net/third_party/nss/ssl/sslerr.h b/net/third_party/nss/ssl/sslerr.h index c940f95..8710a43 100644 --- a/net/third_party/nss/ssl/sslerr.h +++ b/net/third_party/nss/ssl/sslerr.h @@ -204,6 +204,7 @@ SSL_ERROR_RX_UNEXPECTED_UNCOMPRESSED_RECORD = (SSL_ERROR_BASE + 114), SSL_ERROR_WEAK_SERVER_EPHEMERAL_DH_KEY = (SSL_ERROR_BASE + 115), SSL_ERROR_RX_UNEXPECTED_CERT_STATUS = (SSL_ERROR_BASE + 116), +SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID = (SSL_ERROR_BASE + 117), SSL_ERROR_END_OF_LIST /* let the c compiler determine the value of this. */ } SSLErrorCodes; diff --git a/net/third_party/nss/ssl/sslimpl.h b/net/third_party/nss/ssl/sslimpl.h index d73a0e3..d9f2bd7 100644 --- a/net/third_party/nss/ssl/sslimpl.h +++ b/net/third_party/nss/ssl/sslimpl.h @@ -322,9 +322,8 @@ typedef struct { #endif /* NSS_ENABLE_ECC */ typedef struct sslOptionsStr { - /* For clients, this is a validated list of protocols in preference order - * and wire format. For servers, this is the list of support protocols, - * also in wire format. */ + /* If SSL_SetNextProtoNego has been called, then this contains the + * list of supported protocols. */ SECItem nextProtoNego; unsigned int useSecurity : 1; /* 1 */ @@ -827,7 +826,6 @@ const ssl3CipherSuiteDef *suite_def; #ifdef NSS_ENABLE_ECC PRUint32 negotiatedECCurves; /* bit mask */ #endif /* NSS_ENABLE_ECC */ - PRBool nextProtoNego;/* Our peer has sent this extension */ } SSL3HandshakeState; @@ -886,14 +884,11 @@ struct ssl3StateStr { ssl3CipherSpec specs[2]; /* one is current, one is pending. */ /* In a client: if the server supports Next Protocol Negotiation, then - * this is the protocol that was requested. - * In a server: this is the protocol that the client requested via Next - * Protocol Negotiation. + * this is the protocol that was negotiated. * - * In either case, if the data pointer is non-NULL, then it is malloced - * data. */ + * If the data pointer is non-NULL, then it is malloced data. */ SECItem nextProto; - int nextProtoState; /* See SSL_NEXT_PROTO_* defines */ + int nextProtoState; /* See NEXT_PROTO_* defines */ }; typedef struct { @@ -1129,6 +1124,8 @@ const unsigned char * preferredCipher; SSLHandshakeCallback handshakeCallback; void *handshakeCallbackData; void *pkcs11PinArg; + SSLNextProtoCallback nextProtoCallback; + void *nextProtoArg; PRIntervalTime rTimeout; /* timeout for NSPR I/O */ PRIntervalTime wTimeout; /* timeout for NSPR I/O */ diff --git a/net/third_party/nss/ssl/sslsock.c b/net/third_party/nss/ssl/sslsock.c index 8cc57ad..6f870f9 100644 --- a/net/third_party/nss/ssl/sslsock.c +++ b/net/third_party/nss/ssl/sslsock.c @@ -1310,12 +1310,10 @@ SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd) return fd; } -/* SSL_SetNextProtoNego sets the list of supported protocols for the given - * socket. The list is a series of 8-bit, length prefixed strings. */ SECStatus -SSL_SetNextProtoNego(PRFileDesc *fd, const unsigned char *data, - unsigned short length) -{ +SSL_SetNextProtoCallback(PRFileDesc *fd, + SSLNextProtoCallback callback, + void *arg) { sslSocket *ss = ssl_FindSocket(fd); if (!ss) { @@ -1324,6 +1322,74 @@ SSL_SetNextProtoNego(PRFileDesc *fd, const unsigned char *data, return SECFailure; } + ssl_GetSSL3HandshakeLock(ss); + ss->nextProtoCallback = callback; + ss->nextProtoArg = arg; + ssl_ReleaseSSL3HandshakeLock(ss); +} + +/* NextProtoStandardCallback is set as an NPN callback for the case when the + * user of the sockets wants the standard selection algorithm. */ +static SECStatus +NextProtoStandardCallback(void *arg, + PRFileDesc *fd, + const unsigned char *protos, + unsigned int protos_len, + unsigned char *protoOut, + unsigned int *protoOutLen) +{ + unsigned int i, j; + const unsigned char *result; + + sslSocket *ss = ssl_FindSocket(fd); + PORT_Assert(ss); + + if (protos_len == 0) { + /* The server supports the extension, but doesn't have any protocols + * configured. In this case we request our favoured protocol. */ + goto pick_first; + } + + /* For each protocol in server preference, see if we support it. */ + for (i = 0; i < protos_len; ) { + for (j = 0; j < ss->opt.nextProtoNego.len; ) { + if (protos[i] == ss->opt.nextProtoNego.data[j] && + memcmp(&protos[i+1], &ss->opt.nextProtoNego.data[j+1], + protos[i]) == 0) { + /* We found a match. */ + ss->ssl3.nextProtoState = SSL_NEXT_PROTO_NEGOTIATED; + result = &protos[i]; + goto found; + } + j += (unsigned int)ss->opt.nextProtoNego.data[j] + 1; + } + i += (unsigned int)protos[i] + 1; + } + +pick_first: + ss->ssl3.nextProtoState = SSL_NEXT_PROTO_NO_OVERLAP; + result = ss->opt.nextProtoNego.data; + +found: + memcpy(protoOut, result + 1, result[0]); + *protoOutLen = result[0]; + return SECSuccess; +} + +SECStatus +SSL_SetNextProtoNego(PRFileDesc *fd, const unsigned char *data, + unsigned int length) +{ + SECStatus rv; + + sslSocket *ss = ssl_FindSocket(fd); + + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetNextProtoNego", + SSL_GETPID(), fd)); + return SECFailure; + } + if (ssl3_ValidateNextProtoNego(data, length) != SECSuccess) return SECFailure; @@ -1340,18 +1406,9 @@ SSL_SetNextProtoNego(PRFileDesc *fd, const unsigned char *data, ss->opt.nextProtoNego.type = siBuffer; ssl_ReleaseSSL3HandshakeLock(ss); - return SECSuccess; + return SSL_SetNextProtoCallback(fd, NextProtoStandardCallback, NULL); } -/* SSL_GetNextProto reads the resulting Next Protocol Negotiation result for - * the given socket. It's only valid to call this once the handshake has - * completed. - * - * state is set to one of the SSL_NEXT_PROTO_* constants. The negotiated - * protocol, if any, is written into buf, which must be at least buf_len - * bytes long. If the negotiated protocol is longer than this, it is truncated. - * The number of bytes copied is written into length. - */ SECStatus SSL_GetNextProto(PRFileDesc *fd, int *state, unsigned char *buf, unsigned int *length, unsigned int buf_len) |