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/third_party | |
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/third_party')
-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 |
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) |