diff options
author | wtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-19 04:01:09 +0000 |
---|---|---|
committer | wtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-19 04:01:09 +0000 |
commit | 2c1d72b03a23f33c1472fb999151ac8025a841c5 (patch) | |
tree | b0ae2490c210c4326a52e88130f4b67b13db3e27 | |
parent | d5873769389168954d097c9fcec34ddcb7480e83 (diff) | |
download | chromium_src-2c1d72b03a23f33c1472fb999151ac8025a841c5.zip chromium_src-2c1d72b03a23f33c1472fb999151ac8025a841c5.tar.gz chromium_src-2c1d72b03a23f33c1472fb999151ac8025a841c5.tar.bz2 |
Update to the latest False Start code from NSS upstream.
NSS now calls the handshake callback at the true completion of the
handshake, whether it False Started or not. And it is necessary to
register a CanFalseStartCallback.
Remove the workaround for NSS bug 562434, which has been fixed.
R=agl@chromium.org,rsleevi@chromium.org
BUG=none
TEST=none
Review URL: https://codereview.chromium.org/61293009
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@235907 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/socket/ssl_client_socket_nss.cc | 170 | ||||
-rw-r--r-- | net/third_party/nss/patches/canfalsestart.patch | 960 | ||||
-rw-r--r-- | net/third_party/nss/ssl/exports_win.def | 2 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl.h | 61 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl3con.c | 147 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl3gthr.c | 61 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslimpl.h | 13 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslsecur.c | 73 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslsock.c | 2 |
9 files changed, 889 insertions, 600 deletions
diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc index 60f0324..6f1910c 100644 --- a/net/socket/ssl_client_socket_nss.cc +++ b/net/socket/ssl_client_socket_nss.cc @@ -707,10 +707,19 @@ class SSLClientSocketNSS::Core : public base::RefCountedThreadSafe<Core> { SECKEYPrivateKey** result_private_key); #endif + // Called by NSS to determine if we can False Start. + // |arg| contains a pointer to the current SSLClientSocketNSS::Core. + static SECStatus CanFalseStartCallback(PRFileDesc* socket, + void* arg, + PRBool* can_false_start); + // Called by NSS once the handshake has completed. // |arg| contains a pointer to the current SSLClientSocketNSS::Core. static void HandshakeCallback(PRFileDesc* socket, void* arg); + // Called once the handshake has succeeded. + void HandshakeSucceeded(); + // Handles an NSS error generated while handshaking or performing IO. // Returns a network error code mapped from the original NSS error. int HandleNSSError(PRErrorCode error, bool handshake_error); @@ -862,6 +871,8 @@ class SSLClientSocketNSS::Core : public base::RefCountedThreadSafe<Core> { bool channel_id_needed_; // True if the handshake state machine was interrupted for client auth. bool client_auth_cert_needed_; + // True if NSS has False Started. + bool false_started_; // True if NSS has called HandshakeCallback. bool handshake_callback_called_; @@ -930,6 +941,7 @@ SSLClientSocketNSS::Core::Core( channel_id_xtn_negotiated_(false), channel_id_needed_(false), client_auth_cert_needed_(false), + false_started_(false), handshake_callback_called_(false), transport_recv_busy_(false), transport_recv_eof_(false), @@ -1018,6 +1030,13 @@ bool SSLClientSocketNSS::Core::Init(PRFileDesc* socket, } } + rv = SSL_SetCanFalseStartCallback( + nss_fd_, SSLClientSocketNSS::Core::CanFalseStartCallback, this); + if (rv != SECSuccess) { + LogFailedNSSFunction(*weak_net_log_, "SSL_SetCanFalseStartCallback", ""); + return false; + } + rv = SSL_HandshakeCallback( nss_fd_, SSLClientSocketNSS::Core::HandshakeCallback, this); if (rv != SECSuccess) { @@ -1144,7 +1163,7 @@ int SSLClientSocketNSS::Core::Read(IOBuffer* buf, int buf_len, } DCHECK(OnNSSTaskRunner()); - DCHECK(handshake_callback_called_); + DCHECK(false_started_ || handshake_callback_called_); DCHECK_EQ(STATE_NONE, next_handshake_state_); DCHECK(user_read_callback_.is_null()); DCHECK(user_connect_callback_.is_null()); @@ -1198,7 +1217,7 @@ int SSLClientSocketNSS::Core::Write(IOBuffer* buf, int buf_len, } DCHECK(OnNSSTaskRunner()); - DCHECK(handshake_callback_called_); + DCHECK(false_started_ || handshake_callback_called_); DCHECK_EQ(STATE_NONE, next_handshake_state_); DCHECK(user_write_callback_.is_null()); DCHECK(user_connect_callback_.is_null()); @@ -1261,26 +1280,7 @@ SECStatus SSLClientSocketNSS::Core::OwnAuthCertHandler( PRBool checksig, PRBool is_server) { Core* core = reinterpret_cast<Core*>(arg); - if (!core->handshake_callback_called_) { - // Only need to turn off False Start in the initial handshake. Also, it is - // unsafe to call SSL_OptionSet in a renegotiation because the "first - // handshake" lock isn't already held, which will result in an assertion - // failure in the ssl_Get1stHandshakeLock call in SSL_OptionSet. - PRBool negotiated_extension; - SECStatus rv = SSL_HandshakeNegotiatedExtension(socket, - ssl_app_layer_protocol_xtn, - &negotiated_extension); - if (rv != SECSuccess || !negotiated_extension) { - rv = SSL_HandshakeNegotiatedExtension(socket, - ssl_next_proto_nego_xtn, - &negotiated_extension); - } - if (rv != SECSuccess || !negotiated_extension) { - // If the server doesn't support NPN or ALPN, then we don't do False - // Start with it. - SSL_OptionSet(socket, SSL_ENABLE_FALSE_START, PR_FALSE); - } - } else { + if (core->handshake_callback_called_) { // Disallow the server certificate to change in a renegotiation. CERTCertificate* old_cert = core->nss_handshake_state_.server_cert_chain[0]; ScopedCERTCertificate new_cert(SSL_PeerCertificate(socket)); @@ -1608,6 +1608,30 @@ SECStatus SSLClientSocketNSS::Core::ClientAuthHandler( #endif // NSS_PLATFORM_CLIENT_AUTH // static +SECStatus SSLClientSocketNSS::Core::CanFalseStartCallback( + PRFileDesc* socket, + void* arg, + PRBool* can_false_start) { + // If the server doesn't support NPN or ALPN, then we don't do False + // Start with it. + PRBool negotiated_extension; + SECStatus rv = SSL_HandshakeNegotiatedExtension(socket, + ssl_app_layer_protocol_xtn, + &negotiated_extension); + if (rv != SECSuccess || !negotiated_extension) { + rv = SSL_HandshakeNegotiatedExtension(socket, + ssl_next_proto_nego_xtn, + &negotiated_extension); + } + if (rv != SECSuccess || !negotiated_extension) { + *can_false_start = PR_FALSE; + return SECSuccess; + } + + return SSL_RecommendedCanFalseStart(socket, can_false_start); +} + +// static void SSLClientSocketNSS::Core::HandshakeCallback( PRFileDesc* socket, void* arg) { @@ -1615,27 +1639,35 @@ void SSLClientSocketNSS::Core::HandshakeCallback( DCHECK(core->OnNSSTaskRunner()); core->handshake_callback_called_ = true; + if (core->false_started_) { + core->false_started_ = false; + // If we False Started, DoHandshake already called HandshakeSucceeded. + return; + } + core->HandshakeSucceeded(); +} - HandshakeState* nss_state = &core->nss_handshake_state_; +void SSLClientSocketNSS::Core::HandshakeSucceeded() { + DCHECK(OnNSSTaskRunner()); PRBool last_handshake_resumed; - SECStatus rv = SSL_HandshakeResumedSession(socket, &last_handshake_resumed); + SECStatus rv = SSL_HandshakeResumedSession(nss_fd_, &last_handshake_resumed); if (rv == SECSuccess && last_handshake_resumed) { - nss_state->resumed_handshake = true; + nss_handshake_state_.resumed_handshake = true; } else { - nss_state->resumed_handshake = false; + nss_handshake_state_.resumed_handshake = false; } - core->RecordChannelIDSupportOnNSSTaskRunner(); - core->UpdateServerCert(); - core->UpdateConnectionStatus(); - core->UpdateNextProto(); + RecordChannelIDSupportOnNSSTaskRunner(); + UpdateServerCert(); + UpdateConnectionStatus(); + UpdateNextProto(); // Update the network task runners view of the handshake state whenever // a handshake has completed. - core->PostOrRunCallback( - FROM_HERE, base::Bind(&Core::OnHandshakeStateUpdated, core, - *nss_state)); + PostOrRunCallback( + FROM_HERE, base::Bind(&Core::OnHandshakeStateUpdated, this, + nss_handshake_state_)); } int SSLClientSocketNSS::Core::HandleNSSError(PRErrorCode nss_error, @@ -1710,7 +1742,7 @@ int SSLClientSocketNSS::Core::DoHandshakeLoop(int last_io_result) { int SSLClientSocketNSS::Core::DoReadLoop(int result) { DCHECK(OnNSSTaskRunner()); - DCHECK(handshake_callback_called_); + DCHECK(false_started_ || handshake_callback_called_); DCHECK_EQ(STATE_NONE, next_handshake_state_); if (result < 0) @@ -1739,7 +1771,7 @@ int SSLClientSocketNSS::Core::DoReadLoop(int result) { int SSLClientSocketNSS::Core::DoWriteLoop(int result) { DCHECK(OnNSSTaskRunner()); - DCHECK(handshake_callback_called_); + DCHECK(false_started_ || handshake_callback_called_); DCHECK_EQ(STATE_NONE, next_handshake_state_); if (result < 0) @@ -1796,51 +1828,45 @@ int SSLClientSocketNSS::Core::DoHandshake() { LOG(WARNING) << "Couldn't invalidate SSL session: " << PR_GetError(); } else if (rv == SECSuccess) { if (!handshake_callback_called_) { - // Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=562434 - - // SSL_ForceHandshake returned SECSuccess prematurely. - rv = SECFailure; - net_error = ERR_SSL_PROTOCOL_ERROR; - PostOrRunCallback( - FROM_HERE, - base::Bind(&AddLogEventWithCallback, weak_net_log_, - NetLog::TYPE_SSL_HANDSHAKE_ERROR, - CreateNetLogSSLErrorCallback(net_error, 0))); - } else { + false_started_ = true; + HandshakeSucceeded(); + } + + // TODO(wtc): move this block of code to OwnAuthCertHandler. #if defined(SSL_ENABLE_OCSP_STAPLING) - // TODO(agl): figure out how to plumb an OCSP response into the Mac - // system library and update IsOCSPStaplingSupported for Mac. - if (IsOCSPStaplingSupported()) { - const SECItemArray* ocsp_responses = - SSL_PeerStapledOCSPResponses(nss_fd_); - if (ocsp_responses->len) { + // TODO(agl): figure out how to plumb an OCSP response into the Mac + // system library and update IsOCSPStaplingSupported for Mac. + if (IsOCSPStaplingSupported()) { + const SECItemArray* ocsp_responses = + SSL_PeerStapledOCSPResponses(nss_fd_); + if (ocsp_responses->len) { #if defined(OS_WIN) - if (nss_handshake_state_.server_cert) { - CRYPT_DATA_BLOB ocsp_response_blob; - ocsp_response_blob.cbData = ocsp_responses->items[0].len; - ocsp_response_blob.pbData = ocsp_responses->items[0].data; - BOOL ok = CertSetCertificateContextProperty( - nss_handshake_state_.server_cert->os_cert_handle(), - CERT_OCSP_RESPONSE_PROP_ID, - CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG, - &ocsp_response_blob); - if (!ok) { - VLOG(1) << "Failed to set OCSP response property: " - << GetLastError(); - } + if (nss_handshake_state_.server_cert) { + CRYPT_DATA_BLOB ocsp_response_blob; + ocsp_response_blob.cbData = ocsp_responses->items[0].len; + ocsp_response_blob.pbData = ocsp_responses->items[0].data; + BOOL ok = CertSetCertificateContextProperty( + nss_handshake_state_.server_cert->os_cert_handle(), + CERT_OCSP_RESPONSE_PROP_ID, + CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG, + &ocsp_response_blob); + if (!ok) { + VLOG(1) << "Failed to set OCSP response property: " + << GetLastError(); } + } #elif defined(USE_NSS) - CacheOCSPResponseFromSideChannelFunction cache_ocsp_response = - GetCacheOCSPResponseFromSideChannelFunction(); + CacheOCSPResponseFromSideChannelFunction cache_ocsp_response = + GetCacheOCSPResponseFromSideChannelFunction(); - cache_ocsp_response( - CERT_GetDefaultCertDB(), - nss_handshake_state_.server_cert_chain[0], PR_Now(), - &ocsp_responses->items[0], NULL); + cache_ocsp_response( + CERT_GetDefaultCertDB(), + nss_handshake_state_.server_cert_chain[0], PR_Now(), + &ocsp_responses->items[0], NULL); #endif - } } - #endif } + #endif // Done! } else { PRErrorCode prerr = PR_GetError(); diff --git a/net/third_party/nss/patches/canfalsestart.patch b/net/third_party/nss/patches/canfalsestart.patch index d2a9752..a3fb181 100644 --- a/net/third_party/nss/patches/canfalsestart.patch +++ b/net/third_party/nss/patches/canfalsestart.patch @@ -1,8 +1,8 @@ Index: net/third_party/nss/ssl/ssl.h =================================================================== ---- net/third_party/nss/ssl/ssl.h (revision 227363) +--- net/third_party/nss/ssl/ssl.h (revision 227672) +++ net/third_party/nss/ssl/ssl.h (working copy) -@@ -121,14 +121,22 @@ +@@ -121,14 +121,17 @@ #define SSL_ENABLE_FALSE_START 22 /* Enable SSL false start (off by */ /* default, applies only to */ /* clients). False start is a */ @@ -22,62 +22,42 @@ Index: net/third_party/nss/ssl/ssl.h + * 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. ++ * 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 -@@ -741,14 +749,59 @@ +@@ -741,14 +744,45 @@ 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. ++** 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. +** -+** 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. ++** 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 customize TLS false start should set this callback ++/* 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. -+** -+** 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. ++** handshake will be canceled. NSS's recommended criteria can be evaluated by ++** calling SSL_RecommendedCanFalseStart. +** -+** See the description of SSL_HandshakeCallback for important information on -+** how registering a custom false start callback affects when the handshake -+** callback gets called. ++** 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); @@ -85,62 +65,420 @@ Index: net/third_party/nss/ssl/ssl.h +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); ++/* 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 +Index: net/third_party/nss/ssl/ssl3con.c +=================================================================== +--- net/third_party/nss/ssl/ssl3con.c (revision 227672) ++++ net/third_party/nss/ssl/ssl3con.c (working copy) +@@ -2890,7 +2890,7 @@ + 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) ); + +@@ -7344,36 +7344,72 @@ + 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; + + 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; ++ default: ++ result = PR_FALSE; ++ break; ++ } + +- 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); +@@ -7463,6 +7499,9 @@ + } + 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; + } +@@ -7500,20 +7539,59 @@ + 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. */ + } + } ++ + rv = ssl3_SendEncryptedExtensions(ss); + if (rv != SECSuccess) { + goto loser; /* err code was set. */ + } + ++ if (!ss->firstHsDone) { ++ 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); + if (rv != SECSuccess) { + goto loser; /* err code was set. */ +@@ -7526,10 +7604,7 @@ + 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; + +@@ -10147,13 +10222,6 @@ + + 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) { +@@ -10278,6 +10346,12 @@ + } 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 +@@ -10287,7 +10361,27 @@ + 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 != idle_handshake); ++ ++ if (ss->opt.enableFalseStart && ++ !ss->firstHsDone && ++ !ss->sec.isServer && ++ !ss->ssl3.hs.isResuming && ++ ssl3_WaitingForStartOfServerSecondRound(ss)) { ++ /* 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. ++ */ ++ rv = ssl3_CheckFalseStart(ss); ++ } else { ++ rv = SECSuccess; ++ } + } + + done: +@@ -10913,9 +11007,6 @@ + return rv; + } + +- ss->gs.writeOffset = 0; +- ss->gs.readOffset = 0; +- + if (ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa) { + effectiveExchKeyType = kt_rsa; + } else { +@@ -10980,6 +11071,9 @@ + return rv; + } + ++/* The return type is SECStatus instead of void because this function needs ++ * to have type sslRestartTarget. ++ */ + SECStatus + ssl3_FinishHandshake(sslSocket * ss) + { +@@ -10989,19 +11083,16 @@ + + /* 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; + } +@@ -11966,7 +12057,6 @@ + + ssl_ReleaseSSL3HandshakeLock(ss); + return rv; +- + } + + /* Index: net/third_party/nss/ssl/ssl3gthr.c =================================================================== ---- net/third_party/nss/ssl/ssl3gthr.c (revision 227363) +--- net/third_party/nss/ssl/ssl3gthr.c (revision 227672) +++ net/third_party/nss/ssl/ssl3gthr.c (working copy) -@@ -374,9 +374,7 @@ - */ - if (ss->opt.enableFalseStart) { - ssl_GetSSL3HandshakeLock(ss); +@@ -275,11 +275,17 @@ + { + 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; + +@@ -364,24 +370,52 @@ + + cText.buf = &ss->gs.inbuf; + rv = ssl3_HandleRecord(ss, &cText, &ss->gs.buf); ++ ++ 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 (rv < 0) { + 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); +- 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 && -Index: net/third_party/nss/ssl/sslinfo.c -=================================================================== ---- net/third_party/nss/ssl/sslinfo.c (revision 227363) -+++ net/third_party/nss/ssl/sslinfo.c (working copy) -@@ -26,7 +26,6 @@ - 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 @@ - memset(&inf, 0, sizeof inf); - inf.length = PR_MIN(sizeof inf, len); +- } 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 @@ + { + int rv; -- 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; ++ /* 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); Index: net/third_party/nss/ssl/sslauth.c =================================================================== ---- net/third_party/nss/ssl/sslauth.c (revision 227363) +--- net/third_party/nss/ssl/sslauth.c (revision 227672) +++ net/third_party/nss/ssl/sslauth.c (working copy) @@ -100,7 +100,6 @@ sslSocket *ss; @@ -168,7 +506,7 @@ Index: net/third_party/nss/ssl/sslauth.c } else { Index: net/third_party/nss/ssl/sslimpl.h =================================================================== ---- net/third_party/nss/ssl/sslimpl.h (revision 227363) +--- net/third_party/nss/ssl/sslimpl.h (revision 227672) +++ net/third_party/nss/ssl/sslimpl.h (working copy) @@ -881,6 +881,8 @@ /* Shared state between ssl3_HandleFinished and ssl3_FinishHandshake */ @@ -199,28 +537,69 @@ Index: net/third_party/nss/ssl/sslimpl.h void *pkcs11PinArg; SSLNextProtoCallback nextProtoCallback; void *nextProtoArg; -@@ -1423,7 +1431,6 @@ +@@ -1423,7 +1431,19 @@ 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, +Index: net/third_party/nss/ssl/sslinfo.c +=================================================================== +--- net/third_party/nss/ssl/sslinfo.c (revision 227672) ++++ net/third_party/nss/ssl/sslinfo.c (working copy) +@@ -26,7 +26,6 @@ + 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 @@ + 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; Index: net/third_party/nss/ssl/sslsecur.c =================================================================== ---- net/third_party/nss/ssl/sslsecur.c (revision 227363) +--- net/third_party/nss/ssl/sslsecur.c (revision 227672) +++ net/third_party/nss/ssl/sslsecur.c (working copy) -@@ -99,21 +99,12 @@ +@@ -97,23 +97,13 @@ + ss->securityHandshake = 0; + } if (ss->handshake == 0) { - ssl_GetRecvBufLock(ss); - ss->gs.recordLen = 0; -+ ss->gs.writeOffset = 0; -+ ss->gs.readOffset = 0; - ssl_ReleaseRecvBufLock(ss); - - SSL_TRC(3, ("%d: SSL[%d]: handshake is completed", - SSL_GETPID(), ss->fd)); +- 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 */ @@ -228,14 +607,45 @@ Index: net/third_party/nss/ssl/sslsecur.c - (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); -@@ -206,6 +197,7 @@ +@@ -134,6 +124,24 @@ + 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_Get1stHandshakeLock(ss); ss->firstHsDone = PR_FALSE; @@ -243,7 +653,7 @@ Index: net/third_party/nss/ssl/sslsecur.c if ( asServer ) { ss->handshake = ssl2_BeginServerHandshake; ss->handshaking = sslHandshakingAsServer; -@@ -221,6 +213,8 @@ +@@ -221,6 +230,8 @@ ssl_ReleaseRecvBufLock(ss); ssl_GetSSL3HandshakeLock(ss); @@ -252,16 +662,7 @@ Index: net/third_party/nss/ssl/sslsecur.c /* ** Blow away old security state and get a fresh setup. -@@ -266,7 +260,7 @@ - - /* SSL v2 protocol does not support subsequent handshakes. */ - if (ss->version < SSL_LIBRARY_VERSION_3_0) { -- PORT_SetError(SEC_ERROR_INVALID_ARGS); -+ PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2); - rv = SECFailure; - } else { - ssl_GetSSL3HandshakeLock(ss); -@@ -331,6 +325,75 @@ +@@ -331,6 +342,71 @@ return SECSuccess; } @@ -270,7 +671,7 @@ Index: net/third_party/nss/ssl/sslsecur.c +*/ +SECStatus +SSL_SetCanFalseStartCallback(PRFileDesc *fd, SSLCanFalseStartCallback cb, -+ void *client_data) ++ void *arg) +{ + sslSocket *ss; + @@ -290,7 +691,7 @@ Index: net/third_party/nss/ssl/sslsecur.c + ssl_GetSSL3HandshakeLock(ss); + + ss->canFalseStartCallback = cb; -+ ss->canFalseStartCallbackData = client_data; ++ ss->canFalseStartCallbackData = arg; + + ssl_ReleaseSSL3HandshakeLock(ss); + ssl_Release1stHandshakeLock(ss); @@ -298,19 +699,15 @@ Index: net/third_party/nss/ssl/sslsecur.c + return SECSuccess; +} + -+/* A utility function that can be called from a custom SSLCanFalseStartCallback -+** 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) ++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_DefaultCanFalseStart", ++ SSL_DBG(("%d: SSL[%d]: bad socket in SSL_RecommendedCanFalseStart", + SSL_GETPID(), fd)); + return SECFailure; + } @@ -337,9 +734,43 @@ Index: net/third_party/nss/ssl/sslsecur.c /* 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 -@@ -1195,12 +1258,7 @@ +@@ -524,6 +600,9 @@ + 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 @@ + + done: + ssl_ReleaseRecvBufLock(ss); ++ ssl_Release1stHandshakeLock(ss); + return rv; + } + +@@ -1156,7 +1236,8 @@ + int + ssl_SecureSend(sslSocket *ss, const unsigned char *buf, int len, int flags) + { +- int rv = 0; ++ int rv = 0; ++ PRBool falseStart = PR_FALSE; + + SSL_TRC(2, ("%d: SSL[%d]: SecureSend: sending %d bytes", + SSL_GETPID(), ss->fd, len)); +@@ -1191,19 +1272,14 @@ + 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) { +- 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 || @@ -347,15 +778,46 @@ Index: net/third_party/nss/ssl/sslsecur.c - ssl3_CanFalseStart(ss)) { - canFalseStart = PR_TRUE; - } -+ canFalseStart = ss->ssl3.hs.canFalseStart; ++ falseStart = ss->ssl3.hs.canFalseStart; ssl_ReleaseSSL3HandshakeLock(ss); } - if (!canFalseStart && +- if (!canFalseStart && ++ if (!falseStart && + (ss->handshake || ss->nextHandshake || ss->securityHandshake)) { + rv = ssl_Do1stHandshake(ss); + } +@@ -1228,6 +1304,17 @@ + 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 Index: net/third_party/nss/ssl/sslsock.c =================================================================== ---- net/third_party/nss/ssl/sslsock.c (revision 227363) +--- net/third_party/nss/ssl/sslsock.c (revision 227672) +++ net/third_party/nss/ssl/sslsock.c (working copy) -@@ -2457,10 +2457,14 @@ +@@ -366,6 +366,8 @@ + ss->badCertArg = os->badCertArg; + ss->handshakeCallback = os->handshakeCallback; + ss->handshakeCallbackData = os->handshakeCallbackData; ++ ss->canFalseStartCallback = os->canFalseStartCallback; ++ ss->canFalseStartCallbackData = os->canFalseStartCallbackData; + ss->pkcs11PinArg = os->pkcs11PinArg; + ss->getChannelID = os->getChannelID; + ss->getChannelIDArg = os->getChannelIDArg; +@@ -2457,10 +2459,14 @@ } 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 @@ -373,265 +835,3 @@ Index: net/third_party/nss/ssl/sslsock.c } } } else if ((new_flags & PR_POLL_READ) && (SSL_DataPending(fd) > 0)) { -Index: net/third_party/nss/ssl/ssl3con.c -=================================================================== ---- net/third_party/nss/ssl/ssl3con.c (revision 227363) -+++ net/third_party/nss/ssl/ssl3con.c (working copy) -@@ -2890,7 +2890,7 @@ - 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) ); - -@@ -7344,35 +7344,42 @@ - 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) ); -+ PORT_Assert( !ss->ssl3.hs.authCertificatePending ); - -- /* 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. -- */ -+ /* 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 && -+ 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; -+ } - -- /* 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); -+ if (!ss->canFalseStartCallback) { -+ rv = SSL_DefaultCanFalseStart(ss->fd, &ss->ssl3.hs.canFalseStart); -+ } else { -+ rv = (ss->canFalseStartCallback)(ss->fd, -+ ss->canFalseStartCallbackData, -+ &ss->ssl3.hs.canFalseStart); -+ } -+ -+ if (rv != SECSuccess) { -+ ss->ssl3.hs.canFalseStart = PR_FALSE; -+ } -+ - return rv; - } - -@@ -7500,20 +7507,59 @@ - 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. */ - } - } -+ - rv = ssl3_SendEncryptedExtensions(ss); - if (rv != SECSuccess) { - goto loser; /* err code was set. */ - } - -+ if (!ss->firstHsDone) { -+ 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); - if (rv != SECSuccess) { - goto loser; /* err code was set. */ -@@ -7526,8 +7572,16 @@ - 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)) { -+ if (ss->handshakeCallback && -+ (ss->ssl3.hs.canFalseStart && !ss->canFalseStartCallback)) { -+ /* Call the handshake callback here for backwards compatibility with -+ * applications that were using false start before -+ * canFalseStartCallback was added. Note that we do this after calling -+ * ssl3_SendFinished, which includes a call to ssl3_FlushHandshake, -+ * just in case the application is relying on having the handshake -+ * messages flushed to the network before its handshake callback is -+ * called. -+ */ - (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData); - } - -@@ -10147,13 +10201,6 @@ - - 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) { -@@ -10278,6 +10325,12 @@ - } 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 -@@ -10287,7 +10340,39 @@ - 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_change_cipher || -+ ss->ssl3.hs.ws == wait_finished || -+ ss->ssl3.hs.ws == wait_new_session_ticket); -+ -+ /* ssl3_SendClientSecondRound deferred the false start check because -+ * certificate authentication was pending, so we have to do it now. -+ */ -+ if (ss->opt.enableFalseStart && -+ !ss->firstHsDone && -+ !ss->sec.isServer && -+ !ss->ssl3.hs.isResuming && -+ (ss->ssl3.hs.ws == wait_change_cipher || -+ ss->ssl3.hs.ws == wait_finished || -+ ss->ssl3.hs.ws == wait_new_session_ticket)) { -+ rv = ssl3_CheckFalseStart(ss); -+ if (rv == SECSuccess && -+ ss->handshakeCallback && -+ (ss->ssl3.hs.canFalseStart && !ss->canFalseStartCallback)) { -+ /* 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 = SECSuccess; -+ } - } - - done: -@@ -10983,6 +11068,8 @@ - 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 ); -@@ -10990,6 +11077,7 @@ - /* 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); -@@ -10997,9 +11085,14 @@ - } - - ss->ssl3.hs.ws = idle_handshake; -+ falseStarted = ss->ssl3.hs.canFalseStart; -+ ss->ssl3.hs.canFalseStart = PR_FALSE; /* False Start phase is complete */ - -- /* Do the handshake callback for sslv3 here, if we cannot false start. */ -- if (ss->handshakeCallback != NULL && !ssl3_CanFalseStart(ss)) { -+ /* 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 && -+ !(falseStarted && !ss->canFalseStartCallback)) { - (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData); - } - diff --git a/net/third_party/nss/ssl/exports_win.def b/net/third_party/nss/ssl/exports_win.def index 09c70b0..a9dc8eb 100644 --- a/net/third_party/nss/ssl/exports_win.def +++ b/net/third_party/nss/ssl/exports_win.def @@ -50,6 +50,8 @@ SSL_ExportKeyingMaterial SSL_VersionRangeSet SSL_GetSRTPCipher SSL_SetSRTPCiphers +SSL_RecommendedCanFalseStart +SSL_SetCanFalseStartCallback ; Chromium patches SSL_PeerCertificateChain diff --git a/net/third_party/nss/ssl/ssl.h b/net/third_party/nss/ssl/ssl.h index 47468a0..67cc3a7 100644 --- a/net/third_party/nss/ssl/ssl.h +++ b/net/third_party/nss/ssl/ssl.h @@ -129,13 +129,8 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd); * 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. + * 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 @@ -749,45 +744,30 @@ SSL_IMPORT SECStatus SSL_SetMaxServerCacheLocks(PRUint32 maxLocks); SSL_IMPORT SECStatus SSL_InheritMPServerSIDCache(const char * envString); /* -** 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. +** 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. ** -** 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. +** 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 customize TLS false start should set this callback +/* 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. -** -** 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. +** handshake will be canceled. NSS's recommended criteria can be evaluated by +** calling SSL_RecommendedCanFalseStart. ** -** See the description of SSL_HandshakeCallback for important information on -** how registering a custom false start callback affects when the handshake -** callback gets called. +** 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); @@ -795,12 +775,13 @@ typedef SECStatus (PR_CALLBACK *SSLCanFalseStartCallback)( 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); +/* 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 diff --git a/net/third_party/nss/ssl/ssl3con.c b/net/third_party/nss/ssl/ssl3con.c index 396c408..0f1eea4 100644 --- a/net/third_party/nss/ssl/ssl3con.c +++ b/net/third_party/nss/ssl/ssl3con.c @@ -7374,40 +7374,69 @@ ssl3_RestartHandshakeAfterCertReq(sslSocket * ss, static SECStatus ssl3_CheckFalseStart(sslSocket *ss) { - SECStatus rv; - PRBool maybeFalseStart = PR_TRUE; - PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) ); 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); - 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; - } + PORT_Assert( !ss->ssl3.hs.canFalseStart ); if (!ss->canFalseStartCallback) { - rv = SSL_DefaultCanFalseStart(ss->fd, &ss->ssl3.hs.canFalseStart); + SSL_TRC(3, ("%d: SSL[%d]: no false start callback so no false start", + SSL_GETPID(), ss->fd)); } else { - rv = (ss->canFalseStartCallback)(ss->fd, - ss->canFalseStartCallbackData, - &ss->ssl3.hs.canFalseStart); + 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; + } } - if (rv != SECSuccess) { - ss->ssl3.hs.canFalseStart = PR_FALSE; + ss->ssl3.hs.canFalseStart = PR_FALSE; + return SECSuccess; +} + +PRBool +ssl3_WaitingForStartOfServerSecondRound(sslSocket *ss) +{ + PRBool result; + + PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) ); + + 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; + default: + result = PR_FALSE; + break; } - return rv; + return result; } static SECStatus ssl3_SendClientSecondRound(sslSocket *ss); @@ -7497,6 +7526,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; } @@ -7566,7 +7598,7 @@ ssl3_SendClientSecondRound(sslSocket *ss) * 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. + * done before its false start callback is called. */ ssl_ReleaseXmitBufLock(ss); rv = ssl3_CheckFalseStart(ss); @@ -7626,18 +7658,7 @@ ssl3_SendClientSecondRound(sslSocket *ss) else ss->ssl3.hs.ws = wait_change_cipher; - if (ss->handshakeCallback && - (ss->ssl3.hs.canFalseStart && !ss->canFalseStartCallback)) { - /* Call the handshake callback here for backwards compatibility with - * applications that were using false start before - * canFalseStartCallback was added. Note that we do this after calling - * ssl3_SendFinished, which includes a call to ssl3_FlushHandshake, - * just in case the application is relying on having the handshake - * messages flushed to the network before its handshake callback is - * called. - */ - (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData); - } + PORT_Assert(ssl3_WaitingForStartOfServerSecondRound(ss)); return SECSuccess; @@ -10394,36 +10415,24 @@ ssl3_AuthCertificateComplete(sslSocket *ss, PRErrorCode error) rv = SECSuccess; } } else { - SSL_TRC(3, ("%d: SSL3[%p]: certificate authentication won the race" - " with peer's finished message", SSL_GETPID(), ss->fd)); + 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_change_cipher || - ss->ssl3.hs.ws == wait_finished || - ss->ssl3.hs.ws == wait_new_session_ticket); + PORT_Assert(ss->ssl3.hs.ws != idle_handshake); - /* ssl3_SendClientSecondRound deferred the false start check because - * certificate authentication was pending, so we have to do it now. - */ if (ss->opt.enableFalseStart && !ss->firstHsDone && !ss->sec.isServer && !ss->ssl3.hs.isResuming && - (ss->ssl3.hs.ws == wait_change_cipher || - ss->ssl3.hs.ws == wait_finished || - ss->ssl3.hs.ws == wait_new_session_ticket)) { + ssl3_WaitingForStartOfServerSecondRound(ss)) { + /* 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. + */ rv = ssl3_CheckFalseStart(ss); - if (rv == SECSuccess && - ss->handshakeCallback && - (ss->ssl3.hs.canFalseStart && !ss->canFalseStartCallback)) { - /* 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 = SECSuccess; } @@ -11071,9 +11080,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 { @@ -11138,36 +11144,28 @@ 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) { - 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; - falseStarted = ss->ssl3.hs.canFalseStart; ss->ssl3.hs.canFalseStart = PR_FALSE; /* False Start phase is complete */ + ss->ssl3.hs.ws = idle_handshake; - /* 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 && - !(falseStarted && !ss->canFalseStartCallback)) { - (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData); - } + ssl_FinishHandshake(ss); return SECSuccess; } @@ -12132,7 +12130,6 @@ process_it: ssl_ReleaseSSL3HandshakeLock(ss); return rv; - } /* diff --git a/net/third_party/nss/ssl/ssl3gthr.c b/net/third_party/nss/ssl/ssl3gthr.c index 7385d65..39cefa7 100644 --- a/net/third_party/nss/ssl/ssl3gthr.c +++ b/net/third_party/nss/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; @@ -364,22 +370,52 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags) cText.buf = &ss->gs.inbuf; rv = ssl3_HandleRecord(ss, &cText, &ss->gs.buf); + + 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 (rv < 0) { 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.canFalseStart; - 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; @@ -402,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/net/third_party/nss/ssl/sslimpl.h b/net/third_party/nss/ssl/sslimpl.h index ca68727..79aca60 100644 --- a/net/third_party/nss/ssl/sslimpl.h +++ b/net/third_party/nss/ssl/sslimpl.h @@ -1446,6 +1446,19 @@ extern void ssl3_SetAlwaysBlock(sslSocket *ss); extern SECStatus ssl_EnableNagleDelay(sslSocket *ss, PRBool enabled); +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/net/third_party/nss/ssl/sslsecur.c b/net/third_party/nss/ssl/sslsecur.c index 6c7532e..31c343f 100644 --- a/net/third_party/nss/ssl/sslsecur.c +++ b/net/third_party/nss/ssl/sslsecur.c @@ -97,14 +97,13 @@ ssl_Do1stHandshake(sslSocket *ss) ss->securityHandshake = 0; } if (ss->handshake == 0) { - ssl_GetRecvBufLock(ss); - ss->gs.recordLen = 0; - ss->gs.writeOffset = 0; - ss->gs.readOffset = 0; - ssl_ReleaseRecvBufLock(ss); - - SSL_TRC(3, ("%d: SSL[%d]: handshake is completed", - SSL_GETPID(), ss->fd)); + /* 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); + } break; } rv = (*ss->handshake)(ss); @@ -125,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. @@ -260,7 +277,7 @@ SSL_ReHandshake(PRFileDesc *fd, PRBool flushCache) /* SSL v2 protocol does not support subsequent handshakes. */ if (ss->version < SSL_LIBRARY_VERSION_3_0) { - PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2); + PORT_SetError(SEC_ERROR_INVALID_ARGS); rv = SECFailure; } else { ssl_GetSSL3HandshakeLock(ss); @@ -330,7 +347,7 @@ SSL_HandshakeCallback(PRFileDesc *fd, SSLHandshakeCallback cb, */ SECStatus SSL_SetCanFalseStartCallback(PRFileDesc *fd, SSLCanFalseStartCallback cb, - void *client_data) + void *arg) { sslSocket *ss; @@ -350,7 +367,7 @@ SSL_SetCanFalseStartCallback(PRFileDesc *fd, SSLCanFalseStartCallback cb, ssl_GetSSL3HandshakeLock(ss); ss->canFalseStartCallback = cb; - ss->canFalseStartCallbackData = client_data; + ss->canFalseStartCallbackData = arg; ssl_ReleaseSSL3HandshakeLock(ss); ssl_Release1stHandshakeLock(ss); @@ -358,19 +375,15 @@ SSL_SetCanFalseStartCallback(PRFileDesc *fd, SSLCanFalseStartCallback cb, return SECSuccess; } -/* A utility function that can be called from a custom SSLCanFalseStartCallback -** 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) +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_DefaultCanFalseStart", + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_RecommendedCanFalseStart", SSL_GETPID(), fd)); return SECFailure; } @@ -587,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; @@ -653,6 +669,7 @@ DoRecv(sslSocket *ss, unsigned char *out, int len, int flags) done: ssl_ReleaseRecvBufLock(ss); + ssl_Release1stHandshakeLock(ss); return rv; } @@ -1219,7 +1236,8 @@ 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; + PRBool falseStart = PR_FALSE; SSL_TRC(2, ("%d: SSL[%d]: SecureSend: sending %d bytes", SSL_GETPID(), ss->fd, len)); @@ -1254,14 +1272,14 @@ 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; 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); - canFalseStart = ss->ssl3.hs.canFalseStart; + falseStart = ss->ssl3.hs.canFalseStart; ssl_ReleaseSSL3HandshakeLock(ss); } - if (!canFalseStart && + if (!falseStart && (ss->handshake || ss->nextHandshake || ss->securityHandshake)) { rv = ssl_Do1stHandshake(ss); } @@ -1286,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/net/third_party/nss/ssl/sslsock.c b/net/third_party/nss/ssl/sslsock.c index 072fad5..b5c17f0 100644 --- a/net/third_party/nss/ssl/sslsock.c +++ b/net/third_party/nss/ssl/sslsock.c @@ -366,6 +366,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; ss->getChannelID = os->getChannelID; ss->getChannelIDArg = os->getChannelIDArg; |