nss: CVE-2013-1740 Upstream-Status: Backport the patch comes from: http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2013-1740 https://bugzilla.mozilla.org/show_bug.cgi?id=919877 https://bugzilla.mozilla.org/show_bug.cgi?id=713933 changeset: 10946:f28426e944ae user: Wan-Teh Chang date: Tue Nov 26 16:44:39 2013 -0800 summary: Bug 713933: Handle the return value of both ssl3_HandleRecord calls changeset: 10945:774c7dec7565 user: Wan-Teh Chang date: Mon Nov 25 19:16:23 2013 -0800 summary: Bug 713933: Declare the |falseStart| local variable in the smallest changeset: 10848:141fae8fb2e8 user: Wan-Teh Chang date: Mon Sep 23 11:25:41 2013 -0700 summary: Bug 681839: Allow SSL_HandshakeNegotiatedExtension to be called before the handshake is finished, r=brian@briansmith.org changeset: 10898:1b9c43d28713 user: Brian Smith date: Thu Oct 31 15:40:42 2013 -0700 summary: Bug 713933: Make SSL False Start work with asynchronous certificate validation, r=wtc Signed-off-by: Li Wang --- nss/lib/ssl/ssl.def | 7 ++ nss/lib/ssl/ssl.h | 54 +++++++++++--- nss/lib/ssl/ssl3con.c | 188 +++++++++++++++++++++++++++++++++++------------ nss/lib/ssl/ssl3gthr.c | 63 ++++++++++++---- nss/lib/ssl/sslauth.c | 10 +-- nss/lib/ssl/sslimpl.h | 22 +++++- nss/lib/ssl/sslinfo.c | 10 +-- nss/lib/ssl/sslreveal.c | 9 +-- nss/lib/ssl/sslsecur.c | 139 ++++++++++++++++++++++++++++------- nss/lib/ssl/sslsock.c | 12 ++- 10 files changed, 386 insertions(+), 128 deletions(-) diff --git a/nss/lib/ssl/ssl.def b/nss/lib/ssl/ssl.def index fbf7fc5..e937bd4 100644 --- a/nss/lib/ssl/ssl.def +++ b/nss/lib/ssl/ssl.def @@ -163,3 +163,10 @@ SSL_SetStapledOCSPResponses; ;+ local: ;+*; ;+}; +;+NSS_3.15.3 { # NSS 3.15.3 release +;+ global: +SSL_RecommendedCanFalseStart; +SSL_SetCanFalseStartCallback; +;+ local: +;+*; +;+}; diff --git a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h index 6db0e34..ddeaaef 100644 --- a/nss/lib/ssl/ssl.h +++ b/nss/lib/ssl/ssl.h @@ -121,14 +121,17 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd); #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. + * + * In addition to enabling this option, the application must register a + * callback using the SSL_SetCanFalseStartCallback function. + */ /* 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 @@ -653,14 +656,45 @@ SSL_IMPORT SECStatus SSL_SetMaxServerCacheLocks(PRUint32 maxLocks); 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 gets called when a TLS handshake is complete. The +** handshake callback is called after verifying the peer's Finished message and +** before processing incoming application data. +** +** For the initial handshake: If the handshake false started (see +** SSL_ENABLE_FALSE_START), then application data may already have been sent +** before the handshake callback is called. If we did not false start then the +** callback will get called before any application data is sent. */ 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 enable TLS false start must 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. NSS's recommended criteria can be evaluated by +** calling SSL_RecommendedCanFalseStart. +** +** If no false start callback is registered then false start will never be +** done, even if the SSL_ENABLE_FALSE_START option is enabled. +**/ +typedef SECStatus (PR_CALLBACK *SSLCanFalseStartCallback)( + PRFileDesc *fd, void *arg, PRBool *canFalseStart); + +SSL_IMPORT SECStatus SSL_SetCanFalseStartCallback( + PRFileDesc *fd, SSLCanFalseStartCallback callback, void *arg); + +/* This function sets *canFalseStart according to the recommended criteria for +** false start. These criteria may change from release to release and may depend +** on which handshake features have been negotiated and/or properties of the +** certifciates/keys used on the connection. +*/ +SSL_IMPORT SECStatus SSL_RecommendedCanFalseStart(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 diff --git a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c index 61d24d9..f39ba09 100644 --- a/nss/lib/ssl/ssl3con.c +++ b/nss/lib/ssl/ssl3con.c @@ -2535,7 +2535,7 @@ ssl3_SendRecord( sslSocket * ss, SSL_TRC(3, ("%d: SSL3[%d] SendRecord type: %s nIn=%d", SSL_GETPID(), ss->fd, ssl3_DecodeContentType(type), nIn)); - PRINT_BUF(3, (ss, "Send record (plain text)", pIn, nIn)); + PRINT_BUF(50, (ss, "Send record (plain text)", pIn, nIn)); PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss) ); @@ -6674,36 +6674,73 @@ done: return rv; } +static SECStatus +ssl3_CheckFalseStart(sslSocket *ss) +{ + PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) ); + PORT_Assert( !ss->ssl3.hs.authCertificatePending ); + PORT_Assert( !ss->ssl3.hs.canFalseStart ); + + if (!ss->canFalseStartCallback) { + SSL_TRC(3, ("%d: SSL[%d]: no false start callback so no false start", + SSL_GETPID(), ss->fd)); + } else { + PRBool maybeFalseStart; + SECStatus rv; + + /* 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); + maybeFalseStart = ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10; + ssl_ReleaseSpecReadLock(ss); + + if (!maybeFalseStart) { + SSL_TRC(3, ("%d: SSL[%d]: no false start due to weak cipher", + SSL_GETPID(), ss->fd)); + } else { + rv = (ss->canFalseStartCallback)(ss->fd, + ss->canFalseStartCallbackData, + &ss->ssl3.hs.canFalseStart); + if (rv == SECSuccess) { + SSL_TRC(3, ("%d: SSL[%d]: false start callback returned %s", + SSL_GETPID(), ss->fd, + ss->ssl3.hs.canFalseStart ? "TRUE" : "FALSE")); + } else { + SSL_TRC(3, ("%d: SSL[%d]: false start callback failed (%s)", + SSL_GETPID(), ss->fd, + PR_ErrorToName(PR_GetError()))); + } + return rv; + } + } + + ss->ssl3.hs.canFalseStart = PR_FALSE; + return SECSuccess; +} + PRBool -ssl3_CanFalseStart(sslSocket *ss) { - PRBool rv; +ssl3_WaitingForStartOfServerSecondRound(sslSocket *ss) +{ + PRBool result = PR_FALSE; 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. - */ + switch (ss->ssl3.hs.ws) { + case wait_new_session_ticket: + result = PR_TRUE; + break; + case wait_change_cipher: + result = !ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn); + break; + case wait_finished: + break; + default: + PR_NOT_REACHED("ssl3_WaitingForStartOfServerSecondRound"); + } - 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); - ssl_ReleaseSpecReadLock(ss); - return rv; + return result; } static SECStatus ssl3_SendClientSecondRound(sslSocket *ss); @@ -6785,6 +6822,9 @@ ssl3_SendClientSecondRound(sslSocket *ss) } if (ss->ssl3.hs.authCertificatePending && (sendClientCert || ss->ssl3.sendEmptyCert || ss->firstHsDone)) { + SSL_TRC(3, ("%d: SSL3[%p]: deferring ssl3_SendClientSecondRound because" + " certificate authentication is still pending.", + SSL_GETPID(), ss->fd)); ss->ssl3.hs.restartTarget = ssl3_SendClientSecondRound; return SECWouldBlock; } @@ -6822,14 +6862,50 @@ ssl3_SendClientSecondRound(sslSocket *ss) goto loser; /* err code was set. */ } - /* XXX: If the server's certificate hasn't been authenticated by this - * point, then we may be leaking this NPN message to an attacker. + /* This must be done after we've set ss->ssl3.cwSpec in + * ssl3_SendChangeCipherSpecs because SSL_GetChannelInfo uses information + * from cwSpec. This must be done before we call ssl3_CheckFalseStart + * because the false start callback (if any) may need the information from + * the functions that depend on this being set. */ + ss->enoughFirstHsDone = PR_TRUE; + if (!ss->firstHsDone) { + /* XXX: If the server's certificate hasn't been authenticated by this + * point, then we may be leaking this NPN message to an attacker. + */ rv = ssl3_SendNextProto(ss); if (rv != SECSuccess) { goto loser; /* err code was set. */ } + + if (ss->opt.enableFalseStart) { + if (!ss->ssl3.hs.authCertificatePending) { + /* When we fix bug 589047, we will need to know whether we are + * false starting before we try to flush the client second + * round to the network. With that in mind, we purposefully + * call ssl3_CheckFalseStart before calling ssl3_SendFinished, + * which includes a call to ssl3_FlushHandshake, so that + * no application develops a reliance on such flushing being + * done before its false start callback is called. + */ + ssl_ReleaseXmitBufLock(ss); + rv = ssl3_CheckFalseStart(ss); + ssl_GetXmitBufLock(ss); + if (rv != SECSuccess) { + goto loser; + } + } else { + /* The certificate authentication and the server's Finished + * message are racing each other. If the certificate + * authentication wins, then we will try to false start in + * ssl3_AuthCertificateComplete. + */ + SSL_TRC(3, ("%d: SSL3[%p]: deferring false start check because" + " certificate authentication is still pending.", + SSL_GETPID(), ss->fd)); + } + } } rv = ssl3_SendFinished(ss, 0); @@ -6844,10 +6920,7 @@ ssl3_SendClientSecondRound(sslSocket *ss) 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); - } + PORT_Assert(ssl3_WaitingForStartOfServerSecondRound(ss)); return SECSuccess; @@ -9421,13 +9494,6 @@ ssl3_AuthCertificate(sslSocket *ss) 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) { @@ -9551,6 +9617,12 @@ ssl3_AuthCertificateComplete(sslSocket *ss, PRErrorCode error) } else if (ss->ssl3.hs.restartTarget != NULL) { sslRestartTarget target = ss->ssl3.hs.restartTarget; ss->ssl3.hs.restartTarget = NULL; + + if (target == ssl3_FinishHandshake) { + SSL_TRC(3,("%d: SSL3[%p]: certificate authentication lost the race" + " with peer's finished message", SSL_GETPID(), ss->fd)); + } + rv = target(ss); /* Even if we blocked here, we have accomplished enough to claim * success. Any remaining work will be taken care of by subsequent @@ -9560,7 +9632,29 @@ ssl3_AuthCertificateComplete(sslSocket *ss, PRErrorCode error) rv = SECSuccess; } } else { - rv = SECSuccess; + SSL_TRC(3, ("%d: SSL3[%p]: certificate authentication won the race with" + " peer's finished message", SSL_GETPID(), ss->fd)); + + PORT_Assert(!ss->firstHsDone); + PORT_Assert(!ss->sec.isServer); + PORT_Assert(!ss->ssl3.hs.isResuming); + PORT_Assert(ss->ssl3.hs.ws == wait_new_session_ticket || + ss->ssl3.hs.ws == wait_change_cipher || + ss->ssl3.hs.ws == wait_finished); + + /* ssl3_SendClientSecondRound deferred the false start check because + * certificate authentication was pending, so we do it now if we still + * haven't received any of the server's second round yet. + */ + if (ss->opt.enableFalseStart && + !ss->firstHsDone && + !ss->sec.isServer && + !ss->ssl3.hs.isResuming && + ssl3_WaitingForStartOfServerSecondRound(ss)) { + rv = ssl3_CheckFalseStart(ss); + } else { + rv = SECSuccess; + } } done: @@ -10023,9 +10117,6 @@ xmit_loser: return rv; } - ss->gs.writeOffset = 0; - ss->gs.readOffset = 0; - if (ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa) { effectiveExchKeyType = kt_rsa; } else { @@ -10090,6 +10181,9 @@ xmit_loser: return rv; } +/* The return type is SECStatus instead of void because this function needs + * to have type sslRestartTarget. + */ SECStatus ssl3_FinishHandshake(sslSocket * ss) { @@ -10099,19 +10193,16 @@ ssl3_FinishHandshake(sslSocket * ss) /* The first handshake is now completed. */ ss->handshake = NULL; - ss->firstHsDone = PR_TRUE; if (ss->ssl3.hs.cacheSID) { (*ss->sec.cache)(ss->sec.ci.sid); ss->ssl3.hs.cacheSID = PR_FALSE; } + ss->ssl3.hs.canFalseStart = PR_FALSE; /* False Start phase is complete */ 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)) { - (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData); - } + ssl_FinishHandshake(ss); return SECSuccess; } @@ -11045,7 +11136,6 @@ process_it: ssl_ReleaseSSL3HandshakeLock(ss); return rv; - } /* diff --git a/nss/lib/ssl/ssl3gthr.c b/nss/lib/ssl/ssl3gthr.c index 6d62515..03e369d 100644 --- a/nss/lib/ssl/ssl3gthr.c +++ b/nss/lib/ssl/ssl3gthr.c @@ -275,11 +275,17 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags) { SSL3Ciphertext cText; int rv; - PRBool canFalseStart = PR_FALSE; + PRBool keepGoing = PR_TRUE; SSL_TRC(30, ("ssl3_GatherCompleteHandshake")); + /* ssl3_HandleRecord may end up eventually calling ssl_FinishHandshake, + * which requires the 1stHandshakeLock, which must be acquired before the + * RecvBufLock. + */ + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) ); + do { PRBool handleRecordNow = PR_FALSE; @@ -368,20 +374,48 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags) if (rv < 0) { return ss->recvdCloseNotify ? 0 : rv; } + if (rv == (int) SECSuccess && ss->gs.buf.len > 0) { + /* We have application data to return to the application. This + * prioritizes returning application data to the application over + * completing any renegotiation handshake we may be doing. + */ + PORT_Assert(ss->firstHsDone); + PORT_Assert(cText.type == content_application_data); + break; + } - /* 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); - ssl_ReleaseSSL3HandshakeLock(ss); + PORT_Assert(keepGoing); + ssl_GetSSL3HandshakeLock(ss); + if (ss->ssl3.hs.ws == idle_handshake) { + /* We are done with the current handshake so stop trying to + * handshake. Note that it would be safe to test ss->firstHsDone + * instead of ss->ssl3.hs.ws. By testing ss->ssl3.hs.ws instead, + * we prioritize completing a renegotiation handshake over sending + * application data. + */ + PORT_Assert(ss->firstHsDone); + PORT_Assert(!ss->ssl3.hs.canFalseStart); + keepGoing = PR_FALSE; + } else if (ss->ssl3.hs.canFalseStart) { + /* Prioritize sending application data over trying to complete + * the handshake if we're false starting. + * + * If we were to do this check at the beginning of the loop instead + * of here, then this function would become be a no-op after + * receiving the ServerHelloDone in the false start case, and we + * would never complete the handshake. + */ + PORT_Assert(!ss->firstHsDone); + + if (ssl3_WaitingForStartOfServerSecondRound(ss)) { + keepGoing = PR_FALSE; + } else { + ss->ssl3.hs.canFalseStart = PR_FALSE; + } } - } while (ss->ssl3.hs.ws != idle_handshake && - !canFalseStart && - ss->gs.buf.len == 0); + ssl_ReleaseSSL3HandshakeLock(ss); + } while (keepGoing); + ss->gs.readOffset = 0; ss->gs.writeOffset = ss->gs.buf.len; @@ -404,7 +438,10 @@ ssl3_GatherAppDataRecord(sslSocket *ss, int flags) { int rv; + /* ssl3_GatherCompleteHandshake requires both of these locks. */ + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) ); + do { rv = ssl3_GatherCompleteHandshake(ss, flags); } while (rv > 0 && ss->gs.buf.len == 0); diff --git a/nss/lib/ssl/sslauth.c b/nss/lib/ssl/sslauth.c index d2f57bf..cb956d4 100644 --- a/nss/lib/ssl/sslauth.c +++ b/nss/lib/ssl/sslauth.c @@ -60,7 +60,6 @@ SSL_SecurityStatus(PRFileDesc *fd, int *op, char **cp, int *kp0, int *kp1, sslSocket *ss; const char *cipherName; PRBool isDes = PR_FALSE; - PRBool enoughFirstHsDone = PR_FALSE; ss = ssl_FindSocket(fd); if (!ss) { @@ -78,14 +77,7 @@ SSL_SecurityStatus(PRFileDesc *fd, int *op, char **cp, int *kp0, int *kp1, *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 { diff --git a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h index 90e9567..bf0d67f 100644 --- a/nss/lib/ssl/sslimpl.h +++ b/nss/lib/ssl/sslimpl.h @@ -842,6 +842,8 @@ const ssl3CipherSuiteDef *suite_def; /* Shared state between ssl3_HandleFinished and ssl3_FinishHandshake */ PRBool cacheSID; + PRBool canFalseStart; /* Can/did we False Start */ + /* clientSigAndHash contains the contents of the signature_algorithms * extension (if any) from the client. This is only valid for TLS 1.2 * or later. */ @@ -1116,6 +1118,10 @@ struct sslSocketStr { unsigned long clientAuthRequested; unsigned long delayDisabled; /* Nagle delay disabled */ unsigned long firstHsDone; /* first handshake is complete. */ + unsigned long enoughFirstHsDone; /* enough of the first handshake is + * done for callbacks to be able to + * retrieve channel security + * parameters from the SSL socket. */ unsigned long handshakeBegun; unsigned long lastWriteBlocked; unsigned long recvdCloseNotify; /* received SSL EOF. */ @@ -1156,6 +1162,8 @@ const unsigned char * preferredCipher; void *badCertArg; SSLHandshakeCallback handshakeCallback; void *handshakeCallbackData; + SSLCanFalseStartCallback canFalseStartCallback; + void *canFalseStartCallbackData; void *pkcs11PinArg; SSLNextProtoCallback nextProtoCallback; void *nextProtoArg; @@ -1358,7 +1366,19 @@ extern void ssl3_SetAlwaysBlock(sslSocket *ss); extern SECStatus ssl_EnableNagleDelay(sslSocket *ss, PRBool enabled); -extern PRBool ssl3_CanFalseStart(sslSocket *ss); +extern void ssl_FinishHandshake(sslSocket *ss); + +/* Returns PR_TRUE if we are still waiting for the server to respond to our + * client second round. Once we've received any part of the server's second + * round then we don't bother trying to false start since it is almost always + * the case that the NewSessionTicket, ChangeCipherSoec, and Finished messages + * were sent in the same packet and we want to process them all at the same + * time. If we were to try to false start in the middle of the server's second + * round, then we would increase the number of I/O operations + * (SSL_ForceHandshake/PR_Recv/PR_Send/etc.) needed to finish the handshake. + */ +extern PRBool ssl3_WaitingForStartOfServerSecondRound(sslSocket *ss); + extern SECStatus ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec, PRBool isServer, diff --git a/nss/lib/ssl/sslinfo.c b/nss/lib/ssl/sslinfo.c index 9f2597e..d0c23b7 100644 --- a/nss/lib/ssl/sslinfo.c +++ b/nss/lib/ssl/sslinfo.c @@ -26,7 +26,6 @@ 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); @@ -43,14 +42,7 @@ SSL_GetChannelInfo(PRFileDesc *fd, SSLChannelInfo *info, PRUintn len) 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; diff --git a/nss/lib/ssl/sslreveal.c b/nss/lib/ssl/sslreveal.c index dc14794..d972998 100644 --- a/nss/lib/ssl/sslreveal.c +++ b/nss/lib/ssl/sslreveal.c @@ -77,7 +77,6 @@ SSL_HandshakeNegotiatedExtension(PRFileDesc * socket, { /* some decisions derived from SSL_GetChannelInfo */ sslSocket * sslsocket = NULL; - PRBool enoughFirstHsDone = PR_FALSE; if (!pYes) { PORT_SetError(SEC_ERROR_INVALID_ARGS); @@ -93,14 +92,8 @@ SSL_HandshakeNegotiatedExtension(PRFileDesc * socket, *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) { 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 diff --git a/nss/lib/ssl/sslsecur.c b/nss/lib/ssl/sslsecur.c index 49bb42b..d0df442 100644 --- a/nss/lib/ssl/sslsecur.c +++ b/nss/lib/ssl/sslsecur.c @@ -97,23 +97,13 @@ ssl_Do1stHandshake(sslSocket *ss) ss->securityHandshake = 0; } if (ss->handshake == 0) { - ssl_GetRecvBufLock(ss); - ss->gs.recordLen = 0; - ssl_ReleaseRecvBufLock(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->handshakeCallback)(ss->fd, ss->handshakeCallbackData); + /* for v3 this is done in ssl3_FinishHandshake */ + if (!ss->firstHsDone && ss->version < SSL_LIBRARY_VERSION_3_0) { + ssl_GetRecvBufLock(ss); + ss->gs.recordLen = 0; + ssl_FinishHandshake(ss); + ssl_ReleaseRecvBufLock(ss); } - ss->firstHsDone = PR_TRUE; - ss->gs.writeOffset = 0; - ss->gs.readOffset = 0; break; } rv = (*ss->handshake)(ss); @@ -134,6 +124,24 @@ ssl_Do1stHandshake(sslSocket *ss) return rv; } +void +ssl_FinishHandshake(sslSocket *ss) +{ + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) ); + + SSL_TRC(3, ("%d: SSL[%d]: handshake is completed", SSL_GETPID(), ss->fd)); + + ss->firstHsDone = PR_TRUE; + ss->enoughFirstHsDone = PR_TRUE; + ss->gs.writeOffset = 0; + ss->gs.readOffset = 0; + + if (ss->handshakeCallback) { + (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData); + } +} + /* * Handshake function that blocks. Used to force a * retry on a connection on the next read/write. @@ -206,6 +214,7 @@ SSL_ResetHandshake(PRFileDesc *s, PRBool asServer) ssl_Get1stHandshakeLock(ss); ss->firstHsDone = PR_FALSE; + ss->enoughFirstHsDone = PR_FALSE; if ( asServer ) { ss->handshake = ssl2_BeginServerHandshake; ss->handshaking = sslHandshakingAsServer; @@ -221,6 +230,8 @@ SSL_ResetHandshake(PRFileDesc *s, PRBool asServer) 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. @@ -331,6 +342,71 @@ SSL_HandshakeCallback(PRFileDesc *fd, SSLHandshakeCallback cb, 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 *arg) +{ + 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 = arg; + + ssl_ReleaseSSL3HandshakeLock(ss); + ssl_Release1stHandshakeLock(ss); + + return SECSuccess; +} + +SECStatus +SSL_RecommendedCanFalseStart(PRFileDesc *fd, PRBool *canFalseStart) +{ + sslSocket *ss; + + *canFalseStart = PR_FALSE; + ss = ssl_FindSocket(fd); + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_RecommendedCanFalseStart", + SSL_GETPID(), fd)); + return SECFailure; + } + + if (!ss->ssl3.initialized) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + if (ss->version < SSL_LIBRARY_VERSION_3_0) { + PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2); + 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 @@ -524,6 +600,9 @@ DoRecv(sslSocket *ss, unsigned char *out, int len, int flags) int amount; int available; + /* ssl3_GatherAppDataRecord may call ssl_FinishHandshake, which needs the + * 1stHandshakeLock. */ + ssl_Get1stHandshakeLock(ss); ssl_GetRecvBufLock(ss); available = ss->gs.writeOffset - ss->gs.readOffset; @@ -590,6 +669,7 @@ DoRecv(sslSocket *ss, unsigned char *out, int len, int flags) done: ssl_ReleaseRecvBufLock(ss); + ssl_Release1stHandshakeLock(ss); return rv; } @@ -1156,7 +1236,7 @@ ssl_SecureRead(sslSocket *ss, unsigned char *buf, int len) int ssl_SecureSend(sslSocket *ss, const unsigned char *buf, int len, int flags) { - int rv = 0; + int rv = 0; SSL_TRC(2, ("%d: SSL[%d]: SecureSend: sending %d bytes", SSL_GETPID(), ss->fd, len)); @@ -1191,19 +1271,15 @@ ssl_SecureSend(sslSocket *ss, const unsigned char *buf, int len, int flags) ss->writerThread = PR_GetCurrentThread(); /* If any of these is non-zero, the initial handshake is not done. */ if (!ss->firstHsDone) { - PRBool canFalseStart = PR_FALSE; + PRBool falseStart = PR_FALSE; ssl_Get1stHandshakeLock(ss); - if (ss->version >= SSL_LIBRARY_VERSION_3_0) { + if (ss->opt.enableFalseStart && + 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; - } + falseStart = ss->ssl3.hs.canFalseStart; ssl_ReleaseSSL3HandshakeLock(ss); } - if (!canFalseStart && + if (!falseStart && (ss->handshake || ss->nextHandshake || ss->securityHandshake)) { rv = ssl_Do1stHandshake(ss); } @@ -1228,6 +1304,17 @@ ssl_SecureSend(sslSocket *ss, const unsigned char *buf, int len, int flags) goto done; } + if (!ss->firstHsDone) { + PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_3_0); +#ifdef DEBUG + ssl_GetSSL3HandshakeLock(ss); + PORT_Assert(ss->ssl3.hs.canFalseStart); + ssl_ReleaseSSL3HandshakeLock(ss); +#endif + SSL_TRC(3, ("%d: SSL[%d]: SecureSend: sending data due to false start", + SSL_GETPID(), ss->fd)); + } + /* Send out the data using one of these functions: * ssl2_SendClear, ssl2_SendStream, ssl2_SendBlock, * ssl3_SendApplicationData diff --git a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c index cd4a7a7..73e069b 100644 --- a/nss/lib/ssl/sslsock.c +++ b/nss/lib/ssl/sslsock.c @@ -349,6 +349,8 @@ ssl_DupSocket(sslSocket *os) ss->badCertArg = os->badCertArg; ss->handshakeCallback = os->handshakeCallback; ss->handshakeCallbackData = os->handshakeCallbackData; + ss->canFalseStartCallback = os->canFalseStartCallback; + ss->canFalseStartCallbackData = os->canFalseStartCallbackData; ss->pkcs11PinArg = os->pkcs11PinArg; /* Create security data */ @@ -2341,10 +2343,14 @@ ssl_Poll(PRFileDesc *fd, PRInt16 how_flags, PRInt16 *p_out_flags) } 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. */ - new_flags |= PR_POLL_READ; /* do select on read. */ + 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)) { -- 1.7.9.5