Index: nss/mozilla/security/nss/cmd/tstclnt/tstclnt.c
===================================================================
--- nss.orig/mozilla/security/nss/cmd/tstclnt/tstclnt.c	2011-12-23 17:38:01.308069653 +0100
+++ nss/mozilla/security/nss/cmd/tstclnt/tstclnt.c	2011-12-23 17:39:21.705916020 +0100
@@ -217,6 +217,8 @@
     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");
@@ -293,6 +295,16 @@
     }
 }
 
+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.
@@ -307,6 +319,20 @@
     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,
@@ -498,11 +524,37 @@
 	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;
@@ -530,6 +582,7 @@
     PRBool             clientSpeaksFirst = PR_FALSE;
     PRBool             wrStarted = PR_FALSE;
     PRBool             skipProtoHeader = PR_FALSE;
+    ServerCertAuth     serverCertAuth;
     int                headerSeparatorPtrnId = 0;
     int                error = 0;
     PRUint16           portno = 443;
@@ -539,6 +592,10 @@
     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], '\\');
@@ -553,7 +610,7 @@
     }
 
     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 '?':
@@ -565,6 +622,8 @@
 
           case 'B': bypassPKCS11 = 1; 			break;
 
+          case 'O': serverCertAuth.shouldPause = PR_FALSE; break;
+
           case 'S': skipProtoHeader = PR_TRUE;                 break;
 
           case 'T': disableTLS  = 1; 			break;
@@ -650,14 +709,8 @@
     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)
@@ -876,7 +929,13 @@
 
     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);
     }
@@ -984,6 +1043,14 @@
 	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;
 
@@ -1042,6 +1109,15 @@
 		    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,
Index: nss/mozilla/security/nss/lib/ssl/SSLerrs.h
===================================================================
--- nss.orig/mozilla/security/nss/lib/ssl/SSLerrs.h	2011-12-23 17:38:01.336068902 +0100
+++ nss/mozilla/security/nss/lib/ssl/SSLerrs.h	2011-12-23 17:39:19.681970238 +0100
@@ -411,3 +411,9 @@
 
 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: nss/mozilla/security/nss/lib/ssl/ssl.def
===================================================================
--- nss.orig/mozilla/security/nss/lib/ssl/ssl.def	2011-12-23 17:38:01.336068902 +0100
+++ nss/mozilla/security/nss/lib/ssl/ssl.def	2011-12-23 17:39:19.681970238 +0100
@@ -169,6 +169,7 @@
 SSL_SetNextProtoCallback;
 SSL_SetNextProtoNego;
 SSL_GetNextProto;
+SSL_RestartHandshakeAfterAuthCertificate;
 ;+    local:
 ;+       *;
 ;+};
Index: nss/mozilla/security/nss/lib/ssl/ssl.h
===================================================================
--- nss.orig/mozilla/security/nss/lib/ssl/ssl.h	2011-12-23 17:38:01.336068902 +0100
+++ nss/mozilla/security/nss/lib/ssl/ssl.h	2011-12-23 17:39:19.681970238 +0100
@@ -339,6 +339,19 @@
 ** 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,
@@ -442,6 +455,15 @@
 ** 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, 
@@ -740,6 +762,53 @@
  */
 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: nss/mozilla/security/nss/lib/ssl/ssl3con.c
===================================================================
--- nss.orig/mozilla/security/nss/lib/ssl/ssl3con.c	2011-12-23 17:38:12.791762037 +0100
+++ nss/mozilla/security/nss/lib/ssl/ssl3con.c	2011-12-23 17:39:19.685970130 +0100
@@ -5649,70 +5649,18 @@
     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 &&
@@ -5726,6 +5674,8 @@
     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.
@@ -5735,7 +5685,6 @@
 {
     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));
@@ -5751,6 +5700,64 @@
 	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) {
@@ -5760,10 +5767,7 @@
 	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. */
@@ -5775,17 +5779,21 @@
     	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) {
@@ -7814,8 +7822,6 @@
 {
     ssl3CertNode *   c;
     ssl3CertNode *   lastCert 	= NULL;
-    ssl3CertNode *   certs 	= NULL;
-    PRArenaPool *    arena 	= NULL;
     PRInt32          remaining  = 0;
     PRInt32          size;
     SECStatus        rv;
@@ -7872,11 +7878,11 @@
 	    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 */
     }
 
@@ -7926,7 +7932,7 @@
 	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 */
 	}
@@ -7944,7 +7950,7 @@
 	if (lastCert) {
 	    lastCert->next = c;
 	} else {
-	    certs = c;
+	    ss->ssl3.peerCertChain = c;
 	}
 	lastCert = c;
     }
@@ -7954,6 +7960,8 @@
 
     SECKEY_UpdateCertPQG(ss->sec.peerCert);
 
+    ss->ssl3.hs.authCertificatePending = PR_FALSE;
+
     /*
      * Ask caller-supplied callback function to validate cert chain.
      */
@@ -7961,24 +7969,26 @@
 					   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);
@@ -8026,14 +8036,7 @@
 	    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. */
@@ -8044,11 +8047,17 @@
 	    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:
@@ -8099,7 +8108,6 @@
     (void)SSL3_SendAlert(ss, alert_fatal, desc);
 
 loser:
-    ss->ssl3.peerCertChain = certs;  certs = NULL;  arena = NULL;
     ssl3_CleanupPeerCerts(ss);
 
     if (ss->sec.peerCert != NULL) {
@@ -8110,43 +8118,49 @@
     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;
 }
 
@@ -8499,9 +8513,6 @@
         return rv;
     }
 
-    /* The first handshake is now completed. */
-    ss->handshake           = NULL;
-    ss->firstHsDone         = PR_TRUE;
     ss->gs.writeOffset = 0;
     ss->gs.readOffset  = 0;
 
@@ -8551,10 +8562,42 @@
 	/* 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. */
Index: nss/mozilla/security/nss/lib/ssl/ssl3gthr.c
===================================================================
--- nss.orig/mozilla/security/nss/lib/ssl/ssl3gthr.c	2011-12-23 17:38:01.336068902 +0100
+++ nss/mozilla/security/nss/lib/ssl/ssl3gthr.c	2011-12-23 17:39:19.685970130 +0100
@@ -192,21 +192,53 @@
 
     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;
 	}
Index: nss/mozilla/security/nss/lib/ssl/sslerr.h
===================================================================
--- nss.orig/mozilla/security/nss/lib/ssl/sslerr.h	2011-12-23 17:38:01.336068902 +0100
+++ nss/mozilla/security/nss/lib/ssl/sslerr.h	2011-12-23 17:39:19.685970130 +0100
@@ -208,6 +208,8 @@
 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;
Index: nss/mozilla/security/nss/lib/ssl/sslimpl.h
===================================================================
--- nss.orig/mozilla/security/nss/lib/ssl/sslimpl.h	2011-12-23 17:38:01.336068902 +0100
+++ nss/mozilla/security/nss/lib/ssl/sslimpl.h	2011-12-23 17:39:19.685970130 +0100
@@ -750,6 +750,8 @@
     PRUint32 sniNameArrSize;
 };
 
+typedef SECStatus (*sslRestartTarget)(sslSocket *);
+
 /*
 ** This is the "hs" member of the "ssl3" struct.
 ** This entire struct is protected by ssl3HandshakeLock
@@ -789,6 +791,13 @@
 #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;
 
 
@@ -1340,7 +1349,6 @@
 /* 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,
@@ -1350,12 +1358,7 @@
 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
Index: nss/mozilla/security/nss/lib/ssl/sslsecur.c
===================================================================
--- nss.orig/mozilla/security/nss/lib/ssl/sslsecur.c	2011-12-23 17:38:01.336068902 +0100
+++ nss/mozilla/security/nss/lib/ssl/sslsecur.c	2011-12-23 17:39:19.689970023 +0100
@@ -1463,29 +1463,8 @@
     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,
@@ -1493,46 +1472,47 @@
 				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)
 {
-    int rv	= SECSuccess;
+    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);
 
-    ssl_Get1stHandshakeLock(ss); 
+    if (!ss) {
+	SSL_DBG(("%d: SSL[%d]: bad socket in SSL_RestartHandshakeAfterPeerCert",
+		 SSL_GETPID(), fd));
+	return SECFailure;
+    }
+
+    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;
 }
 
Index: nss/mozilla/security/nss/tests/ssl/ssl.sh
===================================================================
--- nss.orig/mozilla/security/nss/tests/ssl/ssl.sh	2011-12-23 17:38:01.340068796 +0100
+++ nss/mozilla/security/nss/tests/ssl/ssl.sh	2011-12-23 17:39:21.705916020 +0100
@@ -308,6 +308,16 @@
       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"
@@ -350,11 +360,11 @@
             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=$?
