From 0601ca68ad19aa7d319bf2d76013bee9de19384c Mon Sep 17 00:00:00 2001 From: "bsmith%mozilla.com" Date: Sat, 29 Oct 2011 00:29:11 +0000 Subject: [PATCH] Bug 547312: Implement client-side support for NPN; original patch by agl r=wtc; changes by bsmith r=agl git-svn-id: svn://10.0.0.236/trunk@263024 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/security/nss/lib/ssl/SSLerrs.h | 3 + mozilla/security/nss/lib/ssl/ssl.def | 8 ++ mozilla/security/nss/lib/ssl/ssl.h | 58 +++++++++- mozilla/security/nss/lib/ssl/ssl3con.c | 55 ++++++++- mozilla/security/nss/lib/ssl/ssl3ext.c | 130 ++++++++++++++++++++- mozilla/security/nss/lib/ssl/ssl3prot.h | 5 +- mozilla/security/nss/lib/ssl/sslerr.h | 4 +- mozilla/security/nss/lib/ssl/sslimpl.h | 17 ++- mozilla/security/nss/lib/ssl/sslsock.c | 146 +++++++++++++++++++++++- mozilla/security/nss/lib/ssl/sslt.h | 5 +- 10 files changed, 420 insertions(+), 11 deletions(-) diff --git a/mozilla/security/nss/lib/ssl/SSLerrs.h b/mozilla/security/nss/lib/ssl/SSLerrs.h index b7e991b8783..44b967e309d 100644 --- a/mozilla/security/nss/lib/ssl/SSLerrs.h +++ b/mozilla/security/nss/lib/ssl/SSLerrs.h @@ -405,3 +405,6 @@ ER3(SSL_ERROR_RX_UNEXPECTED_UNCOMPRESSED_RECORD, (SSL_ERROR_BASE + 114), ER3(SSL_ERROR_WEAK_SERVER_EPHEMERAL_DH_KEY, (SSL_ERROR_BASE + 115), "SSL received a weak ephemeral Diffie-Hellman key in Server Key Exchange handshake message.") + +ER3(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID, (SSL_ERROR_BASE + 116), +"SSL received invalid NPN extension data.") diff --git a/mozilla/security/nss/lib/ssl/ssl.def b/mozilla/security/nss/lib/ssl/ssl.def index 70638eadc9c..1a787d6c866 100644 --- a/mozilla/security/nss/lib/ssl/ssl.def +++ b/mozilla/security/nss/lib/ssl/ssl.def @@ -164,3 +164,11 @@ NSSSSL_GetVersion; ;+ local: ;+ *; ;+}; +;+NSS_3.13.2 { # NSS 3.13.2 release +;+ global: +SSL_SetNextProtoCallback; +SSL_SetNextProtoNego; +SSL_GetNextProto; +;+ local: +;+ *; +;+}; diff --git a/mozilla/security/nss/lib/ssl/ssl.h b/mozilla/security/nss/lib/ssl/ssl.h index c27f8d92e65..7a973b15cfa 100644 --- a/mozilla/security/nss/lib/ssl/ssl.h +++ b/mozilla/security/nss/lib/ssl/ssl.h @@ -36,7 +36,7 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -/* $Id: ssl.h,v 1.44 2011-10-06 22:42:33 wtc%google.com Exp $ */ +/* $Id: ssl.h,v 1.45 2011-10-29 00:29:11 bsmith%mozilla.com Exp $ */ #ifndef __ssl_h_ #define __ssl_h_ @@ -181,6 +181,62 @@ 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 for the client, when a + * Next Protocol Negotiation (NPN) extension has been received from the server. + * |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|. + * + * The callback must return SECFailure or SECSuccess (not SECWouldBlock). + */ +typedef SECStatus (PR_CALLBACK *SSLNextProtoCallback)( + void *arg, + PRFileDesc *fd, + const unsigned char* protos, + unsigned int protosLen, + unsigned char* protoOut, + unsigned int* protoOutLen, + unsigned int protoMaxOut); + +/* 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 int length); + +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 */ +} SSLNextProtoState; + +/* SSL_GetNextProto can be used in the HandshakeCallback or any time after + * a handshake to retrieve the result of the Next Protocol negotiation. + * + * The length of the negotiated protocol, if any, is written into *bufLen. + * If the negotiated protocol is longer than bufLenMax, then SECFailure is + * returned. Otherwise, the negotiated protocol, if any, is written into buf, + * and SECSuccess is returned. */ +SSL_IMPORT SECStatus SSL_GetNextProto(PRFileDesc *fd, + SSLNextProtoState *state, + unsigned char *buf, + unsigned int *bufLen, + unsigned int bufLenMax); + /* ** 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 86dde88142d..443e5463122 100644 --- a/mozilla/security/nss/lib/ssl/ssl3con.c +++ b/mozilla/security/nss/lib/ssl/ssl3con.c @@ -39,7 +39,7 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -/* $Id: ssl3con.c,v 1.152 2011-10-01 03:59:54 bsmith%mozilla.com Exp $ */ +/* $Id: ssl3con.c,v 1.153 2011-10-29 00:29:11 bsmith%mozilla.com Exp $ */ #include "cert.h" #include "ssl.h" @@ -81,6 +81,7 @@ static SECStatus ssl3_InitState( sslSocket *ss); static SECStatus ssl3_SendCertificate( sslSocket *ss); static SECStatus ssl3_SendEmptyCertificate( sslSocket *ss); static SECStatus ssl3_SendCertificateRequest(sslSocket *ss); +static SECStatus ssl3_SendNextProto( sslSocket *ss); static SECStatus ssl3_SendFinished( sslSocket *ss, PRInt32 flags); static SECStatus ssl3_SendServerHello( sslSocket *ss); static SECStatus ssl3_SendServerHelloDone( sslSocket *ss); @@ -5788,6 +5789,14 @@ ssl3_HandleServerHelloDone(sslSocket *ss) if (rv != SECSuccess) { goto loser; /* err code was set. */ } + + if (!ss->firstHsDone) { + rv = ssl3_SendNextProto(ss); + if (rv != SECSuccess) { + goto loser; /* err code was set. */ + } + } + rv = ssl3_SendFinished(ss, 0); if (rv != SECSuccess) { goto loser; /* err code was set. */ @@ -8220,6 +8229,40 @@ ssl3_ComputeTLSFinished(ssl3CipherSpec *spec, return rv; } +/* called from ssl3_HandleServerHelloDone + */ +static SECStatus +ssl3_SendNextProto(sslSocket *ss) +{ + SECStatus rv; + int padding_len; + static const unsigned char padding[32] = {0}; + + if (ss->ssl3.nextProto.len == 0) + return SECSuccess; + + PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); + PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + padding_len = 32 - ((ss->ssl3.nextProto.len + 2) % 32); + + rv = ssl3_AppendHandshakeHeader(ss, next_proto, ss->ssl3.nextProto.len + + 2 + padding_len); + if (rv != SECSuccess) { + return rv; /* error code set by AppendHandshakeHeader */ + } + rv = ssl3_AppendHandshakeVariable(ss, ss->ssl3.nextProto.data, + ss->ssl3.nextProto.len, 1); + if (rv != SECSuccess) { + return rv; /* error code set by AppendHandshake */ + } + rv = ssl3_AppendHandshakeVariable(ss, padding, padding_len, 1); + if (rv != SECSuccess) { + return rv; /* error code set by AppendHandshake */ + } + return rv; +} + /* called from ssl3_HandleServerHelloDone * ssl3_HandleClientHello * ssl3_HandleFinished @@ -8473,6 +8516,14 @@ ssl3_HandleFinished(sslSocket *ss, SSL3Opaque *b, PRUint32 length, if (doStepUp || ss->writerThread == PR_GetCurrentThread()) { flags = ssl_SEND_FLAG_FORCE_INTO_BUFFER; } + + if (!isServer && !ss->firstHsDone) { + rv = ssl3_SendNextProto(ss); + if (rv != SECSuccess) { + goto xmit_loser; /* err code was set. */ + } + } + rv = ssl3_SendFinished(ss, flags); if (rv != SECSuccess) { goto xmit_loser; /* err is set. */ @@ -9540,6 +9591,8 @@ ssl3_DestroySSL3Info(sslSocket *ss) ssl3_DestroyCipherSpec(&ss->ssl3.specs[1], PR_TRUE/*freeSrvName*/); ss->ssl3.initialized = PR_FALSE; + + SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE); } /* End of ssl3con.c */ diff --git a/mozilla/security/nss/lib/ssl/ssl3ext.c b/mozilla/security/nss/lib/ssl/ssl3ext.c index 179e55c601a..efee93c93a3 100644 --- a/mozilla/security/nss/lib/ssl/ssl3ext.c +++ b/mozilla/security/nss/lib/ssl/ssl3ext.c @@ -41,7 +41,7 @@ * ***** END LICENSE BLOCK ***** */ /* TLS extension code moved here from ssl3ecc.c */ -/* $Id: ssl3ext.c,v 1.16 2011-03-24 01:40:14 alexei.volkov.bugs%sun.com Exp $ */ +/* $Id: ssl3ext.c,v 1.17 2011-10-29 00:29:11 bsmith%mozilla.com Exp $ */ #include "nssrenam.h" #include "nss.h" @@ -78,6 +78,12 @@ static PRInt32 ssl3_SendRenegotiationInfoXtn(sslSocket * ss, PRBool append, PRUint32 maxBytes); 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_ServerHandleNextProtoNegoXtn(sslSocket *ss, + PRUint16 ex_type, SECItem *data); +static PRInt32 ssl3_ClientSendNextProtoNegoXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes); /* * Write bytes. Using this function means the SECItem structure @@ -235,6 +241,7 @@ static const ssl3HelloExtensionHandler clientHelloHandlers[] = { #endif { ssl_session_ticket_xtn, &ssl3_ServerHandleSessionTicketXtn }, { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn }, + { ssl_next_proto_neg_xtn, &ssl3_ServerHandleNextProtoNegoXtn }, { -1, NULL } }; @@ -245,6 +252,7 @@ static const ssl3HelloExtensionHandler serverHelloHandlersTLS[] = { /* TODO: add a handler for ssl_ec_point_formats_xtn */ { ssl_session_ticket_xtn, &ssl3_ClientHandleSessionTicketXtn }, { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn }, + { ssl_next_proto_neg_xtn, &ssl3_ClientHandleNextProtoNegoXtn }, { -1, NULL } }; @@ -267,7 +275,8 @@ ssl3HelloExtensionSender clientHelloSendersTLS[SSL_MAX_EXTENSIONS] = { { ssl_elliptic_curves_xtn, &ssl3_SendSupportedCurvesXtn }, { ssl_ec_point_formats_xtn, &ssl3_SendSupportedPointFormatsXtn }, #endif - { ssl_session_ticket_xtn, &ssl3_SendSessionTicketXtn } + { ssl_session_ticket_xtn, &ssl3_SendSessionTicketXtn }, + { ssl_next_proto_neg_xtn, &ssl3_ClientSendNextProtoNegoXtn } /* any extra entries will appear as { 0, NULL } */ }; @@ -534,6 +543,123 @@ ssl3_SendSessionTicketXtn( return -1; } +/* handle an incoming Next Protocol Negotiation extension. */ +static SECStatus +ssl3_ServerHandleNextProtoNegoXtn(sslSocket * ss, PRUint16 ex_type, SECItem *data) +{ + if (ss->firstHsDone || data->len != 0) { + /* Clients MUST send an empty NPN extension, if any. */ + PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); + return SECFailure; + } + + return SECSuccess; +} + +/* ssl3_ValidateNextProtoNego checks that the given block of data is valid: none + * 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 int length) +{ + unsigned int offset = 0; + + while (offset < length) { + unsigned int newOffset = offset + 1 + (unsigned int) data[offset]; + /* Reject embedded nulls to protect against buggy applications that + * store protocol identifiers in null-terminated strings. + */ + if (newOffset > length || data[offset] == 0) { + PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); + return SECFailure; + } + offset = newOffset; + } + + if (offset > length) { + PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); + return SECFailure; + } + + return SECSuccess; +} + +static SECStatus +ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data) +{ + SECStatus rv; + unsigned char resultBuffer[255]; + unsigned char * newData; + SECItem result = { siBuffer, resultBuffer, 0 }; + + if (ss->firstHsDone) { + PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); + 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); + if (!ss->nextProtoCallback) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + 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 result) { + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + return SECFailure; + } + + SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE); + return SECITEM_CopyItem(NULL, &ss->ssl3.nextProto, &result); +} + +static PRInt32 +ssl3_ClientSendNextProtoNegoXtn(sslSocket * ss, PRBool append, + PRUint32 maxBytes) +{ + PRInt32 extension_length; + + /* Renegotiations do not send this extension. */ + if (!ss->nextProtoCallback || ss->firstHsDone) { + return 0; + } + + extension_length = 4; + + if (append && maxBytes >= extension_length) { + SECStatus rv; + rv = ssl3_AppendHandshakeNumber(ss, ssl_next_proto_neg_xtn, 2); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendHandshakeNumber(ss, 0, 2); + if (rv != SECSuccess) + goto loser; + ss->xtnData.advertised[ss->xtnData.numAdvertised++] = + ssl_next_proto_neg_xtn; + } else if (maxBytes < extension_length) { + return 0; + } + + return extension_length; + +loser: + return -1; +} + /* * NewSessionTicket * Called from ssl3_HandleFinished diff --git a/mozilla/security/nss/lib/ssl/ssl3prot.h b/mozilla/security/nss/lib/ssl/ssl3prot.h index f67a55ebeec..1741532e05f 100644 --- a/mozilla/security/nss/lib/ssl/ssl3prot.h +++ b/mozilla/security/nss/lib/ssl/ssl3prot.h @@ -38,7 +38,7 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -/* $Id: ssl3prot.h,v 1.19 2010-06-24 09:24:18 nelson%bolyard.com Exp $ */ +/* $Id: ssl3prot.h,v 1.20 2011-10-29 00:29:11 bsmith%mozilla.com Exp $ */ #ifndef __ssl3proto_h_ #define __ssl3proto_h_ @@ -157,7 +157,8 @@ typedef enum { server_hello_done = 14, certificate_verify = 15, client_key_exchange = 16, - finished = 20 + finished = 20, + next_proto = 67 } SSL3HandshakeType; typedef struct { diff --git a/mozilla/security/nss/lib/ssl/sslerr.h b/mozilla/security/nss/lib/ssl/sslerr.h index 560a6433e59..fbfc792e85b 100644 --- a/mozilla/security/nss/lib/ssl/sslerr.h +++ b/mozilla/security/nss/lib/ssl/sslerr.h @@ -36,7 +36,7 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -/* $Id: sslerr.h,v 1.14 2011-10-05 18:07:18 emaldona%redhat.com Exp $ */ +/* $Id: sslerr.h,v 1.15 2011-10-29 00:29:11 bsmith%mozilla.com Exp $ */ #ifndef __SSL_ERR_H_ #define __SSL_ERR_H_ @@ -205,6 +205,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 + 116), + 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 0bfa07a467d..ac9b6d33ead 100644 --- a/mozilla/security/nss/lib/ssl/sslimpl.h +++ b/mozilla/security/nss/lib/ssl/sslimpl.h @@ -39,7 +39,7 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -/* $Id: sslimpl.h,v 1.84 2011-10-22 16:45:40 emaldona%redhat.com Exp $ */ +/* $Id: sslimpl.h,v 1.85 2011-10-29 00:29:11 bsmith%mozilla.com Exp $ */ #ifndef __sslimpl_h_ #define __sslimpl_h_ @@ -313,6 +313,10 @@ typedef struct { #endif /* NSS_ENABLE_ECC */ typedef struct sslOptionsStr { + /* 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 */ @@ -828,6 +832,12 @@ 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 negotiated. + */ + SECItem nextProto; + SSLNextProtoState nextProtoState; }; typedef struct { @@ -1059,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 */ @@ -1569,6 +1581,9 @@ extern PRBool ssl_GetSessionTicketKeysPKCS11(SECKEYPrivateKey *svrPrivKey, #define TLS_EX_SESS_TICKET_LIFETIME_HINT (2 * 24 * 60 * 60) /* 2 days */ #define TLS_EX_SESS_TICKET_VERSION (0x0100) +extern SECStatus ssl3_ValidateNextProtoNego(const unsigned char* data, + unsigned int length); + /* Construct a new NSPR socket for the app to use */ extern PRFileDesc *ssl_NewPRSocket(sslSocket *ss, PRFileDesc *fd); extern void ssl_FreePRSocket(PRFileDesc *fd); diff --git a/mozilla/security/nss/lib/ssl/sslsock.c b/mozilla/security/nss/lib/ssl/sslsock.c index d85ecf8d051..11fdfe43fa6 100644 --- a/mozilla/security/nss/lib/ssl/sslsock.c +++ b/mozilla/security/nss/lib/ssl/sslsock.c @@ -40,7 +40,7 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -/* $Id: sslsock.c,v 1.75 2011-10-22 16:45:40 emaldona%redhat.com Exp $ */ +/* $Id: sslsock.c,v 1.76 2011-10-29 00:29:11 bsmith%mozilla.com Exp $ */ #include "seccomon.h" #include "cert.h" #include "keyhi.h" @@ -163,6 +163,7 @@ static const sslSocketOps ssl_secure_ops = { /* SSL. */ ** default settings for socket enables */ static sslOptions ssl_defaults = { + { siBuffer, NULL, 0 }, /* nextProtoNego */ PR_TRUE, /* useSecurity */ PR_FALSE, /* useSocks */ PR_FALSE, /* requestCertificate */ @@ -440,6 +441,7 @@ ssl_DestroySocketContents(sslSocket *ss) ssl3_FreeKeyPair(ss->ephemeralECDHKeyPair); ss->ephemeralECDHKeyPair = NULL; } + SECITEM_FreeItem(&ss->opt.nextProtoNego, PR_FALSE); PORT_Assert(!ss->xtnData.sniNameArr); if (ss->xtnData.sniNameArr) { PORT_Free(ss->xtnData.sniNameArr); @@ -1301,6 +1303,148 @@ SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd) return fd; } +SECStatus +SSL_SetNextProtoCallback(PRFileDesc *fd, SSLNextProtoCallback callback, + void *arg) +{ + sslSocket *ss = ssl_FindSocket(fd); + + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetNextProtoCallback", SSL_GETPID(), + fd)); + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + ssl_GetSSL3HandshakeLock(ss); + ss->nextProtoCallback = callback; + ss->nextProtoArg = arg; + ssl_ReleaseSSL3HandshakeLock(ss); + + return SECSuccess; +} + +/* NextProtoStandardCallback is set as an NPN callback for the case when + * SSL_SetNextProtoNego is used. + */ +static SECStatus +ssl_NextProtoNegoCallback(void *arg, PRFileDesc *fd, + const unsigned char *protos, unsigned int protos_len, + unsigned char *protoOut, unsigned int *protoOutLen, + unsigned int protoMaxLen) +{ + unsigned int i, j; + const unsigned char *result; + sslSocket *ss = ssl_FindSocket(fd); + + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in ssl_NextProtoNegoCallback", + SSL_GETPID(), fd)); + PORT_SetError(SEC_ERROR_INVALID_ARGS); + 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; ) { + if (protos[i] == ss->opt.nextProtoNego.data[j] && + PORT_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 += 1 + (unsigned int)ss->opt.nextProtoNego.data[j]; + } + i += 1 + (unsigned int)protos[i]; + } + +pick_first: + ss->ssl3.nextProtoState = SSL_NEXT_PROTO_NO_OVERLAP; + result = ss->opt.nextProtoNego.data; + +found: + *protoOutLen = result[0]; + if (protoMaxLen < result[0]) { + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + return SECFailure; + } + memcpy(protoOut, result + 1, result[0]); + return SECSuccess; +} + +SECStatus +SSL_SetNextProtoNego(PRFileDesc *fd, const unsigned char *data, + unsigned int length) +{ + sslSocket *ss = ssl_FindSocket(fd); + SECStatus rv; + SECItem dataItem = { siBuffer, (unsigned char *) data, length }; + + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetNextProtoNego", + SSL_GETPID(), fd)); + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + if (ssl3_ValidateNextProtoNego(data, length) != SECSuccess) + return SECFailure; + + ssl_GetSSL3HandshakeLock(ss); + SECITEM_FreeItem(&ss->opt.nextProtoNego, PR_FALSE); + rv = SECITEM_CopyItem(NULL, &ss->opt.nextProtoNego, &dataItem); + ssl_ReleaseSSL3HandshakeLock(ss); + + if (rv != SECSuccess) + return rv; + + return SSL_SetNextProtoCallback(fd, ssl_NextProtoNegoCallback, NULL); +} + +SECStatus +SSL_GetNextProto(PRFileDesc *fd, SSLNextProtoState *state, unsigned char *buf, + unsigned int *bufLen, unsigned int bufLenMax) +{ + sslSocket *ss = ssl_FindSocket(fd); + + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_GetNextProto", SSL_GETPID(), + fd)); + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + if (!state || !buf || !bufLen) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + *state = ss->ssl3.nextProtoState; + + if (ss->ssl3.nextProtoState != SSL_NEXT_PROTO_NO_SUPPORT && + ss->ssl3.nextProto.data) { + *bufLen = ss->ssl3.nextProto.len; + if (*bufLen > bufLenMax) { + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + *bufLen = 0; + return SECFailure; + } + PORT_Memcpy(buf, ss->ssl3.nextProto.data, ss->ssl3.nextProto.len); + } else { + *bufLen = 0; + } + + return SECSuccess; +} + PRFileDesc * SSL_ReconfigFD(PRFileDesc *model, PRFileDesc *fd) { diff --git a/mozilla/security/nss/lib/ssl/sslt.h b/mozilla/security/nss/lib/ssl/sslt.h index a947e4a5902..3a12d6e89c3 100644 --- a/mozilla/security/nss/lib/ssl/sslt.h +++ b/mozilla/security/nss/lib/ssl/sslt.h @@ -37,7 +37,7 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -/* $Id: sslt.h,v 1.16 2010-02-04 03:21:11 wtc%google.com Exp $ */ +/* $Id: sslt.h,v 1.17 2011-10-29 00:29:11 bsmith%mozilla.com Exp $ */ #ifndef __sslt_h_ #define __sslt_h_ @@ -203,9 +203,10 @@ typedef enum { ssl_ec_point_formats_xtn = 11, #endif ssl_session_ticket_xtn = 35, + ssl_next_proto_neg_xtn = 13172, ssl_renegotiation_info_xtn = 0xff01 /* experimental number */ } SSLExtensionType; -#define SSL_MAX_EXTENSIONS 5 +#define SSL_MAX_EXTENSIONS 6 #endif /* __sslt_h_ */