| 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;
|
| +}
|
|
|