# HG changeset patch # User Patrick McManus # Date 1372656196 25200 # Node ID f6bc026a0c368178b4d327bf05de785305161d72 # Parent 89a5e4356ad1f7bc9d9d24f6409c6d963dde3ca4 Bug 713933: Make false start work with asynchronous certificate verification, r=bsmith diff --git a/security/nss/lib/ssl/ssl.def b/security/nss/lib/ssl/ssl.def --- a/security/nss/lib/ssl/ssl.def +++ b/security/nss/lib/ssl/ssl.def @@ -158,8 +158,15 @@ SSL_SetSRTPCiphers; ;+}; ;+NSS_3.15 { # NSS 3.15 release ;+ global: SSL_PeerStapledOCSPResponses; SSL_SetStapledOCSPResponses; ;+ local: ;+*; ;+}; +;+NSS_3.15.2 { # NSS 3.15.2 release +;+ global: +SSL_SetCanFalseStartCallback; +SSL_DefaultCanFalseStart; +;+ local: +;+*; +;+}; diff --git a/security/nss/lib/ssl/ssl.h b/security/nss/lib/ssl/ssl.h --- a/security/nss/lib/ssl/ssl.h +++ b/security/nss/lib/ssl/ssl.h @@ -116,24 +116,32 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRF #define SSL_REQUIRE_SAFE_NEGOTIATION 21 /* Peer must send Signaling */ /* Cipher Suite Value (SCSV) or */ /* Renegotiation Info (RI) */ /* extension in ALL handshakes. */ /* default: off */ #define SSL_ENABLE_FALSE_START 22 /* Enable SSL false start (off by */ /* default, applies only to */ /* clients). False start is a */ -/* mode where an SSL client will start sending application data before */ -/* verifying the server's Finished message. This means that we could end up */ -/* sending data to an imposter. However, the data will be encrypted and */ -/* only the true server can derive the session key. Thus, so long as the */ -/* cipher isn't broken this is safe. Because of this, False Start will only */ -/* occur on RSA or DH ciphersuites where the cipher's key length is >= 80 */ -/* bits. The advantage of False Start is that it saves a round trip for */ -/* client-speaks-first protocols when performing a full handshake. */ +/* mode where an SSL client will start sending application data before + * verifying the server's Finished message. This means that we could end up + * sending data to an imposter. However, the data will be encrypted and + * only the true server can derive the session key. Thus, so long as the + * cipher isn't broken this is safe. The advantage of false start is that + * it saves a round trip for client-speaks-first protocols when performing a + * full handshake. + * + * See SSL_DefaultCanFalseStart for the default criteria that NSS uses to + * determine whether to false start or not. See SSL_SetCanFalseStartCallback + * for how to change that criteria. In addition to those criteria, false start + * will only be done when the server selects a cipher suite with an effective + * key length of 80 bits or more (including RC4-128). Also, see + * SSL_HandshakeCallback for a description on how false start affects when the + * handshake callback gets called. + */ /* For SSL 3.0 and TLS 1.0, by default we prevent chosen plaintext attacks * on SSL CBC mode cipher suites (see RFC 4346 Section F.3) by splitting * non-empty application_data records into two records; the first record has * only the first byte of plaintext, and the second has the rest. * * This only prevents the attack in the sending direction; the connection may * still be vulnerable to such attacks if the peer does not implement a similar @@ -648,24 +656,69 @@ SSL_IMPORT SECStatus SSL_SetMaxServerCac /* called in child to inherit SID Cache variables. * If envString is NULL, this function will use the value of the environment * variable "SSL_INHERITANCE", otherwise the string value passed in will be * used. */ SSL_IMPORT SECStatus SSL_InheritMPServerSIDCache(const char * envString); /* -** Set the callback on a particular socket that gets called when we finish -** performing a handshake. +** Set the callback that normally gets called when the TLS handshake +** is complete. If false start is not enabled, then the handshake callback is +** called after verifying the peer's Finished message and before sending +** outgoing application data and before processing incoming application data. +** +** If false start is enabled and there is a custom CanFalseStartCallback +** callback set, then the handshake callback gets called after the peer's +** Finished message has been verified, which may be after application data is +** sent. +** +** If false start is enabled and there is not a custom CanFalseStartCallback +** callback established with SSL_SetCanFalseStartCallback then the handshake +** callback gets called before any application data is sent, which may be +** before the peer's Finished message has been verified. */ typedef void (PR_CALLBACK *SSLHandshakeCallback)(PRFileDesc *fd, void *client_data); SSL_IMPORT SECStatus SSL_HandshakeCallback(PRFileDesc *fd, SSLHandshakeCallback cb, void *client_data); +/* Applications that wish to customize TLS false start should set this callback +** function. NSS will invoke the functon to determine if a particular +** connection should use false start or not. SECSuccess indicates that the +** callback completed successfully, and if so *canFalseStart indicates if false +** start can be used. If the callback does not return SECSuccess then the +** handshake will be canceled. +** +** Applications that do not set the callback will use an internal set of +** criteria to determine if the connection should false start. If +** the callback is set false start will never be used without invoking the +** callback function, but some connections (e.g. resumed connections) will +** never use false start and therefore will not invoke the callback. +** +** NSS's internal criteria for this connection can be evaluated by calling +** SSL_DefaultCanFalseStart() from the custom callback. +** +** See the description of SSL_HandshakeCallback for important information on +** how registering a custom false start callback affects when the handshake +** callback gets called. +**/ +typedef SECStatus (PR_CALLBACK *SSLCanFalseStartCallback)( + PRFileDesc *fd, void *arg, PRBool *canFalseStart); + +SSL_IMPORT SECStatus SSL_SetCanFalseStartCallback( + PRFileDesc *fd, SSLCanFalseStartCallback callback, void *arg); + +/* A utility function that can be called from a custom CanFalseStartCallback +** function to determine what NSS would have done for this connection if the +** custom callback was not implemented. +**/ +SSL_IMPORT SECStatus SSL_DefaultCanFalseStart(PRFileDesc *fd, + PRBool *canFalseStart); + /* ** For the server, request a new handshake. For the client, begin a new ** handshake. If flushCache is non-zero, the SSL3 cache entry will be ** flushed first, ensuring that a full SSL handshake will be done. ** If flushCache is zero, and an SSL connection is established, it will ** do the much faster session restart handshake. This will change the ** session keys without doing another private key operation. */ diff --git a/security/nss/lib/ssl/ssl3con.c b/security/nss/lib/ssl/ssl3con.c --- a/security/nss/lib/ssl/ssl3con.c +++ b/security/nss/lib/ssl/ssl3con.c @@ -6664,45 +6664,61 @@ loser: PORT_SetError(errCode); rv = SECFailure; done: if (arena != NULL) PORT_FreeArena(arena, PR_FALSE); return rv; } -PRBool -ssl3_CanFalseStart(sslSocket *ss) { - PRBool rv; +static SECStatus +ssl3_CheckFalseStart(sslSocket *ss) +{ + SECStatus rv; + PRBool maybeFalseStart = PR_TRUE; PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) ); - - /* XXX: does not take into account whether we are waiting for - * SSL_AuthCertificateComplete or SSL_RestartHandshakeAfterCertReq. If/when - * that is done, this function could return different results each time it - * would be called. - */ + PORT_Assert( !ss->ssl3.hs.authCertificatePending ); + + /* An attacker can control the selected ciphersuite so we only wish to + * do False Start in the case that the selected ciphersuite is + * sufficiently strong that the attack can gain no advantage. + * Therefore we always require an 80-bit cipher. */ ssl_GetSpecReadLock(ss); - rv = ss->opt.enableFalseStart && - !ss->sec.isServer && - !ss->ssl3.hs.isResuming && - ss->ssl3.cwSpec && - - /* An attacker can control the selected ciphersuite so we only wish to - * do False Start in the case that the selected ciphersuite is - * sufficiently strong that the attack can gain no advantage. - * Therefore we require an 80-bit cipher and a forward-secret key - * exchange. */ - ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10 && - (ss->ssl3.hs.kea_def->kea == kea_dhe_dss || - ss->ssl3.hs.kea_def->kea == kea_dhe_rsa || - ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa || - ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa); + if (ss->ssl3.cwSpec->cipher_def->secret_key_size < 10) { + ss->ssl3.hs.canFalseStart = PR_FALSE; + maybeFalseStart = PR_FALSE; + } ssl_ReleaseSpecReadLock(ss); + if (!maybeFalseStart) { + return SECSuccess; + } + + if (!ss->canFalseStartCallback) { + rv = SSL_DefaultCanFalseStart(ss->fd, &ss->ssl3.hs.canFalseStart); + + if (rv == SECSuccess && + ss->ssl3.hs.canFalseStart && ss->handshakeCallback) { + /* Call the handshake callback here for backwards compatibility + * with applications that were using false start before + * canFalseStartCallback was added. + */ + (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData); + } + } else { + rv = (ss->canFalseStartCallback)(ss->fd, + ss->canFalseStartCallbackData, + &ss->ssl3.hs.canFalseStart); + } + + if (rv != SECSuccess) { + ss->ssl3.hs.canFalseStart = PR_FALSE; + } + return rv; } static SECStatus ssl3_SendClientSecondRound(sslSocket *ss); /* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete * ssl3 Server Hello Done message. * Caller must hold Handshake and RecvBuf locks. @@ -6722,16 +6738,17 @@ ssl3_HandleServerHelloDone(sslSocket *ss ws != wait_server_cert && ws != wait_server_key && ws != wait_cert_request) { SSL3_SendAlert(ss, alert_fatal, unexpected_message); PORT_SetError(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE); return SECFailure; } + ss->enoughFirstHsDone = PR_TRUE; rv = ssl3_SendClientSecondRound(ss); return rv; } /* Called from ssl3_HandleServerHelloDone and ssl3_AuthCertificateComplete. * * Caller must hold Handshake and RecvBuf locks. @@ -6820,35 +6837,47 @@ ssl3_SendClientSecondRound(sslSocket *ss /* XXX: If the server's certificate hasn't been authenticated by this * point, then we may be leaking this NPN message to an attacker. */ if (!ss->firstHsDone) { rv = ssl3_SendNextProto(ss); if (rv != SECSuccess) { goto loser; /* err code was set. */ } + + if (ss->opt.enableFalseStart) { + if (!ss->ssl3.hs.authCertificatePending) { + rv = ssl3_CheckFalseStart(ss); + if (rv != SECSuccess) { + goto loser; + } + } else { + /* The certificate authentication and the server's Finished + * message are going to race each other. If the certificate + * authentication wins, then we will try to false start. If the + * server's Finished message wins, then ssl3_HandleFinished will + * reset restartTarget to ssl3_FinishHandshake. + */ + ss->ssl3.hs.restartTarget = ssl3_CheckFalseStart; + } + } } rv = ssl3_SendFinished(ss, 0); if (rv != SECSuccess) { goto loser; /* err code was set. */ } ssl_ReleaseXmitBufLock(ss); /*******************************/ if (ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn)) ss->ssl3.hs.ws = wait_new_session_ticket; else ss->ssl3.hs.ws = wait_change_cipher; - /* Do the handshake callback for sslv3 here, if we can false start. */ - if (ss->handshakeCallback != NULL && ssl3_CanFalseStart(ss)) { - (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData); - } - return SECSuccess; loser: ssl_ReleaseXmitBufLock(ss); return rv; } /* @@ -9411,23 +9440,16 @@ ssl3_AuthCertificate(sslSocket *ss) if (ss->sec.isServer) { errCode = SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS; rv = SECFailure; goto loser; } ss->ssl3.hs.authCertificatePending = PR_TRUE; rv = SECSuccess; - - /* XXX: Async cert validation and False Start don't work together - * safely yet; if we leave False Start enabled, we may end up false - * starting (sending application data) before we - * SSL_AuthCertificateComplete has been called. - */ - ss->opt.enableFalseStart = PR_FALSE; } if (rv != SECSuccess) { ssl3_SendAlertForCertError(ss, errCode); goto loser; } } @@ -10065,16 +10087,21 @@ xmit_loser: ssl_ReleaseSpecReadLock(ss); /*************************************/ /* If the wrap failed, we don't cache the sid. * The connection continues normally however. */ ss->ssl3.hs.cacheSID = rv == SECSuccess; } + /* Cancel false start check since we already completed the handshake */ + if (ss->ssl3.hs.restartTarget == ssl3_CheckFalseStart) { + ss->ssl3.hs.restartTarget = NULL; + } + if (ss->ssl3.hs.authCertificatePending) { if (ss->ssl3.hs.restartTarget) { PR_NOT_REACHED("ssl3_HandleFinished: unexpected restartTarget"); PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure; } ss->ssl3.hs.restartTarget = ssl3_FinishHandshake; @@ -10083,33 +10110,41 @@ xmit_loser: rv = ssl3_FinishHandshake(ss); return rv; } SECStatus ssl3_FinishHandshake(sslSocket * ss) { + PRBool falseStarted; + PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) ); PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) ); PORT_Assert( ss->ssl3.hs.restartTarget == NULL ); /* The first handshake is now completed. */ ss->handshake = NULL; ss->firstHsDone = PR_TRUE; + ss->enoughFirstHsDone = PR_TRUE; if (ss->ssl3.hs.cacheSID) { (*ss->sec.cache)(ss->sec.ci.sid); ss->ssl3.hs.cacheSID = PR_FALSE; } ss->ssl3.hs.ws = idle_handshake; - - /* Do the handshake callback for sslv3 here, if we cannot false start. */ - if (ss->handshakeCallback != NULL && !ssl3_CanFalseStart(ss)) { + falseStarted = ss->ssl3.hs.canFalseStart; + ss->ssl3.hs.canFalseStart = PR_FALSE; /* False Start phase is complete */ + + /* Call the handshake callback for sslv3 here, unless we called it already + * for the case where false start was done without a canFalseStartCallback. + */ + if (ss->handshakeCallback != NULL && + !(falseStarted && !ss->canFalseStartCallback)) { (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData); } return SECSuccess; } /* Called from ssl3_HandleHandshake() when it has gathered a complete ssl3 * hanshake message. diff --git a/security/nss/lib/ssl/ssl3gthr.c b/security/nss/lib/ssl/ssl3gthr.c --- a/security/nss/lib/ssl/ssl3gthr.c +++ b/security/nss/lib/ssl/ssl3gthr.c @@ -369,19 +369,17 @@ ssl3_GatherCompleteHandshake(sslSocket * return ss->recvdCloseNotify ? 0 : rv; } /* If we kicked off a false start in ssl3_HandleServerHelloDone, break * out of this loop early without finishing the handshake. */ if (ss->opt.enableFalseStart) { ssl_GetSSL3HandshakeLock(ss); - canFalseStart = (ss->ssl3.hs.ws == wait_change_cipher || - ss->ssl3.hs.ws == wait_new_session_ticket) && - ssl3_CanFalseStart(ss); + canFalseStart = ss->ssl3.hs.canFalseStart; ssl_ReleaseSSL3HandshakeLock(ss); } } while (ss->ssl3.hs.ws != idle_handshake && !canFalseStart && ss->gs.buf.len == 0); ss->gs.readOffset = 0; ss->gs.writeOffset = ss->gs.buf.len; diff --git a/security/nss/lib/ssl/sslauth.c b/security/nss/lib/ssl/sslauth.c --- a/security/nss/lib/ssl/sslauth.c +++ b/security/nss/lib/ssl/sslauth.c @@ -55,17 +55,16 @@ SSL_LocalCertificate(PRFileDesc *fd) /* NEED LOCKS IN HERE. */ SECStatus SSL_SecurityStatus(PRFileDesc *fd, int *op, char **cp, int *kp0, int *kp1, char **ip, char **sp) { sslSocket *ss; const char *cipherName; PRBool isDes = PR_FALSE; - PRBool enoughFirstHsDone = PR_FALSE; ss = ssl_FindSocket(fd); if (!ss) { SSL_DBG(("%d: SSL[%d]: bad socket in SecurityStatus", SSL_GETPID(), fd)); return SECFailure; } @@ -73,24 +72,17 @@ SSL_SecurityStatus(PRFileDesc *fd, int * if (kp0) *kp0 = 0; if (kp1) *kp1 = 0; if (ip) *ip = 0; if (sp) *sp = 0; if (op) { *op = SSL_SECURITY_STATUS_OFF; } - if (ss->firstHsDone) { - enoughFirstHsDone = PR_TRUE; - } else if (ss->version >= SSL_LIBRARY_VERSION_3_0 && - ssl3_CanFalseStart(ss)) { - enoughFirstHsDone = PR_TRUE; - } - - if (ss->opt.useSecurity && enoughFirstHsDone) { + if (ss->opt.useSecurity && ss->enoughFirstHsDone) { if (ss->version < SSL_LIBRARY_VERSION_3_0) { cipherName = ssl_cipherName[ss->sec.cipherType]; } else { cipherName = ssl3_cipherName[ss->sec.cipherType]; } PORT_Assert(cipherName); if (cipherName) { if (PORT_Strstr(cipherName, "DES")) isDes = PR_TRUE; diff --git a/security/nss/lib/ssl/sslimpl.h b/security/nss/lib/ssl/sslimpl.h --- a/security/nss/lib/ssl/sslimpl.h +++ b/security/nss/lib/ssl/sslimpl.h @@ -863,16 +863,18 @@ const ssl3CipherSuiteDef *suite_def; * in progress. */ unsigned char cookie[32]; /* The cookie */ unsigned char cookieLen; /* The length of the cookie */ PRIntervalTime rtTimerStarted; /* When the timer was started */ DTLSTimerCb rtTimerCb; /* The function to call on expiry */ PRUint32 rtTimeoutMs; /* The length of the current timeout * used for backoff (in ms) */ PRUint32 rtRetries; /* The retry counter */ + PRBool canFalseStart; /* Can/did we False Start */ + } SSL3HandshakeState; /* ** This is the "ssl3" struct, as in "ss->ssl3". ** note: ** usually, crSpec == cwSpec and prSpec == pwSpec. @@ -1111,16 +1113,20 @@ struct sslSocketStr { sslOptions opt; /* Enabled version range */ SSLVersionRange vrange; /* State flags */ unsigned long clientAuthRequested; unsigned long delayDisabled; /* Nagle delay disabled */ unsigned long firstHsDone; /* first handshake is complete. */ + unsigned long enoughFirstHsDone; /* enough of the handshake is done + * for callbacks to be able to + * retrieve channel security + * parameters from callback functions. */ unsigned long handshakeBegun; unsigned long lastWriteBlocked; unsigned long recvdCloseNotify; /* received SSL EOF. */ unsigned long TCPconnected; unsigned long appDataBuffered; unsigned long peerRequestedProtection; /* from old renegotiation */ /* version of the protocol to use */ @@ -1151,16 +1157,18 @@ const unsigned char * preferredCipher; SSLGetClientAuthData getClientAuthData; void *getClientAuthDataArg; SSLSNISocketConfig sniSocketConfig; void *sniSocketConfigArg; SSLBadCertHandler handleBadCert; void *badCertArg; SSLHandshakeCallback handshakeCallback; void *handshakeCallbackData; + SSLCanFalseStartCallback canFalseStartCallback; + void *canFalseStartCallbackData; void *pkcs11PinArg; SSLNextProtoCallback nextProtoCallback; void *nextProtoArg; PRIntervalTime rTimeout; /* timeout for NSPR I/O */ PRIntervalTime wTimeout; /* timeout for NSPR I/O */ PRIntervalTime cTimeout; /* timeout for NSPR I/O */ @@ -1353,17 +1361,16 @@ extern int ssl3_SendApplicationDat extern PRBool ssl_FdIsBlocking(PRFileDesc *fd); extern PRBool ssl_SocketIsBlocking(sslSocket *ss); extern void ssl3_SetAlwaysBlock(sslSocket *ss); extern SECStatus ssl_EnableNagleDelay(sslSocket *ss, PRBool enabled); -extern PRBool ssl3_CanFalseStart(sslSocket *ss); extern SECStatus ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec, PRBool isServer, PRBool isDTLS, PRBool capRecordVersion, SSL3ContentType type, const SSL3Opaque * pIn, PRUint32 contentLen, diff --git a/security/nss/lib/ssl/sslinfo.c b/security/nss/lib/ssl/sslinfo.c --- a/security/nss/lib/ssl/sslinfo.c +++ b/security/nss/lib/ssl/sslinfo.c @@ -21,41 +21,33 @@ ssl_GetCompressionMethodName(SSLCompress } SECStatus SSL_GetChannelInfo(PRFileDesc *fd, SSLChannelInfo *info, PRUintn len) { sslSocket * ss; SSLChannelInfo inf; sslSessionID * sid; - PRBool enoughFirstHsDone = PR_FALSE; if (!info || len < sizeof inf.length) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } ss = ssl_FindSocket(fd); if (!ss) { SSL_DBG(("%d: SSL[%d]: bad socket in SSL_GetChannelInfo", SSL_GETPID(), fd)); return SECFailure; } memset(&inf, 0, sizeof inf); inf.length = PR_MIN(sizeof inf, len); - if (ss->firstHsDone) { - enoughFirstHsDone = PR_TRUE; - } else if (ss->version >= SSL_LIBRARY_VERSION_3_0 && - ssl3_CanFalseStart(ss)) { - enoughFirstHsDone = PR_TRUE; - } - - if (ss->opt.useSecurity && enoughFirstHsDone) { + if (ss->opt.useSecurity && ss->enoughFirstHsDone) { sid = ss->sec.ci.sid; inf.protocolVersion = ss->version; inf.authKeyBits = ss->sec.authKeyBits; inf.keaKeyBits = ss->sec.keaKeyBits; if (ss->version < SSL_LIBRARY_VERSION_3_0) { /* SSL2 */ inf.cipherSuite = ss->sec.cipherType | 0xff00; inf.compressionMethod = ssl_compression_null; inf.compressionMethodName = "N/A"; diff --git a/security/nss/lib/ssl/sslreveal.c b/security/nss/lib/ssl/sslreveal.c --- a/security/nss/lib/ssl/sslreveal.c +++ b/security/nss/lib/ssl/sslreveal.c @@ -72,40 +72,33 @@ SSL_RevealURL(PRFileDesc * fd) SECStatus SSL_HandshakeNegotiatedExtension(PRFileDesc * socket, SSLExtensionType extId, PRBool *pYes) { /* some decisions derived from SSL_GetChannelInfo */ sslSocket * sslsocket = NULL; - PRBool enoughFirstHsDone = PR_FALSE; if (!pYes) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } sslsocket = ssl_FindSocket(socket); if (!sslsocket) { SSL_DBG(("%d: SSL[%d]: bad socket in HandshakeNegotiatedExtension", SSL_GETPID(), socket)); return SECFailure; } *pYes = PR_FALSE; - if (sslsocket->firstHsDone) { - enoughFirstHsDone = PR_TRUE; - } else if (sslsocket->ssl3.initialized && ssl3_CanFalseStart(sslsocket)) { - enoughFirstHsDone = PR_TRUE; - } - /* according to public API SSL_GetChannelInfo, this doesn't need a lock */ - if (sslsocket->opt.useSecurity && enoughFirstHsDone) { + if (sslsocket->opt.useSecurity && sslsocket->enoughFirstHsDone) { if (sslsocket->ssl3.initialized) { /* SSL3 and TLS */ /* now we know this socket went through ssl3_InitState() and * ss->xtnData got initialized, which is the only member accessed by * ssl3_ExtensionNegotiated(); * Member xtnData appears to get accessed in functions that handle * the handshake (hello messages and extension sending), * therefore the handshake lock should be sufficient. */ diff --git a/security/nss/lib/ssl/sslsecur.c b/security/nss/lib/ssl/sslsecur.c --- a/security/nss/lib/ssl/sslsecur.c +++ b/security/nss/lib/ssl/sslsecur.c @@ -103,20 +103,22 @@ ssl_Do1stHandshake(sslSocket *ss) SSL_TRC(3, ("%d: SSL[%d]: handshake is completed", SSL_GETPID(), ss->fd)); /* call handshake callback for ssl v2 */ /* for v3 this is done in ssl3_HandleFinished() */ if ((ss->handshakeCallback != NULL) && /* has callback */ (!ss->firstHsDone) && /* only first time */ (ss->version < SSL_LIBRARY_VERSION_3_0)) { /* not ssl3 */ - ss->firstHsDone = PR_TRUE; + ss->firstHsDone = PR_TRUE; + ss->enoughFirstHsDone = PR_TRUE; (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData); } - ss->firstHsDone = PR_TRUE; + ss->firstHsDone = PR_TRUE; + ss->enoughFirstHsDone = PR_TRUE; ss->gs.writeOffset = 0; ss->gs.readOffset = 0; break; } rv = (*ss->handshake)(ss); ++loopCount; /* This code must continue to loop on SECWouldBlock, * or any positive value. See XXX_1 comments. @@ -201,31 +203,34 @@ SSL_ResetHandshake(PRFileDesc *s, PRBool SSL_LOCK_READER(ss); SSL_LOCK_WRITER(ss); /* Reset handshake state */ ssl_Get1stHandshakeLock(ss); ss->firstHsDone = PR_FALSE; + ss->enoughFirstHsDone = PR_FALSE; if ( asServer ) { ss->handshake = ssl2_BeginServerHandshake; ss->handshaking = sslHandshakingAsServer; } else { ss->handshake = ssl2_BeginClientHandshake; ss->handshaking = sslHandshakingAsClient; } ss->nextHandshake = 0; ss->securityHandshake = 0; ssl_GetRecvBufLock(ss); status = ssl_InitGather(&ss->gs); ssl_ReleaseRecvBufLock(ss); ssl_GetSSL3HandshakeLock(ss); + ss->ssl3.hs.canFalseStart = PR_FALSE; + ss->ssl3.hs.restartTarget = NULL; /* ** Blow away old security state and get a fresh setup. */ ssl_GetXmitBufLock(ss); ssl_ResetSecurityInfo(&ss->sec, PR_TRUE); status = ssl_CreateSecurityInfo(ss); ssl_ReleaseXmitBufLock(ss); @@ -326,16 +331,84 @@ SSL_HandshakeCallback(PRFileDesc *fd, SS ss->handshakeCallbackData = client_data; ssl_ReleaseSSL3HandshakeLock(ss); ssl_Release1stHandshakeLock(ss); return SECSuccess; } +/* Register an application callback to be called when false start may happen. +** Acquires and releases HandshakeLock. +*/ +SECStatus +SSL_SetCanFalseStartCallback(PRFileDesc *fd, SSLCanFalseStartCallback cb, + void *client_data) +{ + sslSocket *ss; + + ss = ssl_FindSocket(fd); + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetCanFalseStartCallback", + SSL_GETPID(), fd)); + return SECFailure; + } + + if (!ss->opt.useSecurity) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + ssl_Get1stHandshakeLock(ss); + ssl_GetSSL3HandshakeLock(ss); + + ss->canFalseStartCallback = cb; + ss->canFalseStartCallbackData = client_data; + + ssl_ReleaseSSL3HandshakeLock(ss); + ssl_Release1stHandshakeLock(ss); + + return SECSuccess; +} + +/* A utility function that can be called from a custom CanFalseStartCallback +** function to determine what NSS would have done for this connection if the +** custom callback was not implemented. +*/ +SECStatus +SSL_DefaultCanFalseStart(PRFileDesc *fd, PRBool *canFalseStart) +{ + sslSocket *ss; + *canFalseStart = PR_FALSE; + ss = ssl_FindSocket(fd); + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_DefaultCanFalseStart", + SSL_GETPID(), fd)); + return SECFailure; + } + + if (!ss->ssl3.initialized) { + PORT_SetError(SEC_ERROR_NOT_INITIALIZED); + return SECFailure; + } + + if (ss->version <= SSL_LIBRARY_VERSION_3_0) { + PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_VERSION); + return SECFailure; + } + + /* Require a forward-secret key exchange. */ + *canFalseStart = ss->ssl3.hs.kea_def->kea == kea_dhe_dss || + ss->ssl3.hs.kea_def->kea == kea_dhe_rsa || + ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa || + ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa; + + return SECSuccess; +} + /* Try to make progress on an SSL handshake by attempting to read the ** next handshake from the peer, and sending any responses. ** For non-blocking sockets, returns PR_ERROR_WOULD_BLOCK if it cannot ** read the next handshake from the underlying socket. ** For SSLv2, returns when handshake is complete or fatal error occurs. ** For SSLv3, returns when handshake is complete, or application data has ** arrived that must be taken by application before handshake can continue, ** or a fatal error occurs. @@ -1190,22 +1263,17 @@ ssl_SecureSend(sslSocket *ss, const unsi if (len > 0) ss->writerThread = PR_GetCurrentThread(); /* If any of these is non-zero, the initial handshake is not done. */ if (!ss->firstHsDone) { PRBool canFalseStart = PR_FALSE; ssl_Get1stHandshakeLock(ss); if (ss->version >= SSL_LIBRARY_VERSION_3_0) { ssl_GetSSL3HandshakeLock(ss); - if ((ss->ssl3.hs.ws == wait_change_cipher || - ss->ssl3.hs.ws == wait_finished || - ss->ssl3.hs.ws == wait_new_session_ticket) && - ssl3_CanFalseStart(ss)) { - canFalseStart = PR_TRUE; - } + canFalseStart = ss->ssl3.hs.canFalseStart; ssl_ReleaseSSL3HandshakeLock(ss); } if (!canFalseStart && (ss->handshake || ss->nextHandshake || ss->securityHandshake)) { rv = ssl_Do1stHandshake(ss); } ssl_Release1stHandshakeLock(ss); } diff --git a/security/nss/lib/ssl/sslsock.c b/security/nss/lib/ssl/sslsock.c --- a/security/nss/lib/ssl/sslsock.c +++ b/security/nss/lib/ssl/sslsock.c @@ -2336,19 +2336,23 @@ ssl_Poll(PRFileDesc *fd, PRInt16 how_fla ** The code should select on write, not read. */ new_flags ^= PR_POLL_READ; /* don't select on read. */ new_flags |= PR_POLL_WRITE; /* do select on write. */ } } else if (new_flags & PR_POLL_WRITE) { /* The caller is trying to write, but the handshake is ** blocked waiting for data to read, and the first - ** handshake has been sent. so do NOT to poll on write. + ** handshake has been sent. So do NOT to poll on write + ** unless we did false start. */ - new_flags ^= PR_POLL_WRITE; /* don't select on write. */ + if (!(ss->version >= SSL_LIBRARY_VERSION_3_0 && + ss->ssl3.hs.canFalseStart)) { + new_flags ^= PR_POLL_WRITE; /* don't select on write. */ + } new_flags |= PR_POLL_READ; /* do select on read. */ } } } else if ((new_flags & PR_POLL_READ) && (SSL_DataPending(fd) > 0)) { *p_out_flags = PR_POLL_READ; /* it's ready already. */ return new_flags; } else if ((ss->lastWriteBlocked) && (how_flags & PR_POLL_READ) && (ss->pendingBuf.len != 0)) { /* write data waiting to be sent */