Bug 898431: Update NSS to NSS 3.15.4 RTM (NSS_3_15_4_RTM), r=me, a=bbajaj
authorBrian Smith <brian@briansmith.org>
Mon, 06 Jan 2014 14:20:15 -0800
changeset 167848 2c2ee62fd2fa1abc81b1517dc6c29f2e54486c56
parent 167841 d10eb10bfd0b4dc852f4df62d8871bb4227f2501
child 167849 4efbfe0c7320e04aedfdaa92fb74dba4430213db
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersme, bbajaj
bugs898431
milestone27.0
Bug 898431: Update NSS to NSS 3.15.4 RTM (NSS_3_15_4_RTM), r=me, a=bbajaj
security/nss/TAG-INFO
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/ocspi.h
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_ocspcertid.c
security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocspresponse.c
security/nss/lib/nss/nss.def
security/nss/lib/nss/nss.h
security/nss/lib/softoken/softkver.h
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
security/nss/lib/util/nssutil.h
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-NSS_3_15_4_BETA8
+NSS_3_15_4_RTM
--- 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
@@ -141,44 +141,18 @@ endif
 # When OS_TARGET=WIN95 is specified, then a Windows 95 target is built.
 # The difference between the Win95 target and the WinNT target is that
 # the WinNT target uses Windows NT specific features not available
 # in Windows 95. The Win95 target will run on Windows NT, but (supposedly)
 # 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.
+# cross-compilation, except on Windows, where it defaults to WIN95.
 #
-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)
@@ -206,17 +180,17 @@ ifeq ($(OS_ARCH), Windows_NT)
 	# MKS's uname -m returns "586" on a Pentium machine.
 	#
 	ifneq (,$(findstring 86,$(CPU_ARCH)))
 	    CPU_ARCH = x386
 	endif
     endif
 endif
 #
-# If uname -s returns "CYGWIN_NT-4.0", we assume that we are using
+# If uname -s returns "CYGWIN_NT-*", we assume that we are using
 # the uname.exe in the Cygwin tools.
 #
 ifeq (CYGWIN_NT,$(findstring CYGWIN_NT,$(OS_ARCH)))
     OS_RELEASE := $(patsubst CYGWIN_NT-%,%,$(OS_ARCH))
     OS_ARCH = WINNT
     ifndef CPU_ARCH
     ifeq (WOW64,$(findstring WOW64,$(OS_RELEASE)))
         OS_RELEASE := $(patsubst %-WOW64,%,$(OS_RELEASE))
@@ -226,17 +200,17 @@ ifeq (CYGWIN_NT,$(findstring CYGWIN_NT,$
 	# Cygwin's uname -m returns "i686" on a Pentium Pro machine.
 	#
 	ifneq (,$(findstring 86,$(CPU_ARCH)))
 	    CPU_ARCH = x386
 	endif
     endif
 endif
 #
-# If uname -s returns "MINGW32_NT-5.1", we assume that we are using
+# If uname -s returns "MINGW32_NT-*", we assume that we are using
 # the uname.exe in the MSYS toolkit.
 #
 ifeq (MINGW32_NT,$(findstring MINGW32_NT,$(OS_ARCH)))
     OS_RELEASE := $(patsubst MINGW32_NT-%,%,$(OS_ARCH))
     OS_ARCH = WINNT
     USE_MSYS = 1
     ifndef CPU_ARCH
 	CPU_ARCH := $(shell uname -m)
@@ -256,18 +230,22 @@ ifeq ($(OS_TARGET),Android)
    OS_TEST := arm
    OS_ARCH = Android
    ifndef OS_TARGET_RELEASE
 	OS_TARGET_RELEASE := 8
    endif
 endif
 
 ifndef OS_TARGET
+ifeq ($(OS_ARCH), WINNT)
+    OS_TARGET = WIN95
+else
     OS_TARGET = $(OS_ARCH)
 endif
+endif
 
 ifeq ($(OS_TARGET), WIN95)
     OS_RELEASE = 4.0
 endif
 
 ifdef OS_TARGET_RELEASE
     OS_RELEASE = $(OS_TARGET_RELEASE)
 endif
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,8 +5,9 @@
 
 /*
  * 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, 
@@ -745,24 +745,33 @@ ocsp_FreshenCacheItemNextFetchAttemptTim
 
     PR_ExitMonitor(OCSP_Global.monitor);
 }
 
 static PRBool
 ocsp_IsCacheItemFresh(OCSPCacheItem *cacheItem)
 {
     PRTime now;
-    PRBool retval;
-
-    PR_EnterMonitor(OCSP_Global.monitor);
+    PRBool fresh;
+
     now = PR_Now();
-    retval = (cacheItem->nextFetchAttemptTime > now);
-    OCSP_TRACE(("OCSP ocsp_IsCacheItemFresh: %d\n", retval));
-    PR_ExitMonitor(OCSP_Global.monitor);
-    return retval;
+
+    fresh = cacheItem->nextFetchAttemptTime > now;
+
+    /* Work around broken OCSP responders that return unknown responses for
+     * certificates, especially certificates that were just recently issued.
+     */
+    if (fresh && cacheItem->certStatusArena &&
+        cacheItem->certStatus.certStatusType == ocspCertStatus_unknown) {
+        fresh = PR_FALSE;
+    }
+
+    OCSP_TRACE(("OCSP ocsp_IsCacheItemFresh: %d\n", fresh));
+
+    return fresh;
 }
 
 /*
  * Status in *certIDWasConsumed will always be correct, regardless of 
  * return value.
  * If the caller is unable to transfer ownership of certID,
  * then the caller must set certIDWasConsumed to NULL,
  * and this function will potentially duplicate the certID object.
@@ -779,16 +788,29 @@ ocsp_CreateOrUpdateCacheEntry(OCSPCacheD
   
     if (certIDWasConsumed)
         *certIDWasConsumed = PR_FALSE;
   
     PR_EnterMonitor(OCSP_Global.monitor);
     PORT_Assert(OCSP_Global.maxCacheEntries >= 0);
   
     cacheItem = ocsp_FindCacheEntry(cache, certID);
+
+    /* Don't replace an unknown or revoked entry with an error entry, even if
+     * the existing entry is expired. Instead, we'll continue to use the
+     * existing (possibly expired) cache entry until we receive a valid signed
+     * response to replace it.
+     */
+    if (!single && cacheItem && cacheItem->certStatusArena &&
+        (cacheItem->certStatus.certStatusType == ocspCertStatus_revoked ||
+         cacheItem->certStatus.certStatusType == ocspCertStatus_unknown)) {
+        PR_ExitMonitor(OCSP_Global.monitor);
+        return SECSuccess;
+    }
+
     if (!cacheItem) {
         CERTOCSPCertID *myCertID;
         if (certIDWasConsumed) {
             myCertID = certID;
             *certIDWasConsumed = PR_TRUE;
         } else {
             myCertID = cert_DupOCSPCertID(certID);
             if (!myCertID) {
@@ -1624,18 +1646,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 +1733,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 +3484,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 +3502,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 +3535,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 +3641,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 +3663,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 +3690,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 +3722,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 +3795,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 +3898,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 +4466,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 +4513,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);
@@ -4906,70 +4932,75 @@ ocsp_CertHasGoodStatus(ocspCertStatus *s
 
 static SECStatus
 ocsp_SingleResponseCertHasGoodStatus(CERTOCSPSingleResponse *single, 
                                      PRTime time)
 {
     return ocsp_CertHasGoodStatus(single->certStatus, time);
 }
 
-/* Return value SECFailure means: not found or not fresh.
+/* SECFailure means the arguments were invalid.
  * On SECSuccess, the out parameters contain the OCSP status.
  * rvOcsp contains the overall result of the OCSP operation.
  * Depending on input parameter ignoreGlobalOcspFailureSetting,
  * a soft failure might be converted into *rvOcsp=SECSuccess.
  * If the cached attempt to obtain OCSP information had resulted
  * in a failure, missingResponseError shows the error code of
  * that failure.
+ * cacheFreshness is ocspMissing if no entry was found,
+ *                   ocspFresh if a fresh entry was found, or
+ *                   ocspStale if a stale entry was found.
  */
 SECStatus
-ocsp_GetCachedOCSPResponseStatusIfFresh(CERTOCSPCertID *certID, 
-                                        PRTime time,
-                                        PRBool ignoreGlobalOcspFailureSetting,
-                                        SECStatus *rvOcsp,
-                                        SECErrorCodes *missingResponseError)
+ocsp_GetCachedOCSPResponseStatus(CERTOCSPCertID *certID,
+                                 PRTime time,
+                                 PRBool ignoreGlobalOcspFailureSetting,
+                                 SECStatus *rvOcsp,
+                                 SECErrorCodes *missingResponseError,
+                                 OCSPFreshness *cacheFreshness)
 {
     OCSPCacheItem *cacheItem = NULL;
-    SECStatus rv = SECFailure;
   
-    if (!certID || !missingResponseError || !rvOcsp) {
+    if (!certID || !missingResponseError || !rvOcsp || !cacheFreshness) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
     }
     *rvOcsp = SECFailure;
     *missingResponseError = 0;
+    *cacheFreshness = ocspMissing;
   
     PR_EnterMonitor(OCSP_Global.monitor);
     cacheItem = ocsp_FindCacheEntry(&OCSP_Global.cache, certID);
-    if (cacheItem && ocsp_IsCacheItemFresh(cacheItem)) {
+    if (cacheItem) {
+        *cacheFreshness = ocsp_IsCacheItemFresh(cacheItem) ? ocspFresh
+                                                           : ocspStale;
         /* having an arena means, we have a cached certStatus */
         if (cacheItem->certStatusArena) {
             *rvOcsp = ocsp_CertHasGoodStatus(&cacheItem->certStatus, time);
             if (*rvOcsp != SECSuccess) {
                 *missingResponseError = PORT_GetError();
             }
-            rv = SECSuccess;
         } else {
             /*
              * No status cached, the previous attempt failed.
              * If OCSP is required, we never decide based on a failed attempt 
              * However, if OCSP is optional, a recent OCSP failure is
              * an allowed good state.
              */
-            if (!ignoreGlobalOcspFailureSetting &&
+            if (*cacheFreshness == ocspFresh &&
+                !ignoreGlobalOcspFailureSetting &&
                 OCSP_Global.ocspFailureMode == 
                     ocspMode_FailureIsNotAVerificationFailure) {
-                rv = SECSuccess;
                 *rvOcsp = SECSuccess;
             }
             *missingResponseError = cacheItem->missingResponseError;
         }
     }
     PR_ExitMonitor(OCSP_Global.monitor);
-    return rv;
+    return SECSuccess;
 }
 
 PRBool
 ocsp_FetchingFailureIsVerificationFailure(void)
 {
     PRBool isFailure;
 
     PR_EnterMonitor(OCSP_Global.monitor);
@@ -5030,41 +5061,62 @@ ocsp_FetchingFailureIsVerificationFailur
  *   memory, error performing ASN.1 decoding, etc.).
  */    
 SECStatus 
 CERT_CheckOCSPStatus(CERTCertDBHandle *handle, CERTCertificate *cert,
 		     PRTime time, void *pwArg)
 {
     CERTOCSPCertID *certID;
     PRBool certIDWasConsumed = PR_FALSE;
-    SECStatus rv = SECFailure;
+    SECStatus rv;
     SECStatus rvOcsp;
-    SECErrorCodes dummy_error_code; /* we ignore this */
+    SECErrorCodes cachedErrorCode;
+    OCSPFreshness cachedResponseFreshness;
   
     OCSP_TRACE_CERT(cert);
     OCSP_TRACE_TIME("## requested validity time:", time);
   
     certID = CERT_CreateOCSPCertID(cert, time);
     if (!certID)
         return SECFailure;
-    rv = ocsp_GetCachedOCSPResponseStatusIfFresh(
+    rv = ocsp_GetCachedOCSPResponseStatus(
         certID, time, PR_FALSE, /* ignoreGlobalOcspFailureSetting */
-        &rvOcsp, &dummy_error_code);
-    if (rv == SECSuccess) {
+        &rvOcsp, &cachedErrorCode, &cachedResponseFreshness);
+    if (rv != SECSuccess) {
+        CERT_DestroyOCSPCertID(certID);
+        return SECFailure;
+    }
+    if (cachedResponseFreshness == ocspFresh) {
         CERT_DestroyOCSPCertID(certID);
         return rvOcsp;
     }
-    rv = ocsp_GetOCSPStatusFromNetwork(handle, certID, cert, time, pwArg, 
+
+    rv = ocsp_GetOCSPStatusFromNetwork(handle, certID, cert, time, pwArg,
                                        &certIDWasConsumed, 
                                        &rvOcsp);
     if (rv != SECSuccess) {
-        /* we were unable to obtain ocsp status. Check if we should
-         * return cert status revoked. */
-        rvOcsp = ocsp_FetchingFailureIsVerificationFailure() ?
-            SECFailure : SECSuccess;
+        PRErrorCode err = PORT_GetError();
+        if (ocsp_FetchingFailureIsVerificationFailure()) {
+            PORT_SetError(err);
+            rvOcsp = SECFailure;
+        } else if (cachedResponseFreshness == ocspStale &&
+                   (cachedErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT ||
+                    cachedErrorCode == SEC_ERROR_REVOKED_CERTIFICATE)) {
+            /* If we couldn't get a response for a certificate that the OCSP
+             * responder previously told us was bad, then assume it is still
+             * bad until we hear otherwise, as it is very unlikely that the
+             * certificate status has changed from "revoked" to "good" and it
+             * is also unlikely that the certificate status has changed from
+             * "unknown" to "good", except for some buggy OCSP responders.
+             */
+            PORT_SetError(cachedErrorCode);
+            rvOcsp = SECFailure;
+        } else {
+            rvOcsp = SECSuccess;
+        }
     }
     if (!certIDWasConsumed) {
         CERT_DestroyOCSPCertID(certID);
     }
     return rvOcsp;
 }
 
 /*
@@ -5104,16 +5156,17 @@ CERT_CacheOCSPResponseFromSideChannel(CE
 {
     CERTOCSPCertID *certID = NULL;
     PRBool certIDWasConsumed = PR_FALSE;
     SECStatus rv = SECFailure;
     SECStatus rvOcsp = SECFailure;
     SECErrorCodes dummy_error_code; /* we ignore this */
     CERTOCSPResponse *decodedResponse = NULL;
     CERTOCSPSingleResponse *singleResponse = NULL;
+    OCSPFreshness freshness;
 
     /* The OCSP cache can be in three states regarding this certificate:
      *    + Good (cached, timely, 'good' response, or revoked in the future)
      *    + Revoked (cached, timely, but doesn't fit in the last category)
      *    + Miss (no knowledge)
      *
      * Likewise, the side-channel information can be
      *    + Good (timely, 'good' response, or revoked in the future)
@@ -5151,20 +5204,24 @@ CERT_CacheOCSPResponseFromSideChannel(CE
 
     if (!cert || !encodedResponse) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
     }
     certID = CERT_CreateOCSPCertID(cert, time);
     if (!certID)
         return SECFailure;
-    rv = ocsp_GetCachedOCSPResponseStatusIfFresh(
-        certID, time, PR_FALSE, /* ignoreGlobalOcspFailureSetting */
-        &rvOcsp, &dummy_error_code);
-    if (rv == SECSuccess && rvOcsp == SECSuccess) {
+
+    /* We pass PR_TRUE for ignoreGlobalOcspFailureSetting so that a cached
+     * error entry is not interpreted as being a 'Good' entry here.
+     */
+    rv = ocsp_GetCachedOCSPResponseStatus(
+        certID, time, PR_TRUE, /* ignoreGlobalOcspFailureSetting */
+        &rvOcsp, &dummy_error_code, &freshness);
+    if (rv == SECSuccess && rvOcsp == SECSuccess && freshness == ocspFresh) {
         /* The cached value is good. We don't want to waste time validating
          * this OCSP response. This is the first column in the table above. */
         CERT_DestroyOCSPCertID(certID);
         return rv;
     }
 
     /* The logic for caching the more recent response is handled in
      * ocsp_CacheSingleResponse. */
@@ -5273,47 +5330,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/ocspi.h
+++ b/security/nss/lib/certhigh/ocspi.h
@@ -36,22 +36,25 @@ ocsp_VerifyResponseSignature(CERTCertifi
 
 CERTOCSPRequest *
 cert_CreateSingleCertOCSPRequest(CERTOCSPCertID *certID, 
                                  CERTCertificate *singleCert, 
                                  PRTime time,
                                  PRBool addServiceLocator,
                                  CERTCertificate *signerCert);
 
+typedef enum { ocspMissing, ocspFresh, ocspStale } OCSPFreshness;
+
 SECStatus
-ocsp_GetCachedOCSPResponseStatusIfFresh(CERTOCSPCertID *certID, 
-                                        PRTime time,
-                                        PRBool ignoreOcspFailureMode,
-                                        SECStatus *rvOcsp,
-                                        SECErrorCodes *missingResponseError);
+ocsp_GetCachedOCSPResponseStatus(CERTOCSPCertID *certID,
+                                 PRTime time,
+                                 PRBool ignoreOcspFailureMode,
+                                 SECStatus *rvOcsp,
+                                 SECErrorCodes *missingResponseError,
+                                 OCSPFreshness *freshness);
 
 /*
  * FUNCTION: cert_ProcessOCSPResponse
  *  Same behavior and basic parameters as CERT_GetOCSPStatusForCertID.
  *  In addition it can update the OCSP cache (using information
  *  available internally to this function).
  * INPUTS:
  *  CERTCertDBHandle *handle
--- 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_ocspcertid.c
+++ b/security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocspcertid.c
@@ -179,32 +179,33 @@ PKIX_PL_OcspCertID_GetFreshCacheStatus(
         PKIX_Boolean *hasFreshStatus,
         PKIX_Boolean *statusIsGood,
         SECErrorCodes *missingResponseError,
         void *plContext)
 {
         PRTime time = 0;
         SECStatus rv;
         SECStatus rvOcsp;
+        OCSPFreshness freshness;
 
         PKIX_ENTER(DATE, "PKIX_PL_OcspCertID_GetFreshCacheStatus");
         PKIX_NULLCHECK_THREE(cid, hasFreshStatus, statusIsGood);
 
         if (validity != NULL) {
                 PKIX_CHECK(pkix_pl_Date_GetPRTime(validity, &time, plContext),
                         PKIX_DATEGETPRTIMEFAILED);
         } else {
                 time = PR_Now();
         }
 
-        rv = ocsp_GetCachedOCSPResponseStatusIfFresh(
+        rv = ocsp_GetCachedOCSPResponseStatus(
                 cid->certID, time, PR_TRUE, /*ignoreGlobalOcspFailureSetting*/
-                &rvOcsp, missingResponseError);
+                &rvOcsp, missingResponseError, &freshness);
 
-        *hasFreshStatus = (rv == SECSuccess);
+        *hasFreshStatus = (rv == SECSuccess && freshness == ocspFresh);
         if (*hasFreshStatus) {
                 *statusIsGood = (rvOcsp == SECSuccess);
         }
 cleanup:
         PKIX_RETURN(OCSPCERTID);
 }
 
 /*
--- 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/nss/nss.h
+++ b/security/nss/lib/nss/nss.h
@@ -28,22 +28,22 @@
 
 /*
  * NSS's major version, minor version, patch level, build number, and whether
  * this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define NSS_VERSION  "3.15.4" _NSS_ECC_STRING _NSS_CUSTOMIZED " Beta"
+#define NSS_VERSION  "3.15.4" _NSS_ECC_STRING _NSS_CUSTOMIZED
 #define NSS_VMAJOR   3
 #define NSS_VMINOR   15
 #define NSS_VPATCH   4
 #define NSS_VBUILD   0
-#define NSS_BETA     PR_TRUE
+#define NSS_BETA     PR_FALSE
 
 #ifndef RC_INVOKED
 
 #include "seccomon.h"
 
 typedef struct NSSInitParametersStr NSSInitParameters;
 
 /*
--- a/security/nss/lib/softoken/softkver.h
+++ b/security/nss/lib/softoken/softkver.h
@@ -20,16 +20,16 @@
 
 /*
  * Softoken's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define SOFTOKEN_VERSION  "3.15.4" SOFTOKEN_ECC_STRING " Beta"
+#define SOFTOKEN_VERSION  "3.15.4" SOFTOKEN_ECC_STRING
 #define SOFTOKEN_VMAJOR   3
 #define SOFTOKEN_VMINOR   15
 #define SOFTOKEN_VPATCH   4
 #define SOFTOKEN_VBUILD   0
-#define SOFTOKEN_BETA     PR_TRUE
+#define SOFTOKEN_BETA     PR_FALSE
 
 #endif /* _SOFTKVER_H_ */
--- 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 
@@ -9966,24 +10044,21 @@ ssl3_AuthCertificateComplete(sslSocket *
 	 */
 	if (rv == SECWouldBlock) {
 	    rv = SECSuccess;
 	}
     } else {
 	SSL_TRC(3, ("%d: SSL3[%p]: certificate authentication won the race with"
         	    " peer's finished message", SSL_GETPID(), ss->fd));
 
-	PORT_Assert(!ss->firstHsDone);
-	PORT_Assert(!ss->sec.isServer);
 	PORT_Assert(!ss->ssl3.hs.isResuming);
 	PORT_Assert(ss->ssl3.hs.ws != idle_handshake);
 
 	if (ss->opt.enableFalseStart &&
 	    !ss->firstHsDone &&
-	    !ss->sec.isServer &&
 	    !ss->ssl3.hs.isResuming &&
 	    ssl3_WaitingForStartOfServerSecondRound(ss)) {
 	    /* ssl3_SendClientSecondRound deferred the false start check because
 	     * certificate authentication was pending, so we do it now if we still
 	     * haven't received any of the server's second round yet.
 	     */
 	    rv = ssl3_CheckFalseStart(ss);
 	} else {
@@ -10402,16 +10477,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 +10601,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 +11692,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 +12019,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);
+    }
 }
--- a/security/nss/lib/util/nssutil.h
+++ b/security/nss/lib/util/nssutil.h
@@ -14,22 +14,22 @@
 
 /*
  * NSS utilities's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <Beta>]"
  */
-#define NSSUTIL_VERSION  "3.15.4 Beta"
+#define NSSUTIL_VERSION  "3.15.4"
 #define NSSUTIL_VMAJOR   3
 #define NSSUTIL_VMINOR   15
 #define NSSUTIL_VPATCH   4
 #define NSSUTIL_VBUILD   0
-#define NSSUTIL_BETA     PR_TRUE
+#define NSSUTIL_BETA     PR_FALSE
 
 SEC_BEGIN_PROTOS
 
 /*
  * Returns a const string of the UTIL library version.
  */
 extern const char *NSSUTIL_GetVersion(void);