Bug 698552: Add SSL_RestartAfterAuthCertificate to mozilla-central's copy of NSS_3_13_2_BETA1, r=kaie, r=honzab
authorBrian Smith <bsmith@mozilla.com>
Thu, 01 Dec 2011 14:33:37 -0800
changeset 82732 0ef53633ccc715a5705788ae22c8a844ba0bd88d
parent 82731 9381d62e583db35eb8dc6a81b03c76b84e68a6b1
child 82733 7cd14fbf2789fd83ab093b032c81f22d3736e23a
push id519
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 00:38:35 +0000
treeherdermozilla-beta@788ea1ef610b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskaie, honzab
bugs698552
milestone11.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 698552: Add SSL_RestartAfterAuthCertificate to mozilla-central's copy of NSS_3_13_2_BETA1, r=kaie, r=honzab
security/nss/cmd/tstclnt/tstclnt.c
security/nss/lib/ssl/SSLerrs.h
security/nss/lib/ssl/ssl.def
security/nss/lib/ssl/ssl.h
security/nss/lib/ssl/ssl3con.c
security/nss/lib/ssl/ssl3gthr.c
security/nss/lib/ssl/sslerr.h
security/nss/lib/ssl/sslimpl.h
security/nss/lib/ssl/sslsecur.c
security/nss/tests/ssl/ssl.sh
security/patches/README
security/patches/bug-542832-ssl-restart-4.patch
security/patches/bug-542832-ssl-restart-tstclnt-4.patch
--- a/security/nss/cmd/tstclnt/tstclnt.c
+++ b/security/nss/cmd/tstclnt/tstclnt.c
@@ -212,16 +212,18 @@ static void Usage(const char *progName)
                     "-n nickname");
     fprintf(stderr, 
             "%-20s Bypass PKCS11 layer for SSL encryption and MACing.\n", "-B");
     fprintf(stderr, "%-20s Disable SSL v2.\n", "-2");
     fprintf(stderr, "%-20s Disable SSL v3.\n", "-3");
     fprintf(stderr, "%-20s Disable TLS (SSL v3.1).\n", "-T");
     fprintf(stderr, "%-20s Prints only payload data. Skips HTTP header.\n", "-S");
     fprintf(stderr, "%-20s Client speaks first. \n", "-f");
+    fprintf(stderr, "%-20s Use synchronous certificate validation "
+                    "(required for SSL2)\n", "-O");
     fprintf(stderr, "%-20s Override bad server cert. Make it OK.\n", "-o");
     fprintf(stderr, "%-20s Disable SSL socket locking.\n", "-s");
     fprintf(stderr, "%-20s Verbose progress reporting.\n", "-v");
     fprintf(stderr, "%-20s Use export policy.\n", "-x");
     fprintf(stderr, "%-20s Ping the server and then exit.\n", "-q");
     fprintf(stderr, "%-20s Renegotiate N times (resuming session if N>1).\n", "-r N");
     fprintf(stderr, "%-20s Enable the session ticket extension.\n", "-u");
     fprintf(stderr, "%-20s Enable compression.\n", "-z");
@@ -288,30 +290,54 @@ disableAllSSLCiphers(void)
 	    fprintf(stderr,
 	            "SSL_CipherPrefSet didn't like value 0x%04x (i = %d): %s\n",
 	    	   suite, i, SECU_Strerror(err));
 	    exit(2);
 	}
     }
 }
 
+typedef struct
+{
+   PRBool shouldPause; /* PR_TRUE if we should use asynchronous peer cert 
+                        * authentication */
+   PRBool isPaused;    /* PR_TRUE if libssl is waiting for us to validate the
+                        * peer's certificate and restart the handshake. */
+   void * dbHandle;    /* Certificate database handle to use while
+                        * authenticating the peer's certificate. */
+} ServerCertAuth;
+
 /*
  * Callback is called when incoming certificate is not valid.
  * Returns SECSuccess to accept the cert anyway, SECFailure to reject.
  */
 static SECStatus 
 ownBadCertHandler(void * arg, PRFileDesc * socket)
 {
     PRErrorCode err = PR_GetError();
     /* can log invalid cert here */
     fprintf(stderr, "Bad server certificate: %d, %s\n", err, 
             SECU_Strerror(err));
     return SECSuccess;	/* override, say it's OK. */
 }
 
+static SECStatus 
+ownAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig,
+                       PRBool isServer)
+{
+    ServerCertAuth * serverCertAuth = (ServerCertAuth *) arg;
+
+    FPRINTF(stderr, "using asynchronous certificate validation\n", progName);
+
+    PORT_Assert(serverCertAuth->shouldPause);
+    PORT_Assert(!serverCertAuth->isPaused);
+    serverCertAuth->isPaused = PR_TRUE;
+    return SECWouldBlock;
+}
+
 SECStatus
 own_GetClientAuthData(void *                       arg,
                       PRFileDesc *                 socket,
                       struct CERTDistNamesStr *    caNames,
                       struct CERTCertificateStr ** pRetCert,
                       struct SECKEYPrivateKeyStr **pRetKey)
 {
     if (verbose > 1) {
@@ -493,21 +519,47 @@ separateReqHeader(const PRFileDesc* outF
     } else if (((c) >= 'a') && ((c) <= 'f')) { \
 	i = (c) - 'a' + 10; \
     } else if (((c) >= 'A') && ((c) <= 'F')) { \
 	i = (c) - 'A' + 10; \
     } else { \
 	Usage(progName); \
     }
 
+static SECStatus
+restartHandshakeAfterServerCertIfNeeded(PRFileDesc * fd,
+                                        ServerCertAuth * serverCertAuth,
+                                        PRBool override)
+{
+    SECStatus rv;
+    
+    if (!serverCertAuth->isPaused)
+	return SECSuccess;
+    
+    FPRINTF(stderr, "%s: handshake was paused by auth certificate hook\n",
+            progName);
+
+    serverCertAuth->isPaused = PR_FALSE;
+    rv = SSL_AuthCertificate(serverCertAuth->dbHandle, fd, PR_TRUE, PR_FALSE);
+    if (rv != SECSuccess && override) {
+        rv = ownBadCertHandler(NULL, fd);
+    }
+    if (rv != SECSuccess) {
+	return rv;
+    }
+    
+    rv = SSL_RestartHandshakeAfterAuthCertificate(fd);
+
+    return rv;
+}
+    
 int main(int argc, char **argv)
 {
     PRFileDesc *       s;
     PRFileDesc *       std_out;
-    CERTCertDBHandle * handle;
     char *             host	=  NULL;
     char *             certDir  =  NULL;
     char *             nickname =  NULL;
     char *             cipherString = NULL;
     char *             tmp;
     int                multiplier = 0;
     SECStatus          rv;
     PRStatus           status;
@@ -525,51 +577,58 @@ int main(int argc, char **argv)
     int                enableFalseStart = 0;
     PRSocketOptionData opt;
     PRNetAddr          addr;
     PRPollDesc         pollset[2];
     PRBool             pingServerFirst = PR_FALSE;
     PRBool             clientSpeaksFirst = PR_FALSE;
     PRBool             wrStarted = PR_FALSE;
     PRBool             skipProtoHeader = PR_FALSE;
+    ServerCertAuth     serverCertAuth;
     int                headerSeparatorPtrnId = 0;
     int                error = 0;
     PRUint16           portno = 443;
     char *             hs1SniHostName = NULL;
     char *             hs2SniHostName = NULL;
     PLOptState *optstate;
     PLOptStatus optstatus;
     PRStatus prStatus;
 
+    serverCertAuth.shouldPause = PR_TRUE;
+    serverCertAuth.isPaused = PR_FALSE;
+    serverCertAuth.dbHandle = NULL;
+
     progName = strrchr(argv[0], '/');
     if (!progName)
 	progName = strrchr(argv[0], '\\');
     progName = progName ? progName+1 : argv[0];
 
     tmp = PR_GetEnv("NSS_DEBUG_TIMEOUT");
     if (tmp && tmp[0]) {
        int sec = PORT_Atoi(tmp);
        if (sec > 0) {
            maxInterval = PR_SecondsToInterval(sec);
        }
     }
 
     optstate = PL_CreateOptState(argc, argv,
-                                 "23BSTW:a:c:d:fgh:m:n:op:qr:suvw:xz");
+                                 "23BOSTW:a:c:d:fgh:m:n:op:qr:suvw:xz");
     while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
 	switch (optstate->option) {
 	  case '?':
 	  default : Usage(progName); 			break;
 
           case '2': disableSSL2 = 1; 			break;
 
           case '3': disableSSL3 = 1; 			break;
 
           case 'B': bypassPKCS11 = 1; 			break;
 
+          case 'O': serverCertAuth.shouldPause = PR_FALSE; break;
+
           case 'S': skipProtoHeader = PR_TRUE;                 break;
 
           case 'T': disableTLS  = 1; 			break;
 
           case 'a': if (!hs1SniHostName) {
                         hs1SniHostName = PORT_Strdup(optstate->value);
                     } else if (!hs2SniHostName) {
                         hs2SniHostName =  PORT_Strdup(optstate->value);
@@ -645,24 +704,18 @@ int main(int argc, char **argv)
     } else {
 	char *certDirTmp = certDir;
 	certDir = SECU_ConfigDirectory(certDirTmp);
 	PORT_Free(certDirTmp);
     }
     rv = NSS_Init(certDir);
     if (rv != SECSuccess) {
 	SECU_PrintError(progName, "unable to open cert database");
-#if 0
-    rv = CERT_OpenVolatileCertDB(handle);
-	CERT_SetDefaultCertDB(handle);
-#else
 	return 1;
-#endif
     }
-    handle = CERT_GetDefaultCertDB();
 
     /* set the policy bits true for all the cipher suites. */
     if (useExportPolicy)
 	NSS_SetExportPolicy();
     else
 	NSS_SetDomesticPolicy();
 
     /* all the SSL2 and SSL3 cipher suites are enabled by default. */
@@ -871,17 +924,23 @@ int main(int argc, char **argv)
     rv = SSL_OptionSet(s, SSL_ENABLE_FALSE_START, enableFalseStart);
     if (rv != SECSuccess) {
 	SECU_PrintError(progName, "error enabling false start");
 	return 1;
     }
 
     SSL_SetPKCS11PinArg(s, &pwdata);
 
-    SSL_AuthCertificateHook(s, SSL_AuthCertificate, (void *)handle);
+    serverCertAuth.dbHandle = CERT_GetDefaultCertDB();
+
+    if (serverCertAuth.shouldPause) {
+	SSL_AuthCertificateHook(s, ownAuthCertificate, &serverCertAuth);
+    } else {
+	SSL_AuthCertificateHook(s, SSL_AuthCertificate, serverCertAuth.dbHandle);
+    }
     if (override) {
 	SSL_BadCertHook(s, ownBadCertHandler, NULL);
     }
     SSL_GetClientAuthDataHook(s, own_GetClientAuthData, (void *)nickname);
     SSL_HandshakeCallback(s, handshakeCallback, hs2SniHostName);
     if (hs1SniHostName) {
         SSL_SetURL(s, hs1SniHostName);
     } else {
@@ -979,16 +1038,24 @@ int main(int argc, char **argv)
     ** socket, read data from socket and write to stdout.
     */
     FPRINTF(stderr, "%s: ready...\n", progName);
 
     while (pollset[SSOCK_FD].in_flags | pollset[STDIN_FD].in_flags) {
 	char buf[4000];	/* buffer for stdin */
 	int nb;		/* num bytes read from stdin. */
 
+	rv = restartHandshakeAfterServerCertIfNeeded(s, &serverCertAuth,
+						     override);
+	if (rv != SECSuccess) {
+	    error = 254; /* 254 (usually) means "handshake failed" */
+	    SECU_PrintError(progName, "authentication of server cert failed");
+	    goto done;
+	}
+	        
 	pollset[SSOCK_FD].out_flags = 0;
 	pollset[STDIN_FD].out_flags = 0;
 
 	FPRINTF(stderr, "%s: about to call PR_Poll !\n", progName);
 	filesReady = PR_Poll(pollset, npds, PR_INTERVAL_NO_TIMEOUT);
 	if (filesReady < 0) {
 	    SECU_PrintError(progName, "select failed");
 	    error = 1;
@@ -1037,16 +1104,25 @@ int main(int argc, char **argv)
 			    goto done;
 			}
 			cc = 0;
 		    }
 		    bufp += cc;
 		    nb   -= cc;
 		    if (nb <= 0) 
 		    	break;
+
+		    rv = restartHandshakeAfterServerCertIfNeeded(s,
+				&serverCertAuth, override);
+		    if (rv != SECSuccess) {
+			error = 254; /* 254 (usually) means "handshake failed" */
+			SECU_PrintError(progName, "authentication of server cert failed");
+			goto done;
+		    }
+
 		    pollset[SSOCK_FD].in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
 		    pollset[SSOCK_FD].out_flags = 0;
 		    FPRINTF(stderr,
 		            "%s: about to call PR_Poll on writable socket !\n", 
 			    progName);
 		    cc = PR_Poll(pollset, 1, PR_INTERVAL_NO_TIMEOUT);
 		    FPRINTF(stderr,
 		            "%s: PR_Poll returned with writable socket !\n", 
--- a/security/nss/lib/ssl/SSLerrs.h
+++ b/security/nss/lib/ssl/SSLerrs.h
@@ -406,8 +406,14 @@ ER3(SSL_ERROR_RX_UNEXPECTED_UNCOMPRESSED
 ER3(SSL_ERROR_WEAK_SERVER_EPHEMERAL_DH_KEY,    (SSL_ERROR_BASE + 115),
 "SSL received a weak ephemeral Diffie-Hellman key in Server Key Exchange handshake message.")
 
 ER3(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID,      (SSL_ERROR_BASE + 116),
 "SSL received invalid NPN extension data.")
 
 ER3(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2,  (SSL_ERROR_BASE + 117),
 "SSL feature not supported for SSL 2.0 connections.")
+
+ER3(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS, (SSL_ERROR_BASE + 118),
+"SSL feature not supported for servers.")
+
+ER3(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_CLIENTS, (SSL_ERROR_BASE + 119),
+"SSL feature not supported for clients.")
--- a/security/nss/lib/ssl/ssl.def
+++ b/security/nss/lib/ssl/ssl.def
@@ -164,11 +164,12 @@ NSSSSL_GetVersion;
 ;+    local:
 ;+       *;
 ;+};
 ;+NSS_3.13.2 {    # NSS 3.13.2 release
 ;+    global:
 SSL_SetNextProtoCallback;
 SSL_SetNextProtoNego;
 SSL_GetNextProto;
+SSL_RestartHandshakeAfterAuthCertificate;
 ;+    local:
 ;+       *;
 ;+};
--- a/security/nss/lib/ssl/ssl.h
+++ b/security/nss/lib/ssl/ssl.h
@@ -334,16 +334,29 @@ SSL_IMPORT SECStatus SSL_SecurityStatus(
 **	"fd" the socket "file" descriptor
 */
 SSL_IMPORT CERTCertificate *SSL_PeerCertificate(PRFileDesc *fd);
 
 /*
 ** Authenticate certificate hook. Called when a certificate comes in
 ** (because of SSL_REQUIRE_CERTIFICATE in SSL_Enable) to authenticate the
 ** certificate.
+**
+** The authenticate certificate hook must return SECSuccess to indicate the
+** certificate is valid, SECFailure to indicate the certificate is invalid,
+** or SECWouldBlock if the application will authenticate the certificate
+** asynchronously.
+**
+** If the authenticate certificate hook returns SECFailure, then the bad cert
+** hook will be called. The bad cert handler is NEVER called if the
+** authenticate certificate hook returns SECWouldBlock.
+** 
+** See the documentation for SSL_RestartHandshakeAfterAuthCertificate for more
+** information about the asynchronous behavior that occurs when the
+** authenticate certificate hook returns SECWouldBlock.
 */
 typedef SECStatus (PR_CALLBACK *SSLAuthCertificate)(void *arg, PRFileDesc *fd, 
                                                     PRBool checkSig,
                                                     PRBool isServer);
 
 SSL_IMPORT SECStatus SSL_AuthCertificateHook(PRFileDesc *fd, 
 					     SSLAuthCertificate f,
 				             void *arg);
@@ -437,16 +450,25 @@ SSL_IMPORT PRFileDesc *SSL_ReconfigFD(PR
  *	a - pkcs11 application specific data
  */
 SSL_IMPORT SECStatus SSL_SetPKCS11PinArg(PRFileDesc *fd, void *a);
 
 /*
 ** This is a callback for dealing with server certs that are not authenticated
 ** by the client.  The client app can decide that it actually likes the
 ** cert by some external means and restart the connection.
+**
+** The bad cert hook must return SECSuccess to override the result of the
+** authenticate certificate hook, SECFailure if the certificate should still be
+** considered invalid, or SECWouldBlock if the application will authenticate
+** the certificate asynchronously.
+**
+** See the documentation for SSL_RestartHandshakeAfterAuthCertificate for more
+** information about the asynchronous behavior that occurs when the bad cert
+** hook returns SECWouldBlock.
 */
 typedef SECStatus (PR_CALLBACK *SSLBadCertHandler)(void *arg, PRFileDesc *fd);
 SSL_IMPORT SECStatus SSL_BadCertHook(PRFileDesc *fd, SSLBadCertHandler f, 
 				     void *arg);
 
 /*
 ** Configure SSL socket for running a secure server. Needs the
 ** certificate for the server and the servers private key. The arguments
@@ -735,11 +757,58 @@ SSL_IMPORT SECStatus SSL_HandshakeNegoti
  */
 extern PRBool NSSSSL_VersionCheck(const char *importedVersion);
 
 /*
  * Returns a const string of the SSL library version.
  */
 extern const char *NSSSSL_GetVersion(void);
 
+/* Restart an SSL connection that was paused to do asynchronous certificate
+ * chain validation (when the auth certificate hook or bad cert handler
+ * returned SECWouldBlock).
+ *
+ * Currently, this function works only for the client role of a connection; it
+ * does not work for the server role.
+ *
+ * The application MUST call SSL_RestartHandshakeAfterAuthCertificate after it
+ * has successfully validated the peer's certificate to continue the SSL
+ * handshake.
+ *
+ * The application MUST NOT call SSL_RestartHandshakeAfterAuthCertificate when
+ * certificate validation fails; instead, it should just close the connection.
+ *
+ * This function will not complete the entire handshake. The application must
+ * call SSL_ForceHandshake, PR_Recv, PR_Send, etc. after calling this function
+ * to force the handshake to complete.
+ *
+ * libssl will wait for the peer's certificate to be authenticated before
+ * calling the handshake callback, sending a client certificate,
+ * sending any application data, or returning any application data to the
+ * application (on the first handshake on a connection only).
+ *
+ * However, libssl may send and receive handshake messages while waiting for
+ * the application to call SSL_RestartHandshakeAfterAuthCertificate, and it may
+ * call other callbacks (e.g, the client auth data hook) before
+ * SSL_RestartHandshakeAfterAuthCertificate has been called. 
+ *
+ * An application that uses this asynchronous mechanism will usually have lower
+ * handshake latency if it has to do public key operations on the certificate
+ * chain during the authentication, especially if it does so in parallel on
+ * another thread. However, if the application can authenticate the peer's
+ * certificate quickly then it may be more efficient to use the synchronous
+ * mechanism (i.e. returning SECFailure/SECSuccess instead of SECWouldBlock
+ * from the authenticate certificate hook).
+ *
+ * Be careful about converting an application from synchronous cert validation
+ * to asynchronous certificate validation. A naive conversion is likely to
+ * result in deadlocks; e.g. the application will wait in PR_Poll for network
+ * I/O on the connection while all network I/O on the connection is blocked
+ * waiting for this function to be called.
+ *
+ * Returns SECFailure on failure, SECSuccess on success. Never returns
+ * SECWouldBlock.
+ */
+SSL_IMPORT SECStatus SSL_RestartHandshakeAfterAuthCertificate(PRFileDesc *fd);
+
 SEC_END_PROTOS
 
 #endif /* __ssl_h_ */
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -5644,153 +5644,161 @@ loser:
     PORT_SetError(errCode);
     rv = SECFailure;
 done:
     if (arena != NULL)
     	PORT_FreeArena(arena, PR_FALSE);
     return rv;
 }
 
-/*
- * attempt to restart the handshake after asynchronously handling
- * a request for the client's certificate.
- *
- * inputs:
- *	cert	Client cert chosen by application.
- *		Note: ssl takes this reference, and does not bump the
- *		reference count.  The caller should drop its reference
- *		without calling CERT_DestroyCert after calling this function.
- *
- *	key	Private key associated with cert.  This function makes a
- *		copy of the private key, so the caller remains responsible
- *		for destroying its copy after this function returns.
- *
- *	certChain  DER-encoded certs, client cert and its signers.
- *		Note: ssl takes this reference, and does not copy the chain.
- *		The caller should drop its reference without destroying the
- *		chain.  SSL will free the chain when it is done with it.
- *
- * Return value: XXX
- *
- * XXX This code only works on the initial handshake on a connection, XXX
- *     It does not work on a subsequent handshake (redo).
- *
- * Caller holds 1stHandshakeLock.
- */
-SECStatus
-ssl3_RestartHandshakeAfterCertReq(sslSocket *         ss,
-				CERTCertificate *    cert,
-				SECKEYPrivateKey *   key,
-				CERTCertificateList *certChain)
-{
-    SECStatus        rv          = SECSuccess;
-
-    if (MSB(ss->version) == MSB(SSL_LIBRARY_VERSION_3_0)) {
-	/* XXX This code only works on the initial handshake on a connection,
-	** XXX It does not work on a subsequent handshake (redo).
-	*/
-	if (ss->handshake != 0) {
-	    ss->handshake               = ssl_GatherRecord1stHandshake;
-	    ss->ssl3.clientCertificate = cert;
-	    ss->ssl3.clientCertChain   = certChain;
-	    if (key == NULL) {
-		(void)SSL3_SendAlert(ss, alert_warning, no_certificate);
-		ss->ssl3.clientPrivateKey = NULL;
-	    } else {
-		ss->ssl3.clientPrivateKey = SECKEY_CopyPrivateKey(key);
-	    }
-	    ssl_GetRecvBufLock(ss);
-	    if (ss->ssl3.hs.msgState.buf != NULL) {
-		rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf);
-	    }
-	    ssl_ReleaseRecvBufLock(ss);
-	}
-    }
-    return rv;
-}
-
 PRBool
 ssl3_CanFalseStart(sslSocket *ss) {
     PRBool rv;
 
     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
 
+    /* XXX: does not take into account whether we are waiting for
+     * SSL_RestartHandshakeAfterAuthCertificate or
+     * SSL_RestartHandshakeAfterCertReq. If/when that is done, this function
+     * could return different results each time it would be called.
+     */
+
     ssl_GetSpecReadLock(ss);
     rv = ss->opt.enableFalseStart &&
 	 !ss->sec.isServer &&
 	 !ss->ssl3.hs.isResuming &&
 	 ss->ssl3.cwSpec &&
 	 ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10 &&
 	(ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_rsa ||
 	 ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_dh  ||
 	 ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_ecdh);
     ssl_ReleaseSpecReadLock(ss);
     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.
  */
 static SECStatus
 ssl3_HandleServerHelloDone(sslSocket *ss)
 {
     SECStatus     rv;
     SSL3WaitState ws          = ss->ssl3.hs.ws;
-    PRBool        send_verify = PR_FALSE;
 
     SSL_TRC(3, ("%d: SSL3[%d]: handle server_hello_done handshake",
 		SSL_GETPID(), ss->fd));
     PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
 
     if (ws != wait_hello_done  &&
         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;
     }
 
+    rv = ssl3_SendClientSecondRound(ss);
+
+    return rv;
+}
+
+/* Called from ssl3_HandleServerHelloDone and
+ * ssl3_RestartHandshakeAfterServerCert.
+ *
+ * Caller must hold Handshake and RecvBuf locks.
+ */
+static SECStatus
+ssl3_SendClientSecondRound(sslSocket *ss)
+{
+    SECStatus rv;
+    PRBool sendClientCert;
+
+    PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
+    PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
+
+    sendClientCert = !ss->ssl3.sendEmptyCert &&
+		     ss->ssl3.clientCertChain  != NULL &&
+		     ss->ssl3.clientPrivateKey != NULL;
+
+    /* We must wait for the server's certificate to be authenticated before
+     * sending the client certificate in order to disclosing the client
+     * certificate to an attacker that does not have a valid cert for the
+     * domain we are connecting to.
+     *
+     * XXX: We should do the same for the NPN extension, but for that we
+     * need an option to give the application the ability to leak the NPN
+     * information to get better performance.
+     *
+     * During the initial handshake on a connection, we never send/receive
+     * application data until we have authenticated the server's certificate;
+     * i.e. we have fully authenticated the handshake before using the cipher
+     * specs agreed upon for that handshake. During a renegotiation, we may
+     * continue sending and receiving application data during the handshake
+     * interleaved with the handshake records. If we were to send the client's
+     * second round for a renegotiation before the server's certificate was
+     * authenticated, then the application data sent/received after this point
+     * would be using cipher spec that hadn't been authenticated. By waiting
+     * until the server's certificate has been authenticated during 
+     * renegotiations, we ensure that renegotiations have the same property
+     * as initial handshakes; i.e. we have fully authenticated the handshake
+     * before using the cipher specs agreed upon for that handshake for
+     * application data.
+     */
+    if (ss->ssl3.hs.restartTarget) {
+        PR_NOT_REACHED("unexpected ss->ssl3.hs.restartTarget");
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    if (ss->ssl3.hs.authCertificatePending &&
+        (sendClientCert || ss->ssl3.sendEmptyCert || ss->firstHsDone)) {
+        ss->ssl3.hs.restartTarget = ssl3_SendClientSecondRound;
+        return SECWouldBlock;
+    }
+
     ssl_GetXmitBufLock(ss);		/*******************************/
 
     if (ss->ssl3.sendEmptyCert) {
 	ss->ssl3.sendEmptyCert = PR_FALSE;
 	rv = ssl3_SendEmptyCertificate(ss);
 	/* Don't send verify */
 	if (rv != SECSuccess) {
 	    goto loser;	/* error code is set. */
     	}
-    } else
-    if (ss->ssl3.clientCertChain  != NULL &&
-	ss->ssl3.clientPrivateKey != NULL) {
-	send_verify = PR_TRUE;
+    } else if (sendClientCert) {
 	rv = ssl3_SendCertificate(ss);
 	if (rv != SECSuccess) {
 	    goto loser;	/* error code is set. */
     	}
     }
 
     rv = ssl3_SendClientKeyExchange(ss);
     if (rv != SECSuccess) {
     	goto loser;	/* err is set. */
     }
 
-    if (send_verify) {
+    if (sendClientCert) {
 	rv = ssl3_SendCertificateVerify(ss);
 	if (rv != SECSuccess) {
 	    goto loser;	/* err is set. */
         }
     }
+
     rv = ssl3_SendChangeCipherSpecs(ss);
     if (rv != SECSuccess) {
 	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.
+     */
     if (!ss->firstHsDone) {
 	rv = ssl3_SendNextProto(ss);
 	if (rv != SECSuccess) {
 	    goto loser;	/* err code was set. */
 	}
     }
 
     rv = ssl3_SendFinished(ss, 0);
@@ -7809,18 +7817,16 @@ ssl3_CleanupPeerCerts(sslSocket *ss)
  * ssl3 Certificate message.
  * Caller must hold Handshake and RecvBuf locks.
  */
 static SECStatus
 ssl3_HandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     ssl3CertNode *   c;
     ssl3CertNode *   lastCert 	= NULL;
-    ssl3CertNode *   certs 	= NULL;
-    PRArenaPool *    arena 	= NULL;
     PRInt32          remaining  = 0;
     PRInt32          size;
     SECStatus        rv;
     PRBool           isServer	= (PRBool)(!!ss->sec.isServer);
     PRBool           trusted 	= PR_FALSE;
     PRBool           isTLS;
     SSL3AlertDescription desc	= bad_certificate;
     int              errCode    = SSL_ERROR_RX_MALFORMED_CERTIFICATE;
@@ -7867,21 +7873,21 @@ ssl3_HandleCertificate(sslSocket *ss, SS
 	    goto alert_loser;
     	/* This is TLS's version of a no_certificate alert. */
     	/* I'm a server. I've requested a client cert. He hasn't got one. */
 	rv = ssl3_HandleNoCertificate(ss);
 	if (rv != SECSuccess) {
 	    errCode = PORT_GetError();
 	    goto loser;
 	}
-	goto cert_block;
-    }
-
-    ss->ssl3.peerCertArena = arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
-    if ( arena == NULL ) {
+	goto server_no_cert;
+    }
+
+    ss->ssl3.peerCertArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    if (ss->ssl3.peerCertArena == NULL) {
 	goto loser;	/* don't send alerts on memory errors */
     }
 
     /* First get the peer cert. */
     remaining -= 3;
     if (remaining < 0)
 	goto decode_loser;
 
@@ -7921,17 +7927,17 @@ ssl3_HandleCertificate(sslSocket *ss, SS
 	    goto decode_loser;
 
 	certItem.data = b;
 	certItem.len = size;
 	b      += size;
 	length -= size;
 	remaining -= size;
 
-	c = PORT_ArenaNew(arena, ssl3CertNode);
+	c = PORT_ArenaNew(ss->ssl3.peerCertArena, ssl3CertNode);
 	if (c == NULL) {
 	    goto loser;	/* don't send alerts on memory errors */
 	}
 
 	c->cert = CERT_NewTempCertificate(ss->dbHandle, &certItem, NULL,
 	                                  PR_FALSE, PR_TRUE);
 	if (c->cert == NULL) {
 	    goto ambiguous_err;
@@ -7939,51 +7945,55 @@ ssl3_HandleCertificate(sslSocket *ss, SS
 
 	if (c->cert->trust)
 	    trusted = PR_TRUE;
 
 	c->next = NULL;
 	if (lastCert) {
 	    lastCert->next = c;
 	} else {
-	    certs = c;
+	    ss->ssl3.peerCertChain = c;
 	}
 	lastCert = c;
     }
 
     if (remaining != 0)
         goto decode_loser;
 
     SECKEY_UpdateCertPQG(ss->sec.peerCert);
 
+    ss->ssl3.hs.authCertificatePending = PR_FALSE;
+
     /*
      * Ask caller-supplied callback function to validate cert chain.
      */
     rv = (SECStatus)(*ss->authCertificate)(ss->authCertificateArg, ss->fd,
 					   PR_TRUE, isServer);
     if (rv) {
 	errCode = PORT_GetError();
-	if (!ss->handleBadCert) {
+	if (rv != SECWouldBlock) {
+	    if (ss->handleBadCert) {
+		rv = (*ss->handleBadCert)(ss->badCertArg, ss->fd);
+	    }
+	}
+
+	if (rv == SECWouldBlock) {
+	    if (ss->sec.isServer) {
+		errCode = SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS;
+		rv = SECFailure;
+		goto loser;
+	    }
+
+            ss->ssl3.hs.authCertificatePending = PR_TRUE;
+            rv = SECSuccess;
+	}
+        
+        if (rv != SECSuccess) {
 	    goto bad_cert;
 	}
-	rv = (SECStatus)(*ss->handleBadCert)(ss->badCertArg, ss->fd);
-	if ( rv ) {
-	    if ( rv == SECWouldBlock ) {
-		/* someone will handle this connection asynchronously*/
-		SSL_DBG(("%d: SSL3[%d]: go to async cert handler",
-			 SSL_GETPID(), ss->fd));
-		ss->ssl3.peerCertChain = certs;
-		certs               = NULL;
-		ssl3_SetAlwaysBlock(ss);
-		goto cert_block;
-	    }
-	    /* cert is bad */
-	    goto bad_cert;
-	}
-	/* cert is good */
     }
 
     ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert);
 
     if (!ss->sec.isServer) {
         CERTCertificate *cert = ss->sec.peerCert;
 
 	/* set the server authentication and key exchange types and sizes
@@ -8021,39 +8031,38 @@ ssl3_HandleCertificate(sslSocket *ss, SS
 		     * destroy pubKey and goto bad_cert
 		     */
 		}
 	    }
 #endif /* NSS_ENABLE_ECC */
 	    SECKEY_DestroyPublicKey(pubKey); 
 	    pubKey = NULL;
     	}
-    }
-
-    ss->ssl3.peerCertChain = certs;  certs = NULL;  arena = NULL;
-
-cert_block:
-    if (ss->sec.isServer) {
-	ss->ssl3.hs.ws = wait_client_key;
-    } else {
+
 	ss->ssl3.hs.ws = wait_cert_request; /* disallow server_key_exchange */
 	if (ss->ssl3.hs.kea_def->is_limited ||
 	    /* XXX OR server cert is signing only. */
 #ifdef NSS_ENABLE_ECC
 	    ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa ||
 	    ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa ||
 #endif /* NSS_ENABLE_ECC */
 	    ss->ssl3.hs.kea_def->exchKeyType == kt_dh) {
 	    ss->ssl3.hs.ws = wait_server_key; /* allow server_key_exchange */
 	}
-    }
-
-    /* rv must normally be equal to SECSuccess here.  If we called
-     * handleBadCert, it can also be SECWouldBlock.
-     */
+    } else {
+server_no_cert:
+	ss->ssl3.hs.ws = wait_client_key;
+    }
+
+    PORT_Assert(rv == SECSuccess);
+    if (rv != SECSuccess) {
+	errCode = SEC_ERROR_LIBRARY_FAILURE;
+	rv = SECFailure;
+	goto loser;
+    }
     return rv;
 
 ambiguous_err:
     errCode = PORT_GetError();
     switch (errCode) {
     case PR_OUT_OF_MEMORY_ERROR:
     case SEC_ERROR_BAD_DATABASE:
     case SEC_ERROR_NO_MEMORY:
@@ -8094,63 +8103,68 @@ bad_cert:	/* caller has set errCode. */
 
 decode_loser:
     desc = isTLS ? decode_error : bad_certificate;
 
 alert_loser:
     (void)SSL3_SendAlert(ss, alert_fatal, desc);
 
 loser:
-    ss->ssl3.peerCertChain = certs;  certs = NULL;  arena = NULL;
     ssl3_CleanupPeerCerts(ss);
 
     if (ss->sec.peerCert != NULL) {
 	CERT_DestroyCertificate(ss->sec.peerCert);
 	ss->sec.peerCert = NULL;
     }
     (void)ssl_MapLowLevelError(errCode);
     return SECFailure;
 }
 
-
-/* restart an SSL connection that we stopped to run certificate dialogs
-** XXX	Need to document here how an application marks a cert to show that
-**	the application has accepted it (overridden CERT_VerifyCert).
- *
- * XXX This code only works on the initial handshake on a connection, XXX
- *     It does not work on a subsequent handshake (redo).
- *
- * Return value: XXX
- *
- * Caller holds 1stHandshakeLock.
+static SECStatus ssl3_FinishHandshake(sslSocket *ss);
+
+/* Caller must hold 1stHandshakeLock.
 */
-int
-ssl3_RestartHandshakeAfterServerCert(sslSocket *ss)
-{
-    int rv = SECSuccess;
-
-    if (MSB(ss->version) != MSB(SSL_LIBRARY_VERSION_3_0)) {
-	SET_ERROR_CODE
-    	return SECFailure;
-    }
-    if (!ss->ssl3.initialized) {
-	SET_ERROR_CODE
-    	return SECFailure;
-    }
-
-    if (ss->handshake != NULL) {
-	ss->handshake = ssl_GatherRecord1stHandshake;
-	ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert);
-
-	ssl_GetRecvBufLock(ss);
-	if (ss->ssl3.hs.msgState.buf != NULL) {
-	    rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf);
-	}
-	ssl_ReleaseRecvBufLock(ss);
-    }
+SECStatus
+ssl3_RestartHandshakeAfterAuthCertificate(sslSocket *ss)
+{
+    SECStatus rv;
+
+    PORT_Assert(ss->opt.noLocks || ssl_Have1stHandshakeLock(ss));
+
+    if (ss->sec.isServer) {
+	PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS);
+	return SECFailure;
+    }
+
+    ssl_GetRecvBufLock(ss);
+    ssl_GetSSL3HandshakeLock(ss);
+
+    if (!ss->ssl3.hs.authCertificatePending) {
+        PORT_SetError(PR_INVALID_STATE_ERROR);
+        rv = SECFailure;
+    } else {
+        ss->ssl3.hs.authCertificatePending = PR_FALSE;
+        if (ss->ssl3.hs.restartTarget != NULL) {
+            sslRestartTarget target = ss->ssl3.hs.restartTarget;
+            ss->ssl3.hs.restartTarget = NULL;
+            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
+              * calls to SSL_ForceHandshake/PR_Send/PR_Read/etc. 
+	      */
+            if (rv == SECWouldBlock) {
+                rv = SECSuccess;
+            }
+        } else {
+            rv = SECSuccess;
+        }
+    }
+
+    ssl_ReleaseSSL3HandshakeLock(ss);
+    ssl_ReleaseRecvBufLock(ss);
 
     return rv;
 }
 
 static SECStatus
 ssl3_ComputeTLSFinished(ssl3CipherSpec *spec,
 			PRBool          isServer,
                 const   SSL3Finished *  hashes,
@@ -8494,19 +8508,16 @@ ssl3_HandleFinished(sslSocket *ss, SSL3O
     }
 
 xmit_loser:
     ssl_ReleaseXmitBufLock(ss);	/*************************************/
     if (rv != SECSuccess) {
         return rv;
     }
 
-    /* The first handshake is now completed. */
-    ss->handshake           = NULL;
-    ss->firstHsDone         = PR_TRUE;
     ss->gs.writeOffset = 0;
     ss->gs.readOffset  = 0;
 
     if (ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa) {
 	effectiveExchKeyType = kt_rsa;
     } else {
 	effectiveExchKeyType = ss->ssl3.hs.kea_def->exchKeyType;
     }
@@ -8546,20 +8557,52 @@ xmit_loser:
 					       effectiveExchKeyType);
 	    sid->u.ssl3.keys.msIsWrapped = PR_TRUE;
 	}
 	ssl_ReleaseSpecReadLock(ss);  /*************************************/
 
 	/* If the wrap failed, we don't cache the sid.
 	 * The connection continues normally however.
 	 */
-	if (rv == SECSuccess) {
-	    (*ss->sec.cache)(sid);
-	}
-    }
+	ss->ssl3.hs.cacheSID = rv == SECSuccess;
+    }
+
+    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;
+      return SECWouldBlock;
+    }
+    
+    rv = ssl3_FinishHandshake(ss);
+    return rv;
+}
+
+SECStatus
+ssl3_FinishHandshake(sslSocket * ss)
+{
+    SECStatus rv;
+    
+    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;
+
+    if (ss->sec.ci.sid->cached == never_cached &&
+	!ss->opt.noCache && ss->sec.cache && ss->ssl3.hs.cacheSID) {
+	(*ss->sec.cache)(ss->sec.ci.sid);
+    }
+
     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);
     }
 
     return SECSuccess;
--- a/security/nss/lib/ssl/ssl3gthr.c
+++ b/security/nss/lib/ssl/ssl3gthr.c
@@ -187,31 +187,63 @@ int
 ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
 {
     SSL3Ciphertext cText;
     int            rv;
     PRBool         canFalseStart = PR_FALSE;
 
     PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
     do {
-	/* bring in the next sslv3 record. */
-	rv = ssl3_GatherData(ss, &ss->gs, flags);
-	if (rv <= 0) {
-	    return rv;
-	}
-	
-	/* decipher it, and handle it if it's a handshake. 
-	 * If it's application data, ss->gs.buf will not be empty upon return. 
-	 * If it's a change cipher spec, alert, or handshake message,
-	 * ss->gs.buf.len will be 0 when ssl3_HandleRecord returns SECSuccess.
-	 */
-	cText.type    = (SSL3ContentType)ss->gs.hdr[0];
-	cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2];
-	cText.buf     = &ss->gs.inbuf;
-	rv = ssl3_HandleRecord(ss, &cText, &ss->gs.buf);
+        /* Without this, we may end up wrongly reporting
+         * SSL_ERROR_RX_UNEXPECTED_* errors if we receive any records from the
+         * peer while we are waiting to be restarted. 
+         */
+        ssl_GetSSL3HandshakeLock(ss);
+        rv = ss->ssl3.hs.restartTarget == NULL ? SECSuccess : SECFailure;
+        ssl_ReleaseSSL3HandshakeLock(ss);
+        if (rv != SECSuccess) {
+            PORT_SetError(PR_WOULD_BLOCK_ERROR);
+            return (int) SECFailure;
+        }
+
+        /* Treat an empty msgState like a NULL msgState. (Most of the time
+         * when ssl3_HandleHandshake returns SECWouldBlock, it leaves
+         * behind a non-NULL but zero-length msgState).
+         * Test: async_cert_restart_server_sends_hello_request_first_in_separate_record
+         */
+        if (ss->ssl3.hs.msgState.buf != NULL) {
+            if (ss->ssl3.hs.msgState.len == 0) {
+                ss->ssl3.hs.msgState.buf = NULL;
+            }
+        }
+
+        if (ss->ssl3.hs.msgState.buf != NULL) {
+            /* ssl3_HandleHandshake previously returned SECWouldBlock and the
+             * as-yet-unprocessed plaintext of that previous handshake record.
+             * We need to process it now before we overwrite it with the next
+             * handshake record.
+             */
+	    rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf);
+        } else {
+	    /* bring in the next sslv3 record. */
+	    rv = ssl3_GatherData(ss, &ss->gs, flags);
+	    if (rv <= 0) {
+	        return rv;
+	    }
+
+	    /* decipher it, and handle it if it's a handshake. 
+	     * If it's application data, ss->gs.buf will not be empty upon return. 
+	     * If it's a change cipher spec, alert, or handshake message,
+	     * ss->gs.buf.len will be 0 when ssl3_HandleRecord returns SECSuccess.
+	     */
+	    cText.type    = (SSL3ContentType)ss->gs.hdr[0];
+	    cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2];
+	    cText.buf     = &ss->gs.inbuf;
+	    rv = ssl3_HandleRecord(ss, &cText, &ss->gs.buf);
+        }
 	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) {
--- a/security/nss/lib/ssl/sslerr.h
+++ b/security/nss/lib/ssl/sslerr.h
@@ -203,14 +203,16 @@ SSL_ERROR_UNSAFE_NEGOTIATION            
 
 SSL_ERROR_RX_UNEXPECTED_UNCOMPRESSED_RECORD	= (SSL_ERROR_BASE + 114),
 
 SSL_ERROR_WEAK_SERVER_EPHEMERAL_DH_KEY  = (SSL_ERROR_BASE + 115),
 
 SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID	= (SSL_ERROR_BASE + 116),
 
 SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2 = (SSL_ERROR_BASE + 117),
+SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS = (SSL_ERROR_BASE + 118),
+SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_CLIENTS = (SSL_ERROR_BASE + 119),
 
 SSL_ERROR_END_OF_LIST	/* let the c compiler determine the value of this. */
 } SSLErrorCodes;
 #endif /* NO_SECURITY_ERROR_ENUM */
 
 #endif /* __SSL_ERR_H_ */
--- a/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/lib/ssl/sslimpl.h
@@ -745,16 +745,18 @@ struct TLSExtensionDataStr {
     /* SNI Extension related data
      * Names data is not coppied from the input buffer. It can not be
      * used outside the scope where input buffer is defined and that
      * is beyond ssl3_HandleClientHello function. */
     SECItem *sniNameArr;
     PRUint32 sniNameArrSize;
 };
 
+typedef SECStatus (*sslRestartTarget)(sslSocket *);
+
 /*
 ** This is the "hs" member of the "ssl3" struct.
 ** This entire struct is protected by ssl3HandshakeLock
 */
 typedef struct SSL3HandshakeStateStr {
     SSL3Random            server_random;
     SSL3Random            client_random;
     SSL3WaitState         ws;
@@ -784,16 +786,23 @@ const ssl3CipherSuiteDef *suite_def;
     union {
 	TLSFinished       tFinished[2]; /* client, then server */
 	SSL3Hashes        sFinished[2];
 	SSL3Opaque        data[72];
     }                     finishedMsgs;
 #ifdef NSS_ENABLE_ECC
     PRUint32              negotiatedECCurves; /* bit mask */
 #endif /* NSS_ENABLE_ECC */
+
+    PRBool                authCertificatePending;
+    /* Which function should SSL_RestartHandshake* call if we're blocked?
+     * One of NULL, ssl3_SendClientSecondRound, or ssl3_FinishHandshake. */
+    sslRestartTarget      restartTarget;
+    /* Shared state between ssl3_HandleFinished and ssl3_FinishHandshake */
+    PRBool                cacheSID; 
 } SSL3HandshakeState;
 
 
 
 /*
 ** This is the "ssl3" struct, as in "ss->ssl3".
 ** note:
 ** usually,   crSpec == cwSpec and prSpec == pwSpec.  
@@ -1335,32 +1344,26 @@ extern SECStatus ssl3_KeyAndMacDeriveByp
 		    PRBool isTLS, PRBool isExport);
 extern  SECStatus ssl3_MasterKeyDeriveBypass( ssl3CipherSpec * pwSpec,
 		    const unsigned char * cr, const unsigned char * sr,
 		    const SECItem * pms, PRBool isTLS, PRBool isRSA);
 
 /* These functions are called from secnav, even though they're "private". */
 
 extern int ssl2_SendErrorMessage(struct sslSocketStr *ss, int error);
-extern int SSL_RestartHandshakeAfterServerCert(struct sslSocketStr *ss);
 extern int SSL_RestartHandshakeAfterCertReq(struct sslSocketStr *ss,
 					    CERTCertificate *cert,
 					    SECKEYPrivateKey *key,
 					    CERTCertificateList *certChain);
 extern sslSocket *ssl_FindSocket(PRFileDesc *fd);
 extern void ssl_FreeSocket(struct sslSocketStr *ssl);
 extern SECStatus SSL3_SendAlert(sslSocket *ss, SSL3AlertLevel level,
 				SSL3AlertDescription desc);
 
-extern SECStatus ssl3_RestartHandshakeAfterCertReq(sslSocket *    ss,
-					     CERTCertificate *    cert, 
-					     SECKEYPrivateKey *   key,
-					     CERTCertificateList *certChain);
-
-extern int ssl3_RestartHandshakeAfterServerCert(sslSocket *ss);
+extern SECStatus ssl3_RestartHandshakeAfterAuthCertificate(sslSocket *ss);
 
 /*
  * for dealing with SSL 3.0 clients sending SSL 2.0 format hellos
  */
 extern SECStatus ssl3_HandleV2ClientHello(
     sslSocket *ss, unsigned char *buffer, int length);
 extern SECStatus ssl3_StartHandshakeHash(
     sslSocket *ss, unsigned char *buf, int length);
--- a/security/nss/lib/ssl/sslsecur.c
+++ b/security/nss/lib/ssl/sslsecur.c
@@ -1458,86 +1458,66 @@ SSL_CertDBHandleSet(PRFileDesc *fd, CERT
     if (!dbHandle) {
     	PORT_SetError(SEC_ERROR_INVALID_ARGS);
 	return SECFailure;
     }
     ss->dbHandle = dbHandle;
     return SECSuccess;
 }
 
-/*
- * attempt to restart the handshake after asynchronously handling
- * a request for the client's certificate.
- *
- * inputs:  
- *	cert	Client cert chosen by application.
- *		Note: ssl takes this reference, and does not bump the 
- *		reference count.  The caller should drop its reference
- *		without calling CERT_DestroyCert after calling this function.
- *
- *	key	Private key associated with cert.  This function makes a 
- *		copy of the private key, so the caller remains responsible 
- *		for destroying its copy after this function returns.
- *
- *	certChain  Chain of signers for cert.  
- *		Note: ssl takes this reference, and does not copy the chain.
- *		The caller should drop its reference without destroying the 
- *		chain.  SSL will free the chain when it is done with it.
- *
- * Return value: XXX
- *
- * XXX This code only works on the initial handshake on a connection, XXX
- *     It does not work on a subsequent handshake (redo).
+/* DO NOT USE. This function was exported in ssl.def with the wrong signature;
+ * this implementation exists to maintain link-time compatibility.
  */
 int
 SSL_RestartHandshakeAfterCertReq(sslSocket *         ss,
 				CERTCertificate *    cert, 
 				SECKEYPrivateKey *   key,
 				CERTCertificateList *certChain)
 {
-    int              ret;
-
-    ssl_Get1stHandshakeLock(ss);   /************************************/
+    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+    return -1;
+}
 
-    if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
-	ret = ssl3_RestartHandshakeAfterCertReq(ss, cert, key, certChain);
-    } else {
-    	PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
-    	ret = SECFailure;
-    }
-
-    ssl_Release1stHandshakeLock(ss);  /************************************/
-    return ret;
+/* DO NOT USE. This function was exported in ssl.def with the wrong signature;
+ * this implementation exists to maintain link-time compatibility.
+ */
+int
+SSL_RestartHandshakeAfterServerCert(sslSocket * ss)
+{
+    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+    return -1;
 }
 
+/* See documentation in ssl.h */
+SECStatus
+SSL_RestartHandshakeAfterAuthCertificate(PRFileDesc *fd)
+{
+    SECStatus rv = SECSuccess;
+    sslSocket *ss = ssl_FindSocket(fd);
 
-/* restart an SSL connection that we stopped to run certificate dialogs 
-** XXX	Need to document here how an application marks a cert to show that
-**	the application has accepted it (overridden CERT_VerifyCert).
- *
- * XXX This code only works on the initial handshake on a connection, XXX
- *     It does not work on a subsequent handshake (redo).
- *
- * Return value: XXX
-*/
-int
-SSL_RestartHandshakeAfterServerCert(sslSocket *ss)
-{
-    int rv	= SECSuccess;
+    if (!ss) {
+	SSL_DBG(("%d: SSL[%d]: bad socket in SSL_RestartHandshakeAfterPeerCert",
+		 SSL_GETPID(), fd));
+	return SECFailure;
+    }
+
+    ssl_Get1stHandshakeLock(ss);
 
-    ssl_Get1stHandshakeLock(ss); 
-
-    if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
-	rv = ssl3_RestartHandshakeAfterServerCert(ss);
+    if (!ss->ssl3.initialized) {
+	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+	rv = SECFailure;
+    } else if (ss->version < SSL_LIBRARY_VERSION_3_0) {
+	PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
+	rv = SECFailure;
     } else {
-    	PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
-    	rv = SECFailure;
+	rv = ssl3_RestartHandshakeAfterAuthCertificate(ss);
     }
 
     ssl_Release1stHandshakeLock(ss);
+
     return rv;
 }
 
 /* For more info see ssl.h */
 SECStatus 
 SSL_SNISocketConfigHook(PRFileDesc *fd, SSLSNISocketConfig func,
                         void *arg)
 {
--- a/security/nss/tests/ssl/ssl.sh
+++ b/security/nss/tests/ssl/ssl.sh
@@ -303,16 +303,26 @@ ssl_cov()
                
   exec < ${SSLCOV}
   while read ectype tls param testname
   do
       echo "${testname}" | grep "EXPORT" > /dev/null 
       EXP=$?
       echo "${testname}" | grep "SSL2" > /dev/null
       SSL2=$?
+
+      if [ "${SSL2}" -eq 0 ] ; then
+          # We cannot use asynchronous cert verification with SSL2
+          SSL2_FLAGS=-O
+      else
+          # Do not enable SSL2 for non-SSL2-specific tests. SSL2 is disabled by
+          # default in libssl but it is enabled by default in tstclnt; we want
+          # to test the libssl default whenever possible.
+          SSL2_FLAGS=-2
+      fi
       
       if [ "$NORM_EXT" = "Extended Test" -a "${SSL2}" -eq 0 ] ; then
           echo "$SCRIPTNAME: skipping  $testname for $NORM_EXT"
       elif [ "$ectype" = "ECC" -a -z "$NSS_ENABLE_ECC" ] ; then
           echo "$SCRIPTNAME: skipping  $testname (ECC only)"
       elif [ "$SERVER_MODE" = "fips" -o "$CLIENT_MODE" = "fips" ] && [ "$SSL2" -eq 0 -o "$EXP" -eq 0 ] ; then
           echo "$SCRIPTNAME: skipping  $testname (non-FIPS only)"
       elif [ "`echo $ectype | cut -b 1`" != "#" ] ; then
@@ -345,21 +355,21 @@ ssl_cov()
               is_selfserv_alive
             else
               kill_selfserv
               start_selfserv
               mixed=0
             fi
           fi
 
-          echo "tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} ${TLS_FLAG} ${CLIENT_OPTIONS} \\"
+          echo "tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} ${TLS_FLAG} ${SSL2_FLAGS} ${CLIENT_OPTIONS} \\"
           echo "        -f -d ${P_R_CLIENTDIR} -v -w nss < ${REQUEST_FILE}"
 
           rm ${TMP}/$HOST.tmp.$$ 2>/dev/null
-          ${PROFTOOL} ${BINDIR}/tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} ${TLS_FLAG} ${CLIENT_OPTIONS} -f \
+          ${PROFTOOL} ${BINDIR}/tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} ${TLS_FLAG} ${SSL2_FLAGS} ${CLIENT_OPTIONS} -f \
                   -d ${P_R_CLIENTDIR} -v -w nss < ${REQUEST_FILE} \
                   >${TMP}/$HOST.tmp.$$  2>&1
           ret=$?
           cat ${TMP}/$HOST.tmp.$$ 
           rm ${TMP}/$HOST.tmp.$$ 2>/dev/null
           html_msg $ret 0 "${testname}" \
                    "produced a returncode of $ret, expected is 0"
       fi
--- a/security/patches/README
+++ b/security/patches/README
@@ -1,2 +1,7 @@
 This directory contains patches that were added locally
 on top of the NSS release.
+
+bug-542832-ssl-restart-4.patch and bug-542832-ssl-restart-tstclnt-4.patch were
+added so that we could test the new PSM SSL threading code (bug 674147) and
+SPDY (bug 528288). These patches will be removed when the NSS 3.13.2 release
+that includes them is imported into mozilla-central.
new file mode 100644
--- /dev/null
+++ b/security/patches/bug-542832-ssl-restart-4.patch
@@ -0,0 +1,1076 @@
+Index: mozilla/security/nss/lib/ssl/SSLerrs.h
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/SSLerrs.h,v
+retrieving revision 1.15
+diff -u -8 -p -r1.15 SSLerrs.h
+--- mozilla/security/nss/lib/ssl/SSLerrs.h	11 Nov 2011 19:06:51 -0000	1.15
++++ mozilla/security/nss/lib/ssl/SSLerrs.h	16 Nov 2011 08:21:01 -0000
+@@ -406,8 +406,14 @@ ER3(SSL_ERROR_RX_UNEXPECTED_UNCOMPRESSED
+ ER3(SSL_ERROR_WEAK_SERVER_EPHEMERAL_DH_KEY,    (SSL_ERROR_BASE + 115),
+ "SSL received a weak ephemeral Diffie-Hellman key in Server Key Exchange handshake message.")
+ 
+ ER3(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID,      (SSL_ERROR_BASE + 116),
+ "SSL received invalid NPN extension data.")
+ 
+ ER3(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2,  (SSL_ERROR_BASE + 117),
+ "SSL feature not supported for SSL 2.0 connections.")
++
++ER3(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS, (SSL_ERROR_BASE + 118),
++"SSL feature not supported for servers.")
++
++ER3(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_CLIENTS, (SSL_ERROR_BASE + 119),
++"SSL feature not supported for clients.")
+Index: mozilla/security/nss/lib/ssl/ssl.def
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/ssl.def,v
+retrieving revision 1.27
+diff -u -8 -p -r1.27 ssl.def
+--- mozilla/security/nss/lib/ssl/ssl.def	29 Oct 2011 00:29:11 -0000	1.27
++++ mozilla/security/nss/lib/ssl/ssl.def	16 Nov 2011 08:21:01 -0000
+@@ -164,11 +164,12 @@ NSSSSL_GetVersion;
+ ;+    local:
+ ;+       *;
+ ;+};
+ ;+NSS_3.13.2 {    # NSS 3.13.2 release
+ ;+    global:
+ SSL_SetNextProtoCallback;
+ SSL_SetNextProtoNego;
+ SSL_GetNextProto;
++SSL_RestartHandshakeAfterAuthCertificate;
+ ;+    local:
+ ;+       *;
+ ;+};
+Index: mozilla/security/nss/lib/ssl/ssl.h
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/ssl.h,v
+retrieving revision 1.45
+diff -u -8 -p -r1.45 ssl.h
+--- mozilla/security/nss/lib/ssl/ssl.h	29 Oct 2011 00:29:11 -0000	1.45
++++ mozilla/security/nss/lib/ssl/ssl.h	16 Nov 2011 08:21:01 -0000
+@@ -334,16 +334,29 @@ SSL_IMPORT SECStatus SSL_SecurityStatus(
+ **	"fd" the socket "file" descriptor
+ */
+ SSL_IMPORT CERTCertificate *SSL_PeerCertificate(PRFileDesc *fd);
+ 
+ /*
+ ** Authenticate certificate hook. Called when a certificate comes in
+ ** (because of SSL_REQUIRE_CERTIFICATE in SSL_Enable) to authenticate the
+ ** certificate.
++**
++** The authenticate certificate hook must return SECSuccess to indicate the
++** certificate is valid, SECFailure to indicate the certificate is invalid,
++** or SECWouldBlock if the application will authenticate the certificate
++** asynchronously.
++**
++** If the authenticate certificate hook returns SECFailure, then the bad cert
++** hook will be called. The bad cert handler is NEVER called if the
++** authenticate certificate hook returns SECWouldBlock.
++** 
++** See the documentation for SSL_RestartHandshakeAfterAuthCertificate for more
++** information about the asynchronous behavior that occurs when the
++** authenticate certificate hook returns SECWouldBlock.
+ */
+ typedef SECStatus (PR_CALLBACK *SSLAuthCertificate)(void *arg, PRFileDesc *fd, 
+                                                     PRBool checkSig,
+                                                     PRBool isServer);
+ 
+ SSL_IMPORT SECStatus SSL_AuthCertificateHook(PRFileDesc *fd, 
+ 					     SSLAuthCertificate f,
+ 				             void *arg);
+@@ -437,16 +450,25 @@ SSL_IMPORT PRFileDesc *SSL_ReconfigFD(PR
+  *	a - pkcs11 application specific data
+  */
+ SSL_IMPORT SECStatus SSL_SetPKCS11PinArg(PRFileDesc *fd, void *a);
+ 
+ /*
+ ** This is a callback for dealing with server certs that are not authenticated
+ ** by the client.  The client app can decide that it actually likes the
+ ** cert by some external means and restart the connection.
++**
++** The bad cert hook must return SECSuccess to override the result of the
++** authenticate certificate hook, SECFailure if the certificate should still be
++** considered invalid, or SECWouldBlock if the application will authenticate
++** the certificate asynchronously.
++**
++** See the documentation for SSL_RestartHandshakeAfterAuthCertificate for more
++** information about the asynchronous behavior that occurs when the bad cert
++** hook returns SECWouldBlock.
+ */
+ typedef SECStatus (PR_CALLBACK *SSLBadCertHandler)(void *arg, PRFileDesc *fd);
+ SSL_IMPORT SECStatus SSL_BadCertHook(PRFileDesc *fd, SSLBadCertHandler f, 
+ 				     void *arg);
+ 
+ /*
+ ** Configure SSL socket for running a secure server. Needs the
+ ** certificate for the server and the servers private key. The arguments
+@@ -735,11 +757,58 @@ SSL_IMPORT SECStatus SSL_HandshakeNegoti
+  */
+ extern PRBool NSSSSL_VersionCheck(const char *importedVersion);
+ 
+ /*
+  * Returns a const string of the SSL library version.
+  */
+ extern const char *NSSSSL_GetVersion(void);
+ 
++/* Restart an SSL connection that was paused to do asynchronous certificate
++ * chain validation (when the auth certificate hook or bad cert handler
++ * returned SECWouldBlock).
++ *
++ * Currently, this function works only for the client role of a connection; it
++ * does not work for the server role.
++ *
++ * The application MUST call SSL_RestartHandshakeAfterAuthCertificate after it
++ * has successfully validated the peer's certificate to continue the SSL
++ * handshake.
++ *
++ * The application MUST NOT call SSL_RestartHandshakeAfterAuthCertificate when
++ * certificate validation fails; instead, it should just close the connection.
++ *
++ * This function will not complete the entire handshake. The application must
++ * call SSL_ForceHandshake, PR_Recv, PR_Send, etc. after calling this function
++ * to force the handshake to complete.
++ *
++ * libssl will wait for the peer's certificate to be authenticated before
++ * calling the handshake callback, sending a client certificate,
++ * sending any application data, or returning any application data to the
++ * application (on the first handshake on a connection only).
++ *
++ * However, libssl may send and receive handshake messages while waiting for
++ * the application to call SSL_RestartHandshakeAfterAuthCertificate, and it may
++ * call other callbacks (e.g, the client auth data hook) before
++ * SSL_RestartHandshakeAfterAuthCertificate has been called. 
++ *
++ * An application that uses this asynchronous mechanism will usually have lower
++ * handshake latency if it has to do public key operations on the certificate
++ * chain during the authentication, especially if it does so in parallel on
++ * another thread. However, if the application can authenticate the peer's
++ * certificate quickly then it may be more efficient to use the synchronous
++ * mechanism (i.e. returning SECFailure/SECSuccess instead of SECWouldBlock
++ * from the authenticate certificate hook).
++ *
++ * Be careful about converting an application from synchronous cert validation
++ * to asynchronous certificate validation. A naive conversion is likely to
++ * result in deadlocks; e.g. the application will wait in PR_Poll for network
++ * I/O on the connection while all network I/O on the connection is blocked
++ * waiting for this function to be called.
++ *
++ * Returns SECFailure on failure, SECSuccess on success. Never returns
++ * SECWouldBlock.
++ */
++SSL_IMPORT SECStatus SSL_RestartHandshakeAfterAuthCertificate(PRFileDesc *fd);
++
+ SEC_END_PROTOS
+ 
+ #endif /* __ssl_h_ */
+Index: mozilla/security/nss/lib/ssl/ssl3con.c
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/ssl3con.c,v
+retrieving revision 1.155
+diff -u -8 -p -r1.155 ssl3con.c
+--- mozilla/security/nss/lib/ssl/ssl3con.c	11 Nov 2011 19:06:52 -0000	1.155
++++ mozilla/security/nss/lib/ssl/ssl3con.c	16 Nov 2011 08:21:02 -0000
+@@ -5644,153 +5644,161 @@ loser:
+     PORT_SetError(errCode);
+     rv = SECFailure;
+ done:
+     if (arena != NULL)
+     	PORT_FreeArena(arena, PR_FALSE);
+     return rv;
+ }
+ 
+-/*
+- * attempt to restart the handshake after asynchronously handling
+- * a request for the client's certificate.
+- *
+- * inputs:
+- *	cert	Client cert chosen by application.
+- *		Note: ssl takes this reference, and does not bump the
+- *		reference count.  The caller should drop its reference
+- *		without calling CERT_DestroyCert after calling this function.
+- *
+- *	key	Private key associated with cert.  This function makes a
+- *		copy of the private key, so the caller remains responsible
+- *		for destroying its copy after this function returns.
+- *
+- *	certChain  DER-encoded certs, client cert and its signers.
+- *		Note: ssl takes this reference, and does not copy the chain.
+- *		The caller should drop its reference without destroying the
+- *		chain.  SSL will free the chain when it is done with it.
+- *
+- * Return value: XXX
+- *
+- * XXX This code only works on the initial handshake on a connection, XXX
+- *     It does not work on a subsequent handshake (redo).
+- *
+- * Caller holds 1stHandshakeLock.
+- */
+-SECStatus
+-ssl3_RestartHandshakeAfterCertReq(sslSocket *         ss,
+-				CERTCertificate *    cert,
+-				SECKEYPrivateKey *   key,
+-				CERTCertificateList *certChain)
+-{
+-    SECStatus        rv          = SECSuccess;
+-
+-    if (MSB(ss->version) == MSB(SSL_LIBRARY_VERSION_3_0)) {
+-	/* XXX This code only works on the initial handshake on a connection,
+-	** XXX It does not work on a subsequent handshake (redo).
+-	*/
+-	if (ss->handshake != 0) {
+-	    ss->handshake               = ssl_GatherRecord1stHandshake;
+-	    ss->ssl3.clientCertificate = cert;
+-	    ss->ssl3.clientCertChain   = certChain;
+-	    if (key == NULL) {
+-		(void)SSL3_SendAlert(ss, alert_warning, no_certificate);
+-		ss->ssl3.clientPrivateKey = NULL;
+-	    } else {
+-		ss->ssl3.clientPrivateKey = SECKEY_CopyPrivateKey(key);
+-	    }
+-	    ssl_GetRecvBufLock(ss);
+-	    if (ss->ssl3.hs.msgState.buf != NULL) {
+-		rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf);
+-	    }
+-	    ssl_ReleaseRecvBufLock(ss);
+-	}
+-    }
+-    return rv;
+-}
+-
+ PRBool
+ ssl3_CanFalseStart(sslSocket *ss) {
+     PRBool rv;
+ 
+     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
+ 
++    /* XXX: does not take into account whether we are waiting for
++     * SSL_RestartHandshakeAfterAuthCertificate or
++     * SSL_RestartHandshakeAfterCertReq. If/when that is done, this function
++     * could return different results each time it would be called.
++     */
++
+     ssl_GetSpecReadLock(ss);
+     rv = ss->opt.enableFalseStart &&
+ 	 !ss->sec.isServer &&
+ 	 !ss->ssl3.hs.isResuming &&
+ 	 ss->ssl3.cwSpec &&
+ 	 ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10 &&
+ 	(ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_rsa ||
+ 	 ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_dh  ||
+ 	 ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_ecdh);
+     ssl_ReleaseSpecReadLock(ss);
+     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.
+  */
+ static SECStatus
+ ssl3_HandleServerHelloDone(sslSocket *ss)
+ {
+     SECStatus     rv;
+     SSL3WaitState ws          = ss->ssl3.hs.ws;
+-    PRBool        send_verify = PR_FALSE;
+ 
+     SSL_TRC(3, ("%d: SSL3[%d]: handle server_hello_done handshake",
+ 		SSL_GETPID(), ss->fd));
+     PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
+     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
+ 
+     if (ws != wait_hello_done  &&
+         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;
+     }
+ 
++    rv = ssl3_SendClientSecondRound(ss);
++
++    return rv;
++}
++
++/* Called from ssl3_HandleServerHelloDone and
++ * ssl3_RestartHandshakeAfterServerCert.
++ *
++ * Caller must hold Handshake and RecvBuf locks.
++ */
++static SECStatus
++ssl3_SendClientSecondRound(sslSocket *ss)
++{
++    SECStatus rv;
++    PRBool sendClientCert;
++
++    PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
++    PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
++
++    sendClientCert = !ss->ssl3.sendEmptyCert &&
++		     ss->ssl3.clientCertChain  != NULL &&
++		     ss->ssl3.clientPrivateKey != NULL;
++
++    /* We must wait for the server's certificate to be authenticated before
++     * sending the client certificate in order to disclosing the client
++     * certificate to an attacker that does not have a valid cert for the
++     * domain we are connecting to.
++     *
++     * XXX: We should do the same for the NPN extension, but for that we
++     * need an option to give the application the ability to leak the NPN
++     * information to get better performance.
++     *
++     * During the initial handshake on a connection, we never send/receive
++     * application data until we have authenticated the server's certificate;
++     * i.e. we have fully authenticated the handshake before using the cipher
++     * specs agreed upon for that handshake. During a renegotiation, we may
++     * continue sending and receiving application data during the handshake
++     * interleaved with the handshake records. If we were to send the client's
++     * second round for a renegotiation before the server's certificate was
++     * authenticated, then the application data sent/received after this point
++     * would be using cipher spec that hadn't been authenticated. By waiting
++     * until the server's certificate has been authenticated during 
++     * renegotiations, we ensure that renegotiations have the same property
++     * as initial handshakes; i.e. we have fully authenticated the handshake
++     * before using the cipher specs agreed upon for that handshake for
++     * application data.
++     */
++    if (ss->ssl3.hs.restartTarget) {
++        PR_NOT_REACHED("unexpected ss->ssl3.hs.restartTarget");
++        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
++        return SECFailure;
++    }
++    if (ss->ssl3.hs.authCertificatePending &&
++        (sendClientCert || ss->ssl3.sendEmptyCert || ss->firstHsDone)) {
++        ss->ssl3.hs.restartTarget = ssl3_SendClientSecondRound;
++        return SECWouldBlock;
++    }
++
+     ssl_GetXmitBufLock(ss);		/*******************************/
+ 
+     if (ss->ssl3.sendEmptyCert) {
+ 	ss->ssl3.sendEmptyCert = PR_FALSE;
+ 	rv = ssl3_SendEmptyCertificate(ss);
+ 	/* Don't send verify */
+ 	if (rv != SECSuccess) {
+ 	    goto loser;	/* error code is set. */
+     	}
+-    } else
+-    if (ss->ssl3.clientCertChain  != NULL &&
+-	ss->ssl3.clientPrivateKey != NULL) {
+-	send_verify = PR_TRUE;
++    } else if (sendClientCert) {
+ 	rv = ssl3_SendCertificate(ss);
+ 	if (rv != SECSuccess) {
+ 	    goto loser;	/* error code is set. */
+     	}
+     }
+ 
+     rv = ssl3_SendClientKeyExchange(ss);
+     if (rv != SECSuccess) {
+     	goto loser;	/* err is set. */
+     }
+ 
+-    if (send_verify) {
++    if (sendClientCert) {
+ 	rv = ssl3_SendCertificateVerify(ss);
+ 	if (rv != SECSuccess) {
+ 	    goto loser;	/* err is set. */
+         }
+     }
++
+     rv = ssl3_SendChangeCipherSpecs(ss);
+     if (rv != SECSuccess) {
+ 	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.
++     */
+     if (!ss->firstHsDone) {
+ 	rv = ssl3_SendNextProto(ss);
+ 	if (rv != SECSuccess) {
+ 	    goto loser;	/* err code was set. */
+ 	}
+     }
+ 
+     rv = ssl3_SendFinished(ss, 0);
+@@ -7809,18 +7817,16 @@ ssl3_CleanupPeerCerts(sslSocket *ss)
+  * ssl3 Certificate message.
+  * Caller must hold Handshake and RecvBuf locks.
+  */
+ static SECStatus
+ ssl3_HandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
+ {
+     ssl3CertNode *   c;
+     ssl3CertNode *   lastCert 	= NULL;
+-    ssl3CertNode *   certs 	= NULL;
+-    PRArenaPool *    arena 	= NULL;
+     PRInt32          remaining  = 0;
+     PRInt32          size;
+     SECStatus        rv;
+     PRBool           isServer	= (PRBool)(!!ss->sec.isServer);
+     PRBool           trusted 	= PR_FALSE;
+     PRBool           isTLS;
+     SSL3AlertDescription desc	= bad_certificate;
+     int              errCode    = SSL_ERROR_RX_MALFORMED_CERTIFICATE;
+@@ -7867,21 +7873,21 @@ ssl3_HandleCertificate(sslSocket *ss, SS
+ 	    goto alert_loser;
+     	/* This is TLS's version of a no_certificate alert. */
+     	/* I'm a server. I've requested a client cert. He hasn't got one. */
+ 	rv = ssl3_HandleNoCertificate(ss);
+ 	if (rv != SECSuccess) {
+ 	    errCode = PORT_GetError();
+ 	    goto loser;
+ 	}
+-	goto cert_block;
++	goto server_no_cert;
+     }
+ 
+-    ss->ssl3.peerCertArena = arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+-    if ( arena == NULL ) {
++    ss->ssl3.peerCertArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
++    if (ss->ssl3.peerCertArena == NULL) {
+ 	goto loser;	/* don't send alerts on memory errors */
+     }
+ 
+     /* First get the peer cert. */
+     remaining -= 3;
+     if (remaining < 0)
+ 	goto decode_loser;
+ 
+@@ -7921,17 +7927,17 @@ ssl3_HandleCertificate(sslSocket *ss, SS
+ 	    goto decode_loser;
+ 
+ 	certItem.data = b;
+ 	certItem.len = size;
+ 	b      += size;
+ 	length -= size;
+ 	remaining -= size;
+ 
+-	c = PORT_ArenaNew(arena, ssl3CertNode);
++	c = PORT_ArenaNew(ss->ssl3.peerCertArena, ssl3CertNode);
+ 	if (c == NULL) {
+ 	    goto loser;	/* don't send alerts on memory errors */
+ 	}
+ 
+ 	c->cert = CERT_NewTempCertificate(ss->dbHandle, &certItem, NULL,
+ 	                                  PR_FALSE, PR_TRUE);
+ 	if (c->cert == NULL) {
+ 	    goto ambiguous_err;
+@@ -7939,51 +7945,55 @@ ssl3_HandleCertificate(sslSocket *ss, SS
+ 
+ 	if (c->cert->trust)
+ 	    trusted = PR_TRUE;
+ 
+ 	c->next = NULL;
+ 	if (lastCert) {
+ 	    lastCert->next = c;
+ 	} else {
+-	    certs = c;
++	    ss->ssl3.peerCertChain = c;
+ 	}
+ 	lastCert = c;
+     }
+ 
+     if (remaining != 0)
+         goto decode_loser;
+ 
+     SECKEY_UpdateCertPQG(ss->sec.peerCert);
+ 
++    ss->ssl3.hs.authCertificatePending = PR_FALSE;
++
+     /*
+      * Ask caller-supplied callback function to validate cert chain.
+      */
+     rv = (SECStatus)(*ss->authCertificate)(ss->authCertificateArg, ss->fd,
+ 					   PR_TRUE, isServer);
+     if (rv) {
+ 	errCode = PORT_GetError();
+-	if (!ss->handleBadCert) {
+-	    goto bad_cert;
++	if (rv != SECWouldBlock) {
++	    if (ss->handleBadCert) {
++		rv = (*ss->handleBadCert)(ss->badCertArg, ss->fd);
++	    }
+ 	}
+-	rv = (SECStatus)(*ss->handleBadCert)(ss->badCertArg, ss->fd);
+-	if ( rv ) {
+-	    if ( rv == SECWouldBlock ) {
+-		/* someone will handle this connection asynchronously*/
+-		SSL_DBG(("%d: SSL3[%d]: go to async cert handler",
+-			 SSL_GETPID(), ss->fd));
+-		ss->ssl3.peerCertChain = certs;
+-		certs               = NULL;
+-		ssl3_SetAlwaysBlock(ss);
+-		goto cert_block;
++
++	if (rv == SECWouldBlock) {
++	    if (ss->sec.isServer) {
++		errCode = SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS;
++		rv = SECFailure;
++		goto loser;
+ 	    }
+-	    /* cert is bad */
++
++            ss->ssl3.hs.authCertificatePending = PR_TRUE;
++            rv = SECSuccess;
++	}
++        
++        if (rv != SECSuccess) {
+ 	    goto bad_cert;
+ 	}
+-	/* cert is good */
+     }
+ 
+     ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert);
+ 
+     if (!ss->sec.isServer) {
+         CERTCertificate *cert = ss->sec.peerCert;
+ 
+ 	/* set the server authentication and key exchange types and sizes
+@@ -8021,39 +8031,38 @@ ssl3_HandleCertificate(sslSocket *ss, SS
+ 		     * destroy pubKey and goto bad_cert
+ 		     */
+ 		}
+ 	    }
+ #endif /* NSS_ENABLE_ECC */
+ 	    SECKEY_DestroyPublicKey(pubKey); 
+ 	    pubKey = NULL;
+     	}
+-    }
+ 
+-    ss->ssl3.peerCertChain = certs;  certs = NULL;  arena = NULL;
+-
+-cert_block:
+-    if (ss->sec.isServer) {
+-	ss->ssl3.hs.ws = wait_client_key;
+-    } else {
+ 	ss->ssl3.hs.ws = wait_cert_request; /* disallow server_key_exchange */
+ 	if (ss->ssl3.hs.kea_def->is_limited ||
+ 	    /* XXX OR server cert is signing only. */
+ #ifdef NSS_ENABLE_ECC
+ 	    ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa ||
+ 	    ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa ||
+ #endif /* NSS_ENABLE_ECC */
+ 	    ss->ssl3.hs.kea_def->exchKeyType == kt_dh) {
+ 	    ss->ssl3.hs.ws = wait_server_key; /* allow server_key_exchange */
+ 	}
++    } else {
++server_no_cert:
++	ss->ssl3.hs.ws = wait_client_key;
+     }
+ 
+-    /* rv must normally be equal to SECSuccess here.  If we called
+-     * handleBadCert, it can also be SECWouldBlock.
+-     */
++    PORT_Assert(rv == SECSuccess);
++    if (rv != SECSuccess) {
++	errCode = SEC_ERROR_LIBRARY_FAILURE;
++	rv = SECFailure;
++	goto loser;
++    }
+     return rv;
+ 
+ ambiguous_err:
+     errCode = PORT_GetError();
+     switch (errCode) {
+     case PR_OUT_OF_MEMORY_ERROR:
+     case SEC_ERROR_BAD_DATABASE:
+     case SEC_ERROR_NO_MEMORY:
+@@ -8094,64 +8103,69 @@ bad_cert:	/* caller has set errCode. */
+ 
+ decode_loser:
+     desc = isTLS ? decode_error : bad_certificate;
+ 
+ alert_loser:
+     (void)SSL3_SendAlert(ss, alert_fatal, desc);
+ 
+ loser:
+-    ss->ssl3.peerCertChain = certs;  certs = NULL;  arena = NULL;
+     ssl3_CleanupPeerCerts(ss);
+ 
+     if (ss->sec.peerCert != NULL) {
+ 	CERT_DestroyCertificate(ss->sec.peerCert);
+ 	ss->sec.peerCert = NULL;
+     }
+     (void)ssl_MapLowLevelError(errCode);
+     return SECFailure;
+ }
+ 
++static SECStatus ssl3_FinishHandshake(sslSocket *ss);
+ 
+-/* restart an SSL connection that we stopped to run certificate dialogs
+-** XXX	Need to document here how an application marks a cert to show that
+-**	the application has accepted it (overridden CERT_VerifyCert).
+- *
+- * XXX This code only works on the initial handshake on a connection, XXX
+- *     It does not work on a subsequent handshake (redo).
+- *
+- * Return value: XXX
+- *
+- * Caller holds 1stHandshakeLock.
++/* Caller must hold 1stHandshakeLock.
+ */
+-int
+-ssl3_RestartHandshakeAfterServerCert(sslSocket *ss)
++SECStatus
++ssl3_RestartHandshakeAfterAuthCertificate(sslSocket *ss)
+ {
+-    int rv = SECSuccess;
++    SECStatus rv;
+ 
+-    if (MSB(ss->version) != MSB(SSL_LIBRARY_VERSION_3_0)) {
+-	SET_ERROR_CODE
+-    	return SECFailure;
+-    }
+-    if (!ss->ssl3.initialized) {
+-	SET_ERROR_CODE
+-    	return SECFailure;
++    PORT_Assert(ss->opt.noLocks || ssl_Have1stHandshakeLock(ss));
++
++    if (ss->sec.isServer) {
++	PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS);
++	return SECFailure;
+     }
+ 
+-    if (ss->handshake != NULL) {
+-	ss->handshake = ssl_GatherRecord1stHandshake;
+-	ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert);
++    ssl_GetRecvBufLock(ss);
++    ssl_GetSSL3HandshakeLock(ss);
+ 
+-	ssl_GetRecvBufLock(ss);
+-	if (ss->ssl3.hs.msgState.buf != NULL) {
+-	    rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf);
+-	}
+-	ssl_ReleaseRecvBufLock(ss);
++    if (!ss->ssl3.hs.authCertificatePending) {
++        PORT_SetError(PR_INVALID_STATE_ERROR);
++        rv = SECFailure;
++    } else {
++        ss->ssl3.hs.authCertificatePending = PR_FALSE;
++        if (ss->ssl3.hs.restartTarget != NULL) {
++            sslRestartTarget target = ss->ssl3.hs.restartTarget;
++            ss->ssl3.hs.restartTarget = NULL;
++            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
++              * calls to SSL_ForceHandshake/PR_Send/PR_Read/etc. 
++	      */
++            if (rv == SECWouldBlock) {
++                rv = SECSuccess;
++            }
++        } else {
++            rv = SECSuccess;
++        }
+     }
+ 
++    ssl_ReleaseSSL3HandshakeLock(ss);
++    ssl_ReleaseRecvBufLock(ss);
++
+     return rv;
+ }
+ 
+ static SECStatus
+ ssl3_ComputeTLSFinished(ssl3CipherSpec *spec,
+ 			PRBool          isServer,
+                 const   SSL3Finished *  hashes,
+                         TLSFinished  *  tlsFinished)
+@@ -8494,19 +8508,16 @@ ssl3_HandleFinished(sslSocket *ss, SSL3O
+     }
+ 
+ xmit_loser:
+     ssl_ReleaseXmitBufLock(ss);	/*************************************/
+     if (rv != SECSuccess) {
+         return rv;
+     }
+ 
+-    /* The first handshake is now completed. */
+-    ss->handshake           = NULL;
+-    ss->firstHsDone         = PR_TRUE;
+     ss->gs.writeOffset = 0;
+     ss->gs.readOffset  = 0;
+ 
+     if (ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa) {
+ 	effectiveExchKeyType = kt_rsa;
+     } else {
+ 	effectiveExchKeyType = ss->ssl3.hs.kea_def->exchKeyType;
+     }
+@@ -8546,20 +8557,52 @@ xmit_loser:
+ 					       effectiveExchKeyType);
+ 	    sid->u.ssl3.keys.msIsWrapped = PR_TRUE;
+ 	}
+ 	ssl_ReleaseSpecReadLock(ss);  /*************************************/
+ 
+ 	/* If the wrap failed, we don't cache the sid.
+ 	 * The connection continues normally however.
+ 	 */
+-	if (rv == SECSuccess) {
+-	    (*ss->sec.cache)(sid);
+-	}
++	ss->ssl3.hs.cacheSID = rv == SECSuccess;
+     }
++
++    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;
++      return SECWouldBlock;
++    }
++    
++    rv = ssl3_FinishHandshake(ss);
++    return rv;
++}
++
++SECStatus
++ssl3_FinishHandshake(sslSocket * ss)
++{
++    SECStatus rv;
++    
++    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;
++
++    if (ss->sec.ci.sid->cached == never_cached &&
++	!ss->opt.noCache && ss->sec.cache && ss->ssl3.hs.cacheSID) {
++	(*ss->sec.cache)(ss->sec.ci.sid);
++    }
++
+     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);
+     }
+ 
+     return SECSuccess;
+Index: mozilla/security/nss/lib/ssl/ssl3gthr.c
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/ssl3gthr.c,v
+retrieving revision 1.10
+diff -u -8 -p -r1.10 ssl3gthr.c
+--- mozilla/security/nss/lib/ssl/ssl3gthr.c	30 Jul 2010 03:00:17 -0000	1.10
++++ mozilla/security/nss/lib/ssl/ssl3gthr.c	16 Nov 2011 08:21:02 -0000
+@@ -187,31 +187,63 @@ int
+ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
+ {
+     SSL3Ciphertext cText;
+     int            rv;
+     PRBool         canFalseStart = PR_FALSE;
+ 
+     PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
+     do {
+-	/* bring in the next sslv3 record. */
+-	rv = ssl3_GatherData(ss, &ss->gs, flags);
+-	if (rv <= 0) {
+-	    return rv;
+-	}
+-	
+-	/* decipher it, and handle it if it's a handshake. 
+-	 * If it's application data, ss->gs.buf will not be empty upon return. 
+-	 * If it's a change cipher spec, alert, or handshake message,
+-	 * ss->gs.buf.len will be 0 when ssl3_HandleRecord returns SECSuccess.
+-	 */
+-	cText.type    = (SSL3ContentType)ss->gs.hdr[0];
+-	cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2];
+-	cText.buf     = &ss->gs.inbuf;
+-	rv = ssl3_HandleRecord(ss, &cText, &ss->gs.buf);
++        /* Without this, we may end up wrongly reporting
++         * SSL_ERROR_RX_UNEXPECTED_* errors if we receive any records from the
++         * peer while we are waiting to be restarted. 
++         */
++        ssl_GetSSL3HandshakeLock(ss);
++        rv = ss->ssl3.hs.restartTarget == NULL ? SECSuccess : SECFailure;
++        ssl_ReleaseSSL3HandshakeLock(ss);
++        if (rv != SECSuccess) {
++            PORT_SetError(PR_WOULD_BLOCK_ERROR);
++            return (int) SECFailure;
++        }
++
++        /* Treat an empty msgState like a NULL msgState. (Most of the time
++         * when ssl3_HandleHandshake returns SECWouldBlock, it leaves
++         * behind a non-NULL but zero-length msgState).
++         * Test: async_cert_restart_server_sends_hello_request_first_in_separate_record
++         */
++        if (ss->ssl3.hs.msgState.buf != NULL) {
++            if (ss->ssl3.hs.msgState.len == 0) {
++                ss->ssl3.hs.msgState.buf = NULL;
++            }
++        }
++
++        if (ss->ssl3.hs.msgState.buf != NULL) {
++            /* ssl3_HandleHandshake previously returned SECWouldBlock and the
++             * as-yet-unprocessed plaintext of that previous handshake record.
++             * We need to process it now before we overwrite it with the next
++             * handshake record.
++             */
++	    rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf);
++        } else {
++	    /* bring in the next sslv3 record. */
++	    rv = ssl3_GatherData(ss, &ss->gs, flags);
++	    if (rv <= 0) {
++	        return rv;
++	    }
++
++	    /* decipher it, and handle it if it's a handshake. 
++	     * If it's application data, ss->gs.buf will not be empty upon return. 
++	     * If it's a change cipher spec, alert, or handshake message,
++	     * ss->gs.buf.len will be 0 when ssl3_HandleRecord returns SECSuccess.
++	     */
++	    cText.type    = (SSL3ContentType)ss->gs.hdr[0];
++	    cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2];
++	    cText.buf     = &ss->gs.inbuf;
++	    rv = ssl3_HandleRecord(ss, &cText, &ss->gs.buf);
++        }
+ 	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) {
+Index: mozilla/security/nss/lib/ssl/sslerr.h
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/sslerr.h,v
+retrieving revision 1.16
+diff -u -8 -p -r1.16 sslerr.h
+--- mozilla/security/nss/lib/ssl/sslerr.h	11 Nov 2011 19:06:52 -0000	1.16
++++ mozilla/security/nss/lib/ssl/sslerr.h	16 Nov 2011 08:21:02 -0000
+@@ -203,14 +203,16 @@ SSL_ERROR_UNSAFE_NEGOTIATION            
+ 
+ SSL_ERROR_RX_UNEXPECTED_UNCOMPRESSED_RECORD	= (SSL_ERROR_BASE + 114),
+ 
+ SSL_ERROR_WEAK_SERVER_EPHEMERAL_DH_KEY  = (SSL_ERROR_BASE + 115),
+ 
+ SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID	= (SSL_ERROR_BASE + 116),
+ 
+ SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2 = (SSL_ERROR_BASE + 117),
++SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS = (SSL_ERROR_BASE + 118),
++SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_CLIENTS = (SSL_ERROR_BASE + 119),
+ 
+ SSL_ERROR_END_OF_LIST	/* let the c compiler determine the value of this. */
+ } SSLErrorCodes;
+ #endif /* NO_SECURITY_ERROR_ENUM */
+ 
+ #endif /* __SSL_ERR_H_ */
+Index: mozilla/security/nss/lib/ssl/sslimpl.h
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/sslimpl.h,v
+retrieving revision 1.87
+diff -u -8 -p -r1.87 sslimpl.h
+--- mozilla/security/nss/lib/ssl/sslimpl.h	11 Nov 2011 19:06:52 -0000	1.87
++++ mozilla/security/nss/lib/ssl/sslimpl.h	16 Nov 2011 08:21:02 -0000
+@@ -745,16 +745,18 @@ struct TLSExtensionDataStr {
+     /* SNI Extension related data
+      * Names data is not coppied from the input buffer. It can not be
+      * used outside the scope where input buffer is defined and that
+      * is beyond ssl3_HandleClientHello function. */
+     SECItem *sniNameArr;
+     PRUint32 sniNameArrSize;
+ };
+ 
++typedef SECStatus (*sslRestartTarget)(sslSocket *);
++
+ /*
+ ** This is the "hs" member of the "ssl3" struct.
+ ** This entire struct is protected by ssl3HandshakeLock
+ */
+ typedef struct SSL3HandshakeStateStr {
+     SSL3Random            server_random;
+     SSL3Random            client_random;
+     SSL3WaitState         ws;
+@@ -784,16 +786,23 @@ const ssl3CipherSuiteDef *suite_def;
+     union {
+ 	TLSFinished       tFinished[2]; /* client, then server */
+ 	SSL3Hashes        sFinished[2];
+ 	SSL3Opaque        data[72];
+     }                     finishedMsgs;
+ #ifdef NSS_ENABLE_ECC
+     PRUint32              negotiatedECCurves; /* bit mask */
+ #endif /* NSS_ENABLE_ECC */
++
++    PRBool                authCertificatePending;
++    /* Which function should SSL_RestartHandshake* call if we're blocked?
++     * One of NULL, ssl3_SendClientSecondRound, or ssl3_FinishHandshake. */
++    sslRestartTarget      restartTarget;
++    /* Shared state between ssl3_HandleFinished and ssl3_FinishHandshake */
++    PRBool                cacheSID; 
+ } SSL3HandshakeState;
+ 
+ 
+ 
+ /*
+ ** This is the "ssl3" struct, as in "ss->ssl3".
+ ** note:
+ ** usually,   crSpec == cwSpec and prSpec == pwSpec.  
+@@ -1335,32 +1344,26 @@ extern SECStatus ssl3_KeyAndMacDeriveByp
+ 		    PRBool isTLS, PRBool isExport);
+ extern  SECStatus ssl3_MasterKeyDeriveBypass( ssl3CipherSpec * pwSpec,
+ 		    const unsigned char * cr, const unsigned char * sr,
+ 		    const SECItem * pms, PRBool isTLS, PRBool isRSA);
+ 
+ /* These functions are called from secnav, even though they're "private". */
+ 
+ extern int ssl2_SendErrorMessage(struct sslSocketStr *ss, int error);
+-extern int SSL_RestartHandshakeAfterServerCert(struct sslSocketStr *ss);
+ extern int SSL_RestartHandshakeAfterCertReq(struct sslSocketStr *ss,
+ 					    CERTCertificate *cert,
+ 					    SECKEYPrivateKey *key,
+ 					    CERTCertificateList *certChain);
+ extern sslSocket *ssl_FindSocket(PRFileDesc *fd);
+ extern void ssl_FreeSocket(struct sslSocketStr *ssl);
+ extern SECStatus SSL3_SendAlert(sslSocket *ss, SSL3AlertLevel level,
+ 				SSL3AlertDescription desc);
+ 
+-extern SECStatus ssl3_RestartHandshakeAfterCertReq(sslSocket *    ss,
+-					     CERTCertificate *    cert, 
+-					     SECKEYPrivateKey *   key,
+-					     CERTCertificateList *certChain);
+-
+-extern int ssl3_RestartHandshakeAfterServerCert(sslSocket *ss);
++extern SECStatus ssl3_RestartHandshakeAfterAuthCertificate(sslSocket *ss);
+ 
+ /*
+  * for dealing with SSL 3.0 clients sending SSL 2.0 format hellos
+  */
+ extern SECStatus ssl3_HandleV2ClientHello(
+     sslSocket *ss, unsigned char *buffer, int length);
+ extern SECStatus ssl3_StartHandshakeHash(
+     sslSocket *ss, unsigned char *buf, int length);
+Index: mozilla/security/nss/lib/ssl/sslsecur.c
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/sslsecur.c,v
+retrieving revision 1.51
+diff -u -8 -p -r1.51 sslsecur.c
+--- mozilla/security/nss/lib/ssl/sslsecur.c	11 Nov 2011 19:06:52 -0000	1.51
++++ mozilla/security/nss/lib/ssl/sslsecur.c	16 Nov 2011 08:21:02 -0000
+@@ -1458,86 +1458,66 @@ SSL_CertDBHandleSet(PRFileDesc *fd, CERT
+     if (!dbHandle) {
+     	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ 	return SECFailure;
+     }
+     ss->dbHandle = dbHandle;
+     return SECSuccess;
+ }
+ 
+-/*
+- * attempt to restart the handshake after asynchronously handling
+- * a request for the client's certificate.
+- *
+- * inputs:  
+- *	cert	Client cert chosen by application.
+- *		Note: ssl takes this reference, and does not bump the 
+- *		reference count.  The caller should drop its reference
+- *		without calling CERT_DestroyCert after calling this function.
+- *
+- *	key	Private key associated with cert.  This function makes a 
+- *		copy of the private key, so the caller remains responsible 
+- *		for destroying its copy after this function returns.
+- *
+- *	certChain  Chain of signers for cert.  
+- *		Note: ssl takes this reference, and does not copy the chain.
+- *		The caller should drop its reference without destroying the 
+- *		chain.  SSL will free the chain when it is done with it.
+- *
+- * Return value: XXX
+- *
+- * XXX This code only works on the initial handshake on a connection, XXX
+- *     It does not work on a subsequent handshake (redo).
++/* DO NOT USE. This function was exported in ssl.def with the wrong signature;
++ * this implementation exists to maintain link-time compatibility.
+  */
+ int
+ SSL_RestartHandshakeAfterCertReq(sslSocket *         ss,
+ 				CERTCertificate *    cert, 
+ 				SECKEYPrivateKey *   key,
+ 				CERTCertificateList *certChain)
+ {
+-    int              ret;
+-
+-    ssl_Get1stHandshakeLock(ss);   /************************************/
+-
+-    if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
+-	ret = ssl3_RestartHandshakeAfterCertReq(ss, cert, key, certChain);
+-    } else {
+-    	PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
+-    	ret = SECFailure;
+-    }
+-
+-    ssl_Release1stHandshakeLock(ss);  /************************************/
+-    return ret;
++    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
++    return -1;
+ }
+ 
+-
+-/* restart an SSL connection that we stopped to run certificate dialogs 
+-** XXX	Need to document here how an application marks a cert to show that
+-**	the application has accepted it (overridden CERT_VerifyCert).
+- *
+- * XXX This code only works on the initial handshake on a connection, XXX
+- *     It does not work on a subsequent handshake (redo).
+- *
+- * Return value: XXX
+-*/
++/* DO NOT USE. This function was exported in ssl.def with the wrong signature;
++ * this implementation exists to maintain link-time compatibility.
++ */
+ int
+-SSL_RestartHandshakeAfterServerCert(sslSocket *ss)
++SSL_RestartHandshakeAfterServerCert(sslSocket * ss)
++{
++    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
++    return -1;
++}
++
++/* See documentation in ssl.h */
++SECStatus
++SSL_RestartHandshakeAfterAuthCertificate(PRFileDesc *fd)
+ {
+-    int rv	= SECSuccess;
++    SECStatus rv = SECSuccess;
++    sslSocket *ss = ssl_FindSocket(fd);
++
++    if (!ss) {
++	SSL_DBG(("%d: SSL[%d]: bad socket in SSL_RestartHandshakeAfterPeerCert",
++		 SSL_GETPID(), fd));
++	return SECFailure;
++    }
+ 
+-    ssl_Get1stHandshakeLock(ss); 
++    ssl_Get1stHandshakeLock(ss);
+ 
+-    if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
+-	rv = ssl3_RestartHandshakeAfterServerCert(ss);
++    if (!ss->ssl3.initialized) {
++	PORT_SetError(SEC_ERROR_INVALID_ARGS);
++	rv = SECFailure;
++    } else if (ss->version < SSL_LIBRARY_VERSION_3_0) {
++	PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
++	rv = SECFailure;
+     } else {
+-    	PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
+-    	rv = SECFailure;
++	rv = ssl3_RestartHandshakeAfterAuthCertificate(ss);
+     }
+ 
+     ssl_Release1stHandshakeLock(ss);
++
+     return rv;
+ }
+ 
+ /* For more info see ssl.h */
+ SECStatus 
+ SSL_SNISocketConfigHook(PRFileDesc *fd, SSLSNISocketConfig func,
+                         void *arg)
+ {
new file mode 100644
--- /dev/null
+++ b/security/patches/bug-542832-ssl-restart-tstclnt-4.patch
@@ -0,0 +1,349 @@
+Index: mozilla/security/nss/cmd/tstclnt/tstclnt.c
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/cmd/tstclnt/tstclnt.c,v
+retrieving revision 1.64
+diff -u -8 -p -r1.64 tstclnt.c
+--- mozilla/security/nss/cmd/tstclnt/tstclnt.c	6 Oct 2011 22:42:33 -0000	1.64
++++ mozilla/security/nss/cmd/tstclnt/tstclnt.c	16 Nov 2011 08:24:12 -0000
+@@ -212,16 +212,18 @@ static void Usage(const char *progName)
+                     "-n nickname");
+     fprintf(stderr, 
+             "%-20s Bypass PKCS11 layer for SSL encryption and MACing.\n", "-B");
+     fprintf(stderr, "%-20s Disable SSL v2.\n", "-2");
+     fprintf(stderr, "%-20s Disable SSL v3.\n", "-3");
+     fprintf(stderr, "%-20s Disable TLS (SSL v3.1).\n", "-T");
+     fprintf(stderr, "%-20s Prints only payload data. Skips HTTP header.\n", "-S");
+     fprintf(stderr, "%-20s Client speaks first. \n", "-f");
++    fprintf(stderr, "%-20s Use synchronous certificate validation "
++                    "(required for SSL2)\n", "-O");
+     fprintf(stderr, "%-20s Override bad server cert. Make it OK.\n", "-o");
+     fprintf(stderr, "%-20s Disable SSL socket locking.\n", "-s");
+     fprintf(stderr, "%-20s Verbose progress reporting.\n", "-v");
+     fprintf(stderr, "%-20s Use export policy.\n", "-x");
+     fprintf(stderr, "%-20s Ping the server and then exit.\n", "-q");
+     fprintf(stderr, "%-20s Renegotiate N times (resuming session if N>1).\n", "-r N");
+     fprintf(stderr, "%-20s Enable the session ticket extension.\n", "-u");
+     fprintf(stderr, "%-20s Enable compression.\n", "-z");
+@@ -288,30 +290,54 @@ disableAllSSLCiphers(void)
+ 	    fprintf(stderr,
+ 	            "SSL_CipherPrefSet didn't like value 0x%04x (i = %d): %s\n",
+ 	    	   suite, i, SECU_Strerror(err));
+ 	    exit(2);
+ 	}
+     }
+ }
+ 
++typedef struct
++{
++   PRBool shouldPause; /* PR_TRUE if we should use asynchronous peer cert 
++                        * authentication */
++   PRBool isPaused;    /* PR_TRUE if libssl is waiting for us to validate the
++                        * peer's certificate and restart the handshake. */
++   void * dbHandle;    /* Certificate database handle to use while
++                        * authenticating the peer's certificate. */
++} ServerCertAuth;
++
+ /*
+  * Callback is called when incoming certificate is not valid.
+  * Returns SECSuccess to accept the cert anyway, SECFailure to reject.
+  */
+ static SECStatus 
+ ownBadCertHandler(void * arg, PRFileDesc * socket)
+ {
+     PRErrorCode err = PR_GetError();
+     /* can log invalid cert here */
+     fprintf(stderr, "Bad server certificate: %d, %s\n", err, 
+             SECU_Strerror(err));
+     return SECSuccess;	/* override, say it's OK. */
+ }
+ 
++static SECStatus 
++ownAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig,
++                       PRBool isServer)
++{
++    ServerCertAuth * serverCertAuth = (ServerCertAuth *) arg;
++
++    FPRINTF(stderr, "using asynchronous certificate validation\n", progName);
++
++    PORT_Assert(serverCertAuth->shouldPause);
++    PORT_Assert(!serverCertAuth->isPaused);
++    serverCertAuth->isPaused = PR_TRUE;
++    return SECWouldBlock;
++}
++
+ SECStatus
+ own_GetClientAuthData(void *                       arg,
+                       PRFileDesc *                 socket,
+                       struct CERTDistNamesStr *    caNames,
+                       struct CERTCertificateStr ** pRetCert,
+                       struct SECKEYPrivateKeyStr **pRetKey)
+ {
+     if (verbose > 1) {
+@@ -493,21 +519,47 @@ separateReqHeader(const PRFileDesc* outF
+     } else if (((c) >= 'a') && ((c) <= 'f')) { \
+ 	i = (c) - 'a' + 10; \
+     } else if (((c) >= 'A') && ((c) <= 'F')) { \
+ 	i = (c) - 'A' + 10; \
+     } else { \
+ 	Usage(progName); \
+     }
+ 
++static SECStatus
++restartHandshakeAfterServerCertIfNeeded(PRFileDesc * fd,
++                                        ServerCertAuth * serverCertAuth,
++                                        PRBool override)
++{
++    SECStatus rv;
++    
++    if (!serverCertAuth->isPaused)
++	return SECSuccess;
++    
++    FPRINTF(stderr, "%s: handshake was paused by auth certificate hook\n",
++            progName);
++
++    serverCertAuth->isPaused = PR_FALSE;
++    rv = SSL_AuthCertificate(serverCertAuth->dbHandle, fd, PR_TRUE, PR_FALSE);
++    if (rv != SECSuccess && override) {
++        rv = ownBadCertHandler(NULL, fd);
++    }
++    if (rv != SECSuccess) {
++	return rv;
++    }
++    
++    rv = SSL_RestartHandshakeAfterAuthCertificate(fd);
++
++    return rv;
++}
++    
+ int main(int argc, char **argv)
+ {
+     PRFileDesc *       s;
+     PRFileDesc *       std_out;
+-    CERTCertDBHandle * handle;
+     char *             host	=  NULL;
+     char *             certDir  =  NULL;
+     char *             nickname =  NULL;
+     char *             cipherString = NULL;
+     char *             tmp;
+     int                multiplier = 0;
+     SECStatus          rv;
+     PRStatus           status;
+@@ -525,51 +577,58 @@ int main(int argc, char **argv)
+     int                enableFalseStart = 0;
+     PRSocketOptionData opt;
+     PRNetAddr          addr;
+     PRPollDesc         pollset[2];
+     PRBool             pingServerFirst = PR_FALSE;
+     PRBool             clientSpeaksFirst = PR_FALSE;
+     PRBool             wrStarted = PR_FALSE;
+     PRBool             skipProtoHeader = PR_FALSE;
++    ServerCertAuth     serverCertAuth;
+     int                headerSeparatorPtrnId = 0;
+     int                error = 0;
+     PRUint16           portno = 443;
+     char *             hs1SniHostName = NULL;
+     char *             hs2SniHostName = NULL;
+     PLOptState *optstate;
+     PLOptStatus optstatus;
+     PRStatus prStatus;
+ 
++    serverCertAuth.shouldPause = PR_TRUE;
++    serverCertAuth.isPaused = PR_FALSE;
++    serverCertAuth.dbHandle = NULL;
++
+     progName = strrchr(argv[0], '/');
+     if (!progName)
+ 	progName = strrchr(argv[0], '\\');
+     progName = progName ? progName+1 : argv[0];
+ 
+     tmp = PR_GetEnv("NSS_DEBUG_TIMEOUT");
+     if (tmp && tmp[0]) {
+        int sec = PORT_Atoi(tmp);
+        if (sec > 0) {
+            maxInterval = PR_SecondsToInterval(sec);
+        }
+     }
+ 
+     optstate = PL_CreateOptState(argc, argv,
+-                                 "23BSTW:a:c:d:fgh:m:n:op:qr:suvw:xz");
++                                 "23BOSTW:a:c:d:fgh:m:n:op:qr:suvw:xz");
+     while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
+ 	switch (optstate->option) {
+ 	  case '?':
+ 	  default : Usage(progName); 			break;
+ 
+           case '2': disableSSL2 = 1; 			break;
+ 
+           case '3': disableSSL3 = 1; 			break;
+ 
+           case 'B': bypassPKCS11 = 1; 			break;
+ 
++          case 'O': serverCertAuth.shouldPause = PR_FALSE; break;
++
+           case 'S': skipProtoHeader = PR_TRUE;                 break;
+ 
+           case 'T': disableTLS  = 1; 			break;
+ 
+           case 'a': if (!hs1SniHostName) {
+                         hs1SniHostName = PORT_Strdup(optstate->value);
+                     } else if (!hs2SniHostName) {
+                         hs2SniHostName =  PORT_Strdup(optstate->value);
+@@ -645,24 +704,18 @@ int main(int argc, char **argv)
+     } else {
+ 	char *certDirTmp = certDir;
+ 	certDir = SECU_ConfigDirectory(certDirTmp);
+ 	PORT_Free(certDirTmp);
+     }
+     rv = NSS_Init(certDir);
+     if (rv != SECSuccess) {
+ 	SECU_PrintError(progName, "unable to open cert database");
+-#if 0
+-    rv = CERT_OpenVolatileCertDB(handle);
+-	CERT_SetDefaultCertDB(handle);
+-#else
+ 	return 1;
+-#endif
+     }
+-    handle = CERT_GetDefaultCertDB();
+ 
+     /* set the policy bits true for all the cipher suites. */
+     if (useExportPolicy)
+ 	NSS_SetExportPolicy();
+     else
+ 	NSS_SetDomesticPolicy();
+ 
+     /* all the SSL2 and SSL3 cipher suites are enabled by default. */
+@@ -871,17 +924,23 @@ int main(int argc, char **argv)
+     rv = SSL_OptionSet(s, SSL_ENABLE_FALSE_START, enableFalseStart);
+     if (rv != SECSuccess) {
+ 	SECU_PrintError(progName, "error enabling false start");
+ 	return 1;
+     }
+ 
+     SSL_SetPKCS11PinArg(s, &pwdata);
+ 
+-    SSL_AuthCertificateHook(s, SSL_AuthCertificate, (void *)handle);
++    serverCertAuth.dbHandle = CERT_GetDefaultCertDB();
++
++    if (serverCertAuth.shouldPause) {
++	SSL_AuthCertificateHook(s, ownAuthCertificate, &serverCertAuth);
++    } else {
++	SSL_AuthCertificateHook(s, SSL_AuthCertificate, serverCertAuth.dbHandle);
++    }
+     if (override) {
+ 	SSL_BadCertHook(s, ownBadCertHandler, NULL);
+     }
+     SSL_GetClientAuthDataHook(s, own_GetClientAuthData, (void *)nickname);
+     SSL_HandshakeCallback(s, handshakeCallback, hs2SniHostName);
+     if (hs1SniHostName) {
+         SSL_SetURL(s, hs1SniHostName);
+     } else {
+@@ -979,16 +1038,24 @@ int main(int argc, char **argv)
+     ** socket, read data from socket and write to stdout.
+     */
+     FPRINTF(stderr, "%s: ready...\n", progName);
+ 
+     while (pollset[SSOCK_FD].in_flags | pollset[STDIN_FD].in_flags) {
+ 	char buf[4000];	/* buffer for stdin */
+ 	int nb;		/* num bytes read from stdin. */
+ 
++	rv = restartHandshakeAfterServerCertIfNeeded(s, &serverCertAuth,
++						     override);
++	if (rv != SECSuccess) {
++	    error = 254; /* 254 (usually) means "handshake failed" */
++	    SECU_PrintError(progName, "authentication of server cert failed");
++	    goto done;
++	}
++	        
+ 	pollset[SSOCK_FD].out_flags = 0;
+ 	pollset[STDIN_FD].out_flags = 0;
+ 
+ 	FPRINTF(stderr, "%s: about to call PR_Poll !\n", progName);
+ 	filesReady = PR_Poll(pollset, npds, PR_INTERVAL_NO_TIMEOUT);
+ 	if (filesReady < 0) {
+ 	    SECU_PrintError(progName, "select failed");
+ 	    error = 1;
+@@ -1037,16 +1104,25 @@ int main(int argc, char **argv)
+ 			    goto done;
+ 			}
+ 			cc = 0;
+ 		    }
+ 		    bufp += cc;
+ 		    nb   -= cc;
+ 		    if (nb <= 0) 
+ 		    	break;
++
++		    rv = restartHandshakeAfterServerCertIfNeeded(s,
++				&serverCertAuth, override);
++		    if (rv != SECSuccess) {
++			error = 254; /* 254 (usually) means "handshake failed" */
++			SECU_PrintError(progName, "authentication of server cert failed");
++			goto done;
++		    }
++
+ 		    pollset[SSOCK_FD].in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
+ 		    pollset[SSOCK_FD].out_flags = 0;
+ 		    FPRINTF(stderr,
+ 		            "%s: about to call PR_Poll on writable socket !\n", 
+ 			    progName);
+ 		    cc = PR_Poll(pollset, 1, PR_INTERVAL_NO_TIMEOUT);
+ 		    FPRINTF(stderr,
+ 		            "%s: PR_Poll returned with writable socket !\n", 
+Index: mozilla/security/nss/tests/ssl/ssl.sh
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/tests/ssl/ssl.sh,v
+retrieving revision 1.106
+diff -u -8 -p -r1.106 ssl.sh
+--- mozilla/security/nss/tests/ssl/ssl.sh	29 Jan 2010 22:36:25 -0000	1.106
++++ mozilla/security/nss/tests/ssl/ssl.sh	16 Nov 2011 08:24:14 -0000
+@@ -303,16 +303,26 @@ ssl_cov()
+                
+   exec < ${SSLCOV}
+   while read ectype tls param testname
+   do
+       echo "${testname}" | grep "EXPORT" > /dev/null 
+       EXP=$?
+       echo "${testname}" | grep "SSL2" > /dev/null
+       SSL2=$?
++
++      if [ "${SSL2}" -eq 0 ] ; then
++          # We cannot use asynchronous cert verification with SSL2
++          SSL2_FLAGS=-O
++      else
++          # Do not enable SSL2 for non-SSL2-specific tests. SSL2 is disabled by
++          # default in libssl but it is enabled by default in tstclnt; we want
++          # to test the libssl default whenever possible.
++          SSL2_FLAGS=-2
++      fi
+       
+       if [ "$NORM_EXT" = "Extended Test" -a "${SSL2}" -eq 0 ] ; then
+           echo "$SCRIPTNAME: skipping  $testname for $NORM_EXT"
+       elif [ "$ectype" = "ECC" -a -z "$NSS_ENABLE_ECC" ] ; then
+           echo "$SCRIPTNAME: skipping  $testname (ECC only)"
+       elif [ "$SERVER_MODE" = "fips" -o "$CLIENT_MODE" = "fips" ] && [ "$SSL2" -eq 0 -o "$EXP" -eq 0 ] ; then
+           echo "$SCRIPTNAME: skipping  $testname (non-FIPS only)"
+       elif [ "`echo $ectype | cut -b 1`" != "#" ] ; then
+@@ -345,21 +355,21 @@ ssl_cov()
+               is_selfserv_alive
+             else
+               kill_selfserv
+               start_selfserv
+               mixed=0
+             fi
+           fi
+ 
+-          echo "tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} ${TLS_FLAG} ${CLIENT_OPTIONS} \\"
++          echo "tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} ${TLS_FLAG} ${SSL2_FLAGS} ${CLIENT_OPTIONS} \\"
+           echo "        -f -d ${P_R_CLIENTDIR} -v -w nss < ${REQUEST_FILE}"
+ 
+           rm ${TMP}/$HOST.tmp.$$ 2>/dev/null
+-          ${PROFTOOL} ${BINDIR}/tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} ${TLS_FLAG} ${CLIENT_OPTIONS} -f \
++          ${PROFTOOL} ${BINDIR}/tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} ${TLS_FLAG} ${SSL2_FLAGS} ${CLIENT_OPTIONS} -f \
+                   -d ${P_R_CLIENTDIR} -v -w nss < ${REQUEST_FILE} \
+                   >${TMP}/$HOST.tmp.$$  2>&1
+           ret=$?
+           cat ${TMP}/$HOST.tmp.$$ 
+           rm ${TMP}/$HOST.tmp.$$ 2>/dev/null
+           html_msg $ret 0 "${testname}" \
+                    "produced a returncode of $ret, expected is 0"
+       fi