diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c --- a/nss/lib/ssl/ssl3con.c 2013-07-31 14:17:20.669282120 -0700 +++ b/nss/lib/ssl/ssl3con.c 2013-07-31 14:28:56.549496061 -0700 @@ -9912,8 +9912,10 @@ ssl3_SendNextProto(sslSocket *ss) int padding_len; static const unsigned char padding[32] = {0}; - if (ss->ssl3.nextProto.len == 0) + if (ss->ssl3.nextProto.len == 0 || + ss->ssl3.nextProtoState == SSL_NEXT_PROTO_SELECTED) { return SECSuccess; + } PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); diff -pu a/nss/lib/ssl/ssl3ext.c b/nss/lib/ssl/ssl3ext.c --- a/nss/lib/ssl/ssl3ext.c 2013-07-31 14:10:00.342814862 -0700 +++ b/nss/lib/ssl/ssl3ext.c 2013-07-31 14:28:56.549496061 -0700 @@ -53,8 +53,12 @@ static SECStatus ssl3_HandleRenegotiationInfoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data); static SECStatus ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data); +static SECStatus ssl3_ClientHandleAppProtoXtn(sslSocket *ss, + PRUint16 ex_type, SECItem *data); static SECStatus ssl3_ServerHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data); +static PRInt32 ssl3_ClientSendAppProtoXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes); static PRInt32 ssl3_ClientSendNextProtoNegoXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes); static PRInt32 ssl3_SendUseSRTPXtn(sslSocket *ss, PRBool append, @@ -252,6 +256,7 @@ static const ssl3HelloExtensionHandler serverHelloHandlersTLS[] = { { ssl_session_ticket_xtn, &ssl3_ClientHandleSessionTicketXtn }, { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn }, { ssl_next_proto_nego_xtn, &ssl3_ClientHandleNextProtoNegoXtn }, + { ssl_app_layer_protocol_xtn, &ssl3_ClientHandleAppProtoXtn }, { ssl_use_srtp_xtn, &ssl3_HandleUseSRTPXtn }, { ssl_channel_id_xtn, &ssl3_ClientHandleChannelIDXtn }, { ssl_cert_status_xtn, &ssl3_ClientHandleStatusRequestXtn }, @@ -271,18 +276,19 @@ static const ssl3HelloExtensionHandler serverHelloHandlersSSL3[] = { */ static const ssl3HelloExtensionSender clientHelloSendersTLS[SSL_MAX_EXTENSIONS] = { - { ssl_server_name_xtn, &ssl3_SendServerNameXtn }, - { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn }, + { ssl_server_name_xtn, &ssl3_SendServerNameXtn }, + { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn }, #ifdef NSS_ENABLE_ECC - { ssl_elliptic_curves_xtn, &ssl3_SendSupportedCurvesXtn }, - { ssl_ec_point_formats_xtn, &ssl3_SendSupportedPointFormatsXtn }, + { ssl_elliptic_curves_xtn, &ssl3_SendSupportedCurvesXtn }, + { ssl_ec_point_formats_xtn, &ssl3_SendSupportedPointFormatsXtn }, #endif - { ssl_session_ticket_xtn, &ssl3_SendSessionTicketXtn }, - { ssl_next_proto_nego_xtn, &ssl3_ClientSendNextProtoNegoXtn }, - { ssl_use_srtp_xtn, &ssl3_SendUseSRTPXtn }, - { ssl_channel_id_xtn, &ssl3_ClientSendChannelIDXtn }, - { ssl_cert_status_xtn, &ssl3_ClientSendStatusRequestXtn }, - { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn } + { ssl_session_ticket_xtn, &ssl3_SendSessionTicketXtn }, + { ssl_next_proto_nego_xtn, &ssl3_ClientSendNextProtoNegoXtn }, + { ssl_app_layer_protocol_xtn, &ssl3_ClientSendAppProtoXtn }, + { ssl_use_srtp_xtn, &ssl3_SendUseSRTPXtn }, + { ssl_channel_id_xtn, &ssl3_ClientSendChannelIDXtn }, + { ssl_cert_status_xtn, &ssl3_ClientSendStatusRequestXtn }, + { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn } /* any extra entries will appear as { 0, NULL } */ }; @@ -606,6 +612,11 @@ ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type, PORT_Assert(!ss->firstHsDone); + if (ssl3_ExtensionNegotiated(ss, ssl_app_layer_protocol_xtn)) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + rv = ssl3_ValidateNextProtoNego(data->data, data->len); if (rv != SECSuccess) return rv; @@ -639,6 +650,44 @@ ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type, return SECITEM_CopyItem(NULL, &ss->ssl3.nextProto, &result); } +static SECStatus +ssl3_ClientHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) +{ + const unsigned char* d = data->data; + PRUint16 name_list_len; + SECItem protocol_name; + + if (ssl3_ExtensionNegotiated(ss, ssl_next_proto_nego_xtn)) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + /* The extension data from the server has the following format: + * uint16 name_list_len; + * uint8 len; + * uint8 protocol_name[len]; */ + if (data->len < 4 || data->len > 2 + 1 + 255) { + PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); + return SECFailure; + } + + name_list_len = ((PRUint16) d[0]) << 8 | + ((PRUint16) d[1]); + if (name_list_len != data->len - 2 || + d[2] != data->len - 3) { + PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); + return SECFailure; + } + + protocol_name.data = data->data + 3; + protocol_name.len = data->len - 3; + + SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE); + ss->ssl3.nextProtoState = SSL_NEXT_PROTO_SELECTED; + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + return SECITEM_CopyItem(NULL, &ss->ssl3.nextProto, &protocol_name); +} + static PRInt32 ssl3_ClientSendNextProtoNegoXtn(sslSocket * ss, PRBool append, PRUint32 maxBytes) @@ -672,6 +721,70 @@ loser: return -1; } +static PRInt32 +ssl3_ClientSendAppProtoXtn(sslSocket * ss, PRBool append, PRUint32 maxBytes) +{ + PRInt32 extension_length; + unsigned char *alpn_protos = NULL; + + /* Renegotiations do not send this extension. */ + if (!ss->opt.nextProtoNego.data || ss->firstHsDone) { + return 0; + } + + extension_length = 2 /* extension type */ + 2 /* extension length */ + + 2 /* protocol name list length */ + + ss->opt.nextProtoNego.len; + + if (append && maxBytes >= extension_length) { + /* NPN requires that the client's fallback protocol is first in the + * list. However, ALPN sends protocols in preference order. So we + * allocate a buffer and move the first protocol to the end of the + * list. */ + SECStatus rv; + const unsigned int len = ss->opt.nextProtoNego.len; + + alpn_protos = PORT_Alloc(len); + if (alpn_protos == NULL) { + return SECFailure; + } + if (len > 0) { + /* Each protocol string is prefixed with a single byte length. */ + unsigned int i = ss->opt.nextProtoNego.data[0] + 1; + if (i <= len) { + memcpy(alpn_protos, &ss->opt.nextProtoNego.data[i], len - i); + memcpy(alpn_protos + len - i, ss->opt.nextProtoNego.data, i); + } else { + /* This seems to be invalid data so we'll send as-is. */ + memcpy(alpn_protos, ss->opt.nextProtoNego.data, len); + } + } + + rv = ssl3_AppendHandshakeNumber(ss, ssl_app_layer_protocol_xtn, 2); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendHandshakeVariable(ss, alpn_protos, len, 2); + PORT_Free(alpn_protos); + alpn_protos = NULL; + if (rv != SECSuccess) + goto loser; + ss->xtnData.advertised[ss->xtnData.numAdvertised++] = + ssl_app_layer_protocol_xtn; + } else if (maxBytes < extension_length) { + return 0; + } + + return extension_length; + +loser: + if (alpn_protos) + PORT_Free(alpn_protos); + return -1; +} + static SECStatus ssl3_ClientHandleChannelIDXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) diff -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h --- a/nss/lib/ssl/ssl.h 2013-07-31 14:10:35.113325316 -0700 +++ b/nss/lib/ssl/ssl.h 2013-07-31 14:28:56.589496647 -0700 @@ -203,6 +203,16 @@ SSL_IMPORT SECStatus SSL_SetNextProtoCal * protocol in server-preference order. If no matching protocol is found it * selects the first supported protocol. * + * Using this function also allows the client to transparently support ALPN. + * The same set of protocols will be advertised via ALPN and, if the server + * uses ALPN to select a protocol, SSL_GetNextProto will return + * SSL_NEXT_PROTO_SELECTED as the state. + * + * Since NPN uses the first protocol as the fallback protocol, when sending an + * ALPN extension, the first protocol is moved to the end of the list. This + * indicates that the fallback protocol is the least preferred. The other + * protocols should be in preference order. + * * 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, @@ -212,7 +217,8 @@ SSL_IMPORT SECStatus SSL_SetNextProtoNeg typedef enum SSLNextProtoState { SSL_NEXT_PROTO_NO_SUPPORT = 0, /* No peer support */ SSL_NEXT_PROTO_NEGOTIATED = 1, /* Mutual agreement */ - SSL_NEXT_PROTO_NO_OVERLAP = 2 /* No protocol overlap found */ + SSL_NEXT_PROTO_NO_OVERLAP = 2, /* No protocol overlap found */ + SSL_NEXT_PROTO_SELECTED = 3 /* Server selected proto (ALPN) */ } SSLNextProtoState; /* SSL_GetNextProto can be used in the HandshakeCallback or any time after diff -pu a/nss/lib/ssl/sslt.h b/nss/lib/ssl/sslt.h --- a/nss/lib/ssl/sslt.h 2013-07-31 14:13:43.806096237 -0700 +++ b/nss/lib/ssl/sslt.h 2013-07-31 14:28:56.609496941 -0700 @@ -195,12 +195,13 @@ typedef enum { #endif ssl_signature_algorithms_xtn = 13, ssl_use_srtp_xtn = 14, + ssl_app_layer_protocol_xtn = 16, ssl_session_ticket_xtn = 35, ssl_next_proto_nego_xtn = 13172, ssl_channel_id_xtn = 30031, ssl_renegotiation_info_xtn = 0xff01 /* experimental number */ } SSLExtensionType; -#define SSL_MAX_EXTENSIONS 10 +#define SSL_MAX_EXTENSIONS 11 #endif /* __sslt_h_ */