summaryrefslogtreecommitdiffstats
path: root/net/third_party
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/third_party
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/third_party')
-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
7 files changed, 332 insertions, 186 deletions
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)