diff options
author | wtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-09 09:40:51 +0000 |
---|---|---|
committer | wtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-09 09:40:51 +0000 |
commit | 8f3a23ec70c531406e3b1c605170924da47426e9 (patch) | |
tree | bb865b99b350f371418b956c7d53bae69dc7fa50 /net/third_party | |
parent | 2e9d18e3bc39e93bf6c821a5224f6816970c686d (diff) | |
download | chromium_src-8f3a23ec70c531406e3b1c605170924da47426e9.zip chromium_src-8f3a23ec70c531406e3b1c605170924da47426e9.tar.gz chromium_src-8f3a23ec70c531406e3b1c605170924da47426e9.tar.bz2 |
Make SSL False Start work with asynchronous certificate validation
(SSL_AuthCertificateComplete).
Patch by Brian Smith <brian@briansmith.org>.
NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=713933
R=agl@chromium.org
BUG=none
TEST=none
Review URL: https://codereview.chromium.org/23621040
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@227704 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/third_party')
-rw-r--r-- | net/third_party/nss/README.chromium | 4 | ||||
-rwxr-xr-x | net/third_party/nss/patches/applypatches.sh | 2 | ||||
-rw-r--r-- | net/third_party/nss/patches/canfalsestart.patch | 637 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl.h | 73 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl3con.c | 169 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl3gthr.c | 4 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslauth.c | 10 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslimpl.h | 9 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslinfo.c | 10 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslsecur.c | 94 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslsock.c | 10 |
11 files changed, 931 insertions, 91 deletions
diff --git a/net/third_party/nss/README.chromium b/net/third_party/nss/README.chromium index b7441f5..0e6ef5b 100644 --- a/net/third_party/nss/README.chromium +++ b/net/third_party/nss/README.chromium @@ -131,6 +131,10 @@ Patches: https://code.google.com/p/chromium/issues/detail?id=303398 patches/resumeclienthelloversion.patch + * Make SSL False Start work with asynchronous certificate validation. + https://bugzilla.mozilla.org/show_bug.cgi?id=713933 + patches/canfalsestart.patch + Apply the patches to NSS by running the patches/applypatches.sh script. Read the comments at the top of patches/applypatches.sh for instructions. diff --git a/net/third_party/nss/patches/applypatches.sh b/net/third_party/nss/patches/applypatches.sh index 7b9b913e..ec96d22 100755 --- a/net/third_party/nss/patches/applypatches.sh +++ b/net/third_party/nss/patches/applypatches.sh @@ -61,3 +61,5 @@ patch -p4 < $patches_dir/cachelocks.patch patch -p4 < $patches_dir/ciphersuiteversion.patch patch -p4 < $patches_dir/peercertchain2.patch + +patch -p4 < $patches_dir/canfalsestart.patch diff --git a/net/third_party/nss/patches/canfalsestart.patch b/net/third_party/nss/patches/canfalsestart.patch new file mode 100644 index 0000000..d2a9752 --- /dev/null +++ b/net/third_party/nss/patches/canfalsestart.patch @@ -0,0 +1,637 @@ +Index: net/third_party/nss/ssl/ssl.h +=================================================================== +--- net/third_party/nss/ssl/ssl.h (revision 227363) ++++ net/third_party/nss/ssl/ssl.h (working copy) +@@ -121,14 +121,22 @@ + #define SSL_ENABLE_FALSE_START 22 /* Enable SSL false start (off by */ + /* default, applies only to */ + /* clients). False start is a */ +-/* mode where an SSL client will start sending application data before */ +-/* verifying the server's Finished message. This means that we could end up */ +-/* sending data to an imposter. However, the data will be encrypted and */ +-/* only the true server can derive the session key. Thus, so long as the */ +-/* cipher isn't broken this is safe. Because of this, False Start will only */ +-/* occur on RSA or DH ciphersuites where the cipher's key length is >= 80 */ +-/* bits. The advantage of False Start is that it saves a round trip for */ +-/* client-speaks-first protocols when performing a full handshake. */ ++/* mode where an SSL client will start sending application data before ++ * verifying the server's Finished message. This means that we could end up ++ * sending data to an imposter. However, the data will be encrypted and ++ * only the true server can derive the session key. Thus, so long as the ++ * cipher isn't broken this is safe. The advantage of false start is that ++ * it saves a round trip for client-speaks-first protocols when performing a ++ * full handshake. ++ * ++ * See SSL_DefaultCanFalseStart for the default criteria that NSS uses to ++ * determine whether to false start or not. See SSL_SetCanFalseStartCallback ++ * for how to change that criteria. In addition to those criteria, false start ++ * will only be done when the server selects a cipher suite with an effective ++ * key length of 80 bits or more (including RC4-128). Also, see ++ * SSL_HandshakeCallback for a description on how false start affects when the ++ * handshake callback gets called. ++ */ + + /* For SSL 3.0 and TLS 1.0, by default we prevent chosen plaintext attacks + * on SSL CBC mode cipher suites (see RFC 4346 Section F.3) by splitting +@@ -741,14 +749,59 @@ + SSL_IMPORT SECStatus SSL_InheritMPServerSIDCache(const char * envString); + + /* +-** Set the callback on a particular socket that gets called when we finish +-** performing a handshake. ++** Set the callback that normally gets called when the TLS handshake ++** is complete. If false start is not enabled, then the handshake callback is ++** called after verifying the peer's Finished message and before sending ++** outgoing application data and before processing incoming application data. ++** ++** If false start is enabled and there is a custom CanFalseStartCallback ++** callback set, then the handshake callback gets called after the peer's ++** Finished message has been verified, which may be after application data is ++** sent. ++** ++** If false start is enabled and there is not a custom CanFalseStartCallback ++** callback established with SSL_SetCanFalseStartCallback then the handshake ++** callback gets called before any application data is sent, which may be ++** before the peer's Finished message has been verified. + */ + typedef void (PR_CALLBACK *SSLHandshakeCallback)(PRFileDesc *fd, + void *client_data); + SSL_IMPORT SECStatus SSL_HandshakeCallback(PRFileDesc *fd, + SSLHandshakeCallback cb, void *client_data); + ++/* Applications that wish to customize TLS false start should set this callback ++** function. NSS will invoke the functon to determine if a particular ++** connection should use false start or not. SECSuccess indicates that the ++** callback completed successfully, and if so *canFalseStart indicates if false ++** start can be used. If the callback does not return SECSuccess then the ++** handshake will be canceled. ++** ++** Applications that do not set the callback will use an internal set of ++** criteria to determine if the connection should false start. If ++** the callback is set false start will never be used without invoking the ++** callback function, but some connections (e.g. resumed connections) will ++** never use false start and therefore will not invoke the callback. ++** ++** NSS's internal criteria for this connection can be evaluated by calling ++** SSL_DefaultCanFalseStart() from the custom callback. ++** ++** See the description of SSL_HandshakeCallback for important information on ++** how registering a custom false start callback affects when the handshake ++** callback gets called. ++**/ ++typedef SECStatus (PR_CALLBACK *SSLCanFalseStartCallback)( ++ PRFileDesc *fd, void *arg, PRBool *canFalseStart); ++ ++SSL_IMPORT SECStatus SSL_SetCanFalseStartCallback( ++ PRFileDesc *fd, SSLCanFalseStartCallback callback, void *arg); ++ ++/* A utility function that can be called from a custom CanFalseStartCallback ++** function to determine what NSS would have done for this connection if the ++** custom callback was not implemented. ++**/ ++SSL_IMPORT SECStatus SSL_DefaultCanFalseStart(PRFileDesc *fd, ++ PRBool *canFalseStart); ++ + /* + ** For the server, request a new handshake. For the client, begin a new + ** handshake. If flushCache is non-zero, the SSL3 cache entry will be +Index: net/third_party/nss/ssl/ssl3gthr.c +=================================================================== +--- net/third_party/nss/ssl/ssl3gthr.c (revision 227363) ++++ net/third_party/nss/ssl/ssl3gthr.c (working copy) +@@ -374,9 +374,7 @@ + */ + if (ss->opt.enableFalseStart) { + ssl_GetSSL3HandshakeLock(ss); +- canFalseStart = (ss->ssl3.hs.ws == wait_change_cipher || +- ss->ssl3.hs.ws == wait_new_session_ticket) && +- ssl3_CanFalseStart(ss); ++ canFalseStart = ss->ssl3.hs.canFalseStart; + ssl_ReleaseSSL3HandshakeLock(ss); + } + } while (ss->ssl3.hs.ws != idle_handshake && +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); + +- 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/sslauth.c +=================================================================== +--- net/third_party/nss/ssl/sslauth.c (revision 227363) ++++ net/third_party/nss/ssl/sslauth.c (working copy) +@@ -100,7 +100,6 @@ + sslSocket *ss; + const char *cipherName; + PRBool isDes = PR_FALSE; +- PRBool enoughFirstHsDone = PR_FALSE; + + ss = ssl_FindSocket(fd); + if (!ss) { +@@ -118,14 +117,7 @@ + *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 { +Index: net/third_party/nss/ssl/sslimpl.h +=================================================================== +--- net/third_party/nss/ssl/sslimpl.h (revision 227363) ++++ net/third_party/nss/ssl/sslimpl.h (working copy) +@@ -881,6 +881,8 @@ + /* 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. */ +@@ -1162,6 +1164,10 @@ + 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. */ +@@ -1210,6 +1216,8 @@ + void *badCertArg; + SSLHandshakeCallback handshakeCallback; + void *handshakeCallbackData; ++ SSLCanFalseStartCallback canFalseStartCallback; ++ void *canFalseStartCallbackData; + void *pkcs11PinArg; + SSLNextProtoCallback nextProtoCallback; + void *nextProtoArg; +@@ -1423,7 +1431,6 @@ + + extern SECStatus ssl_EnableNagleDelay(sslSocket *ss, PRBool enabled); + +-extern PRBool ssl3_CanFalseStart(sslSocket *ss); + extern SECStatus + ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec, + PRBool isServer, +Index: net/third_party/nss/ssl/sslsecur.c +=================================================================== +--- net/third_party/nss/ssl/sslsecur.c (revision 227363) ++++ net/third_party/nss/ssl/sslsecur.c (working copy) +@@ -99,21 +99,12 @@ + 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)); +- /* 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); +- } +- ss->firstHsDone = PR_TRUE; +- ss->gs.writeOffset = 0; +- ss->gs.readOffset = 0; + break; + } + rv = (*ss->handshake)(ss); +@@ -206,6 +197,7 @@ + ssl_Get1stHandshakeLock(ss); + + ss->firstHsDone = PR_FALSE; ++ ss->enoughFirstHsDone = PR_FALSE; + if ( asServer ) { + ss->handshake = ssl2_BeginServerHandshake; + ss->handshaking = sslHandshakingAsServer; +@@ -221,6 +213,8 @@ + 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. +@@ -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 @@ + return SECSuccess; + } + ++/* Register an application callback to be called when false start may happen. ++** Acquires and releases HandshakeLock. ++*/ ++SECStatus ++SSL_SetCanFalseStartCallback(PRFileDesc *fd, SSLCanFalseStartCallback cb, ++ void *client_data) ++{ ++ sslSocket *ss; ++ ++ ss = ssl_FindSocket(fd); ++ if (!ss) { ++ SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetCanFalseStartCallback", ++ SSL_GETPID(), fd)); ++ return SECFailure; ++ } ++ ++ if (!ss->opt.useSecurity) { ++ PORT_SetError(SEC_ERROR_INVALID_ARGS); ++ return SECFailure; ++ } ++ ++ ssl_Get1stHandshakeLock(ss); ++ ssl_GetSSL3HandshakeLock(ss); ++ ++ ss->canFalseStartCallback = cb; ++ ss->canFalseStartCallbackData = client_data; ++ ++ ssl_ReleaseSSL3HandshakeLock(ss); ++ ssl_Release1stHandshakeLock(ss); ++ ++ return SECSuccess; ++} ++ ++/* A utility function that can be called from a custom 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) ++{ ++ sslSocket *ss; ++ ++ *canFalseStart = PR_FALSE; ++ ss = ssl_FindSocket(fd); ++ if (!ss) { ++ SSL_DBG(("%d: SSL[%d]: bad socket in SSL_DefaultCanFalseStart", ++ SSL_GETPID(), fd)); ++ return SECFailure; ++ } ++ ++ if (!ss->ssl3.initialized) { ++ PORT_SetError(SEC_ERROR_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 +@@ -1195,12 +1258,7 @@ + ssl_Get1stHandshakeLock(ss); + if (ss->version >= SSL_LIBRARY_VERSION_3_0) { + ssl_GetSSL3HandshakeLock(ss); +- if ((ss->ssl3.hs.ws == wait_change_cipher || +- ss->ssl3.hs.ws == wait_finished || +- ss->ssl3.hs.ws == wait_new_session_ticket) && +- ssl3_CanFalseStart(ss)) { +- canFalseStart = PR_TRUE; +- } ++ canFalseStart = ss->ssl3.hs.canFalseStart; + ssl_ReleaseSSL3HandshakeLock(ss); + } + if (!canFalseStart && +Index: net/third_party/nss/ssl/sslsock.c +=================================================================== +--- net/third_party/nss/ssl/sslsock.c (revision 227363) ++++ net/third_party/nss/ssl/sslsock.c (working copy) +@@ -2457,10 +2457,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 +- ** 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)) { +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/ssl.h b/net/third_party/nss/ssl/ssl.h index 4f51a7e..47468a0 100644 --- a/net/third_party/nss/ssl/ssl.h +++ b/net/third_party/nss/ssl/ssl.h @@ -121,14 +121,22 @@ 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. + * + * See SSL_DefaultCanFalseStart for the default criteria that NSS uses to + * determine whether to false start or not. See SSL_SetCanFalseStartCallback + * for how to change that criteria. In addition to those criteria, false start + * will only be done when the server selects a cipher suite with an effective + * key length of 80 bits or more (including RC4-128). Also, see + * SSL_HandshakeCallback for a description on how false start affects when the + * handshake callback gets called. + */ /* For SSL 3.0 and TLS 1.0, by default we prevent chosen plaintext attacks * on SSL CBC mode cipher suites (see RFC 4346 Section F.3) by splitting @@ -741,14 +749,59 @@ 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 normally gets called when the TLS handshake +** is complete. If false start is not enabled, then the handshake callback is +** called after verifying the peer's Finished message and before sending +** outgoing application data and before processing incoming application data. +** +** If false start is enabled and there is a custom CanFalseStartCallback +** callback set, then the handshake callback gets called after the peer's +** Finished message has been verified, which may be after application data is +** sent. +** +** If false start is enabled and there is not a custom CanFalseStartCallback +** callback established with SSL_SetCanFalseStartCallback then the handshake +** callback gets called before any application data is sent, which may be +** before the peer's Finished message has been verified. */ typedef void (PR_CALLBACK *SSLHandshakeCallback)(PRFileDesc *fd, void *client_data); SSL_IMPORT SECStatus SSL_HandshakeCallback(PRFileDesc *fd, SSLHandshakeCallback cb, void *client_data); +/* Applications that wish to customize TLS false start should set this callback +** function. NSS will invoke the functon to determine if a particular +** connection should use false start or not. SECSuccess indicates that the +** callback completed successfully, and if so *canFalseStart indicates if false +** start can be used. If the callback does not return SECSuccess then the +** handshake will be canceled. +** +** Applications that do not set the callback will use an internal set of +** criteria to determine if the connection should false start. If +** the callback is set false start will never be used without invoking the +** callback function, but some connections (e.g. resumed connections) will +** never use false start and therefore will not invoke the callback. +** +** NSS's internal criteria for this connection can be evaluated by calling +** SSL_DefaultCanFalseStart() from the custom callback. +** +** See the description of SSL_HandshakeCallback for important information on +** how registering a custom false start callback affects when the handshake +** callback gets called. +**/ +typedef SECStatus (PR_CALLBACK *SSLCanFalseStartCallback)( + PRFileDesc *fd, void *arg, PRBool *canFalseStart); + +SSL_IMPORT SECStatus SSL_SetCanFalseStartCallback( + PRFileDesc *fd, SSLCanFalseStartCallback callback, void *arg); + +/* A utility function that can be called from a custom CanFalseStartCallback +** function to determine what NSS would have done for this connection if the +** custom callback was not implemented. +**/ +SSL_IMPORT SECStatus SSL_DefaultCanFalseStart(PRFileDesc *fd, + PRBool *canFalseStart); + /* ** For the server, request a new handshake. For the client, begin a new ** handshake. If flushCache is non-zero, the SSL3 cache entry will be diff --git a/net/third_party/nss/ssl/ssl3con.c b/net/third_party/nss/ssl/ssl3con.c index a7617fb..7b93a63 100644 --- a/net/third_party/nss/ssl/ssl3con.c +++ b/net/third_party/nss/ssl/ssl3con.c @@ -2890,7 +2890,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) ); @@ -7344,35 +7344,42 @@ ssl3_RestartHandshakeAfterCertReq(sslSocket * ss, 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 && - - /* An attacker can control the selected ciphersuite so we only wish to - * do False Start in the case that the selected ciphersuite is - * sufficiently strong that the attack can gain no advantage. - * Therefore we require an 80-bit cipher and a forward-secret key - * exchange. */ - ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10 && - (ss->ssl3.hs.kea_def->kea == kea_dhe_dss || - ss->ssl3.hs.kea_def->kea == kea_dhe_rsa || - ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa || - ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa); + if (ss->ssl3.cwSpec->cipher_def->secret_key_size < 10) { + ss->ssl3.hs.canFalseStart = PR_FALSE; + maybeFalseStart = PR_FALSE; + } ssl_ReleaseSpecReadLock(ss); + if (!maybeFalseStart) { + return SECSuccess; + } + + if (!ss->canFalseStartCallback) { + rv = SSL_DefaultCanFalseStart(ss->fd, &ss->ssl3.hs.canFalseStart); + } 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 @@ 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. */ } } + 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 @@ 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)) { + 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 @@ 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) { @@ -10278,6 +10325,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 @@ -10287,7 +10340,39 @@ 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_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 @@ xmit_loser: 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 @@ ssl3_FinishHandshake(sslSocket * ss) /* 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 @@ ssl3_FinishHandshake(sslSocket * ss) } 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/ssl3gthr.c b/net/third_party/nss/ssl/ssl3gthr.c index 6d62515..7385d65 100644 --- a/net/third_party/nss/ssl/ssl3gthr.c +++ b/net/third_party/nss/ssl/ssl3gthr.c @@ -374,9 +374,7 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags) */ if (ss->opt.enableFalseStart) { ssl_GetSSL3HandshakeLock(ss); - canFalseStart = (ss->ssl3.hs.ws == wait_change_cipher || - ss->ssl3.hs.ws == wait_new_session_ticket) && - ssl3_CanFalseStart(ss); + canFalseStart = ss->ssl3.hs.canFalseStart; ssl_ReleaseSSL3HandshakeLock(ss); } } while (ss->ssl3.hs.ws != idle_handshake && diff --git a/net/third_party/nss/ssl/sslauth.c b/net/third_party/nss/ssl/sslauth.c index 70695e7..695cab8 100644 --- a/net/third_party/nss/ssl/sslauth.c +++ b/net/third_party/nss/ssl/sslauth.c @@ -100,7 +100,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) { @@ -118,14 +117,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/net/third_party/nss/ssl/sslimpl.h b/net/third_party/nss/ssl/sslimpl.h index 59140f8..614eed1 100644 --- a/net/third_party/nss/ssl/sslimpl.h +++ b/net/third_party/nss/ssl/sslimpl.h @@ -881,6 +881,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. */ @@ -1162,6 +1164,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. */ @@ -1210,6 +1216,8 @@ const unsigned char * preferredCipher; void *badCertArg; SSLHandshakeCallback handshakeCallback; void *handshakeCallbackData; + SSLCanFalseStartCallback canFalseStartCallback; + void *canFalseStartCallbackData; void *pkcs11PinArg; SSLNextProtoCallback nextProtoCallback; void *nextProtoArg; @@ -1423,7 +1431,6 @@ extern void ssl3_SetAlwaysBlock(sslSocket *ss); extern SECStatus ssl_EnableNagleDelay(sslSocket *ss, PRBool enabled); -extern PRBool ssl3_CanFalseStart(sslSocket *ss); extern SECStatus ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec, PRBool isServer, diff --git a/net/third_party/nss/ssl/sslinfo.c b/net/third_party/nss/ssl/sslinfo.c index bfc1676..df7e669 100644 --- a/net/third_party/nss/ssl/sslinfo.c +++ b/net/third_party/nss/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/net/third_party/nss/ssl/sslsecur.c b/net/third_party/nss/ssl/sslsecur.c index 0714a0b..6c7532e 100644 --- a/net/third_party/nss/ssl/sslsecur.c +++ b/net/third_party/nss/ssl/sslsecur.c @@ -99,21 +99,12 @@ ssl_Do1stHandshake(sslSocket *ss) 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)); - /* 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); - } - ss->firstHsDone = PR_TRUE; - ss->gs.writeOffset = 0; - ss->gs.readOffset = 0; break; } rv = (*ss->handshake)(ss); @@ -206,6 +197,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 +213,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. @@ -266,7 +260,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(SEC_ERROR_INVALID_ARGS); + PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2); rv = SECFailure; } else { ssl_GetSSL3HandshakeLock(ss); @@ -331,6 +325,75 @@ 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 *client_data) +{ + sslSocket *ss; + + ss = ssl_FindSocket(fd); + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetCanFalseStartCallback", + SSL_GETPID(), fd)); + return SECFailure; + } + + if (!ss->opt.useSecurity) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + ssl_Get1stHandshakeLock(ss); + ssl_GetSSL3HandshakeLock(ss); + + ss->canFalseStartCallback = cb; + ss->canFalseStartCallbackData = client_data; + + ssl_ReleaseSSL3HandshakeLock(ss); + ssl_Release1stHandshakeLock(ss); + + return SECSuccess; +} + +/* A utility function that can be called from a custom 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) +{ + sslSocket *ss; + + *canFalseStart = PR_FALSE; + ss = ssl_FindSocket(fd); + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_DefaultCanFalseStart", + SSL_GETPID(), fd)); + return SECFailure; + } + + if (!ss->ssl3.initialized) { + PORT_SetError(SEC_ERROR_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 @@ -1195,12 +1258,7 @@ ssl_SecureSend(sslSocket *ss, const unsigned char *buf, int len, int flags) ssl_Get1stHandshakeLock(ss); if (ss->version >= SSL_LIBRARY_VERSION_3_0) { ssl_GetSSL3HandshakeLock(ss); - if ((ss->ssl3.hs.ws == wait_change_cipher || - ss->ssl3.hs.ws == wait_finished || - ss->ssl3.hs.ws == wait_new_session_ticket) && - ssl3_CanFalseStart(ss)) { - canFalseStart = PR_TRUE; - } + canFalseStart = ss->ssl3.hs.canFalseStart; ssl_ReleaseSSL3HandshakeLock(ss); } if (!canFalseStart && diff --git a/net/third_party/nss/ssl/sslsock.c b/net/third_party/nss/ssl/sslsock.c index ffbccc6..072fad5 100644 --- a/net/third_party/nss/ssl/sslsock.c +++ b/net/third_party/nss/ssl/sslsock.c @@ -2457,10 +2457,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)) { |