summaryrefslogtreecommitdiffstats
path: root/net/third_party
diff options
context:
space:
mode:
authorwtc <wtc@chromium.org>2014-10-01 19:41:37 -0700
committerCommit bot <commit-bot@chromium.org>2014-10-02 02:41:53 +0000
commitc9089fa21c59d9797398e660e4943b9423db1296 (patch)
tree3fdb71c1b755c30f40932f78d6de51235acf2db8 /net/third_party
parentf09a66bb347776e5e2b5181e5f8550b7d31c0fe6 (diff)
downloadchromium_src-c9089fa21c59d9797398e660e4943b9423db1296.zip
chromium_src-c9089fa21c59d9797398e660e4943b9423db1296.tar.gz
chromium_src-c9089fa21c59d9797398e660e4943b9423db1296.tar.bz2
Merge the server-side support of ALPN from the NSS upstream.
NSS upstream patches: https://hg.mozilla.org/projects/nss/rev/2cb5c73ac307 https://hg.mozilla.org/projects/nss/rev/544eb0f50d40 R=agl@chromium.org BUG=416841 Review URL: https://codereview.chromium.org/595823003 Cr-Commit-Position: refs/heads/master@{#297772}
Diffstat (limited to 'net/third_party')
-rw-r--r--net/third_party/nss/README.chromium4
-rw-r--r--net/third_party/nss/patches/alpnserver.patch349
-rwxr-xr-xnet/third_party/nss/patches/applypatches.sh2
-rw-r--r--net/third_party/nss/ssl/SSLerrs.h22
-rw-r--r--net/third_party/nss/ssl/ssl3ext.c169
-rw-r--r--net/third_party/nss/ssl/ssl3prot.h3
-rw-r--r--net/third_party/nss/ssl/sslerr.h12
-rw-r--r--net/third_party/nss/ssl/sslsock.c18
8 files changed, 526 insertions, 53 deletions
diff --git a/net/third_party/nss/README.chromium b/net/third_party/nss/README.chromium
index a1c5303..f2ead3c 100644
--- a/net/third_party/nss/README.chromium
+++ b/net/third_party/nss/README.chromium
@@ -109,6 +109,10 @@ Patches:
patches/ignorechangecipherspec.patch
https://bugzilla.mozilla.org/show_bug.cgi?id=1009227
+ * Implement server-side components of ALPN (RFC 7301).
+ patches/alpnserver.patch
+ https://bugzilla.mozilla.org/show_bug.cgi?id=996250
+
Apply the patches to NSS by running the patches/applypatches.sh script. Read
the comments at the top of patches/applypatches.sh for instructions.
diff --git a/net/third_party/nss/patches/alpnserver.patch b/net/third_party/nss/patches/alpnserver.patch
new file mode 100644
index 0000000..b4226b7
--- /dev/null
+++ b/net/third_party/nss/patches/alpnserver.patch
@@ -0,0 +1,349 @@
+diff --git a/net/third_party/nss/ssl/SSLerrs.h b/net/third_party/nss/ssl/SSLerrs.h
+index 4ff0b7d..3f0078c 100644
+--- a/net/third_party/nss/ssl/SSLerrs.h
++++ b/net/third_party/nss/ssl/SSLerrs.h
+@@ -413,16 +413,22 @@ ER3(SSL_ERROR_DIGEST_FAILURE, (SSL_ERROR_BASE + 127),
+ ER3(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM, (SSL_ERROR_BASE + 128),
+ "Incorrect signature algorithm specified in a digitally-signed element.")
+
+-ER3(SSL_ERROR_BAD_CHANNEL_ID_DATA, (SSL_ERROR_BASE + 129),
+-"SSL received a malformed TLS Channel ID extension.")
++ER3(SSL_ERROR_NEXT_PROTOCOL_NO_CALLBACK, (SSL_ERROR_BASE + 129),
++"The next protocol negotiation extension was enabled, but the callback was cleared prior to being needed.")
+
+-ER3(SSL_ERROR_INVALID_CHANNEL_ID_KEY, (SSL_ERROR_BASE + 130),
+-"The application provided an invalid TLS Channel ID key.")
++ER3(SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL, (SSL_ERROR_BASE + 130),
++"The server supports no protocols that the client advertises in the ALPN extension.")
+
+-ER3(SSL_ERROR_GET_CHANNEL_ID_FAILED, (SSL_ERROR_BASE + 131),
+-"The application could not get a TLS Channel ID.")
+-
+-ER3(SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT, (SSL_ERROR_BASE + 132),
++ER3(SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT, (SSL_ERROR_BASE + 131),
+ "The connection was using a lesser TLS version as a result of a previous"
+ " handshake failure, but the server indicated that it should not have been"
+ " needed.")
++
++ER3(SSL_ERROR_BAD_CHANNEL_ID_DATA, (SSL_ERROR_BASE + 132),
++"SSL received a malformed TLS Channel ID extension.")
++
++ER3(SSL_ERROR_INVALID_CHANNEL_ID_KEY, (SSL_ERROR_BASE + 133),
++"The application provided an invalid TLS Channel ID key.")
++
++ER3(SSL_ERROR_GET_CHANNEL_ID_FAILED, (SSL_ERROR_BASE + 134),
++"The application could not get a TLS Channel ID.")
+diff --git a/net/third_party/nss/ssl/ssl3ext.c b/net/third_party/nss/ssl/ssl3ext.c
+index 523e49a..f6530fe 100644
+--- a/net/third_party/nss/ssl/ssl3ext.c
++++ b/net/third_party/nss/ssl/ssl3ext.c
+@@ -56,10 +56,14 @@ static SECStatus ssl3_ClientHandleAppProtoXtn(sslSocket *ss,
+ PRUint16 ex_type, SECItem *data);
+ static SECStatus ssl3_ServerHandleNextProtoNegoXtn(sslSocket *ss,
+ PRUint16 ex_type, SECItem *data);
++static SECStatus ssl3_ServerHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type,
++ SECItem *data);
++static PRInt32 ssl3_ClientSendNextProtoNegoXtn(sslSocket *ss, PRBool append,
++ PRUint32 maxBytes);
+ static PRInt32 ssl3_ClientSendAppProtoXtn(sslSocket *ss, PRBool append,
+ PRUint32 maxBytes);
+-static PRInt32 ssl3_ClientSendNextProtoNegoXtn(sslSocket *ss, PRBool append,
+- PRUint32 maxBytes);
++static PRInt32 ssl3_ServerSendAppProtoXtn(sslSocket *ss, PRBool append,
++ PRUint32 maxBytes);
+ static PRInt32 ssl3_SendUseSRTPXtn(sslSocket *ss, PRBool append,
+ PRUint32 maxBytes);
+ static SECStatus ssl3_HandleUseSRTPXtn(sslSocket * ss, PRUint16 ex_type,
+@@ -247,6 +251,7 @@ static const ssl3HelloExtensionHandler clientHelloHandlers[] = {
+ { ssl_session_ticket_xtn, &ssl3_ServerHandleSessionTicketXtn },
+ { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
+ { ssl_next_proto_nego_xtn, &ssl3_ServerHandleNextProtoNegoXtn },
++ { ssl_app_layer_protocol_xtn, &ssl3_ServerHandleAppProtoXtn },
+ { ssl_use_srtp_xtn, &ssl3_HandleUseSRTPXtn },
+ { ssl_cert_status_xtn, &ssl3_ServerHandleStatusRequestXtn },
+ { ssl_signature_algorithms_xtn, &ssl3_ServerHandleSigAlgsXtn },
+@@ -578,7 +583,8 @@ ssl3_SendSessionTicketXtn(
+
+ /* handle an incoming Next Protocol Negotiation extension. */
+ static SECStatus
+-ssl3_ServerHandleNextProtoNegoXtn(sslSocket * ss, PRUint16 ex_type, SECItem *data)
++ssl3_ServerHandleNextProtoNegoXtn(sslSocket * ss, PRUint16 ex_type,
++ SECItem *data)
+ {
+ if (ss->firstHsDone || data->len != 0) {
+ /* Clients MUST send an empty NPN extension, if any. */
+@@ -623,14 +629,93 @@ ssl3_ValidateNextProtoNego(const unsigned char* data, unsigned int length)
+ return SECSuccess;
+ }
+
++/* protocol selection handler for ALPN (server side) and NPN (client side) */
+ static SECStatus
+-ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type,
+- SECItem *data)
++ssl3_SelectAppProtocol(sslSocket *ss, PRUint16 ex_type, SECItem *data)
+ {
+ SECStatus rv;
+ unsigned char resultBuffer[255];
+ SECItem result = { siBuffer, resultBuffer, 0 };
+
++ rv = ssl3_ValidateNextProtoNego(data->data, data->len);
++ if (rv != SECSuccess)
++ return rv;
++
++ PORT_Assert(ss->nextProtoCallback);
++ rv = ss->nextProtoCallback(ss->nextProtoArg, ss->fd, data->data, data->len,
++ result.data, &result.len, sizeof resultBuffer);
++ if (rv != SECSuccess)
++ return rv;
++ /* If the callback wrote more than allowed to |result| it has corrupted our
++ * stack. */
++ if (result.len > sizeof resultBuffer) {
++ PORT_SetError(SEC_ERROR_OUTPUT_LEN);
++ return SECFailure;
++ }
++
++ if (ex_type == ssl_app_layer_protocol_xtn &&
++ ss->ssl3.nextProtoState != SSL_NEXT_PROTO_NEGOTIATED) {
++ /* The callback might say OK, but then it's picked a default.
++ * That's OK for NPN, but not ALPN. */
++ SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE);
++ PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL);
++ (void)SSL3_SendAlert(ss, alert_fatal, no_application_protocol);
++ return SECFailure;
++ }
++
++ ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
++
++ SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE);
++ return SECITEM_CopyItem(NULL, &ss->ssl3.nextProto, &result);
++}
++
++/* handle an incoming ALPN extension at the server */
++static SECStatus
++ssl3_ServerHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data)
++{
++ int count;
++ SECStatus rv;
++
++ /* We expressly don't want to allow ALPN on renegotiation,
++ * despite it being permitted by the spec. */
++ if (ss->firstHsDone || data->len == 0) {
++ /* Clients MUST send a non-empty ALPN extension. */
++ PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
++ return SECFailure;
++ }
++
++ /* unlike NPN, ALPN has extra redundant length information so that
++ * the extension is the same in both ClientHello and ServerHello */
++ count = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
++ if (count < 0) {
++ return SECFailure; /* fatal alert was sent */
++ }
++ if (count != data->len) {
++ return ssl3_DecodeError(ss);
++ }
++
++ if (!ss->nextProtoCallback) {
++ /* we're not configured for it */
++ return SECSuccess;
++ }
++
++ rv = ssl3_SelectAppProtocol(ss, ex_type, data);
++ if (rv != SECSuccess) {
++ return rv;
++ }
++
++ /* prepare to send back a response, if we negotiated */
++ if (ss->ssl3.nextProtoState == SSL_NEXT_PROTO_NEGOTIATED) {
++ return ssl3_RegisterServerHelloExtensionSender(
++ ss, ex_type, ssl3_ServerSendAppProtoXtn);
++ }
++ return SECSuccess;
++}
++
++static SECStatus
++ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type,
++ SECItem *data)
++{
+ PORT_Assert(!ss->firstHsDone);
+
+ if (ssl3_ExtensionNegotiated(ss, ssl_app_layer_protocol_xtn)) {
+@@ -643,37 +728,16 @@ ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type,
+ return SECFailure;
+ }
+
+- rv = ssl3_ValidateNextProtoNego(data->data, data->len);
+- if (rv != SECSuccess)
+- return rv;
+-
+- /* ss->nextProtoCallback cannot normally be NULL if we negotiated the
+- * extension. However, It is possible that an application erroneously
+- * cleared the callback between the time we sent the ClientHello and now.
+- */
+- PORT_Assert(ss->nextProtoCallback != NULL);
++ /* We should only get this call if we sent the extension, so
++ * ss->nextProtoCallback needs to be non-NULL. However, it is possible
++ * that an application erroneously cleared the callback between the time
++ * we sent the ClientHello and now. */
+ if (!ss->nextProtoCallback) {
+- /* XXX Use a better error code. This is an application error, not an
+- * NSS bug. */
+- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
++ PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_NO_CALLBACK);
+ return SECFailure;
+ }
+
+- rv = ss->nextProtoCallback(ss->nextProtoArg, ss->fd, data->data, data->len,
+- result.data, &result.len, sizeof resultBuffer);
+- if (rv != SECSuccess)
+- return rv;
+- /* If the callback wrote more than allowed to |result| it has corrupted our
+- * stack. */
+- if (result.len > sizeof resultBuffer) {
+- PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+- return SECFailure;
+- }
+-
+- ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
+-
+- SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE);
+- return SECITEM_CopyItem(NULL, &ss->ssl3.nextProto, &result);
++ return ssl3_SelectAppProtocol(ss, ex_type, data);
+ }
+
+ static SECStatus
+@@ -814,6 +878,47 @@ loser:
+ return -1;
+ }
+
++static PRInt32
++ssl3_ServerSendAppProtoXtn(sslSocket * ss, PRBool append, PRUint32 maxBytes)
++{
++ PRInt32 extension_length;
++
++ PORT_Assert(ss->opt.enableALPN);
++ PORT_Assert(ss->ssl3.nextProto.data);
++ PORT_Assert(ss->ssl3.nextProto.len > 0);
++ PORT_Assert(ss->ssl3.nextProtoState == SSL_NEXT_PROTO_NEGOTIATED);
++ PORT_Assert(!ss->firstHsDone);
++
++ extension_length = 2 /* extension type */ + 2 /* extension length */ +
++ 2 /* protocol name list */ + 1 /* name length */ +
++ ss->ssl3.nextProto.len;
++
++ if (append && maxBytes >= extension_length) {
++ SECStatus rv;
++ rv = ssl3_AppendHandshakeNumber(ss, ssl_app_layer_protocol_xtn, 2);
++ if (rv != SECSuccess) {
++ return -1;
++ }
++ rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2);
++ if (rv != SECSuccess) {
++ return -1;
++ }
++ rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3.nextProto.len + 1, 2);
++ if (rv != SECSuccess) {
++ return -1;
++ }
++ rv = ssl3_AppendHandshakeVariable(ss, ss->ssl3.nextProto.data,
++ ss->ssl3.nextProto.len, 1);
++ if (rv != SECSuccess) {
++ return -1;
++ }
++ } else if (maxBytes < extension_length) {
++ return 0;
++ }
++
++ return extension_length;
++}
++
+ static SECStatus
+ ssl3_ClientHandleChannelIDXtn(sslSocket *ss, PRUint16 ex_type,
+ SECItem *data)
+diff --git a/net/third_party/nss/ssl/ssl3prot.h b/net/third_party/nss/ssl/ssl3prot.h
+index 4c19ade..d32be38 100644
+--- a/net/third_party/nss/ssl/ssl3prot.h
++++ b/net/third_party/nss/ssl/ssl3prot.h
+@@ -107,7 +107,8 @@ typedef enum {
+ certificate_unobtainable = 111,
+ unrecognized_name = 112,
+ bad_certificate_status_response = 113,
+- bad_certificate_hash_value = 114
++ bad_certificate_hash_value = 114,
++ no_application_protocol = 120
+
+ } SSL3AlertDescription;
+
+diff --git a/net/third_party/nss/ssl/sslerr.h b/net/third_party/nss/ssl/sslerr.h
+index 82ae7df..5184a6e 100644
+--- a/net/third_party/nss/ssl/sslerr.h
++++ b/net/third_party/nss/ssl/sslerr.h
+@@ -193,10 +193,14 @@ SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM = (SSL_ERROR_BASE + 126),
+ SSL_ERROR_DIGEST_FAILURE = (SSL_ERROR_BASE + 127),
+ SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM = (SSL_ERROR_BASE + 128),
+
+-SSL_ERROR_BAD_CHANNEL_ID_DATA = (SSL_ERROR_BASE + 129),
+-SSL_ERROR_INVALID_CHANNEL_ID_KEY = (SSL_ERROR_BASE + 130),
+-SSL_ERROR_GET_CHANNEL_ID_FAILED = (SSL_ERROR_BASE + 131),
+-SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT = (SSL_ERROR_BASE + 132),
++SSL_ERROR_NEXT_PROTOCOL_NO_CALLBACK = (SSL_ERROR_BASE + 129),
++SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL = (SSL_ERROR_BASE + 130),
++
++SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT = (SSL_ERROR_BASE + 131),
++
++SSL_ERROR_BAD_CHANNEL_ID_DATA = (SSL_ERROR_BASE + 132),
++SSL_ERROR_INVALID_CHANNEL_ID_KEY = (SSL_ERROR_BASE + 133),
++SSL_ERROR_GET_CHANNEL_ID_FAILED = (SSL_ERROR_BASE + 134),
+
+ SSL_ERROR_END_OF_LIST /* let the c compiler determine the value of this. */
+ } SSLErrorCodes;
+diff --git a/net/third_party/nss/ssl/sslsock.c b/net/third_party/nss/ssl/sslsock.c
+index 028cd98..421ba21 100644
+--- a/net/third_party/nss/ssl/sslsock.c
++++ b/net/third_party/nss/ssl/sslsock.c
+@@ -1432,6 +1432,11 @@ DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd)
+ return ssl_ImportFD(model, fd, ssl_variant_datagram);
+ }
+
++/* SSL_SetNextProtoCallback is used to select an application protocol
++ * for ALPN and NPN. For ALPN, this runs on the server; for NPN it
++ * runs on the client. */
++/* Note: The ALPN version doesn't allow for the use of a default, setting a
++ * status of SSL_NEXT_PROTO_NO_OVERLAP is treated as a failure. */
+ SECStatus
+ SSL_SetNextProtoCallback(PRFileDesc *fd, SSLNextProtoCallback callback,
+ void *arg)
+@@ -1452,7 +1457,7 @@ SSL_SetNextProtoCallback(PRFileDesc *fd, SSLNextProtoCallback callback,
+ return SECSuccess;
+ }
+
+-/* ssl_NextProtoNegoCallback is set as an NPN callback for the case when
++/* ssl_NextProtoNegoCallback is set as an ALPN/NPN callback when
+ * SSL_SetNextProtoNego is used.
+ */
+ static SECStatus
+@@ -1471,12 +1476,6 @@ ssl_NextProtoNegoCallback(void *arg, PRFileDesc *fd,
+ return SECFailure;
+ }
+
+- 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; ) {
+@@ -1493,7 +1492,10 @@ ssl_NextProtoNegoCallback(void *arg, PRFileDesc *fd,
+ i += 1 + (unsigned int)protos[i];
+ }
+
+-pick_first:
++ /* The other side supports the extension, and either doesn't have any
++ * protocols configured, or none of its options match ours. In this case we
++ * request our favoured protocol. */
++ /* This will be treated as a failure for ALPN. */
+ ss->ssl3.nextProtoState = SSL_NEXT_PROTO_NO_OVERLAP;
+ result = ss->opt.nextProtoNego.data;
+
diff --git a/net/third_party/nss/patches/applypatches.sh b/net/third_party/nss/patches/applypatches.sh
index b5f9d30..075527c 100755
--- a/net/third_party/nss/patches/applypatches.sh
+++ b/net/third_party/nss/patches/applypatches.sh
@@ -50,3 +50,5 @@ patch -p4 < $patches_dir/nssrwlock.patch
patch -p4 < $patches_dir/paddingextvalue.patch
patch -p4 < $patches_dir/reorderextensions.patch
+
+patch -p5 < $patches_dir/alpnserver.patch
diff --git a/net/third_party/nss/ssl/SSLerrs.h b/net/third_party/nss/ssl/SSLerrs.h
index 4ff0b7d..3f0078c 100644
--- a/net/third_party/nss/ssl/SSLerrs.h
+++ b/net/third_party/nss/ssl/SSLerrs.h
@@ -413,16 +413,22 @@ ER3(SSL_ERROR_DIGEST_FAILURE, (SSL_ERROR_BASE + 127),
ER3(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM, (SSL_ERROR_BASE + 128),
"Incorrect signature algorithm specified in a digitally-signed element.")
-ER3(SSL_ERROR_BAD_CHANNEL_ID_DATA, (SSL_ERROR_BASE + 129),
-"SSL received a malformed TLS Channel ID extension.")
+ER3(SSL_ERROR_NEXT_PROTOCOL_NO_CALLBACK, (SSL_ERROR_BASE + 129),
+"The next protocol negotiation extension was enabled, but the callback was cleared prior to being needed.")
-ER3(SSL_ERROR_INVALID_CHANNEL_ID_KEY, (SSL_ERROR_BASE + 130),
-"The application provided an invalid TLS Channel ID key.")
+ER3(SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL, (SSL_ERROR_BASE + 130),
+"The server supports no protocols that the client advertises in the ALPN extension.")
-ER3(SSL_ERROR_GET_CHANNEL_ID_FAILED, (SSL_ERROR_BASE + 131),
-"The application could not get a TLS Channel ID.")
-
-ER3(SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT, (SSL_ERROR_BASE + 132),
+ER3(SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT, (SSL_ERROR_BASE + 131),
"The connection was using a lesser TLS version as a result of a previous"
" handshake failure, but the server indicated that it should not have been"
" needed.")
+
+ER3(SSL_ERROR_BAD_CHANNEL_ID_DATA, (SSL_ERROR_BASE + 132),
+"SSL received a malformed TLS Channel ID extension.")
+
+ER3(SSL_ERROR_INVALID_CHANNEL_ID_KEY, (SSL_ERROR_BASE + 133),
+"The application provided an invalid TLS Channel ID key.")
+
+ER3(SSL_ERROR_GET_CHANNEL_ID_FAILED, (SSL_ERROR_BASE + 134),
+"The application could not get a TLS Channel ID.")
diff --git a/net/third_party/nss/ssl/ssl3ext.c b/net/third_party/nss/ssl/ssl3ext.c
index 523e49a..f6530fe 100644
--- a/net/third_party/nss/ssl/ssl3ext.c
+++ b/net/third_party/nss/ssl/ssl3ext.c
@@ -56,10 +56,14 @@ static SECStatus ssl3_ClientHandleAppProtoXtn(sslSocket *ss,
PRUint16 ex_type, SECItem *data);
static SECStatus ssl3_ServerHandleNextProtoNegoXtn(sslSocket *ss,
PRUint16 ex_type, SECItem *data);
+static SECStatus ssl3_ServerHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type,
+ SECItem *data);
+static PRInt32 ssl3_ClientSendNextProtoNegoXtn(sslSocket *ss, PRBool append,
+ PRUint32 maxBytes);
static PRInt32 ssl3_ClientSendAppProtoXtn(sslSocket *ss, PRBool append,
PRUint32 maxBytes);
-static PRInt32 ssl3_ClientSendNextProtoNegoXtn(sslSocket *ss, PRBool append,
- PRUint32 maxBytes);
+static PRInt32 ssl3_ServerSendAppProtoXtn(sslSocket *ss, PRBool append,
+ PRUint32 maxBytes);
static PRInt32 ssl3_SendUseSRTPXtn(sslSocket *ss, PRBool append,
PRUint32 maxBytes);
static SECStatus ssl3_HandleUseSRTPXtn(sslSocket * ss, PRUint16 ex_type,
@@ -247,6 +251,7 @@ static const ssl3HelloExtensionHandler clientHelloHandlers[] = {
{ ssl_session_ticket_xtn, &ssl3_ServerHandleSessionTicketXtn },
{ ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
{ ssl_next_proto_nego_xtn, &ssl3_ServerHandleNextProtoNegoXtn },
+ { ssl_app_layer_protocol_xtn, &ssl3_ServerHandleAppProtoXtn },
{ ssl_use_srtp_xtn, &ssl3_HandleUseSRTPXtn },
{ ssl_cert_status_xtn, &ssl3_ServerHandleStatusRequestXtn },
{ ssl_signature_algorithms_xtn, &ssl3_ServerHandleSigAlgsXtn },
@@ -578,7 +583,8 @@ ssl3_SendSessionTicketXtn(
/* handle an incoming Next Protocol Negotiation extension. */
static SECStatus
-ssl3_ServerHandleNextProtoNegoXtn(sslSocket * ss, PRUint16 ex_type, SECItem *data)
+ssl3_ServerHandleNextProtoNegoXtn(sslSocket * ss, PRUint16 ex_type,
+ SECItem *data)
{
if (ss->firstHsDone || data->len != 0) {
/* Clients MUST send an empty NPN extension, if any. */
@@ -623,14 +629,93 @@ ssl3_ValidateNextProtoNego(const unsigned char* data, unsigned int length)
return SECSuccess;
}
+/* protocol selection handler for ALPN (server side) and NPN (client side) */
static SECStatus
-ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type,
- SECItem *data)
+ssl3_SelectAppProtocol(sslSocket *ss, PRUint16 ex_type, SECItem *data)
{
SECStatus rv;
unsigned char resultBuffer[255];
SECItem result = { siBuffer, resultBuffer, 0 };
+ rv = ssl3_ValidateNextProtoNego(data->data, data->len);
+ if (rv != SECSuccess)
+ return rv;
+
+ PORT_Assert(ss->nextProtoCallback);
+ rv = ss->nextProtoCallback(ss->nextProtoArg, ss->fd, data->data, data->len,
+ result.data, &result.len, sizeof resultBuffer);
+ if (rv != SECSuccess)
+ return rv;
+ /* If the callback wrote more than allowed to |result| it has corrupted our
+ * stack. */
+ if (result.len > sizeof resultBuffer) {
+ PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+ return SECFailure;
+ }
+
+ if (ex_type == ssl_app_layer_protocol_xtn &&
+ ss->ssl3.nextProtoState != SSL_NEXT_PROTO_NEGOTIATED) {
+ /* The callback might say OK, but then it's picked a default.
+ * That's OK for NPN, but not ALPN. */
+ SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE);
+ PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL);
+ (void)SSL3_SendAlert(ss, alert_fatal, no_application_protocol);
+ return SECFailure;
+ }
+
+ ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
+
+ SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE);
+ return SECITEM_CopyItem(NULL, &ss->ssl3.nextProto, &result);
+}
+
+/* handle an incoming ALPN extension at the server */
+static SECStatus
+ssl3_ServerHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data)
+{
+ int count;
+ SECStatus rv;
+
+ /* We expressly don't want to allow ALPN on renegotiation,
+ * despite it being permitted by the spec. */
+ if (ss->firstHsDone || data->len == 0) {
+ /* Clients MUST send a non-empty ALPN extension. */
+ PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
+ return SECFailure;
+ }
+
+ /* unlike NPN, ALPN has extra redundant length information so that
+ * the extension is the same in both ClientHello and ServerHello */
+ count = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
+ if (count < 0) {
+ return SECFailure; /* fatal alert was sent */
+ }
+ if (count != data->len) {
+ return ssl3_DecodeError(ss);
+ }
+
+ if (!ss->nextProtoCallback) {
+ /* we're not configured for it */
+ return SECSuccess;
+ }
+
+ rv = ssl3_SelectAppProtocol(ss, ex_type, data);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+
+ /* prepare to send back a response, if we negotiated */
+ if (ss->ssl3.nextProtoState == SSL_NEXT_PROTO_NEGOTIATED) {
+ return ssl3_RegisterServerHelloExtensionSender(
+ ss, ex_type, ssl3_ServerSendAppProtoXtn);
+ }
+ return SECSuccess;
+}
+
+static SECStatus
+ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type,
+ SECItem *data)
+{
PORT_Assert(!ss->firstHsDone);
if (ssl3_ExtensionNegotiated(ss, ssl_app_layer_protocol_xtn)) {
@@ -643,37 +728,16 @@ ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type,
return SECFailure;
}
- rv = ssl3_ValidateNextProtoNego(data->data, data->len);
- if (rv != SECSuccess)
- return rv;
-
- /* ss->nextProtoCallback cannot normally be NULL if we negotiated the
- * extension. However, It is possible that an application erroneously
- * cleared the callback between the time we sent the ClientHello and now.
- */
- PORT_Assert(ss->nextProtoCallback != NULL);
+ /* We should only get this call if we sent the extension, so
+ * ss->nextProtoCallback needs to be non-NULL. However, it is possible
+ * that an application erroneously cleared the callback between the time
+ * we sent the ClientHello and now. */
if (!ss->nextProtoCallback) {
- /* XXX Use a better error code. This is an application error, not an
- * NSS bug. */
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_NO_CALLBACK);
return SECFailure;
}
- rv = ss->nextProtoCallback(ss->nextProtoArg, ss->fd, data->data, data->len,
- result.data, &result.len, sizeof resultBuffer);
- if (rv != SECSuccess)
- return rv;
- /* If the callback wrote more than allowed to |result| it has corrupted our
- * stack. */
- if (result.len > sizeof resultBuffer) {
- PORT_SetError(SEC_ERROR_OUTPUT_LEN);
- return SECFailure;
- }
-
- ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
-
- SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE);
- return SECITEM_CopyItem(NULL, &ss->ssl3.nextProto, &result);
+ return ssl3_SelectAppProtocol(ss, ex_type, data);
}
static SECStatus
@@ -814,6 +878,47 @@ loser:
return -1;
}
+static PRInt32
+ssl3_ServerSendAppProtoXtn(sslSocket * ss, PRBool append, PRUint32 maxBytes)
+{
+ PRInt32 extension_length;
+
+ PORT_Assert(ss->opt.enableALPN);
+ PORT_Assert(ss->ssl3.nextProto.data);
+ PORT_Assert(ss->ssl3.nextProto.len > 0);
+ PORT_Assert(ss->ssl3.nextProtoState == SSL_NEXT_PROTO_NEGOTIATED);
+ PORT_Assert(!ss->firstHsDone);
+
+ extension_length = 2 /* extension type */ + 2 /* extension length */ +
+ 2 /* protocol name list */ + 1 /* name length */ +
+ ss->ssl3.nextProto.len;
+
+ if (append && maxBytes >= extension_length) {
+ SECStatus rv;
+ rv = ssl3_AppendHandshakeNumber(ss, ssl_app_layer_protocol_xtn, 2);
+ if (rv != SECSuccess) {
+ return -1;
+ }
+ rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2);
+ if (rv != SECSuccess) {
+ return -1;
+ }
+ rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3.nextProto.len + 1, 2);
+ if (rv != SECSuccess) {
+ return -1;
+ }
+ rv = ssl3_AppendHandshakeVariable(ss, ss->ssl3.nextProto.data,
+ ss->ssl3.nextProto.len, 1);
+ if (rv != SECSuccess) {
+ return -1;
+ }
+ } else if (maxBytes < extension_length) {
+ return 0;
+ }
+
+ return extension_length;
+}
+
static SECStatus
ssl3_ClientHandleChannelIDXtn(sslSocket *ss, PRUint16 ex_type,
SECItem *data)
diff --git a/net/third_party/nss/ssl/ssl3prot.h b/net/third_party/nss/ssl/ssl3prot.h
index 4c19ade..d32be38 100644
--- a/net/third_party/nss/ssl/ssl3prot.h
+++ b/net/third_party/nss/ssl/ssl3prot.h
@@ -107,7 +107,8 @@ typedef enum {
certificate_unobtainable = 111,
unrecognized_name = 112,
bad_certificate_status_response = 113,
- bad_certificate_hash_value = 114
+ bad_certificate_hash_value = 114,
+ no_application_protocol = 120
} SSL3AlertDescription;
diff --git a/net/third_party/nss/ssl/sslerr.h b/net/third_party/nss/ssl/sslerr.h
index 82ae7df..5184a6e 100644
--- a/net/third_party/nss/ssl/sslerr.h
+++ b/net/third_party/nss/ssl/sslerr.h
@@ -193,10 +193,14 @@ SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM = (SSL_ERROR_BASE + 126),
SSL_ERROR_DIGEST_FAILURE = (SSL_ERROR_BASE + 127),
SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM = (SSL_ERROR_BASE + 128),
-SSL_ERROR_BAD_CHANNEL_ID_DATA = (SSL_ERROR_BASE + 129),
-SSL_ERROR_INVALID_CHANNEL_ID_KEY = (SSL_ERROR_BASE + 130),
-SSL_ERROR_GET_CHANNEL_ID_FAILED = (SSL_ERROR_BASE + 131),
-SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT = (SSL_ERROR_BASE + 132),
+SSL_ERROR_NEXT_PROTOCOL_NO_CALLBACK = (SSL_ERROR_BASE + 129),
+SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL = (SSL_ERROR_BASE + 130),
+
+SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT = (SSL_ERROR_BASE + 131),
+
+SSL_ERROR_BAD_CHANNEL_ID_DATA = (SSL_ERROR_BASE + 132),
+SSL_ERROR_INVALID_CHANNEL_ID_KEY = (SSL_ERROR_BASE + 133),
+SSL_ERROR_GET_CHANNEL_ID_FAILED = (SSL_ERROR_BASE + 134),
SSL_ERROR_END_OF_LIST /* let the c compiler determine the value of this. */
} SSLErrorCodes;
diff --git a/net/third_party/nss/ssl/sslsock.c b/net/third_party/nss/ssl/sslsock.c
index 028cd98..421ba21 100644
--- a/net/third_party/nss/ssl/sslsock.c
+++ b/net/third_party/nss/ssl/sslsock.c
@@ -1432,6 +1432,11 @@ DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd)
return ssl_ImportFD(model, fd, ssl_variant_datagram);
}
+/* SSL_SetNextProtoCallback is used to select an application protocol
+ * for ALPN and NPN. For ALPN, this runs on the server; for NPN it
+ * runs on the client. */
+/* Note: The ALPN version doesn't allow for the use of a default, setting a
+ * status of SSL_NEXT_PROTO_NO_OVERLAP is treated as a failure. */
SECStatus
SSL_SetNextProtoCallback(PRFileDesc *fd, SSLNextProtoCallback callback,
void *arg)
@@ -1452,7 +1457,7 @@ SSL_SetNextProtoCallback(PRFileDesc *fd, SSLNextProtoCallback callback,
return SECSuccess;
}
-/* ssl_NextProtoNegoCallback is set as an NPN callback for the case when
+/* ssl_NextProtoNegoCallback is set as an ALPN/NPN callback when
* SSL_SetNextProtoNego is used.
*/
static SECStatus
@@ -1471,12 +1476,6 @@ ssl_NextProtoNegoCallback(void *arg, PRFileDesc *fd,
return SECFailure;
}
- 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; ) {
@@ -1493,7 +1492,10 @@ ssl_NextProtoNegoCallback(void *arg, PRFileDesc *fd,
i += 1 + (unsigned int)protos[i];
}
-pick_first:
+ /* The other side supports the extension, and either doesn't have any
+ * protocols configured, or none of its options match ours. In this case we
+ * request our favoured protocol. */
+ /* This will be treated as a failure for ALPN. */
ss->ssl3.nextProtoState = SSL_NEXT_PROTO_NO_OVERLAP;
result = ss->opt.nextProtoNego.data;