| Index: net/third_party/nss/ssl/ssl.h |
| =================================================================== |
| --- net/third_party/nss/ssl/ssl.h (revision 227672) |
| +++ net/third_party/nss/ssl/ssl.h (working copy) |
| @@ -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 */ |
| -/* mode where an SSL client will start sending application data before */ |
| -/* verifying the server's Finished message. This means that we could end up */ |
| -/* sending data to an imposter. However, the data will be encrypted and */ |
| -/* only the true server can derive the session key. Thus, so long as the */ |
| -/* cipher isn't broken this is safe. Because of this, False Start will only */ |
| -/* occur on RSA or DH ciphersuites where the cipher's key length is >= 80 */ |
| -/* bits. The advantage of False Start is that it saves a round trip for */ |
| -/* client-speaks-first protocols when performing a full handshake. */ |
| +/* mode where an SSL client will start sending application data before |
| + * verifying the server's Finished message. This means that we could end up |
| + * sending data to an imposter. However, the data will be encrypted and |
| + * only the true server can derive the session key. Thus, so long as the |
| + * cipher isn't broken this is safe. The advantage of false start is that |
| + * it saves a round trip for client-speaks-first protocols when performing a |
| + * full handshake. |
| + * |
| + * In addition to enabling this option, the application must register a |
| + * callback using the SSL_SetCanFalseStartCallback function. |
| + */ |
| |
| /* For SSL 3.0 and TLS 1.0, by default we prevent chosen plaintext attacks |
| * on SSL CBC mode cipher suites (see RFC 4346 Section F.3) by splitting |
| @@ -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 gets called when a TLS handshake is complete. The |
| +** handshake callback is called after verifying the peer's Finished message and |
| +** before processing incoming application data. |
| +** |
| +** For the initial handshake: If the handshake false started (see |
| +** SSL_ENABLE_FALSE_START), then application data may already have been sent |
| +** before the handshake callback is called. If we did not false start then the |
| +** callback will get called before any application data is sent. |
| */ |
| typedef void (PR_CALLBACK *SSLHandshakeCallback)(PRFileDesc *fd, |
| void *client_data); |
| SSL_IMPORT SECStatus SSL_HandshakeCallback(PRFileDesc *fd, |
| SSLHandshakeCallback cb, void *client_data); |
| |
| +/* Applications that wish to enable TLS false start must set this callback |
| +** function. NSS will invoke the functon to determine if a particular |
| +** connection should use false start or not. SECSuccess indicates that the |
| +** callback completed successfully, and if so *canFalseStart indicates if false |
| +** start can be used. If the callback does not return SECSuccess then the |
| +** handshake will be canceled. NSS's recommended criteria can be evaluated by |
| +** calling SSL_RecommendedCanFalseStart. |
| +** |
| +** If no false start callback is registered then false start will never be |
| +** done, even if the SSL_ENABLE_FALSE_START option is enabled. |
| +**/ |
| +typedef SECStatus (PR_CALLBACK *SSLCanFalseStartCallback)( |
| + PRFileDesc *fd, void *arg, PRBool *canFalseStart); |
| + |
| +SSL_IMPORT SECStatus SSL_SetCanFalseStartCallback( |
| + PRFileDesc *fd, SSLCanFalseStartCallback callback, void *arg); |
| + |
| +/* This function sets *canFalseStart according to the recommended criteria for |
| +** false start. These criteria may change from release to release and may depend |
| +** on which handshake features have been negotiated and/or properties of the |
| +** certifciates/keys used on the connection. |
| +*/ |
| +SSL_IMPORT SECStatus SSL_RecommendedCanFalseStart(PRFileDesc *fd, |
| + PRBool *canFalseStart); |
| + |
| /* |
| ** For the server, request a new handshake. For the client, begin a new |
| ** handshake. If flushCache is non-zero, the SSL3 cache entry will be |
| 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 227672) |
| +++ net/third_party/nss/ssl/ssl3gthr.c (working copy) |
| @@ -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); |
| - ssl_ReleaseSSL3HandshakeLock(ss); |
| + PORT_Assert(keepGoing); |
| + ssl_GetSSL3HandshakeLock(ss); |
| + if (ss->ssl3.hs.ws == idle_handshake) { |
| + /* We are done with the current handshake so stop trying to |
| + * handshake. Note that it would be safe to test ss->firstHsDone |
| + * instead of ss->ssl3.hs.ws. By testing ss->ssl3.hs.ws instead, |
| + * we prioritize completing a renegotiation handshake over sending |
| + * application data. |
| + */ |
| + PORT_Assert(ss->firstHsDone); |
| + PORT_Assert(!ss->ssl3.hs.canFalseStart); |
| + keepGoing = PR_FALSE; |
| + } else if (ss->ssl3.hs.canFalseStart) { |
| + /* Prioritize sending application data over trying to complete |
| + * the handshake if we're false starting. |
| + * |
| + * If we were to do this check at the beginning of the loop instead |
| + * of here, then this function would become be a no-op after |
| + * receiving the ServerHelloDone in the false start case, and we |
| + * would never complete the handshake. |
| + */ |
| + PORT_Assert(!ss->firstHsDone); |
| + |
| + if (ssl3_WaitingForStartOfServerSecondRound(ss)) { |
| + keepGoing = PR_FALSE; |
| + } else { |
| + ss->ssl3.hs.canFalseStart = PR_FALSE; |
| + } |
| } |
| - } while (ss->ssl3.hs.ws != idle_handshake && |
| - !canFalseStart && |
| - ss->gs.buf.len == 0); |
| + ssl_ReleaseSSL3HandshakeLock(ss); |
| + } while (keepGoing); |
| |
| ss->gs.readOffset = 0; |
| ss->gs.writeOffset = ss->gs.buf.len; |
| @@ -404,7 +438,10 @@ |
| { |
| 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); |
| Index: net/third_party/nss/ssl/sslauth.c |
| =================================================================== |
| --- net/third_party/nss/ssl/sslauth.c (revision 227672) |
| +++ 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 227672) |
| +++ 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,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 227672) |
| +++ net/third_party/nss/ssl/sslsecur.c (working copy) |
| @@ -97,23 +97,13 @@ |
| ss->securityHandshake = 0; |
| } |
| if (ss->handshake == 0) { |
| - ssl_GetRecvBufLock(ss); |
| - ss->gs.recordLen = 0; |
| - ssl_ReleaseRecvBufLock(ss); |
| - |
| - SSL_TRC(3, ("%d: SSL[%d]: handshake is completed", |
| - SSL_GETPID(), ss->fd)); |
| - /* call handshake callback for ssl v2 */ |
| - /* for v3 this is done in ssl3_HandleFinished() */ |
| - if ((ss->handshakeCallback != NULL) && /* has callback */ |
| - (!ss->firstHsDone) && /* only first time */ |
| - (ss->version < SSL_LIBRARY_VERSION_3_0)) { /* not ssl3 */ |
| - ss->firstHsDone = PR_TRUE; |
| - (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData); |
| + /* for v3 this is done in ssl3_FinishHandshake */ |
| + if (!ss->firstHsDone && ss->version < SSL_LIBRARY_VERSION_3_0) { |
| + ssl_GetRecvBufLock(ss); |
| + ss->gs.recordLen = 0; |
| + ssl_FinishHandshake(ss); |
| + ssl_ReleaseRecvBufLock(ss); |
| } |
| - ss->firstHsDone = PR_TRUE; |
| - ss->gs.writeOffset = 0; |
| - ss->gs.readOffset = 0; |
| break; |
| } |
| rv = (*ss->handshake)(ss); |
| @@ -134,6 +124,24 @@ |
| 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; |
| + ss->enoughFirstHsDone = PR_FALSE; |
| if ( asServer ) { |
| ss->handshake = ssl2_BeginServerHandshake; |
| ss->handshaking = sslHandshakingAsServer; |
| @@ -221,6 +230,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. |
| @@ -331,6 +342,71 @@ |
| return SECSuccess; |
| } |
| |
| +/* Register an application callback to be called when false start may happen. |
| +** Acquires and releases HandshakeLock. |
| +*/ |
| +SECStatus |
| +SSL_SetCanFalseStartCallback(PRFileDesc *fd, SSLCanFalseStartCallback cb, |
| + void *arg) |
| +{ |
| + sslSocket *ss; |
| + |
| + ss = ssl_FindSocket(fd); |
| + if (!ss) { |
| + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetCanFalseStartCallback", |
| + SSL_GETPID(), fd)); |
| + return SECFailure; |
| + } |
| + |
| + if (!ss->opt.useSecurity) { |
| + PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| + return SECFailure; |
| + } |
| + |
| + ssl_Get1stHandshakeLock(ss); |
| + ssl_GetSSL3HandshakeLock(ss); |
| + |
| + ss->canFalseStartCallback = cb; |
| + ss->canFalseStartCallbackData = arg; |
| + |
| + ssl_ReleaseSSL3HandshakeLock(ss); |
| + ssl_Release1stHandshakeLock(ss); |
| + |
| + return SECSuccess; |
| +} |
| + |
| +SECStatus |
| +SSL_RecommendedCanFalseStart(PRFileDesc *fd, PRBool *canFalseStart) |
| +{ |
| + sslSocket *ss; |
| + |
| + *canFalseStart = PR_FALSE; |
| + ss = ssl_FindSocket(fd); |
| + if (!ss) { |
| + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_RecommendedCanFalseStart", |
| + SSL_GETPID(), fd)); |
| + return SECFailure; |
| + } |
| + |
| + if (!ss->ssl3.initialized) { |
| + PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| + return SECFailure; |
| + } |
| + |
| + if (ss->version < SSL_LIBRARY_VERSION_3_0) { |
| + PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2); |
| + return SECFailure; |
| + } |
| + |
| + /* Require a forward-secret key exchange. */ |
| + *canFalseStart = ss->ssl3.hs.kea_def->kea == kea_dhe_dss || |
| + ss->ssl3.hs.kea_def->kea == kea_dhe_rsa || |
| + ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa || |
| + ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa; |
| + |
| + return SECSuccess; |
| +} |
| + |
| /* Try to make progress on an SSL handshake by attempting to read the |
| ** next handshake from the peer, and sending any responses. |
| ** For non-blocking sockets, returns PR_ERROR_WOULD_BLOCK if it cannot |
| @@ -524,6 +600,9 @@ |
| 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->opt.enableFalseStart && |
| + ss->version >= SSL_LIBRARY_VERSION_3_0) { |
| ssl_GetSSL3HandshakeLock(ss); |
| - if ((ss->ssl3.hs.ws == wait_change_cipher || |
| - ss->ssl3.hs.ws == wait_finished || |
| - ss->ssl3.hs.ws == wait_new_session_ticket) && |
| - ssl3_CanFalseStart(ss)) { |
| - canFalseStart = PR_TRUE; |
| - } |
| + falseStart = ss->ssl3.hs.canFalseStart; |
| ssl_ReleaseSSL3HandshakeLock(ss); |
| } |
| - if (!canFalseStart && |
| + if (!falseStart && |
| (ss->handshake || ss->nextHandshake || ss->securityHandshake)) { |
| rv = ssl_Do1stHandshake(ss); |
| } |
| @@ -1228,6 +1304,17 @@ |
| 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 227672) |
| +++ net/third_party/nss/ssl/sslsock.c (working copy) |
| @@ -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 |
| - ** 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)) { |