security/patches/bug-713933-false-start.patch
author Nicholas Nethercote <nnethercote@mozilla.com>
Thu, 08 Aug 2013 15:53:04 -0700
changeset 155784 7db702296585f916dd83febbe1e313f087ee55ae
parent 154181 eccb3b98dadbb9a31e03992e55a0bbaa7afb2962
permissions -rw-r--r--
Bug 898914 (part 1) - Remove JSBool.

# HG changeset patch
# User Patrick McManus <mcmanus@ducksong.com>
# Date 1372656196 25200
# Node ID f6bc026a0c368178b4d327bf05de785305161d72
# Parent  89a5e4356ad1f7bc9d9d24f6409c6d963dde3ca4
Bug 713933: Make false start work with asynchronous certificate verification, r=bsmith

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