Index: net/third_party/nss/ssl/sslnonce.c |
diff --git a/net/third_party/nss/ssl/sslnonce.c b/net/third_party/nss/ssl/sslnonce.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..63dc5a28507bf9a7c2194e03f52d47620010f900 |
--- /dev/null |
+++ b/net/third_party/nss/ssl/sslnonce.c |
@@ -0,0 +1,530 @@ |
+/* |
+ * This file implements the CLIENT Session ID cache. |
+ * |
+ * ***** BEGIN LICENSE BLOCK ***** |
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License Version |
+ * 1.1 (the "License"); you may not use this file except in compliance with |
+ * the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * Software distributed under the License is distributed on an "AS IS" basis, |
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
+ * for the specific language governing rights and limitations under the |
+ * License. |
+ * |
+ * The Original Code is the Netscape security libraries. |
+ * |
+ * The Initial Developer of the Original Code is |
+ * Netscape Communications Corporation. |
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000 |
+ * the Initial Developer. All Rights Reserved. |
+ * |
+ * Contributor(s): |
+ * |
+ * Alternatively, the contents of this file may be used under the terms of |
+ * either the GNU General Public License Version 2 or later (the "GPL"), or |
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
+ * in which case the provisions of the GPL or the LGPL are applicable instead |
+ * of those above. If you wish to allow use of your version of this file only |
+ * under the terms of either the GPL or the LGPL, and not to allow others to |
+ * use your version of this file under the terms of the MPL, indicate your |
+ * decision by deleting the provisions above and replace them with the notice |
+ * and other provisions required by the GPL or the LGPL. If you do not delete |
+ * the provisions above, a recipient may use your version of this file under |
+ * the terms of any one of the MPL, the GPL or the LGPL. |
+ * |
+ * ***** END LICENSE BLOCK ***** */ |
+/* $Id: sslnonce.c,v 1.25 2008/03/10 00:01:28 wtc%google.com Exp $ */ |
+ |
+#include "cert.h" |
+#include "pk11pub.h" |
+#include "secitem.h" |
+#include "ssl.h" |
+#include "nss.h" |
+ |
+#include "sslimpl.h" |
+#include "sslproto.h" |
+#include "nssilock.h" |
+#if (defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)) && !defined(_WIN32_WCE) |
+#include <time.h> |
+#endif |
+ |
+PRUint32 ssl_sid_timeout = 100; |
+PRUint32 ssl3_sid_timeout = 86400L; /* 24 hours */ |
+ |
+static sslSessionID *cache = NULL; |
+static PZLock * cacheLock = NULL; |
+ |
+/* sids can be in one of 4 states: |
+ * |
+ * never_cached, created, but not yet put into cache. |
+ * in_client_cache, in the client cache's linked list. |
+ * in_server_cache, entry came from the server's cache file. |
+ * invalid_cache has been removed from the cache. |
+ */ |
+ |
+#define LOCK_CACHE lock_cache() |
+#define UNLOCK_CACHE PZ_Unlock(cacheLock) |
+ |
+static SECStatus |
+ssl_InitClientSessionCacheLock(void) |
+{ |
+ cacheLock = PZ_NewLock(nssILockCache); |
+ return cacheLock ? SECSuccess : SECFailure; |
+} |
+ |
+static SECStatus |
+ssl_FreeClientSessionCacheLock(void) |
+{ |
+ if (cacheLock) { |
+ PZ_DestroyLock(cacheLock); |
+ cacheLock = NULL; |
+ return SECSuccess; |
+ } |
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
+ return SECFailure; |
+} |
+ |
+static PRBool LocksInitializedEarly = PR_FALSE; |
+ |
+static SECStatus |
+FreeSessionCacheLocks() |
+{ |
+ SECStatus rv1, rv2; |
+ rv1 = ssl_FreeSymWrapKeysLock(); |
+ rv2 = ssl_FreeClientSessionCacheLock(); |
+ if ( (SECSuccess == rv1) && (SECSuccess == rv2) ) { |
+ return SECSuccess; |
+ } |
+ return SECFailure; |
+} |
+ |
+static SECStatus |
+InitSessionCacheLocks(void) |
+{ |
+ SECStatus rv1, rv2; |
+ PRErrorCode rc; |
+ rv1 = ssl_InitSymWrapKeysLock(); |
+ rv2 = ssl_InitClientSessionCacheLock(); |
+ if ( (SECSuccess == rv1) && (SECSuccess == rv2) ) { |
+ return SECSuccess; |
+ } |
+ rc = PORT_GetError(); |
+ FreeSessionCacheLocks(); |
+ PORT_SetError(rc); |
+ return SECFailure; |
+} |
+ |
+/* free the session cache locks if they were initialized early */ |
+SECStatus |
+ssl_FreeSessionCacheLocks() |
+{ |
+ PORT_Assert(PR_TRUE == LocksInitializedEarly); |
+ if (!LocksInitializedEarly) { |
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
+ return SECFailure; |
+ } |
+ FreeSessionCacheLocks(); |
+ LocksInitializedEarly = PR_FALSE; |
+ return SECSuccess; |
+} |
+ |
+static PRCallOnceType lockOnce; |
+ |
+/* free the session cache locks if they were initialized lazily */ |
+static SECStatus ssl_ShutdownLocks(void* appData, void* nssData) |
+{ |
+ PORT_Assert(PR_FALSE == LocksInitializedEarly); |
+ if (LocksInitializedEarly) { |
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
+ return SECFailure; |
+ } |
+ FreeSessionCacheLocks(); |
+ memset(&lockOnce, 0, sizeof(lockOnce)); |
+ return SECSuccess; |
+} |
+ |
+static PRStatus initSessionCacheLocksLazily(void) |
+{ |
+ SECStatus rv = InitSessionCacheLocks(); |
+ if (SECSuccess != rv) { |
+ return PR_FAILURE; |
+ } |
+ rv = NSS_RegisterShutdown(ssl_ShutdownLocks, NULL); |
+ PORT_Assert(SECSuccess == rv); |
+ if (SECSuccess != rv) { |
+ return PR_FAILURE; |
+ } |
+ return PR_SUCCESS; |
+} |
+ |
+/* lazyInit means that the call is not happening during a 1-time |
+ * initialization function, but rather during dynamic, lazy initialization |
+ */ |
+SECStatus |
+ssl_InitSessionCacheLocks(PRBool lazyInit) |
+{ |
+ if (LocksInitializedEarly) { |
+ return SECSuccess; |
+ } |
+ |
+ if (lazyInit) { |
+ return (PR_SUCCESS == |
+ PR_CallOnce(&lockOnce, initSessionCacheLocksLazily)) ? |
+ SECSuccess : SECFailure; |
+ } |
+ |
+ if (SECSuccess == InitSessionCacheLocks()) { |
+ LocksInitializedEarly = PR_TRUE; |
+ return SECSuccess; |
+ } |
+ |
+ return SECFailure; |
+} |
+ |
+static void |
+lock_cache(void) |
+{ |
+ ssl_InitSessionCacheLocks(PR_TRUE); |
+ PZ_Lock(cacheLock); |
+} |
+ |
+/* BEWARE: This function gets called for both client and server SIDs !! |
+ * If the unreferenced sid is not in the cache, Free sid and its contents. |
+ */ |
+static void |
+ssl_DestroySID(sslSessionID *sid) |
+{ |
+ SSL_TRC(8, ("SSL: destroy sid: sid=0x%x cached=%d", sid, sid->cached)); |
+ 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); |
+ } |
+ 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->localCert ) { |
+ CERT_DestroyCertificate(sid->localCert); |
+ } |
+ if (sid->u.ssl3.sessionTicket.ticket.data) { |
+ SECITEM_FreeItem(&sid->u.ssl3.sessionTicket.ticket, 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. |
+ * If the sid is still in the cache, it is left there until next time |
+ * the cache list is traversed. |
+ */ |
+static void |
+ssl_FreeLockedSID(sslSessionID *sid) |
+{ |
+ PORT_Assert(sid->references >= 1); |
+ if (--sid->references == 0) { |
+ ssl_DestroySID(sid); |
+ } |
+} |
+ |
+/* 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. |
+ * These locks are necessary because the sid _might_ be in the cache list. |
+ */ |
+void |
+ssl_FreeSID(sslSessionID *sid) |
+{ |
+ LOCK_CACHE; |
+ ssl_FreeLockedSID(sid); |
+ UNLOCK_CACHE; |
+} |
+ |
+/************************************************************************/ |
+ |
+/* |
+** Lookup sid entry in cache by Address, port, and peerID string. |
+** If found, Increment reference count, and return pointer to caller. |
+** If it has timed out or ref count is zero, remove from list and free it. |
+*/ |
+ |
+sslSessionID * |
+ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, const char *peerID, |
+ const char * urlSvrName) |
+{ |
+ sslSessionID **sidp; |
+ sslSessionID * sid; |
+ PRUint32 now; |
+ |
+ if (!urlSvrName) |
+ return NULL; |
+ now = ssl_Time(); |
+ LOCK_CACHE; |
+ sidp = &cache; |
+ while ((sid = *sidp) != 0) { |
+ PORT_Assert(sid->cached == in_client_cache); |
+ PORT_Assert(sid->references >= 1); |
+ |
+ SSL_TRC(8, ("SSL: Lookup1: sid=0x%x", sid)); |
+ |
+ if (sid->expirationTime < now || !sid->references) { |
+ /* |
+ ** This session-id timed out, or was orphaned. |
+ ** Don't even care who it belongs to, blow it out of our cache. |
+ */ |
+ SSL_TRC(7, ("SSL: lookup1, throwing sid out, age=%d refs=%d", |
+ now - sid->creationTime, sid->references)); |
+ |
+ *sidp = sid->next; /* delink it from the list. */ |
+ sid->cached = invalid_cache; /* mark not on list. */ |
+ if (!sid->references) |
+ ssl_DestroySID(sid); |
+ else |
+ ssl_FreeLockedSID(sid); /* drop ref count, free. */ |
+ |
+ } else if (!memcmp(&sid->addr, addr, sizeof(PRIPv6Addr)) && /* server IP addr matches */ |
+ (sid->port == port) && /* server port matches */ |
+ /* proxy (peerID) matches */ |
+ (((peerID == NULL) && (sid->peerID == NULL)) || |
+ ((peerID != NULL) && (sid->peerID != NULL) && |
+ PORT_Strcmp(sid->peerID, peerID) == 0)) && |
+ /* is cacheable */ |
+ (sid->version < SSL_LIBRARY_VERSION_3_0 || |
+ sid->u.ssl3.keys.resumable) && |
+ /* server hostname matches. */ |
+ (sid->urlSvrName != NULL) && |
+ ((0 == PORT_Strcmp(urlSvrName, sid->urlSvrName)) || |
+ ((sid->peerCert != NULL) && (SECSuccess == |
+ CERT_VerifyCertName(sid->peerCert, urlSvrName))) ) |
+ ) { |
+ /* Hit */ |
+ sid->lastAccessTime = now; |
+ sid->references++; |
+ break; |
+ } else { |
+ sidp = &sid->next; |
+ } |
+ } |
+ UNLOCK_CACHE; |
+ return sid; |
+} |
+ |
+/* |
+** 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; |
+ 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) |
+ 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)); |
+ } |
+ 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; |
+ |
+ /* |
+ * Put sid into the cache. Bump reference count to indicate that |
+ * cache is holding a reference. Uncache will reduce the cache |
+ * reference. |
+ */ |
+ LOCK_CACHE; |
+ sid->references++; |
+ sid->cached = in_client_cache; |
+ sid->next = cache; |
+ cache = sid; |
+ UNLOCK_CACHE; |
+} |
+ |
+/* |
+ * If sid "zap" is in the cache, |
+ * removes sid from cache, and decrements reference count. |
+ * Caller must hold cache lock. |
+ */ |
+static void |
+UncacheSID(sslSessionID *zap) |
+{ |
+ sslSessionID **sidp = &cache; |
+ sslSessionID *sid; |
+ |
+ if (zap->cached != in_client_cache) { |
+ return; |
+ } |
+ |
+ SSL_TRC(8,("SSL: Uncache: zap=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x " |
+ "time=%x cipher=%d", |
+ zap, zap->cached, zap->addr.pr_s6_addr32[0], |
+ zap->addr.pr_s6_addr32[1], zap->addr.pr_s6_addr32[2], |
+ zap->addr.pr_s6_addr32[3], zap->port, zap->creationTime, |
+ zap->u.ssl2.cipherType)); |
+ if (zap->version < SSL_LIBRARY_VERSION_3_0) { |
+ PRINT_BUF(8, (0, "sessionID:", |
+ zap->u.ssl2.sessionID, sizeof(zap->u.ssl2.sessionID))); |
+ PRINT_BUF(8, (0, "masterKey:", |
+ zap->u.ssl2.masterKey.data, zap->u.ssl2.masterKey.len)); |
+ PRINT_BUF(8, (0, "cipherArg:", |
+ zap->u.ssl2.cipherArg.data, zap->u.ssl2.cipherArg.len)); |
+ } |
+ |
+ /* See if it's in the cache, if so nuke it */ |
+ while ((sid = *sidp) != 0) { |
+ if (sid == zap) { |
+ /* |
+ ** Bingo. Reduce reference count by one so that when |
+ ** everyone is done with the sid we can free it up. |
+ */ |
+ *sidp = zap->next; |
+ zap->cached = invalid_cache; |
+ ssl_FreeLockedSID(zap); |
+ return; |
+ } |
+ sidp = &sid->next; |
+ } |
+} |
+ |
+/* If sid "zap" is in the cache, |
+ * removes sid from cache, and decrements reference count. |
+ * Although this function is static, it is called externally via |
+ * ss->sec.uncache(). |
+ */ |
+static void |
+LockAndUncacheSID(sslSessionID *zap) |
+{ |
+ LOCK_CACHE; |
+ UncacheSID(zap); |
+ UNLOCK_CACHE; |
+ |
+} |
+ |
+/* choose client or server cache functions for this sslsocket. */ |
+void |
+ssl_ChooseSessionIDProcs(sslSecurityInfo *sec) |
+{ |
+ if (sec->isServer) { |
+ sec->cache = ssl_sid_cache; |
+ sec->uncache = ssl_sid_uncache; |
+ } else { |
+ sec->cache = CacheSID; |
+ sec->uncache = LockAndUncacheSID; |
+ } |
+} |
+ |
+/* wipe out the entire client session cache. */ |
+void |
+SSL_ClearSessionCache(void) |
+{ |
+ LOCK_CACHE; |
+ while(cache != NULL) |
+ UncacheSID(cache); |
+ UNLOCK_CACHE; |
+} |
+ |
+/* returns an unsigned int containing the number of seconds in PR_Now() */ |
+PRUint32 |
+ssl_Time(void) |
+{ |
+ PRUint32 myTime; |
+#if (defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)) && !defined(_WIN32_WCE) |
+ myTime = time(NULL); /* accurate until the year 2038. */ |
+#else |
+ /* portable, but possibly slower */ |
+ PRTime now; |
+ PRInt64 ll; |
+ |
+ 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) |
+{ |
+ SECStatus rv; |
+ |
+ /* 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.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; |
+ } |
+ } 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; |
+ |
+ UNLOCK_CACHE; |
+ return SECSuccess; |
+} |