Bug 898431: Update NSS to NSS_3_15_4_BETA9. Includes the fixes for
authorWan-Teh Chang <wtc@google.com>
Fri, 20 Dec 2013 12:01:06 -0800
changeset 161630 81ab8a7cca294eccced08053999f66c67325bf0b
parent 161629 9494599831b140b7865d27e16b0bee88874e2960
child 161631 39778c27c5fd5fb775e96d9d8cb3ae47714f7cc8
push id25887
push userkwierso@gmail.com
push dateSat, 21 Dec 2013 02:42:17 +0000
treeherdermozilla-central@90c67da3f827 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs898431, 946147, 930874, 930857, 934545, 915408
milestone29.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 898431: Update NSS to NSS_3_15_4_BETA9. Includes the fixes for bug 946147, bug 930874, bug 930857, bug 934545, bug 915408.
security/nss/TAG-INFO
security/nss/automation/buildbot-slave/build.sh
security/nss/cmd/httpserv/httpserv.c
security/nss/cmd/strsclnt/strsclnt.c
security/nss/coreconf/arch.mk
security/nss/coreconf/coreconf.dep
security/nss/lib/certdb/cert.h
security/nss/lib/certhigh/ocsp.c
security/nss/lib/certhigh/ocspsig.c
security/nss/lib/freebl/dh.c
security/nss/lib/libpkix/pkix/checker/pkix_ocspchecker.c
security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocspresponse.c
security/nss/lib/nss/nss.def
security/nss/lib/ssl/ssl3con.c
security/nss/lib/ssl/ssl3ext.c
security/nss/lib/ssl/sslcon.c
security/nss/lib/ssl/sslenum.c
security/nss/lib/ssl/sslimpl.h
security/nss/lib/ssl/sslnonce.c
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-NSS_3_15_4_BETA8
+NSS_3_15_4_BETA9
old mode 100644
new mode 100755
--- a/security/nss/cmd/httpserv/httpserv.c
+++ b/security/nss/cmd/httpserv/httpserv.c
@@ -27,19 +27,18 @@
 #include "prnetdb.h"
 #include "prclist.h"
 #include "plgetopt.h"
 #include "pk11func.h"
 #include "nss.h"
 #include "nssb64.h"
 #include "sechash.h"
 #include "cert.h"
-#include "certt.h"
+#include "certdb.h"
 #include "ocsp.h"
-#include "ocspt.h"
 #include "ocspti.h"
 #include "ocspi.h"
 
 #ifndef PORT_Sprintf
 #define PORT_Sprintf sprintf
 #endif
 
 #ifndef PORT_Strstr
@@ -1066,29 +1065,29 @@ ocsp_CreateSelfCAID(PLArenaPool *arena, 
         goto loser;
     }
 
     if (CERT_GetSubjectNameDigest(arena, cert, SEC_OID_MD2,
                                   &(certID->issuerMD2NameHash)) == NULL) {
         goto loser;
     }
 
-    if (CERT_GetSPKIDigest(arena, cert, SEC_OID_SHA1,
-				   &(certID->issuerKeyHash)) == NULL) {
+    if (CERT_GetSubjectPublicKeyDigest(arena, cert, SEC_OID_SHA1,
+				       &certID->issuerKeyHash) == NULL) {
 	goto loser;
     }
     certID->issuerSHA1KeyHash.data = certID->issuerKeyHash.data;
     certID->issuerSHA1KeyHash.len = certID->issuerKeyHash.len;
     /* cache the other two hash algorithms as well */
-    if (CERT_GetSPKIDigest(arena, cert, SEC_OID_MD5,
-				   &(certID->issuerMD5KeyHash)) == NULL) {
+    if (CERT_GetSubjectPublicKeyDigest(arena, cert, SEC_OID_MD5,
+				       &certID->issuerMD5KeyHash) == NULL) {
 	goto loser;
     }
-    if (CERT_GetSPKIDigest(arena, cert, SEC_OID_MD2,
-				   &(certID->issuerMD2KeyHash)) == NULL) {
+    if (CERT_GetSubjectPublicKeyDigest(arena, cert, SEC_OID_MD2,
+				       &certID->issuerMD2KeyHash) == NULL) {
 	goto loser;
     }
 
     PORT_ArenaUnmark(arena, mark);
     return certID;
 
 loser:
     PORT_ArenaRelease(arena, mark);
@@ -1360,17 +1359,17 @@ main(int argc, char **argv)
 		    PORT_Free(revoInfo->nickname);
 		if (revoInfo->crlFilename)
 		    PORT_Free(revoInfo->crlFilename);
 		if (revoInfo->cert)
 		    CERT_DestroyCertificate(revoInfo->cert);
 		if (revoInfo->id)
 		    CERT_DestroyOCSPCertID(revoInfo->id);
 		if (revoInfo->crl)
-		    CERT_DestroyCrl(revoInfo->crl);
+		    SEC_DestroyCrl(revoInfo->crl);
 		
 		caRevoIter = PR_NEXT_LINK(caRevoIter);
 	    } while (caRevoIter != &caRevoInfos->link);
       
 	}
 	if (NSS_Shutdown() != SECSuccess) {
 	    SECU_PrintError(progName, "NSS_Shutdown");
 	    PR_Cleanup();
--- a/security/nss/cmd/strsclnt/strsclnt.c
+++ b/security/nss/cmd/strsclnt/strsclnt.c
@@ -175,20 +175,21 @@ Usage(const char *progName)
     exit(1);
 }
 
 
 static void
 errWarn(char * funcString)
 {
     PRErrorCode  perr      = PR_GetError();
+    PRInt32      oserr     = PR_GetOSError();
     const char * errString = SECU_Strerror(perr);
 
-    fprintf(stderr, "strsclnt: %s returned error %d:\n%s\n",
-            funcString, perr, errString);
+    fprintf(stderr, "strsclnt: %s returned error %d, OS error %d: %s\n",
+            funcString, perr, oserr, errString);
 }
 
 static void
 errExit(char * funcString)
 {
     errWarn(funcString);
     exit(1);
 }
@@ -760,21 +761,23 @@ retry:
 	    PR_Close(tcp_sock);
 	    return SECSuccess;
 	} 
     }
 
     prStatus = PR_Connect(tcp_sock, addr, PR_INTERVAL_NO_TIMEOUT);
     if (prStatus != PR_SUCCESS) {
         PRErrorCode err = PR_GetError(); /* save error code */
+        PRInt32 oserr = PR_GetOSError();
         if (ThrottleUp) {
             PRTime now = PR_Now();
             PR_Lock(threadLock);
             lastConnectFailure = PR_MAX(now, lastConnectFailure);
             PR_Unlock(threadLock);
+            PR_SetError(err, oserr); /* restore error code */
         }
         if ((err == PR_CONNECT_REFUSED_ERROR) || 
 	    (err == PR_CONNECT_RESET_ERROR)      ) {
 	    int connections = numConnected;
 
 	    PR_Close(tcp_sock);
             PR_Lock(threadLock);
             if (connections > 2 && active_threads >= connections) {
--- a/security/nss/coreconf/arch.mk
+++ b/security/nss/coreconf/arch.mk
@@ -145,42 +145,16 @@ endif
 # at lesser performance (the Win95 target uses threads; the WinNT target
 # uses fibers).
 #
 # If OS_TARGET is not specified, it defaults to $(OS_ARCH), i.e., no
 # cross-compilation.
 #
 
 #
-# The following hack allows one to build on a WIN95 machine (as if
-# s/he were cross-compiling on a WINNT host for a WIN95 target).
-# It also accomodates for MKS's and Cygwin's uname.exe.
-#
-ifeq ($(OS_ARCH),WIN95)
-    OS_ARCH   = WINNT
-    OS_TARGET = WIN95
-endif
-ifeq ($(OS_ARCH),Windows_95)
-    OS_ARCH   = Windows_NT
-    OS_TARGET = WIN95
-endif
-ifeq ($(OS_ARCH),CYGWIN_95-4.0)
-	OS_ARCH   = CYGWIN_NT-4.0
-	OS_TARGET = WIN95
-endif
-ifeq ($(OS_ARCH),CYGWIN_98-4.10)
-	OS_ARCH   = CYGWIN_NT-4.0
-	OS_TARGET = WIN95
-endif
-ifeq ($(OS_ARCH),CYGWIN_ME-4.90)
-	OS_ARCH   = CYGWIN_NT-4.0
-	OS_TARGET = WIN95
-endif
-
-#
 # On WIN32, we also define the variable CPU_ARCH, if it isn't already.
 #
 ifndef CPU_ARCH
     ifeq ($(OS_ARCH), WINNT)
 	CPU_ARCH := $(shell uname -p)
 	ifeq ($(CPU_ARCH),I386)
 	    CPU_ARCH = x386
 	endif
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,9 +5,8 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
-
--- a/security/nss/lib/certdb/cert.h
+++ b/security/nss/lib/certdb/cert.h
@@ -1499,24 +1499,28 @@ CERT_LockCertTrust(const CERTCertificate
 /*
  * Free the cert trust lock
  */
 void
 CERT_UnlockCertTrust(const CERTCertificate *cert);
 
 /*
  * Digest the cert's subject public key using the specified algorithm.
+ * NOTE: this digests the value of the BIT STRING subjectPublicKey (excluding
+ * the tag, length, and number of unused bits) rather than the whole
+ * subjectPublicKeyInfo field.
+ *
  * The necessary storage for the digest data is allocated.  If "fill" is
  * non-null, the data is put there, otherwise a SECItem is allocated.
  * Allocation from "arena" if it is non-null, heap otherwise.  Any problem
  * results in a NULL being returned (and an appropriate error set).
  */ 
 extern SECItem *
-CERT_GetSPKIDigest(PLArenaPool *arena, const CERTCertificate *cert,
-                   SECOidTag digestAlg, SECItem *fill);
+CERT_GetSubjectPublicKeyDigest(PLArenaPool *arena, const CERTCertificate *cert,
+                               SECOidTag digestAlg, SECItem *fill);
 
 /*
  * Digest the cert's subject name using the specified algorithm.
  */
 extern SECItem *
 CERT_GetSubjectNameDigest(PLArenaPool *arena, const CERTCertificate *cert,
                           SECOidTag digestAlg, SECItem *fill);
 
--- a/security/nss/lib/certhigh/ocsp.c
+++ b/security/nss/lib/certhigh/ocsp.c
@@ -102,17 +102,17 @@ static struct OCSPGlobalStruct {
 
 
 
 /* Forward declarations */
 static SECItem *
 ocsp_GetEncodedOCSPResponseFromRequest(PLArenaPool *arena, 
                                        CERTOCSPRequest *request,
                                        const char *location,
-				       const char *mechanism,
+				       const char *method,
 				       PRTime time,
                                        PRBool addServiceLocator,
                                        void *pwArg,
                                        CERTOCSPRequest **pRequest);
 static SECStatus
 ocsp_GetOCSPStatusFromNetwork(CERTCertDBHandle *handle, 
                               CERTOCSPCertID *certID, 
                               CERTCertificate *cert, 
@@ -1624,18 +1624,18 @@ loser:
 /*
  * Digest the cert's subject public key using the specified algorithm.
  * The necessary storage for the digest data is allocated.  If "fill" is
  * non-null, the data is put there, otherwise a SECItem is allocated.
  * Allocation from "arena" if it is non-null, heap otherwise.  Any problem
  * results in a NULL being returned (and an appropriate error set).
  */
 SECItem *
-CERT_GetSPKIDigest(PLArenaPool *arena, const CERTCertificate *cert,
-                           SECOidTag digestAlg, SECItem *fill)
+CERT_GetSubjectPublicKeyDigest(PLArenaPool *arena, const CERTCertificate *cert,
+                               SECOidTag digestAlg, SECItem *fill)
 {
     SECItem spk;
 
     /*
      * Copy just the length and data pointer (nothing needs to be freed)
      * of the subject public key so we can convert the length from bits
      * to bytes, which is what the digest function expects.
      */
@@ -1711,29 +1711,29 @@ ocsp_CreateCertID(PLArenaPool *arena, CE
         goto loser;
     }
 
     if (CERT_GetSubjectNameDigest(arena, issuerCert, SEC_OID_MD2,
                                   &(certID->issuerMD2NameHash)) == NULL) {
         goto loser;
     }
 
-    if (CERT_GetSPKIDigest(arena, issuerCert, SEC_OID_SHA1,
-				   &(certID->issuerKeyHash)) == NULL) {
+    if (CERT_GetSubjectPublicKeyDigest(arena, issuerCert, SEC_OID_SHA1,
+				       &certID->issuerKeyHash) == NULL) {
 	goto loser;
     }
     certID->issuerSHA1KeyHash.data = certID->issuerKeyHash.data;
     certID->issuerSHA1KeyHash.len = certID->issuerKeyHash.len;
     /* cache the other two hash algorithms as well */
-    if (CERT_GetSPKIDigest(arena, issuerCert, SEC_OID_MD5,
-				   &(certID->issuerMD5KeyHash)) == NULL) {
+    if (CERT_GetSubjectPublicKeyDigest(arena, issuerCert, SEC_OID_MD5,
+				       &certID->issuerMD5KeyHash) == NULL) {
 	goto loser;
     }
-    if (CERT_GetSPKIDigest(arena, issuerCert, SEC_OID_MD2,
-				   &(certID->issuerMD2KeyHash)) == NULL) {
+    if (CERT_GetSubjectPublicKeyDigest(arena, issuerCert, SEC_OID_MD2,
+				       &certID->issuerMD2KeyHash) == NULL) {
 	goto loser;
     }
 
 
     /* now we are done with issuerCert */
     CERT_DestroyCertificate(issuerCert);
     issuerCert = NULL;
 
@@ -3462,17 +3462,17 @@ loser:
 	PORT_Free(path);
     if (hostname != NULL)
 	PORT_Free(hostname);
     
     return encodedResponse;
 }
 
 /*
- * FUNCTION: CERT_GetEncodedOCSPResponseByMechanism
+ * FUNCTION: CERT_GetEncodedOCSPResponseByMethod
  *   Creates and sends a request to an OCSP responder, then reads and
  *   returns the (encoded) response.
  * INPUTS:
  *   PLArenaPool *arena
  *     Pointer to arena from which return value will be allocated.
  *     If NULL, result will be allocated from the heap (and thus should
  *     be freed via SECITEM_FreeItem).
  *   CERTCertList *certList
@@ -3480,20 +3480,20 @@ loser:
  *     Note that all of these certificates should have the same issuer,
  *     or it's expected the response will be signed by a trusted responder.
  *     If the certs need to be broken up into multiple requests, that
  *     must be handled by the caller (and thus by having multiple calls
  *     to this routine), who knows about where the request(s) are being
  *     sent and whether there are any trusted responders in place.
  *   const char *location
  *     The location of the OCSP responder (a URL).
- *   const char *mechanism
- *     The protocol mechanisms used when retrieving the OCSP response.
+ *   const char *method
+ *     The protocol method used when retrieving the OCSP response.
  *     Currently support: "GET" (http GET) and "POST" (http POST).
- *     Additionals mechanisms for http or other protocols might be added
+ *     Additionals methods for http or other protocols might be added
  *     in the future.
  *   PRTime time
  *     Indicates the time for which the certificate status is to be 
  *     determined -- this may be used in the search for the cert's issuer
  *     but has no other bearing on the operation.
  *   PRBool addServiceLocator
  *     If true, the Service Locator extension should be added to the
  *     single request(s) for each cert.
@@ -3513,50 +3513,50 @@ loser:
  *   Returns a pointer to the SECItem holding the response.
  *   On error, returns null with error set describing the reason:
  *	SEC_ERROR_UNKNOWN_ISSUER
  *	SEC_ERROR_CERT_BAD_ACCESS_LOCATION
  *	SEC_ERROR_OCSP_BAD_HTTP_RESPONSE
  *   Other errors are low-level problems (no memory, bad database, etc.).
  */
 SECItem *
-CERT_GetEncodedOCSPResponseByMechanism(PLArenaPool *arena, CERTCertList *certList,
-				       const char *location, const char *mechanism,
-				       PRTime time, PRBool addServiceLocator,
-				       CERTCertificate *signerCert, void *pwArg,
-				       CERTOCSPRequest **pRequest)
+CERT_GetEncodedOCSPResponseByMethod(PLArenaPool *arena, CERTCertList *certList,
+				    const char *location, const char *method,
+				    PRTime time, PRBool addServiceLocator,
+				    CERTCertificate *signerCert, void *pwArg,
+				    CERTOCSPRequest **pRequest)
 {
     CERTOCSPRequest *request;
     request = CERT_CreateOCSPRequest(certList, time, addServiceLocator,
                                      signerCert);
     if (!request)
         return NULL;
-    return ocsp_GetEncodedOCSPResponseFromRequest(arena, request, location, 
-                                                  mechanism, time, addServiceLocator, 
+    return ocsp_GetEncodedOCSPResponseFromRequest(arena, request, location,
+                                                  method, time, addServiceLocator,
                                                   pwArg, pRequest);
 }
 
 /*
  * FUNCTION: CERT_GetEncodedOCSPResponse
  *   Creates and sends a request to an OCSP responder, then reads and
  *   returns the (encoded) response.
  *
  * This is a legacy API that behaves identically to
- * CERT_GetEncodedOCSPResponseByMechanism using the "POST" mechanism.
+ * CERT_GetEncodedOCSPResponseByMethod using the "POST" method.
  */
 SECItem *
 CERT_GetEncodedOCSPResponse(PLArenaPool *arena, CERTCertList *certList,
 			    const char *location, PRTime time,
 			    PRBool addServiceLocator,
 			    CERTCertificate *signerCert, void *pwArg,
 			    CERTOCSPRequest **pRequest)
 {
-    return CERT_GetEncodedOCSPResponseByMechanism(arena, certList, location,
-                                                  "POST", time, addServiceLocator,
-						  signerCert, pwArg, pRequest);
+    return CERT_GetEncodedOCSPResponseByMethod(arena, certList, location,
+					       "POST", time, addServiceLocator,
+					       signerCert, pwArg, pRequest);
 }
 
 /* URL encode a buffer that consists of base64-characters, only,
  * which means we can use a simple encoding logic.
  * 
  * No output buffer size checking is performed.
  * You should call the function twice, to calculate the required buffer size.
  * 
@@ -3619,17 +3619,17 @@ enum { max_get_request_size = 255 }; /* 
 static SECItem *
 cert_GetOCSPResponse(PLArenaPool *arena, const char *location, 
                      const SECItem *encodedRequest);
 
 static SECItem *
 ocsp_GetEncodedOCSPResponseFromRequest(PLArenaPool *arena,
                                        CERTOCSPRequest *request,
                                        const char *location,
-				       const char *mechanism,
+				       const char *method,
 				       PRTime time,
                                        PRBool addServiceLocator,
                                        void *pwArg,
                                        CERTOCSPRequest **pRequest)
 {
     SECItem *encodedRequest = NULL;
     SECItem *encodedResponse = NULL;
     SECStatus rv;
@@ -3641,20 +3641,20 @@ ocsp_GetEncodedOCSPResponseFromRequest(P
 					 SEC_OID_PKIX_OCSP_BASIC_RESPONSE);
     if (rv != SECSuccess)
 	goto loser;
 
     encodedRequest = CERT_EncodeOCSPRequest(NULL, request, pwArg);
     if (encodedRequest == NULL)
 	goto loser;
 
-    if (!strcmp(mechanism, "GET")) {
+    if (!strcmp(method, "GET")) {
         encodedResponse = cert_GetOCSPResponse(arena, location, encodedRequest);
     }
-    else if (!strcmp(mechanism, "POST")) {
+    else if (!strcmp(method, "POST")) {
         encodedResponse = CERT_PostOCSPRequest(arena, location, encodedRequest);
     }
     else {
 	goto loser;
     }
 
     if (encodedResponse != NULL && pRequest != NULL) {
 	*pRequest = request;
@@ -3668,27 +3668,27 @@ loser:
 	SECITEM_FreeItem(encodedRequest, PR_TRUE);
     return encodedResponse;
 }
 
 static SECItem *
 cert_FetchOCSPResponse(PLArenaPool *arena,  const char *location, 
                        const SECItem *encodedRequest);
 
-/* using HTTP GET mechanism */
+/* using HTTP GET method */
 static SECItem *
 cert_GetOCSPResponse(PLArenaPool *arena, const char *location, 
                      const SECItem *encodedRequest)
 {
     char *walkOutput = NULL;
     char *fullGetPath = NULL;
     size_t pathLength;
     PRInt32 urlEncodedBufLength;
     size_t base64size;
-    unsigned char b64ReqBuf[max_get_request_size+1];
+    char b64ReqBuf[max_get_request_size+1];
     size_t slashLengthIfNeeded = 0;
     size_t getURLLength;
     SECItem *item;
 
     if (!location || !*location) {
 	return NULL;
     }
     
@@ -3700,17 +3700,18 @@ cert_GetOCSPResponse(PLArenaPool *arena,
     /* Calculation as documented by PL_Base64Encode function.
      * Use integer conversion to avoid having to use function ceil().
      */
     base64size = (((encodedRequest->len +2)/3) * 4);
     if (base64size > max_get_request_size) {
 	return NULL;
     }
     memset(b64ReqBuf, 0, sizeof(b64ReqBuf));
-    PL_Base64Encode(encodedRequest->data, encodedRequest->len, b64ReqBuf);
+    PL_Base64Encode((const char*)encodedRequest->data, encodedRequest->len,
+		    b64ReqBuf);
 
     urlEncodedBufLength = ocsp_UrlEncodeBase64Buf(b64ReqBuf, NULL);
     getURLLength = pathLength + urlEncodedBufLength + slashLengthIfNeeded;
     
     /* urlEncodedBufLength already contains room for the zero terminator.
      * Add another if we must add the '/' char.
      */
     if (arena) {
@@ -3772,29 +3773,29 @@ cert_FetchOCSPResponse(PLArenaPool *aren
     return encodedResponse;
 }
 
 static SECItem *
 ocsp_GetEncodedOCSPResponseForSingleCert(PLArenaPool *arena, 
                                          CERTOCSPCertID *certID, 
                                          CERTCertificate *singleCert, 
                                          const char *location,
-					 const char *mechanism,
+					 const char *method,
 					 PRTime time,
                                          PRBool addServiceLocator,
                                          void *pwArg,
                                          CERTOCSPRequest **pRequest)
 {
     CERTOCSPRequest *request;
     request = cert_CreateSingleCertOCSPRequest(certID, singleCert, time, 
                                                addServiceLocator, NULL);
     if (!request)
         return NULL;
-    return ocsp_GetEncodedOCSPResponseFromRequest(arena, request, location, 
-                                                  mechanism, time, addServiceLocator, 
+    return ocsp_GetEncodedOCSPResponseFromRequest(arena, request, location,
+                                                  method, time, addServiceLocator,
                                                   pwArg, pRequest);
 }
 
 /* Checks a certificate for the key usage extension of OCSP signer. */
 static PRBool
 ocsp_CertIsOCSPDesignatedResponder(CERTCertificate *cert)
 {
     SECStatus rv;
@@ -3875,29 +3876,32 @@ static PRBool
 ocsp_matchcert(SECItem *certIndex,CERTCertificate *testCert)
 {
     SECItem item;
     unsigned char buf[HASH_LENGTH_MAX];
 
     item.data = buf;
     item.len = SHA1_LENGTH;
 
-    if (CERT_GetSPKIDigest(NULL,testCert,SEC_OID_SHA1, &item) == NULL) {
+    if (CERT_GetSubjectPublicKeyDigest(NULL,testCert,SEC_OID_SHA1,
+				       &item) == NULL) {
 	return PR_FALSE;
     }
     if  (SECITEM_ItemsAreEqual(certIndex,&item)) {
 	return PR_TRUE;
     }
-    if (CERT_GetSPKIDigest(NULL,testCert,SEC_OID_MD5, &item) == NULL) {
+    if (CERT_GetSubjectPublicKeyDigest(NULL,testCert,SEC_OID_MD5,
+				       &item) == NULL) {
 	return PR_FALSE;
     }
     if  (SECITEM_ItemsAreEqual(certIndex,&item)) {
 	return PR_TRUE;
     }
-    if (CERT_GetSPKIDigest(NULL,testCert,SEC_OID_MD2, &item) == NULL) {
+    if (CERT_GetSubjectPublicKeyDigest(NULL,testCert,SEC_OID_MD2,
+				       &item) == NULL) {
 	return PR_FALSE;
     }
     if  (SECITEM_ItemsAreEqual(certIndex,&item)) {
 	return PR_TRUE;
     }
 
     return PR_FALSE;
 }
@@ -4440,17 +4444,17 @@ ocsp_AuthorizedResponderForCertID(CERTCe
      * of the cert. For that we will use signer cert key hash and cert subj
      * name hash and will compare them with already calculated issuer key
      * hash and issuer name hash. The hash algorithm is picked from response
      * certID hash to avoid second hash calculation.
      */
 
     hashAlg = SECOID_FindOIDTag(&certID->hashAlgorithm.algorithm);
 
-    keyHash = CERT_GetSPKIDigest(NULL, signerCert, hashAlg, NULL);
+    keyHash = CERT_GetSubjectPublicKeyDigest(NULL, signerCert, hashAlg, NULL);
     if (keyHash != NULL) {
 
         keyHashEQ =
             (SECITEM_CompareItem(keyHash,
                                  &certID->issuerKeyHash) == SECEqual);
         SECITEM_FreeItem(keyHash, PR_TRUE);
     }
     if (keyHashEQ &&
@@ -4487,17 +4491,17 @@ ocsp_AuthorizedResponderForCertID(CERTCe
          * We could leave the SEC_ERROR_UNKNOWN_ISSUER error alone,
          * but the following will give slightly more information.
          * Once we have an error stack, things will be much better.
          */
         PORT_SetError(SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE);
         return PR_FALSE;
     }
 
-    keyHash = CERT_GetSPKIDigest(NULL, issuerCert, hashAlg, NULL);
+    keyHash = CERT_GetSubjectPublicKeyDigest(NULL, issuerCert, hashAlg, NULL);
     nameHash = CERT_GetSubjectNameDigest(NULL, issuerCert, hashAlg, NULL);
 
     CERT_DestroyCertificate(issuerCert);
 
     if (keyHash != NULL && nameHash != NULL) {
         keyHashEQ = 
             (SECITEM_CompareItem(keyHash,
                                  &certID->issuerKeyHash) == SECEqual);
@@ -5273,47 +5277,48 @@ ocsp_GetOCSPStatusFromNetwork(CERTCertDB
     /*
      * XXX If/when signing of requests is supported, that second NULL
      * should be changed to be the signer certificate.  Not sure if that
      * should be passed into this function or retrieved via some operation
      * on the handle/context.
      */
 
     do {
-	const char *mechanism;
+	const char *method;
 	PRBool validResponseWithAccurateInfo = PR_FALSE;
 	retry = PR_FALSE;
 	*rv_ocsp = SECFailure;
 
 	if (currentStage == stageGET) {
-	    mechanism = "GET";
-	} else if (currentStage == stagePOST) {
-	    mechanism = "POST";
+	    method = "GET";
 	} else {
-	    PORT_Assert(0); /* our code is flawed */
+	    PORT_Assert(currentStage == stagePOST);
+	    method = "POST";
 	}
 
 	encodedResponse = 
 	    ocsp_GetEncodedOCSPResponseForSingleCert(NULL, certID, cert,
-						     location, mechanism,
+						     location, method,
 						     time, locationIsDefault,
 						     pwArg, &request);
 
 	if (encodedResponse) {
 	    rv = ocsp_GetDecodedVerifiedSingleResponseForID(handle, certID, cert,
 							    time, pwArg,
 							    encodedResponse,
 							    &decodedResponse,
 							    &singleResponse);
 	    if (rv == SECSuccess) {
 		switch (singleResponse->certStatus->certStatusType) {
 		    case ocspCertStatus_good:
 		    case ocspCertStatus_revoked:
 			validResponseWithAccurateInfo = PR_TRUE;
 			break;
+		    default:
+			break;
 		}
 		*rv_ocsp = ocsp_SingleResponseCertHasGoodStatus(singleResponse, time);
 	    }
 	}
 
 	if (currentStage == stageGET) {
 	    /* only accept GET response if good or revoked */
 	    if (validResponseWithAccurateInfo) {
--- a/security/nss/lib/certhigh/ocspsig.c
+++ b/security/nss/lib/certhigh/ocspsig.c
@@ -467,18 +467,18 @@ CERT_CreateEncodedOCSPSuccessResponse(
 	if (responderIDType == ocspResponderID_byName) {
 	    responderIDTemplate = ocsp_ResponderIDByNameTemplate;
 	    if (CERT_CopyName(tmpArena, &rid->responderIDValue.name,
 			    &responderCert->subject) != SECSuccess)
 		goto done;
 	}
 	else {
 	    responderIDTemplate = ocsp_ResponderIDByKeyTemplate;
-	    if (!CERT_GetSPKIDigest(tmpArena, responderCert, SEC_OID_SHA1,
-					&rid->responderIDValue.keyHash))
+	    if (!CERT_GetSubjectPublicKeyDigest(tmpArena, responderCert,
+				SEC_OID_SHA1, &rid->responderIDValue.keyHash))
 		goto done;
 	}
 
 	if (!SEC_ASN1EncodeItem(tmpArena, &rd->derResponderID, rid,
 		responderIDTemplate))
 	    goto done;
 
 	br->tbsResponseData = rd;
--- a/security/nss/lib/freebl/dh.c
+++ b/security/nss/lib/freebl/dh.c
@@ -198,37 +198,57 @@ cleanup:
 
 SECStatus 
 DH_Derive(SECItem *publicValue, 
           SECItem *prime, 
           SECItem *privateValue, 
           SECItem *derivedSecret, 
           unsigned int outBytes)
 {
-    mp_int p, Xa, Yb, ZZ;
+    mp_int p, Xa, Yb, ZZ, psub1;
     mp_err err = MP_OKAY;
     int len = 0;
     unsigned int nb;
     unsigned char *secret = NULL;
     if (!publicValue || !prime || !privateValue || !derivedSecret) {
 	PORT_SetError(SEC_ERROR_INVALID_ARGS);
 	return SECFailure;
     }
     memset(derivedSecret, 0, sizeof *derivedSecret);
     MP_DIGITS(&p)  = 0;
     MP_DIGITS(&Xa) = 0;
     MP_DIGITS(&Yb) = 0;
     MP_DIGITS(&ZZ) = 0;
+    MP_DIGITS(&psub1) = 0;
     CHECK_MPI_OK( mp_init(&p)  );
     CHECK_MPI_OK( mp_init(&Xa) );
     CHECK_MPI_OK( mp_init(&Yb) );
     CHECK_MPI_OK( mp_init(&ZZ) );
+    CHECK_MPI_OK( mp_init(&psub1) );
     SECITEM_TO_MPINT(*publicValue,  &Yb);
     SECITEM_TO_MPINT(*privateValue, &Xa);
     SECITEM_TO_MPINT(*prime,        &p);
+    CHECK_MPI_OK( mp_sub_d(&p, 1, &psub1) );
+
+    /* We assume that the modulus, p, is a safe prime. That is, p = 2q+1 where
+     * q is also a prime. Thus the orders of the subgroups are factors of 2q:
+     * namely 1, 2, q and 2q.
+     *
+     * We check that the peer's public value isn't zero (which isn't in the
+     * group), one (subgroup of order one) or p-1 (subgroup of order 2). We
+     * also check that the public value is less than p, to avoid being fooled
+     * by values like p+1 or 2*p-1.
+     *
+     * Thus we must be operating in the subgroup of size q or 2q. */
+    if (mp_cmp_d(&Yb, 1) <= 0 ||
+	mp_cmp(&Yb, &psub1) >= 0) {
+	err = MP_BADARG;
+	goto cleanup;
+    }
+
     /* ZZ = (Yb)**Xa mod p */
     CHECK_MPI_OK( mp_exptmod(&Yb, &Xa, &p, &ZZ) );
     /* number of bytes in the derived secret */
     len = mp_unsigned_octet_size(&ZZ);
     if (len <= 0) {
         err = MP_BADARG;
         goto cleanup;
     }
@@ -255,16 +275,17 @@ DH_Derive(SECItem *publicValue,
     } else {
 	memcpy(derivedSecret->data, secret + len - nb, nb);
     }
 cleanup:
     mp_clear(&p);
     mp_clear(&Xa);
     mp_clear(&Yb);
     mp_clear(&ZZ);
+    mp_clear(&psub1);
     if (secret) {
 	/* free the buffer allocated for the full secret. */
 	PORT_ZFree(secret, len);
     }
     if (err) {
 	MP_TO_SEC_ERROR(err);
 	if (derivedSecret->data) 
 	    PORT_ZFree(derivedSecret->data, derivedSecret->len);
--- a/security/nss/lib/libpkix/pkix/checker/pkix_ocspchecker.c
+++ b/security/nss/lib/libpkix/pkix/checker/pkix_ocspchecker.c
@@ -270,31 +270,30 @@ pkix_OcspChecker_CheckExternal(
             /* Do not try HTTP GET, only HTTP POST */
             currentStage = stagePOST;
         } else {
             /* Try HTTP GET first, falling back to POST */
             currentStage = stageGET;
         }
 
         do {
-                const char *mechanism;
+                const char *method;
                 passed = PKIX_TRUE;
 
                 retry = PR_FALSE;
                 if (currentStage == stageGET) {
-                        mechanism = "GET";
-                } else if (currentStage == stagePOST) {
-                        mechanism = "POST";
+                        method = "GET";
                 } else {
-                        PORT_Assert(0); /* our code is flawed */
+                        PORT_Assert(currentStage == stagePOST);
+                        method = "POST";
                 }
 
                 /* send request and create a response object */
                 PKIX_CHECK_NO_GOTO(
-                    pkix_pl_OcspResponse_Create(request, mechanism, NULL,
+                    pkix_pl_OcspResponse_Create(request, method, NULL,
                                                 checker->certVerifyFcn,
                                                 &nbioContext,
                                                 &response,
                                                 plContext),
                     PKIX_OCSPRESPONSECREATEFAILED);
 
                 if (pkixErrorResult) {
                     passed = PKIX_FALSE;
--- a/security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocspresponse.c
+++ b/security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocspresponse.c
@@ -336,17 +336,17 @@ pkix_pl_OcspResponse_RegisterSelf(void *
  *  supplied, the default verification function is used.
  *
  *  The contents of "request" are ignored on calls subsequent to a WOULDBLOCK
  *  return, and the caller is permitted to supply NULL.
  *
  * PARAMETERS
  *  "request"
  *      Address of the OcspRequest for which a response is desired.
- *  "httpMechanism"
+ *  "httpMethod"
  *      GET or POST
  *  "responder"
  *      Address, if non-NULL, of the SEC_HttpClientFcn to be sent the OCSP
  *      query.
  *  "verifyFcn"
  *      Address, if non-NULL, of the OcspResponse_VerifyCallback function to be
  *      used to verify the Cert of the OCSP responder.
  *  "pNBIOContext"
@@ -361,17 +361,17 @@ pkix_pl_OcspResponse_RegisterSelf(void *
  * RETURNS:
  *  Returns NULL if the function succeeds.
  *  Returns an OcspResponse Error if the function fails in a non-fatal way.
  *  Returns a Fatal Error if the function fails in an unrecoverable way.
  */
 PKIX_Error *
 pkix_pl_OcspResponse_Create(
         PKIX_PL_OcspRequest *request,
-        const char *httpMechanism,
+        const char *httpMethod,
         void *responder,
         PKIX_PL_VerifyCallback verifyFcn,
         void **pNBIOContext,
         PKIX_PL_OcspResponse **pResponse,
         void *plContext)
 {
         void *nbioContext = NULL;
         PKIX_PL_OcspResponse *ocspResponse = NULL;
@@ -387,17 +387,17 @@ pkix_pl_OcspResponse_Create(
         SEC_HTTP_REQUEST_SESSION sessionRequest = NULL;
         SECItem *encodedRequest = NULL;
         PRUint16 responseCode = 0;
         char *responseData = NULL;
  
         PKIX_ENTER(OCSPRESPONSE, "pkix_pl_OcspResponse_Create");
         PKIX_NULLCHECK_TWO(pNBIOContext, pResponse);
 
-	if (!strcmp(httpMechanism, "GET") && !strcmp(httpMechanism, "POST")) {
+	if (!strcmp(httpMethod, "GET") && !strcmp(httpMethod, "POST")) {
 		PKIX_ERROR(PKIX_INVALIDOCSPHTTPMETHOD);
 	}
 
         nbioContext = *pNBIOContext;
         *pNBIOContext = NULL;
 
         if (nbioContext != NULL) {
 
@@ -426,17 +426,17 @@ pkix_pl_OcspResponse_Create(
                     httpClient = (const SEC_HttpClientFcn *)responder;
                 } else {
                     httpClient = SEC_GetRegisteredHttpClient();
                 }
 
                 if (httpClient && (httpClient->version == 1)) {
 			char *fullGetPath = NULL;
 			const char *sessionPath = NULL;
-			PRBool usePOST = !strcmp(httpMechanism, "POST");
+			PRBool usePOST = !strcmp(httpMethod, "POST");
 
                         hcv1 = &(httpClient->fcnTable.ftable1);
 
                         PKIX_CHECK(pkix_pl_OcspRequest_GetLocation
                                 (request, &location, plContext),
                                 PKIX_OCSPREQUESTGETLOCATIONFAILED);
 
                         /* parse location -> hostname, port, path */    
@@ -451,34 +451,34 @@ pkix_pl_OcspResponse_Create(
                                 PKIX_ERROR(PKIX_OCSPSERVERERROR);
                         }       
 
 			if (usePOST) {
 				sessionPath = path;
 			} else {
 				/* calculate, are we allowed to use GET? */
 				enum { max_get_request_size = 255 }; /* defined by RFC2560 */
-				unsigned char b64ReqBuf[max_get_request_size+1];
+				char b64ReqBuf[max_get_request_size+1];
 				size_t base64size;
 				size_t slashLengthIfNeeded = 0;
 				size_t pathLength;
 				PRInt32 urlEncodedBufLength;
 				size_t getURLLength;
 				char *walkOutput = NULL;
 
 				pathLength = strlen(path);
 				if (path[pathLength-1] != '/') {
 					slashLengthIfNeeded = 1;
 				}
 				base64size = (((encodedRequest->len +2)/3) * 4);
 				if (base64size > max_get_request_size) {
 					PKIX_ERROR(PKIX_OCSPGETREQUESTTOOBIG);
 				}
 				memset(b64ReqBuf, 0, sizeof(b64ReqBuf));
-				PL_Base64Encode(encodedRequest->data, encodedRequest->len, b64ReqBuf);
+				PL_Base64Encode((const char *)encodedRequest->data, encodedRequest->len, b64ReqBuf);
 				urlEncodedBufLength = ocsp_UrlEncodeBase64Buf(b64ReqBuf, NULL);
 				getURLLength = pathLength + urlEncodedBufLength + slashLengthIfNeeded;
 				fullGetPath = (char*)PORT_Alloc(getURLLength);
 				if (!fullGetPath) {
 					PKIX_ERROR(PKIX_OUTOFMEMORY);
 				}
 				strcpy(fullGetPath, path);
 				walkOutput = fullGetPath + pathLength;
@@ -486,17 +486,17 @@ pkix_pl_OcspResponse_Create(
 					strcpy(walkOutput, "/");
 					++walkOutput;
 				}
 				ocsp_UrlEncodeBase64Buf(b64ReqBuf, walkOutput);
 				sessionPath = fullGetPath;
 			}
 
                         rv = (*hcv1->createFcn)(serverSession, "http",
-                                                sessionPath, httpMechanism,
+                                                sessionPath, httpMethod,
                                                 PR_SecondsToInterval(timeout),
                                                 &sessionRequest);
 			sessionPath = NULL;
 			if (fullGetPath) {
 				PORT_Free(fullGetPath);
 				fullGetPath = NULL;
 			}
 			
--- a/security/nss/lib/nss/nss.def
+++ b/security/nss/lib/nss/nss.def
@@ -1034,16 +1034,14 @@ PK11_Decrypt;
 PK11_Encrypt;
 CERT_PostOCSPRequest;
 CERT_AddCertToListHead;
 ;+    local:
 ;+       *;
 ;+};
 ;+NSS_3.15.4 { 	# NSS 3.15.4 release
 ;+    global:
-CERT_DestroyCrl;
 CERT_ForcePostMethodForOCSP;
-CERT_GetEncodedOCSPResponseByMechanism;
-CERT_GetSPKIDigest;
 CERT_GetSubjectNameDigest;
+CERT_GetSubjectPublicKeyDigest;
 ;+    local:
 ;+       *;
 ;+};
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -77,28 +77,34 @@ static SECStatus ssl3_AESGCMBypass(ssl3K
 
 #define MAX_SEND_BUF_LENGTH 32000 /* watch for 16-bit integer overflow */
 #define MIN_SEND_BUF_LENGTH  4000
 
 /* This list of SSL3 cipher suites is sorted in descending order of
  * precedence (desirability).  It only includes cipher suites we implement.
  * This table is modified by SSL3_SetPolicy(). The ordering of cipher suites
  * in this table must match the ordering in SSL_ImplementedCiphers (sslenum.c)
+ *
+ * Important: See bug 946147 before enabling, reordering, or adding any cipher
+ * suites to this list.
  */
 static ssl3CipherSuiteCfg cipherSuites[ssl_V3_SUITES_IMPLEMENTED] = {
    /*      cipher_suite                     policy       enabled   isPresent */
 
 #ifdef NSS_ENABLE_ECC
  { TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, SSL_ALLOWED, PR_FALSE, PR_FALSE},
  { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,   SSL_ALLOWED, PR_FALSE, PR_FALSE},
+   /* TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA is out of order to work around
+    * bug 946147.
+    */
+ { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,    SSL_ALLOWED, PR_FALSE, PR_FALSE},
  { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,    SSL_ALLOWED, PR_FALSE, PR_FALSE},
  { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,      SSL_ALLOWED, PR_FALSE, PR_FALSE},
  { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, SSL_ALLOWED, PR_FALSE, PR_FALSE},
  { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,   SSL_ALLOWED, PR_FALSE, PR_FALSE},
- { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,    SSL_ALLOWED, PR_FALSE, PR_FALSE},
  { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,      SSL_ALLOWED, PR_FALSE, PR_FALSE},
  { TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,   SSL_ALLOWED, PR_FALSE, PR_FALSE},
  { TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,     SSL_ALLOWED, PR_FALSE, PR_FALSE},
  { TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,        SSL_ALLOWED, PR_FALSE, PR_FALSE},
  { TLS_ECDHE_RSA_WITH_RC4_128_SHA,          SSL_ALLOWED, PR_FALSE, PR_FALSE},
 #endif /* NSS_ENABLE_ECC */
 
  { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,     SSL_ALLOWED, PR_TRUE,  PR_FALSE},
@@ -1600,17 +1606,17 @@ ssl3_InitPendingContextsBypass(sslSocket
     PORT_Assert(ss->opt.noLocks || ssl_HaveSpecWriteLock(ss));
     PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec);
 
     pwSpec        = ss->ssl3.pwSpec;
     cipher_def    = pwSpec->cipher_def;
 
     calg = cipher_def->calg;
 
-    if (calg == calg_aes_gcm) {
+    if (calg == ssl_calg_aes_gcm) {
 	pwSpec->encode = NULL;
 	pwSpec->decode = NULL;
 	pwSpec->destroy = NULL;
 	pwSpec->encodeContext = NULL;
 	pwSpec->decodeContext = NULL;
 	pwSpec->aead = ssl3_AESGCMBypass;
 	ssl3_InitCompressionContext(pwSpec);
 	return SECSuccess;
@@ -1716,16 +1722,17 @@ ssl3_InitPendingContextsBypass(sslSocket
         optArg1 = !optArg1;
         break;
     /* kill warnings. */
     case ssl_calg_null:
     case ssl_calg_rc4:
     case ssl_calg_rc2:
     case ssl_calg_idea:
     case ssl_calg_fortezza:
+    case ssl_calg_aes_gcm:
         break;
     }
 
     rv = (*initFn)(clientContext,
 		   pwSpec->client.write_key_item.data,
 		   pwSpec->client.write_key_item.len,
 		   pwSpec->client.write_iv_item.data,
 		   mode, optArg1, optArg2);
@@ -4827,17 +4834,16 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
     sslSessionID *   sid;
     ssl3CipherSpec * cwSpec;
     SECStatus        rv;
     int              i;
     int              length;
     int              num_suites;
     int              actual_count = 0;
     PRBool           isTLS = PR_FALSE;
-    PRBool           requestingResume = PR_FALSE;
     PRInt32          total_exten_len = 0;
     unsigned         numCompressionMethods;
     PRInt32          flags;
 
     SSL_TRC(3, ("%d: SSL3[%d]: send client_hello handshake", SSL_GETPID(),
 		ss->fd));
 
     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
@@ -4845,16 +4851,19 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
 
     rv = ssl3_InitState(ss);
     if (rv != SECSuccess) {
 	return rv;		/* ssl3_InitState has set the error code. */
     }
     ss->ssl3.hs.sendingSCSV = PR_FALSE; /* Must be reset every handshake */
     PORT_Assert(IS_DTLS(ss) || !resending);
 
+    SECITEM_FreeItem(&ss->ssl3.hs.newSessionTicket.ticket, PR_FALSE);
+    ss->ssl3.hs.receivedNewSessionTicket = PR_FALSE;
+
     /* We might be starting a session renegotiation in which case we should
      * clear previous state.
      */
     PORT_Memset(&ss->xtnData, 0, sizeof(TLSExtensionData));
 
     rv = ssl3_RestartHandshakeHashes(ss);
     if (rv != SECSuccess) {
 	return rv;
@@ -4966,24 +4975,18 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
 	    if (ss->sec.uncache)
                 (*ss->sec.uncache)(sid);
 	    ssl_FreeSID(sid);
 	    sid = NULL;
 	}
     }
 
     if (sid) {
-	requestingResume = PR_TRUE;
 	SSL_AtomicIncrementLong(& ssl3stats.sch_sid_cache_hits );
 
-	/* Are we attempting a stateless session resume? */
-	if (sid->version > SSL_LIBRARY_VERSION_3_0 &&
-	    sid->u.ssl3.sessionTicket.ticket.data)
-	    SSL_AtomicIncrementLong(& ssl3stats.sch_sid_stateless_resumes );
-
 	PRINT_BUF(4, (ss, "client, found session-id:", sid->u.ssl3.sessionID,
 		      sid->u.ssl3.sessionIDLength));
 
 	ss->ssl3.policy = sid->u.ssl3.policy;
     } else {
 	SSL_AtomicIncrementLong(& ssl3stats.sch_sid_cache_misses );
 
 	/*
@@ -5042,22 +5045,34 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
      */
     if (!ss->firstHsDone && !isTLS) {
 	/* Must set this before calling Hello Extension Senders, 
 	 * to suppress sending of empty RI extension.
 	 */
 	ss->ssl3.hs.sendingSCSV = PR_TRUE;
     }
 
+    /* When we attempt session resumption (only), we must lock the sid to
+     * prevent races with other resumption connections that receive a
+     * NewSessionTicket that will cause the ticket in the sid to be replaced.
+     * Once we've copied the session ticket into our ClientHello message, it
+     * is OK for the ticket to change, so we just need to make sure we hold
+     * the lock across the calls to ssl3_CallHelloExtensionSenders.
+     */
+    if (sid->u.ssl3.lock) {
+        PR_RWLock_Rlock(sid->u.ssl3.lock);
+    }
+
     if (isTLS || (ss->firstHsDone && ss->peerRequestedProtection)) {
 	PRUint32 maxBytes = 65535; /* 2^16 - 1 */
 	PRInt32  extLen;
 
 	extLen = ssl3_CallHelloExtensionSenders(ss, PR_FALSE, maxBytes, NULL);
 	if (extLen < 0) {
+	    if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
 	    return SECFailure;
 	}
 	maxBytes        -= extLen;
 	total_exten_len += extLen;
 
 	if (total_exten_len > 0)
 	    total_exten_len += 2;
     }
@@ -5070,18 +5085,20 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
 #endif
 
     if (IS_DTLS(ss)) {
 	ssl3_DisableNonDTLSSuites(ss);
     }
 
     /* how many suites are permitted by policy and user preference? */
     num_suites = count_cipher_suites(ss, ss->ssl3.policy, PR_TRUE);
-    if (!num_suites)
+    if (!num_suites) {
+    	if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
     	return SECFailure;	/* count_cipher_suites has set error code. */
+    }
     if (ss->ssl3.hs.sendingSCSV) {
 	++num_suites;   /* make room for SCSV */
     }
 
     /* count compression methods */
     numCompressionMethods = 0;
     for (i = 0; i < compressionMethodsCount; i++) {
 	if (compressionEnabled(ss, compressions[i]))
@@ -5093,16 +5110,17 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
 	2 + num_suites*sizeof(ssl3CipherSuite) +
 	1 + numCompressionMethods + total_exten_len;
     if (IS_DTLS(ss)) {
 	length += 1 + ss->ssl3.hs.cookieLen;
     }
 
     rv = ssl3_AppendHandshakeHeader(ss, client_hello, length);
     if (rv != SECSuccess) {
+	if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
 	return rv;	/* err set by ssl3_AppendHandshake* */
     }
 
     if (ss->firstHsDone) {
 	/* The client hello version must stay unchanged to work around
 	 * the Windows SChannel bug described above. */
 	PORT_Assert(ss->version == ss->clientHelloVersion);
     }
@@ -5111,117 +5129,140 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
 	PRUint16 version;
 
 	version = dtls_TLSVersionToDTLSVersion(ss->clientHelloVersion);
 	rv = ssl3_AppendHandshakeNumber(ss, version, 2);
     } else {
 	rv = ssl3_AppendHandshakeNumber(ss, ss->clientHelloVersion, 2);
     }
     if (rv != SECSuccess) {
+	if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
 	return rv;	/* err set by ssl3_AppendHandshake* */
     }
 
     if (!resending) { /* Don't re-generate if we are in DTLS re-sending mode */
 	rv = ssl3_GetNewRandom(&ss->ssl3.hs.client_random);
 	if (rv != SECSuccess) {
+	    if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
 	    return rv;	/* err set by GetNewRandom. */
 	}
     }
     rv = ssl3_AppendHandshake(ss, &ss->ssl3.hs.client_random,
                               SSL3_RANDOM_LENGTH);
     if (rv != SECSuccess) {
+	if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
 	return rv;	/* err set by ssl3_AppendHandshake* */
     }
 
     if (sid)
 	rv = ssl3_AppendHandshakeVariable(
 	    ss, sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength, 1);
     else
 	rv = ssl3_AppendHandshakeVariable(ss, NULL, 0, 1);
     if (rv != SECSuccess) {
+	if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
 	return rv;	/* err set by ssl3_AppendHandshake* */
     }
 
     if (IS_DTLS(ss)) {
 	rv = ssl3_AppendHandshakeVariable(
 	    ss, ss->ssl3.hs.cookie, ss->ssl3.hs.cookieLen, 1);
 	if (rv != SECSuccess) {
+	    if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
 	    return rv;	/* err set by ssl3_AppendHandshake* */
 	}
     }
 
     rv = ssl3_AppendHandshakeNumber(ss, num_suites*sizeof(ssl3CipherSuite), 2);
     if (rv != SECSuccess) {
+	if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
 	return rv;	/* err set by ssl3_AppendHandshake* */
     }
 
     if (ss->ssl3.hs.sendingSCSV) {
 	/* Add the actual SCSV */
 	rv = ssl3_AppendHandshakeNumber(ss, TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
 					sizeof(ssl3CipherSuite));
 	if (rv != SECSuccess) {
+	    if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
 	    return rv;	/* err set by ssl3_AppendHandshake* */
 	}
 	actual_count++;
     }
     for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
 	ssl3CipherSuiteCfg *suite = &ss->cipherSuites[i];
 	if (config_match(suite, ss->ssl3.policy, PR_TRUE, &ss->vrange)) {
 	    actual_count++;
 	    if (actual_count > num_suites) {
+		if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
 		/* set error card removal/insertion error */
 		PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
 		return SECFailure;
 	    }
 	    rv = ssl3_AppendHandshakeNumber(ss, suite->cipher_suite,
 					    sizeof(ssl3CipherSuite));
 	    if (rv != SECSuccess) {
+		if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
 		return rv;	/* err set by ssl3_AppendHandshake* */
 	    }
 	}
     }
 
     /* if cards were removed or inserted between count_cipher_suites and
      * generating our list, detect the error here rather than send it off to
      * the server.. */
     if (actual_count != num_suites) {
 	/* Card removal/insertion error */
+	if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
 	PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
 	return SECFailure;
     }
 
     rv = ssl3_AppendHandshakeNumber(ss, numCompressionMethods, 1);
     if (rv != SECSuccess) {
+	if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
 	return rv;	/* err set by ssl3_AppendHandshake* */
     }
     for (i = 0; i < compressionMethodsCount; i++) {
 	if (!compressionEnabled(ss, compressions[i]))
 	    continue;
 	rv = ssl3_AppendHandshakeNumber(ss, compressions[i], 1);
 	if (rv != SECSuccess) {
+	    if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
 	    return rv;	/* err set by ssl3_AppendHandshake* */
 	}
     }
 
     if (total_exten_len) {
 	PRUint32 maxBytes = total_exten_len - 2;
 	PRInt32  extLen;
 
 	rv = ssl3_AppendHandshakeNumber(ss, maxBytes, 2);
 	if (rv != SECSuccess) {
+	    if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
 	    return rv;	/* err set by AppendHandshake. */
 	}
 
 	extLen = ssl3_CallHelloExtensionSenders(ss, PR_TRUE, maxBytes, NULL);
 	if (extLen < 0) {
+	    if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
 	    return SECFailure;
 	}
 	maxBytes -= extLen;
 	PORT_Assert(!maxBytes);
     } 
+
+    if (sid->u.ssl3.lock) {
+        PR_RWLock_Unlock(sid->u.ssl3.lock);
+    }
+
+    if (ss->xtnData.sentSessionTicketInClientHello) {
+        SSL_AtomicIncrementLong(&ssl3stats.sch_sid_stateless_resumes);
+    }
+
     if (ss->ssl3.hs.sendingSCSV) {
 	/* Since we sent the SCSV, pretend we sent empty RI extension. */
 	TLSExtensionData *xtnData = &ss->xtnData;
 	xtnData->advertised[xtnData->numAdvertised++] = 
 	    ssl_renegotiation_info_xtn;
     }
 
     flags = 0;
@@ -6392,18 +6433,17 @@ ssl3_HandleServerHello(sslSocket *ss, SS
 		break; 
 	    }
 	}
 
 	/* Got a Match */
 	SSL_AtomicIncrementLong(& ssl3stats.hsh_sid_cache_hits );
 
 	/* If we sent a session ticket, then this is a stateless resume. */
-	if (sid->version > SSL_LIBRARY_VERSION_3_0 &&
-	    sid->u.ssl3.sessionTicket.ticket.data != NULL)
+	if (ss->xtnData.sentSessionTicketInClientHello)
 	    SSL_AtomicIncrementLong(& ssl3stats.hsh_sid_stateless_resumes );
 
 	if (ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn))
 	    ss->ssl3.hs.ws = wait_new_session_ticket;
 	else
 	    ss->ssl3.hs.ws = wait_change_cipher;
 
 	ss->ssl3.hs.isResuming = PR_TRUE;
@@ -6735,73 +6775,105 @@ loser:
 
 no_memory:	/* no-memory error has already been set. */
     ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
     return SECFailure;
 }
 
 
 /*
- * Returns true if the client authentication key is an RSA or DSA key that
- * may be able to sign only SHA-1 hashes.
+ * Returns the TLS signature algorithm for the client authentication key and
+ * whether it is an RSA or DSA key that may be able to sign only SHA-1 hashes.
  */
-static PRBool
-ssl3_ClientKeyPrefersSHA1(sslSocket *ss)
-{
+static SECStatus
+ssl3_ExtractClientKeyInfo(sslSocket *ss,
+			  TLSSignatureAlgorithm *sigAlg,
+			  PRBool *preferSha1)
+{
+    SECStatus rv = SECSuccess;
     SECKEYPublicKey *pubk;
-    PRBool preferSha1 = PR_FALSE;
+
+    pubk = CERT_ExtractPublicKey(ss->ssl3.clientCertificate);
+    if (pubk == NULL) {
+	rv = SECFailure;
+	goto done;
+    }
+
+    rv = ssl3_TLSSignatureAlgorithmForKeyType(pubk->keyType, sigAlg);
+    if (rv != SECSuccess) {
+	goto done;
+    }
 
     /* If the key is a 1024-bit RSA or DSA key, assume conservatively that
      * it may be unable to sign SHA-256 hashes. This is the case for older
      * Estonian ID cards that have 1024-bit RSA keys. In FIPS 186-2 and
      * older, DSA key size is at most 1024 bits and the hash function must
      * be SHA-1.
      */
-    pubk = CERT_ExtractPublicKey(ss->ssl3.clientCertificate);
-    if (pubk == NULL) {
-	return PR_FALSE;
-    }
     if (pubk->keyType == rsaKey || pubk->keyType == dsaKey) {
-	preferSha1 = SECKEY_PublicKeyStrength(pubk) <= 128;
-    }
-    SECKEY_DestroyPublicKey(pubk);
-    return preferSha1;
-}
-
-/* Destroys the backup handshake hash context if we don't need it. */
+	*preferSha1 = SECKEY_PublicKeyStrength(pubk) <= 128;
+    } else {
+	*preferSha1 = PR_FALSE;
+    }
+
+done:
+    if (pubk)
+	SECKEY_DestroyPublicKey(pubk);
+    return rv;
+}
+
+/* Destroys the backup handshake hash context if we don't need it. Note that
+ * this function selects the hash algorithm for client authentication
+ * signatures; ssl3_SendCertificateVerify uses the presence of the backup hash
+ * to determine whether to use SHA-1 or SHA-256. */
 static void
 ssl3_DestroyBackupHandshakeHashIfNotNeeded(sslSocket *ss,
 					   const SECItem *algorithms)
 {
+    SECStatus rv;
+    TLSSignatureAlgorithm sigAlg;
+    PRBool preferSha1;
+    PRBool supportsSha1 = PR_FALSE;
+    PRBool supportsSha256 = PR_FALSE;
     PRBool needBackupHash = PR_FALSE;
     unsigned int i;
 
 #ifndef NO_PKCS11_BYPASS
     /* Backup handshake hash is not supported in PKCS #11 bypass mode. */
     if (ss->opt.bypassPKCS11) {
 	PORT_Assert(!ss->ssl3.hs.backupHash);
 	return;
     }
 #endif
     PORT_Assert(ss->ssl3.hs.backupHash);
-    /* XXX It would be better to first figure out if the server accepts
-     * SHA-1 and then call ssl3_ClientKeyPrefersSHA1, because
-     * ssl3_ClientKeyPrefersSHA1 is the more expensive operation.
-     */
-    if (ssl3_ClientKeyPrefersSHA1(ss)) {
-	/* Use SHA-1 if the server supports it. */
-	for (i = 0; i < algorithms->len; i += 2) {
-	    if (algorithms->data[i] == tls_hash_sha1 &&
-		(algorithms->data[i+1] == tls_sig_rsa ||
-		 algorithms->data[i+1] == tls_sig_dsa)) {
-		needBackupHash = PR_TRUE;
-		break;
-	    }
-	}
-    }
+
+    /* Determine the key's signature algorithm and whether it prefers SHA-1. */
+    rv = ssl3_ExtractClientKeyInfo(ss, &sigAlg, &preferSha1);
+    if (rv != SECSuccess) {
+	goto done;
+    }
+
+    /* Determine the server's hash support for that signature algorithm. */
+    for (i = 0; i < algorithms->len; i += 2) {
+	if (algorithms->data[i+1] == sigAlg) {
+	    if (algorithms->data[i] == tls_hash_sha1) {
+		supportsSha1 = PR_TRUE;
+	    } else if (algorithms->data[i] == tls_hash_sha256) {
+		supportsSha256 = PR_TRUE;
+	    }
+	}
+    }
+
+    /* If either the server does not support SHA-256 or the client key prefers
+     * SHA-1, leave the backup hash. */
+    if (supportsSha1 && (preferSha1 || !supportsSha256)) {
+	needBackupHash = PR_TRUE;
+    }
+
+done:
     if (!needBackupHash) {
 	PK11_DestroyContext(ss->ssl3.hs.backupHash, PR_TRUE);
 	ss->ssl3.hs.backupHash = NULL;
     }
 }
 
 typedef struct dnameNode {
     struct dnameNode *next;
@@ -9277,54 +9349,60 @@ ssl3_SendEmptyCertificate(sslSocket *ss)
 	rv = ssl3_AppendHandshakeNumber(ss, 0, 3);
     }
     return rv;	/* error, if any, set by functions called above. */
 }
 
 SECStatus
 ssl3_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
-    SECStatus         rv;
-    NewSessionTicket  session_ticket;
+    SECStatus rv;
+    SECItem ticketData;
 
     SSL_TRC(3, ("%d: SSL3[%d]: handle session_ticket handshake",
 		SSL_GETPID(), ss->fd));
 
     PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
 
+    PORT_Assert(!ss->ssl3.hs.newSessionTicket.ticket.data);
+    PORT_Assert(!ss->ssl3.hs.receivedNewSessionTicket);
+
     if (ss->ssl3.hs.ws != wait_new_session_ticket) {
 	SSL3_SendAlert(ss, alert_fatal, unexpected_message);
 	PORT_SetError(SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET);
 	return SECFailure;
     }
 
-    session_ticket.received_timestamp = ssl_Time();
+    /* RFC5077 Section 3.3: "The client MUST NOT treat the ticket as valid
+     * until it has verified the server's Finished message." See the comment in
+     * ssl3_FinishHandshake for more details.
+     */
+    ss->ssl3.hs.newSessionTicket.received_timestamp = ssl_Time();
     if (length < 4) {
 	(void)SSL3_SendAlert(ss, alert_fatal, decode_error);
 	PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
 	return SECFailure;
     }
-    session_ticket.ticket_lifetime_hint =
+    ss->ssl3.hs.newSessionTicket.ticket_lifetime_hint =
 	(PRUint32)ssl3_ConsumeHandshakeNumber(ss, 4, &b, &length);
 
-    rv = ssl3_ConsumeHandshakeVariable(ss, &session_ticket.ticket, 2,
-	&b, &length);
+    rv = ssl3_ConsumeHandshakeVariable(ss, &ticketData, 2, &b, &length);
     if (length != 0 || rv != SECSuccess) {
 	(void)SSL3_SendAlert(ss, alert_fatal, decode_error);
 	PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
 	return SECFailure;  /* malformed */
     }
-
-    rv = ssl3_SetSIDSessionTicket(ss->sec.ci.sid, &session_ticket);
-    if (rv != SECSuccess) {
-	(void)SSL3_SendAlert(ss, alert_fatal, handshake_failure);
-	PORT_SetError(SSL_ERROR_INTERNAL_ERROR_ALERT);
-	return SECFailure;
-    }
+    rv = SECITEM_CopyItem(NULL, &ss->ssl3.hs.newSessionTicket.ticket,
+			  &ticketData);
+    if (rv != SECSuccess) {
+	return rv;
+    }
+    ss->ssl3.hs.receivedNewSessionTicket = PR_TRUE;
+
     ss->ssl3.hs.ws = wait_change_cipher;
     return SECSuccess;
 }
 
 #ifdef NISCC_TEST
 static PRInt32 connNum = 0;
 
 static SECStatus 
@@ -10402,16 +10480,21 @@ ssl3_HandleFinished(sslSocket *ss, SSL3O
 	/* Send a NewSessionTicket message if the client sent us
 	 * either an empty session ticket, or one that did not verify.
 	 * (Note that if either of these conditions was met, then the
 	 * server has sent a SessionTicket extension in the
 	 * ServerHello message.)
 	 */
 	if (isServer && !ss->ssl3.hs.isResuming &&
 	    ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn)) {
+	    /* RFC 5077 Section 3.3: "In the case of a full handshake, the
+	     * server MUST verify the client's Finished message before sending
+	     * the ticket." Presumably, this also means that the client's
+	     * certificate, if any, must be verified beforehand too.
+	     */
 	    rv = ssl3_SendNewSessionTicket(ss);
 	    if (rv != SECSuccess) {
 		goto xmit_loser;
 	    }
 	}
 
 	rv = ssl3_SendChangeCipherSpecs(ss);
 	if (rv != SECSuccess) {
@@ -10521,17 +10604,37 @@ ssl3_FinishHandshake(sslSocket * ss)
 {
     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;
 
+    /* RFC 5077 Section 3.3: "The client MUST NOT treat the ticket as valid
+     * until it has verified the server's Finished message." When the server
+     * sends a NewSessionTicket in a resumption handshake, we must wait until
+     * the handshake is finished (we have verified the server's Finished
+     * AND the server's certificate) before we update the ticket in the sid.
+     *
+     * This must be done before we call (*ss->sec.cache)(ss->sec.ci.sid)
+     * because CacheSID requires the session ticket to already be set, and also
+     * because of the lazy lock creation scheme used by CacheSID and
+     * ssl3_SetSIDSessionTicket.
+     */
+    if (ss->ssl3.hs.receivedNewSessionTicket) {
+	PORT_Assert(!ss->sec.isServer);
+	ssl3_SetSIDSessionTicket(ss->sec.ci.sid, &ss->ssl3.hs.newSessionTicket);
+	/* The sid took over the ticket data */
+	PORT_Assert(!ss->ssl3.hs.newSessionTicket.ticket.data);
+        ss->ssl3.hs.receivedNewSessionTicket = PR_FALSE;
+    }
+
     if (ss->ssl3.hs.cacheSID) {
+	PORT_Assert(ss->sec.ci.sid->cached == never_cached);
 	(*ss->sec.cache)(ss->sec.ci.sid);
 	ss->ssl3.hs.cacheSID = PR_FALSE;
     }
 
     ss->ssl3.hs.canFalseStart = PR_FALSE; /* False Start phase is complete */
     ss->ssl3.hs.ws = idle_handshake;
 
     ssl_FinishHandshake(ss);
@@ -11592,16 +11695,20 @@ ssl3_InitState(sslSocket *ss)
 	PR_INIT_CLIST(&ss->ssl3.hs.lastMessageFlight);
 	dtls_SetMTU(ss, 0); /* Set the MTU to the highest plateau */
     }
 
     PORT_Assert(!ss->ssl3.hs.messages.buf && !ss->ssl3.hs.messages.space);
     ss->ssl3.hs.messages.buf = NULL;
     ss->ssl3.hs.messages.space = 0;
 
+    ss->ssl3.hs.receivedNewSessionTicket = PR_FALSE;
+    PORT_Memset(&ss->ssl3.hs.newSessionTicket, 0,
+		sizeof(ss->ssl3.hs.newSessionTicket));
+
     ss->ssl3.initialized = PR_TRUE;
     return SECSuccess;
 }
 
 /* Returns a reference counted object that contains a key pair.
  * Or NULL on failure.  Initial ref count is 1.
  * Uses the keys in the pair as input.
  */
@@ -11915,16 +12022,18 @@ ssl3_DestroySSL3Info(sslSocket *ss)
 	ss->ssl3.hs.messages.buf = NULL;
 	ss->ssl3.hs.messages.len = 0;
 	ss->ssl3.hs.messages.space = 0;
     }
 
     /* free the SSL3Buffer (msg_body) */
     PORT_Free(ss->ssl3.hs.msg_body.buf);
 
+    SECITEM_FreeItem(&ss->ssl3.hs.newSessionTicket.ticket, PR_FALSE);
+
     /* free up the CipherSpecs */
     ssl3_DestroyCipherSpec(&ss->ssl3.specs[0], PR_TRUE/*freeSrvName*/);
     ssl3_DestroyCipherSpec(&ss->ssl3.specs[1], PR_TRUE/*freeSrvName*/);
 
     /* Destroy the DTLS data */
     if (IS_DTLS(ss)) {
 	dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight);
 	if (ss->ssl3.hs.recvdFragments.buf) {
--- a/security/nss/lib/ssl/ssl3ext.c
+++ b/security/nss/lib/ssl/ssl3ext.c
@@ -474,33 +474,41 @@ loser:
 PRInt32
 ssl3_SendSessionTicketXtn(
 			sslSocket * ss,
 			PRBool      append,
 			PRUint32    maxBytes)
 {
     PRInt32 extension_length;
     NewSessionTicket *session_ticket = NULL;
+    sslSessionID *sid = ss->sec.ci.sid;
 
     /* Ignore the SessionTicket extension if processing is disabled. */
     if (!ss->opt.enableSessionTickets)
 	return 0;
 
     /* Empty extension length = extension_type (2-bytes) +
      * length(extension_data) (2-bytes)
      */
     extension_length = 4;
 
     /* If we are a client then send a session ticket if one is availble.
      * Servers that support the extension and are willing to negotiate the
      * the extension always respond with an empty extension.
      */
     if (!ss->sec.isServer) {
-	sslSessionID *sid = ss->sec.ci.sid;
-	session_ticket = &sid->u.ssl3.sessionTicket;
+	/* The caller must be holding sid->u.ssl3.lock for reading. We cannot
+	 * just acquire and release the lock within this function because the
+	 * caller will call this function twice, and we need the inputs to be
+	 * consistent between the two calls. Note that currently the caller
+	 * will only be holding the lock when we are the client and when we're
+	 * attempting to resume an existing session.
+	 */
+
+	session_ticket = &sid->u.ssl3.locked.sessionTicket;
 	if (session_ticket->ticket.data) {
 	    if (ss->xtnData.ticketTimestampVerified) {
 		extension_length += session_ticket->ticket.len;
 	    } else if (!append &&
 		(session_ticket->ticket_lifetime_hint == 0 ||
 		(session_ticket->ticket_lifetime_hint +
 		    session_ticket->received_timestamp > ssl_Time()))) {
 		extension_length += session_ticket->ticket.len;
@@ -515,16 +523,17 @@ ssl3_SendSessionTicketXtn(
         rv = ssl3_AppendHandshakeNumber(ss, ssl_session_ticket_xtn, 2);
         if (rv != SECSuccess)
 	    goto loser;
 	if (session_ticket && session_ticket->ticket.data &&
 	    ss->xtnData.ticketTimestampVerified) {
 	    rv = ssl3_AppendHandshakeVariable(ss, session_ticket->ticket.data,
 		session_ticket->ticket.len, 2);
 	    ss->xtnData.ticketTimestampVerified = PR_FALSE;
+	    ss->xtnData.sentSessionTicketInClientHello = PR_TRUE;
 	} else {
 	    rv = ssl3_AppendHandshakeNumber(ss, 0, 2);
 	}
         if (rv != SECSuccess)
 	    goto loser;
 
 	if (!ss->sec.isServer) {
 	    TLSExtensionData *xtnData = &ss->xtnData;
--- a/security/nss/lib/ssl/sslcon.c
+++ b/security/nss/lib/ssl/sslcon.c
@@ -622,18 +622,19 @@ ssl2_SendServerFinishedMessage(sslSocket
 	sent = (*ss->sec.send)(ss, msg, sendLen, 0);
 
 	if (sent < 0) {
 	    /* If send failed, it is now a bogus  session-id */
 	    if (ss->sec.uncache)
 		(*ss->sec.uncache)(sid);
 	    rv = (SECStatus)sent;
 	} else if (!ss->opt.noCache) {
-	    /* Put the sid in session-id cache, (may already be there) */
-	    (*ss->sec.cache)(sid);
+	    if (sid->cached == never_cached) {
+		(*ss->sec.cache)(sid);
+	    }
 	    rv = SECSuccess;
 	}
 	ssl_FreeSID(sid);
 	ss->sec.ci.sid = 0;
     }
 done:
     ssl_ReleaseXmitBufLock(ss);    /***************************************/
     return rv;
@@ -2165,17 +2166,17 @@ ssl2_ClientRegSessionID(sslSocket *ss, P
     sslSessionID *sid = ss->sec.ci.sid;
 
     /* Record entry in nonce cache */
     if (sid->peerCert == NULL) {
 	PORT_Memcpy(sid->u.ssl2.sessionID, s, sizeof(sid->u.ssl2.sessionID));
 	sid->peerCert = CERT_DupCertificate(ss->sec.peerCert);
 
     }
-    if (!ss->opt.noCache)
+    if (!ss->opt.noCache && sid->cached == never_cached)
 	(*ss->sec.cache)(sid);
 }
 
 /* Called from ssl2_HandleMessage() */
 static SECStatus
 ssl2_TriggerNextMessage(sslSocket *ss)
 {
     SECStatus        rv;
--- a/security/nss/lib/ssl/sslenum.c
+++ b/security/nss/lib/ssl/sslenum.c
@@ -29,26 +29,40 @@
  *      Camellia also has wide international support across standards
  *      organizations. SEED is only recommended by the Korean government. 3DES
  *      only provides 112 bits of security. RC4 is now deprecated or forbidden
  *      by many standards organizations.
  *    * Within symmetric algorithm sections, order by message authentication
  *      algorithm: GCM, then HMAC-SHA1, then HMAC-SHA256, then HMAC-MD5.
  *    * Within message authentication algorithm sections, order by asymmetric
  *      signature algorithm: ECDSA, then RSA, then DSS.
+ *
+ * Exception: Because some servers ignore the high-order byte of the cipher
+ * suite ID, we must be careful about adding cipher suites with IDs larger
+ * than 0x00ff; see bug 946147. For these broken servers, the first four cipher
+ * suites, with the MSB zeroed, look like:
+ *      TLS_KRB5_EXPORT_WITH_RC4_40_MD5 {0x00,0x2B }
+ *      TLS_RSA_WITH_AES_128_CBC_SHA { 0x00,0x2F }
+ *      TLS_RSA_WITH_3DES_EDE_CBC_SHA { 0x00,0x0A }
+ *      TLS_RSA_WITH_DES_CBC_SHA { 0x00,0x09 }
+ * The broken server only supports the third and fourth ones and will select
+ * the third one.
  */
 const PRUint16 SSL_ImplementedCiphers[] = {
 #ifdef NSS_ENABLE_ECC
     TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
     TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+    /* TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA must appear before
+     * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA to work around bug 946147.
+     */
+    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
     TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
     TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
     TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
     TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
-    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
     TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
     TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
     TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
     TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
     TLS_ECDHE_RSA_WITH_RC4_128_SHA,
 #endif /* NSS_ENABLE_ECC */
 
     TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
--- a/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/lib/ssl/sslimpl.h
@@ -582,34 +582,41 @@ typedef struct {
 
 typedef enum {	never_cached, 
 		in_client_cache, 
 		in_server_cache, 
 		invalid_cache		/* no longer in any cache. */
 } Cached;
 
 struct sslSessionIDStr {
+    /* The global cache lock must be held when accessing these members when the
+     * sid is in any cache.
+     */
     sslSessionID *        next;   /* chain used for client sockets, only */
+    Cached                cached;
+    int                   references;
+    PRUint32              lastAccessTime;	/* seconds since Jan 1, 1970 */
+
+    /* The rest of the members, except for the members of u.ssl3.locked, may
+     * be modified only when the sid is not in any cache.
+     */
 
     CERTCertificate *     peerCert;
     SECItemArray          peerCertStatus; /* client only */
     const char *          peerID;     /* client only */
     const char *          urlSvrName; /* client only */
     CERTCertificate *     localCert;
 
     PRIPv6Addr            addr;
     PRUint16              port;
 
     SSL3ProtocolVersion   version;
 
     PRUint32              creationTime;		/* seconds since Jan 1, 1970 */
-    PRUint32              lastAccessTime;	/* seconds since Jan 1, 1970 */
     PRUint32              expirationTime;	/* seconds since Jan 1, 1970 */
-    Cached                cached;
-    int                   references;
 
     SSLSignType           authAlgorithm;
     PRUint32              authKeyBits;
     SSLKEAType            keaType;
     PRUint32              keaKeyBits;
 
     union {
 	struct {
@@ -665,26 +672,39 @@ struct sslSessionIDStr {
 	    */
 	    SECMODModuleID    clAuthModuleID;
 	    CK_SLOT_ID        clAuthSlotID;
 	    PRUint16          clAuthSeries;
 
             char              masterValid;
 	    char              clAuthValid;
 
-	    /* Session ticket if we have one, is sent as an extension in the
-	     * ClientHello message.  This field is used by clients.
+	    SECItem           srvName;
+
+	    /* This lock is lazily initialized by CacheSID when a sid is first
+	     * cached. Before then, there is no need to lock anything because
+	     * the sid isn't being shared by anything.
 	     */
-	    NewSessionTicket  sessionTicket;
-            SECItem           srvName;
+	    PRRWLock *lock;
+
+	    /* The lock must be held while reading or writing these members
+	     * because they change while the sid is cached.
+	     */
+	    struct {
+		/* The session ticket, if we have one, is sent as an extension
+		 * in the ClientHello message. This field is used only by
+		 * clients. It is protected by lock when lock is non-null
+		 * (after the sid has been added to the client session cache).
+		 */
+		NewSessionTicket sessionTicket;
+	    } locked;
 	} ssl3;
     } u;
 };
 
-
 typedef struct ssl3CipherSuiteDefStr {
     ssl3CipherSuite          cipher_suite;
     SSL3BulkCipher           bulk_cipher_alg;
     SSL3MACAlgorithm         mac_alg;
     SSL3KeyExchangeAlgorithm key_exchange_alg;
 } ssl3CipherSuiteDef;
 
 /*
@@ -754,16 +774,17 @@ struct TLSExtensionDataStr {
     PRUint16 numAdvertised;
     PRUint16 numNegotiated;
     PRUint16 advertised[SSL_MAX_EXTENSIONS];
     PRUint16 negotiated[SSL_MAX_EXTENSIONS];
 
     /* SessionTicket Extension related data. */
     PRBool ticketTimestampVerified;
     PRBool emptySessionTicket;
+    PRBool sentSessionTicketInClientHello;
 
     /* 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;
 };
@@ -836,16 +857,24 @@ const ssl3CipherSuiteDef *suite_def;
     SSL3HandshakeType     msg_type;
     unsigned long         msg_len;
     SECItem               ca_list;     /* used only by client */
     PRBool                isResuming;  /* are we resuming a session */
     PRBool                usedStepDownKey;  /* we did a server key exchange. */
     PRBool                sendingSCSV; /* instead of empty RI */
     sslBuffer             msgState;    /* current state for handshake messages*/
                                        /* protected by recvBufLock */
+
+    /* The session ticket received in a NewSessionTicket message is temporarily
+     * stored in newSessionTicket until the handshake is finished; then it is
+     * moved to the sid.
+     */
+    PRBool                receivedNewSessionTicket;
+    NewSessionTicket      newSessionTicket;
+
     PRUint16              finishedBytes; /* size of single finished below */
     union {
 	TLSFinished       tFinished[2]; /* client, then server */
 	SSL3Finished      sFinished[2];
 	SSL3Opaque        data[72];
     }                     finishedMsgs;
 #ifdef NSS_ENABLE_ECC
     PRUint32              negotiatedECCurves; /* bit mask */
@@ -1741,18 +1770,18 @@ extern PRInt32 ssl3_SendSupportedPointFo
 #endif
 
 /* call the registered extension handlers. */
 extern SECStatus ssl3_HandleHelloExtensions(sslSocket *ss, 
 			SSL3Opaque **b, PRUint32 *length);
 
 /* Hello Extension related routines. */
 extern PRBool ssl3_ExtensionNegotiated(sslSocket *ss, PRUint16 ex_type);
-extern SECStatus ssl3_SetSIDSessionTicket(sslSessionID *sid,
-			NewSessionTicket *session_ticket);
+extern void ssl3_SetSIDSessionTicket(sslSessionID *sid,
+			/*in/out*/ NewSessionTicket *session_ticket);
 extern SECStatus ssl3_SendNewSessionTicket(sslSocket *ss);
 extern PRBool ssl_GetSessionTicketKeys(unsigned char *keyName,
 			unsigned char *encKey, unsigned char *macKey);
 extern PRBool ssl_GetSessionTicketKeysPKCS11(SECKEYPrivateKey *svrPrivKey,
 			SECKEYPublicKey *svrPubKey, void *pwArg,
 			unsigned char *keyName, PK11SymKey **aesKey,
 			PK11SymKey **macKey);
 
--- a/security/nss/lib/ssl/sslnonce.c
+++ b/security/nss/lib/ssl/sslnonce.c
@@ -168,39 +168,46 @@ ssl_DestroySID(sslSessionID *sid)
     PORT_Assert((sid->references == 0));
 
     if (sid->cached == in_client_cache)
     	return;	/* it will get taken care of next time cache is traversed. */
 
     if (sid->version < SSL_LIBRARY_VERSION_3_0) {
 	SECITEM_ZfreeItem(&sid->u.ssl2.masterKey, PR_FALSE);
 	SECITEM_ZfreeItem(&sid->u.ssl2.cipherArg, PR_FALSE);
+    } else {
+        if (sid->u.ssl3.locked.sessionTicket.ticket.data) {
+            SECITEM_FreeItem(&sid->u.ssl3.locked.sessionTicket.ticket,
+                             PR_FALSE);
+        }
+        if (sid->u.ssl3.srvName.data) {
+            SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
+        }
+
+        if (sid->u.ssl3.lock) {
+            PR_DestroyRWLock(sid->u.ssl3.lock);
+        }
     }
+
     if (sid->peerID != NULL)
 	PORT_Free((void *)sid->peerID);		/* CONST */
 
     if (sid->urlSvrName != NULL)
 	PORT_Free((void *)sid->urlSvrName);	/* CONST */
 
     if ( sid->peerCert ) {
 	CERT_DestroyCertificate(sid->peerCert);
     }
     if (sid->peerCertStatus.items) {
         SECITEM_FreeArray(&sid->peerCertStatus, PR_FALSE);
     }
 
     if ( sid->localCert ) {
 	CERT_DestroyCertificate(sid->localCert);
     }
-    if (sid->u.ssl3.sessionTicket.ticket.data) {
-	SECITEM_FreeItem(&sid->u.ssl3.sessionTicket.ticket, PR_FALSE);
-    }
-    if (sid->u.ssl3.srvName.data) {
-	SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
-    }
     
     PORT_ZFree(sid, sizeof(sslSessionID));
 }
 
 /* BEWARE: This function gets called for both client and server SIDs !!
  * Decrement reference count, and 
  *    free sid if ref count is zero, and sid is not in the cache. 
  * Does NOT remove from the cache first.  
@@ -302,56 +309,62 @@ ssl_LookupSID(const PRIPv6Addr *addr, PR
 /*
 ** Add an sid to the cache or return a previously cached entry to the cache.
 ** Although this is static, it is called via ss->sec.cache().
 */
 static void 
 CacheSID(sslSessionID *sid)
 {
     PRUint32  expirationPeriod;
+
+    PORT_Assert(sid->cached == never_cached);
+
     SSL_TRC(8, ("SSL: Cache: sid=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
 		"time=%x cached=%d",
 		sid, sid->cached, sid->addr.pr_s6_addr32[0], 
 		sid->addr.pr_s6_addr32[1], sid->addr.pr_s6_addr32[2],
 		sid->addr.pr_s6_addr32[3],  sid->port, sid->creationTime,
 		sid->cached));
 
-    if (sid->cached == in_client_cache)
-	return;
-
     if (!sid->urlSvrName) {
         /* don't cache this SID because it can never be matched */
         return;
     }
 
     /* XXX should be different trace for version 2 vs. version 3 */
     if (sid->version < SSL_LIBRARY_VERSION_3_0) {
 	expirationPeriod = ssl_sid_timeout;
 	PRINT_BUF(8, (0, "sessionID:",
 		  sid->u.ssl2.sessionID, sizeof(sid->u.ssl2.sessionID)));
 	PRINT_BUF(8, (0, "masterKey:",
 		  sid->u.ssl2.masterKey.data, sid->u.ssl2.masterKey.len));
 	PRINT_BUF(8, (0, "cipherArg:",
 		  sid->u.ssl2.cipherArg.data, sid->u.ssl2.cipherArg.len));
     } else {
 	if (sid->u.ssl3.sessionIDLength == 0 &&
-	    sid->u.ssl3.sessionTicket.ticket.data == NULL)
+	    sid->u.ssl3.locked.sessionTicket.ticket.data == NULL)
 	    return;
+
 	/* Client generates the SessionID if this was a stateless resume. */
 	if (sid->u.ssl3.sessionIDLength == 0) {
 	    SECStatus rv;
 	    rv = PK11_GenerateRandom(sid->u.ssl3.sessionID,
 		SSL3_SESSIONID_BYTES);
 	    if (rv != SECSuccess)
 		return;
 	    sid->u.ssl3.sessionIDLength = SSL3_SESSIONID_BYTES;
 	}
 	expirationPeriod = ssl3_sid_timeout;
 	PRINT_BUF(8, (0, "sessionID:",
 		      sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength));
+
+	sid->u.ssl3.lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, NULL);
+	if (!sid->u.ssl3.lock) {
+	    return;
+	}
     }
     PORT_Assert(sid->creationTime != 0 && sid->expirationTime != 0);
     if (!sid->creationTime)
 	sid->lastAccessTime = sid->creationTime = ssl_Time();
     if (!sid->expirationTime)
 	sid->expirationTime = sid->creationTime + expirationPeriod;
 
     /*
@@ -465,40 +478,43 @@ ssl_Time(void)
     now = PR_Now();
     LL_I2L(ll, 1000000L);
     LL_DIV(now, now, ll);
     LL_L2UI(myTime, now);
 #endif
     return myTime;
 }
 
-SECStatus
-ssl3_SetSIDSessionTicket(sslSessionID *sid, NewSessionTicket *session_ticket)
+void
+ssl3_SetSIDSessionTicket(sslSessionID *sid,
+                         /*in/out*/ NewSessionTicket *newSessionTicket)
 {
-    SECStatus rv;
+    PORT_Assert(sid);
+    PORT_Assert(newSessionTicket);
 
-    /* We need to lock the cache, as this sid might already be in the cache. */
-    LOCK_CACHE;
-
-    /* A server might have sent us an empty ticket, which has the
-     * effect of clearing the previously known ticket.
+    /* if sid->u.ssl3.lock, we are updating an existing entry that is already
+     * cached or was once cached, so we need to acquire and release the write
+     * lock. Otherwise, this is a new session that isn't shared with anything
+     * yet, so no locking is needed.
      */
-    if (sid->u.ssl3.sessionTicket.ticket.data)
-	SECITEM_FreeItem(&sid->u.ssl3.sessionTicket.ticket, PR_FALSE);
-    if (session_ticket->ticket.len > 0) {
-	rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.sessionTicket.ticket,
-	    &session_ticket->ticket);
-	if (rv != SECSuccess) {
-	    UNLOCK_CACHE;
-	    return rv;
+    if (sid->u.ssl3.lock) {
+	PR_RWLock_Wlock(sid->u.ssl3.lock);
+
+	/* A server might have sent us an empty ticket, which has the
+	 * effect of clearing the previously known ticket.
+	 */
+	if (sid->u.ssl3.locked.sessionTicket.ticket.data) {
+	    SECITEM_FreeItem(&sid->u.ssl3.locked.sessionTicket.ticket,
+			     PR_FALSE);
 	}
-    } else {
-	sid->u.ssl3.sessionTicket.ticket.data = NULL;
-	sid->u.ssl3.sessionTicket.ticket.len = 0;
     }
-    sid->u.ssl3.sessionTicket.received_timestamp =
-	session_ticket->received_timestamp;
-    sid->u.ssl3.sessionTicket.ticket_lifetime_hint =
-	session_ticket->ticket_lifetime_hint;
+
+    PORT_Assert(!sid->u.ssl3.locked.sessionTicket.ticket.data);
 
-    UNLOCK_CACHE;
-    return SECSuccess;
+    /* Do a shallow copy, moving the ticket data. */
+    sid->u.ssl3.locked.sessionTicket = *newSessionTicket;
+    newSessionTicket->ticket.data = NULL;
+    newSessionTicket->ticket.len = 0;
+
+    if (sid->u.ssl3.lock) {
+	PR_RWLock_Unlock(sid->u.ssl3.lock);
+    }
 }