summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authoragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-18 18:56:34 +0000
committeragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-18 18:56:34 +0000
commit5285d9763b1680e6344425ec29e3c92e8bfc9b3d (patch)
treefa0d67ea7578fb6c7ee34e644222b5d2cf768e55 /net
parentc6e6617d80159e7c1dbf00ebf44f82b52f89f4ff (diff)
downloadchromium_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.h12
-rw-r--r--net/http/http_network_layer.cc28
-rw-r--r--net/http/http_network_transaction.cc4
-rw-r--r--net/http/http_network_transaction_unittest.cc77
-rw-r--r--net/http/http_stream_factory.cc2
-rw-r--r--net/http/http_stream_factory.h21
-rw-r--r--net/socket/ssl_client_socket_nss.cc95
-rw-r--r--net/socket/ssl_client_socket_nss.h11
-rw-r--r--net/spdy/spdy_network_transaction_unittest.cc15
-rw-r--r--net/third_party/nss/patches/nextproto.patch308
-rw-r--r--net/third_party/nss/ssl/ssl.h45
-rw-r--r--net/third_party/nss/ssl/ssl3con.c2
-rw-r--r--net/third_party/nss/ssl/ssl3ext.c58
-rw-r--r--net/third_party/nss/ssl/sslerr.h1
-rw-r--r--net/third_party/nss/ssl/sslimpl.h17
-rw-r--r--net/third_party/nss/ssl/sslsock.c87
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)