Source code

Revision control

Other Tools

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This file implements the SERVER Session ID cache.
* NOTE: The contents of this file are NOT used by the client.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Note: ssl_FreeSID() in sslnonce.c gets used for both client and server
* cache sids!
*
* About record locking among different server processes:
*
* All processes that are part of the same conceptual server (serving on
* the same address and port) MUST share a common SSL session cache.
* This code makes the content of the shared cache accessible to all
* processes on the same "server". This code works on Unix and Win32 only.
*
* We use NSPR anonymous shared memory and move data to & from shared memory.
* We must do explicit locking of the records for all reads and writes.
* The set of Cache entries are divided up into "sets" of 128 entries.
* Each set is protected by a lock. There may be one or more sets protected
* by each lock. That is, locks to sets are 1:N.
* There is one lock for the entire cert cache.
* There is one lock for the set of wrapped sym wrap keys.
*
* The anonymous shared memory is laid out as if it were declared like this:
*
* struct {
* cacheDescriptor desc;
* sidCacheLock sidCacheLocks[ numSIDCacheLocks];
* sidCacheLock keyCacheLock;
* sidCacheLock certCacheLock;
* sidCacheSet sidCacheSets[ numSIDCacheSets ];
* sidCacheEntry sidCacheData[ numSIDCacheEntries];
* certCacheEntry certCacheData[numCertCacheEntries];
* SSLWrappedSymWrappingKey keyCacheData[SSL_NUM_WRAP_KEYS][SSL_NUM_WRAP_MECHS];
* PRUint8 keyNameSuffix[SELF_ENCRYPT_KEY_VAR_NAME_LEN]
* encKeyCacheEntry ticketEncKey; // Wrapped
* encKeyCacheEntry ticketMacKey; // Wrapped
* PRBool ticketKeysValid;
* sidCacheLock srvNameCacheLock;
* srvNameCacheEntry srvNameData[ numSrvNameCacheEntries ];
* } cacheMemCacheData;
*/
#include "seccomon.h"
#if defined(XP_UNIX) || defined(XP_WIN32) || defined(XP_OS2) || defined(XP_BEOS)
#include "cert.h"
#include "ssl.h"
#include "sslimpl.h"
#include "sslproto.h"
#include "pk11func.h"
#include "base64.h"
#include "keyhi.h"
#include "blapit.h"
#include "nss.h" /* for NSS_RegisterShutdown */
#include "sechash.h"
#include "selfencrypt.h"
#include <stdio.h>
#if defined(XP_UNIX) || defined(XP_BEOS)
#include <syslog.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include "unix_err.h"
#else
#ifdef XP_WIN32
#include <wtypes.h>
#include "win32err.h"
#endif
#endif
#include <sys/types.h>
#include "nspr.h"
#include "sslmutex.h"
/*
** Format of a cache entry in the shared memory.
*/
PR_STATIC_ASSERT(sizeof(PRTime) == 8);
struct sidCacheEntryStr {
/* 16 */ PRIPv6Addr addr; /* client's IP address */
/* 8 */ PRTime creationTime;
/* 8 */ PRTime lastAccessTime;
/* 8 */ PRTime expirationTime;
/* 2 */ PRUint16 version;
/* 1 */ PRUint8 valid;
/* 1 */ PRUint8 sessionIDLength;
/* 32 */ PRUint8 sessionID[SSL3_SESSIONID_BYTES];
/* 2 */ PRUint16 authType;
/* 2 */ PRUint16 authKeyBits;
/* 2 */ PRUint16 keaType;
/* 2 */ PRUint16 keaKeyBits;
/* 4 */ PRUint32 signatureScheme;
/* 4 */ PRUint32 keaGroup;
/* 92 - common header total */
union {
struct {
/* 2 */ ssl3CipherSuite cipherSuite;
/* 52 */ ssl3SidKeys keys; /* keys, wrapped as needed. */
/* 4 */ PRUint32 masterWrapMech;
/* 4 */ PRInt32 certIndex;
/* 4 */ PRInt32 srvNameIndex;
/* 32 */ PRUint8 srvNameHash[SHA256_LENGTH]; /* SHA256 name hash */
/* 2 */ PRUint16 namedCurve;
/*100 */} ssl3;
/* force sizeof(sidCacheEntry) to be a multiple of cache line size */
struct {
/*116 */ PRUint8 filler[116]; /* 92+116==208, a multiple of 16 */
} forceSize;
} u;
};
typedef struct sidCacheEntryStr sidCacheEntry;
/* The length of this struct is supposed to be a power of 2, e.g. 4KB */
struct certCacheEntryStr {
PRUint16 certLength; /* 2 */
PRUint16 sessionIDLength; /* 2 */
PRUint8 sessionID[SSL3_SESSIONID_BYTES]; /* 32 */
PRUint8 cert[SSL_MAX_CACHED_CERT_LEN]; /* 4060 */
}; /* total 4096 */
typedef struct certCacheEntryStr certCacheEntry;
struct sidCacheLockStr {
PRUint32 timeStamp;
sslMutex mutex;
sslPID pid;
};
typedef struct sidCacheLockStr sidCacheLock;
struct sidCacheSetStr {
PRIntn next;
};
typedef struct sidCacheSetStr sidCacheSet;
struct encKeyCacheEntryStr {
PRUint8 bytes[512];
PRInt32 length;
};
typedef struct encKeyCacheEntryStr encKeyCacheEntry;
#define SSL_MAX_DNS_HOST_NAME 1024
struct srvNameCacheEntryStr {
PRUint16 type; /* 2 */
PRUint16 nameLen; /* 2 */
PRUint8 name[SSL_MAX_DNS_HOST_NAME + 12]; /* 1034 */
PRUint8 nameHash[SHA256_LENGTH]; /* 32 */
/* 1072 */
};
typedef struct srvNameCacheEntryStr srvNameCacheEntry;
struct cacheDescStr {
PRUint32 cacheMemSize;
PRUint32 numSIDCacheLocks;
PRUint32 numSIDCacheSets;
PRUint32 numSIDCacheSetsPerLock;
PRUint32 numSIDCacheEntries;
PRUint32 sidCacheSize;
PRUint32 numCertCacheEntries;
PRUint32 certCacheSize;
PRUint32 numKeyCacheEntries;
PRUint32 keyCacheSize;
PRUint32 numSrvNameCacheEntries;
PRUint32 srvNameCacheSize;
PRUint32 ssl3Timeout;
PRUint32 numSIDCacheLocksInitialized;
/* These values are volatile, and are accessed through sharedCache-> */
PRUint32 nextCertCacheEntry; /* certCacheLock protects */
PRBool stopPolling;
PRBool everInherited;
/* The private copies of these values are pointers into shared mem */
/* The copies of these values in shared memory are merely offsets */
sidCacheLock *sidCacheLocks;
sidCacheLock *keyCacheLock;
sidCacheLock *certCacheLock;
sidCacheLock *srvNameCacheLock;
sidCacheSet *sidCacheSets;
sidCacheEntry *sidCacheData;
certCacheEntry *certCacheData;
SSLWrappedSymWrappingKey *keyCacheData;
PRUint8 *ticketKeyNameSuffix;
encKeyCacheEntry *ticketEncKey;
encKeyCacheEntry *ticketMacKey;
PRUint32 *ticketKeysValid;
srvNameCacheEntry *srvNameCacheData;
/* Only the private copies of these pointers are valid */
char *cacheMem;
struct cacheDescStr *sharedCache; /* shared copy of this struct */
PRFileMap *cacheMemMap;
PRThread *poller;
PRUint32 mutexTimeout;
PRBool shared;
};
typedef struct cacheDescStr cacheDesc;
static cacheDesc globalCache;
static const char envVarName[] = { SSL_ENV_VAR_NAME };
static PRBool isMultiProcess = PR_FALSE;
#define DEF_SID_CACHE_ENTRIES 10000
#define DEF_CERT_CACHE_ENTRIES 250
#define MIN_CERT_CACHE_ENTRIES 125 /* the effective size in old releases. */
#define DEF_KEY_CACHE_ENTRIES 250
#define DEF_NAME_CACHE_ENTRIES 1000
#define SID_CACHE_ENTRIES_PER_SET 128
#define SID_ALIGNMENT 16
#define DEF_SSL3_TIMEOUT 86400L /* 24 hours */
#define MAX_SSL3_TIMEOUT 86400L /* 24 hours */
#define MIN_SSL3_TIMEOUT 5 /* seconds */
#if defined(AIX) || defined(LINUX) || defined(NETBSD) || defined(OPENBSD)
#define MAX_SID_CACHE_LOCKS 8 /* two FDs per lock */
#elif defined(OSF1)
#define MAX_SID_CACHE_LOCKS 16 /* one FD per lock */
#else
#define MAX_SID_CACHE_LOCKS 256
#endif
#define SID_HOWMANY(val, size) (((val) + ((size)-1)) / (size))
#define SID_ROUNDUP(val, size) ((size)*SID_HOWMANY((val), (size)))
static sslPID myPid;
static PRUint32 ssl_max_sid_cache_locks = MAX_SID_CACHE_LOCKS;
/* forward static function declarations */
static PRUint32 SIDindex(cacheDesc *cache, const PRIPv6Addr *addr, PRUint8 *s,
unsigned nl);
static SECStatus LaunchLockPoller(cacheDesc *cache);
static SECStatus StopLockPoller(cacheDesc *cache);
struct inheritanceStr {
PRUint32 cacheMemSize;
PRUint32 fmStrLen;
};
typedef struct inheritanceStr inheritance;
#if defined(_WIN32) || defined(XP_OS2)
#define DEFAULT_CACHE_DIRECTORY "\\temp"
#endif /* _win32 */
#if defined(XP_UNIX) || defined(XP_BEOS)
#define DEFAULT_CACHE_DIRECTORY "/tmp"
#endif /* XP_UNIX || XP_BEOS */
/************************************************************************/
/* SSL Session Cache has a smaller set of functions to initialize than
* ssl does. some ssl_functions can't be initialized before NSS has been
* initialized, and the cache may be configured before NSS is initialized
* so thus the special init function */
static SECStatus
ssl_InitSessionCache()
{
/* currently only one function, which is itself idempotent */
return ssl_InitializePRErrorTable();
}
/* This is used to set locking times for the cache. It is not used to set the
* PRTime attributes of sessions, which are driven by ss->now(). */
static PRUint32
ssl_CacheNow()
{
return PR_Now() / PR_USEC_PER_SEC;
}
static PRUint32
LockSidCacheLock(sidCacheLock *lock, PRUint32 now)
{
SECStatus rv = sslMutex_Lock(&lock->mutex);
if (rv != SECSuccess)
return 0;
if (!now) {
now = ssl_CacheNow();
}
lock->timeStamp = now;
lock->pid = myPid;
return now;
}
static SECStatus
UnlockSidCacheLock(sidCacheLock *lock)
{
SECStatus rv;
lock->pid = 0;
rv = sslMutex_Unlock(&lock->mutex);
return rv;
}
/* Returns non-zero |now| or ssl_CacheNow() on success, zero on failure. */
static PRUint32
LockSet(cacheDesc *cache, PRUint32 set, PRUint32 now)
{
PRUint32 lockNum = set % cache->numSIDCacheLocks;
sidCacheLock *lock = cache->sidCacheLocks + lockNum;
return LockSidCacheLock(lock, now);
}
static SECStatus
UnlockSet(cacheDesc *cache, PRUint32 set)
{
PRUint32 lockNum = set % cache->numSIDCacheLocks;
sidCacheLock *lock = cache->sidCacheLocks + lockNum;
return UnlockSidCacheLock(lock);
}
/************************************************************************/
/* Put a certificate in the cache. Update the cert index in the sce.
*/
static PRUint32
CacheCert(cacheDesc *cache, CERTCertificate *cert, sidCacheEntry *sce)
{
PRUint32 now;
certCacheEntry cce;
if ((cert->derCert.len > SSL_MAX_CACHED_CERT_LEN) ||
(cert->derCert.len <= 0) ||
(cert->derCert.data == NULL)) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return 0;
}
cce.sessionIDLength = sce->sessionIDLength;
PORT_Memcpy(cce.sessionID, sce->sessionID, cce.sessionIDLength);
cce.certLength = cert->derCert.len;
PORT_Memcpy(cce.cert, cert->derCert.data, cce.certLength);
/* get lock on cert cache */
now = LockSidCacheLock(cache->certCacheLock, 0);
if (now) {
/* Find where to place the next cert cache entry. */
cacheDesc *sharedCache = cache->sharedCache;
PRUint32 ndx = sharedCache->nextCertCacheEntry;
/* write the entry */
cache->certCacheData[ndx] = cce;
/* remember where we put it. */
sce->u.ssl3.certIndex = ndx;
/* update the "next" cache entry index */
sharedCache->nextCertCacheEntry =
(ndx + 1) % cache->numCertCacheEntries;
UnlockSidCacheLock(cache->certCacheLock);
}
return now;
}
/* Server configuration hash tables need to account the SECITEM.type
* field as well. These functions accomplish that. */
static PLHashNumber
Get32BitNameHash(const SECItem *name)
{
PLHashNumber rv = SECITEM_Hash(name);
PRUint8 *rvc = (PRUint8 *)&rv;
rvc[name->len % sizeof(rv)] ^= name->type;
return rv;
}
/* Put a name in the cache. Update the cert index in the sce.
*/
static PRUint32
CacheSrvName(cacheDesc *cache, SECItem *name, sidCacheEntry *sce)
{
PRUint32 now;
PRUint32 ndx;
srvNameCacheEntry snce;
if (!name || name->len <= 0 ||
name->len > SSL_MAX_DNS_HOST_NAME) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return 0;
}
snce.type = name->type;
snce.nameLen = name->len;
PORT_Memcpy(snce.name, name->data, snce.nameLen);
HASH_HashBuf(HASH_AlgSHA256, snce.nameHash, name->data, name->len);
/* get index of the next name */
ndx = Get32BitNameHash(name);
/* get lock on cert cache */
now = LockSidCacheLock(cache->srvNameCacheLock, 0);
if (now) {
if (cache->numSrvNameCacheEntries > 0) {
/* Fit the index into array */
ndx %= cache->numSrvNameCacheEntries;
/* write the entry */
cache->srvNameCacheData[ndx] = snce;
/* remember where we put it. */
sce->u.ssl3.srvNameIndex = ndx;
/* Copy hash into sid hash */
PORT_Memcpy(sce->u.ssl3.srvNameHash, snce.nameHash, SHA256_LENGTH);
}
UnlockSidCacheLock(cache->srvNameCacheLock);
}
return now;
}
/*
** Convert local SID to shared memory one
*/
static void
ConvertFromSID(sidCacheEntry *to, sslSessionID *from)
{
to->valid = 1;
to->version = from->version;
to->addr = from->addr;
to->creationTime = from->creationTime;
to->lastAccessTime = from->lastAccessTime;
to->expirationTime = from->expirationTime;
to->authType = from->authType;
to->authKeyBits = from->authKeyBits;
to->keaType = from->keaType;
to->keaKeyBits = from->keaKeyBits;
to->keaGroup = from->keaGroup;
to->signatureScheme = from->sigScheme;
to->u.ssl3.cipherSuite = from->u.ssl3.cipherSuite;
to->u.ssl3.keys = from->u.ssl3.keys;
to->u.ssl3.masterWrapMech = from->u.ssl3.masterWrapMech;
to->sessionIDLength = from->u.ssl3.sessionIDLength;
to->u.ssl3.certIndex = -1;
to->u.ssl3.srvNameIndex = -1;
PORT_Memcpy(to->sessionID, from->u.ssl3.sessionID,
to->sessionIDLength);
to->u.ssl3.namedCurve = 0U;
if (from->authType == ssl_auth_ecdsa ||
from->authType == ssl_auth_ecdh_rsa ||
from->authType == ssl_auth_ecdh_ecdsa) {
PORT_Assert(from->namedCurve);
to->u.ssl3.namedCurve = (PRUint16)from->namedCurve->name;
}
SSL_TRC(8, ("%d: SSL3: ConvertSID: time=%d addr=0x%08x%08x%08x%08x "
"cipherSuite=%d",
myPid, to->creationTime / PR_USEC_PER_SEC,
to->addr.pr_s6_addr32[0], to->addr.pr_s6_addr32[1],
to->addr.pr_s6_addr32[2], to->addr.pr_s6_addr32[3],
to->u.ssl3.cipherSuite));
}
/*
** Convert shared memory cache-entry to local memory based one
** This is only called from ServerSessionIDLookup().
*/
static sslSessionID *
ConvertToSID(sidCacheEntry *from,
certCacheEntry *pcce,
srvNameCacheEntry *psnce,
CERTCertDBHandle *dbHandle)
{
sslSessionID *to;
to = PORT_ZNew(sslSessionID);
if (!to) {
return 0;
}
to->u.ssl3.sessionIDLength = from->sessionIDLength;
to->u.ssl3.cipherSuite = from->u.ssl3.cipherSuite;
to->u.ssl3.keys = from->u.ssl3.keys;
to->u.ssl3.masterWrapMech = from->u.ssl3.masterWrapMech;
if (from->u.ssl3.srvNameIndex != -1 && psnce) {
SECItem name;
SECStatus rv;
name.type = psnce->type;
name.len = psnce->nameLen;
name.data = psnce->name;
rv = SECITEM_CopyItem(NULL, &to->u.ssl3.srvName, &name);
if (rv != SECSuccess) {
goto loser;
}
}
PORT_Memcpy(to->u.ssl3.sessionID, from->sessionID, from->sessionIDLength);
to->urlSvrName = NULL;
to->u.ssl3.masterModuleID = (SECMODModuleID)-1; /* invalid value */
to->u.ssl3.masterSlotID = (CK_SLOT_ID)-1; /* invalid value */
to->u.ssl3.masterWrapIndex = 0;
to->u.ssl3.masterWrapSeries = 0;
to->u.ssl3.masterValid = PR_FALSE;
to->u.ssl3.clAuthModuleID = (SECMODModuleID)-1; /* invalid value */
to->u.ssl3.clAuthSlotID = (CK_SLOT_ID)-1; /* invalid value */
to->u.ssl3.clAuthSeries = 0;
to->u.ssl3.clAuthValid = PR_FALSE;
if (from->u.ssl3.certIndex != -1 && pcce) {
SECItem derCert;
derCert.len = pcce->certLength;
derCert.data = pcce->cert;
to->peerCert = CERT_NewTempCertificate(dbHandle, &derCert, NULL,
PR_FALSE, PR_TRUE);
if (to->peerCert == NULL)
goto loser;
}
if (from->authType == ssl_auth_ecdsa ||
from->authType == ssl_auth_ecdh_rsa ||
from->authType == ssl_auth_ecdh_ecdsa) {
to->namedCurve =
ssl_LookupNamedGroup((SSLNamedGroup)from->u.ssl3.namedCurve);
}
to->version = from->version;
to->creationTime = from->creationTime;
to->lastAccessTime = from->lastAccessTime;
to->expirationTime = from->expirationTime;
to->cached = in_server_cache;
to->addr = from->addr;
to->references = 1;
to->authType = from->authType;
to->authKeyBits = from->authKeyBits;
to->keaType = from->keaType;
to->keaKeyBits = from->keaKeyBits;
to->keaGroup = from->keaGroup;
to->sigScheme = from->signatureScheme;
return to;
loser:
if (to) {
SECITEM_FreeItem(&to->u.ssl3.srvName, PR_FALSE);
PORT_Free(to);
}
return NULL;
}
/*
** Perform some mumbo jumbo on the ip-address and the session-id value to
** compute a hash value.
*/
static PRUint32
SIDindex(cacheDesc *cache, const PRIPv6Addr *addr, PRUint8 *s, unsigned nl)
{
PRUint32 rv;
PRUint32 x[8];
memset(x, 0, sizeof x);
if (nl > sizeof x)
nl = sizeof x;
memcpy(x, s, nl);
rv = (addr->pr_s6_addr32[0] ^ addr->pr_s6_addr32[1] ^
addr->pr_s6_addr32[2] ^ addr->pr_s6_addr32[3] ^
x[0] ^ x[1] ^ x[2] ^ x[3] ^ x[4] ^ x[5] ^ x[6] ^ x[7]) %
cache->numSIDCacheSets;
return rv;
}
/*
** Look something up in the cache. This will invalidate old entries
** in the process. Caller has locked the cache set!
** Returns PR_TRUE if found a valid match. PR_FALSE otherwise.
*/
static sidCacheEntry *
FindSID(cacheDesc *cache, PRUint32 setNum, PRUint32 now,
const PRIPv6Addr *addr, unsigned char *sessionID,
unsigned sessionIDLength)
{
PRUint32 ndx = cache->sidCacheSets[setNum].next;
int i;
sidCacheEntry *set = cache->sidCacheData +
(setNum * SID_CACHE_ENTRIES_PER_SET);
for (i = SID_CACHE_ENTRIES_PER_SET; i > 0; --i) {
sidCacheEntry *sce;
ndx = (ndx - 1) % SID_CACHE_ENTRIES_PER_SET;
sce = set + ndx;
if (!sce->valid)
continue;
if (now > sce->expirationTime) {
/* SessionID has timed out. Invalidate the entry. */
SSL_TRC(7, ("%d: timed out sid entry addr=%08x%08x%08x%08x now=%x "
"time+=%x",
myPid, sce->addr.pr_s6_addr32[0],
sce->addr.pr_s6_addr32[1], sce->addr.pr_s6_addr32[2],
sce->addr.pr_s6_addr32[3], now,
sce->expirationTime));
sce->valid = 0;
continue;
}
/*
** Next, examine specific session-id/addr data to see if the cache
** entry matches our addr+session-id value
*/
if (sessionIDLength == sce->sessionIDLength &&
!memcmp(&sce->addr, addr, sizeof(PRIPv6Addr)) &&
!memcmp(sce->sessionID, sessionID, sessionIDLength)) {
/* Found it */
return sce;
}
}
PORT_SetError(SSL_ERROR_SESSION_NOT_FOUND);
return NULL;
}
/************************************************************************/
/* This is the primary function for finding entries in the server's sid cache.
* Although it is static, this function is called via the global function
* pointer ssl_sid_lookup.
*
* sslNow is the time that the calling socket understands, which might be
* different than what the cache uses to maintain its locks.
*/
static sslSessionID *
ServerSessionIDLookup(PRTime sslNow, const PRIPv6Addr *addr,
unsigned char *sessionID,
unsigned int sessionIDLength,
CERTCertDBHandle *dbHandle)
{
sslSessionID *sid = 0;
sidCacheEntry *psce;
certCacheEntry *pcce = 0;
srvNameCacheEntry *psnce = 0;
cacheDesc *cache = &globalCache;
PRUint32 now;
PRUint32 set;
PRInt32 cndx;
sidCacheEntry sce;
certCacheEntry cce;
srvNameCacheEntry snce;
set = SIDindex(cache, addr, sessionID, sessionIDLength);
now = LockSet(cache, set, 0);
if (!now)
return NULL;
psce = FindSID(cache, set, now, addr, sessionID, sessionIDLength);
if (psce) {
if ((cndx = psce->u.ssl3.certIndex) != -1) {
PRUint32 gotLock = LockSidCacheLock(cache->certCacheLock, now);
if (gotLock) {
pcce = &cache->certCacheData[cndx];
/* See if the cert's session ID matches the sce cache. */
if ((pcce->sessionIDLength == psce->sessionIDLength) &&
!PORT_Memcmp(pcce->sessionID, psce->sessionID,
pcce->sessionIDLength)) {
cce = *pcce;
} else {
/* The cert doesen't match the SID cache entry,
** so invalidate the SID cache entry.
*/
psce->valid = 0;
psce = 0;
pcce = 0;
}
UnlockSidCacheLock(cache->certCacheLock);
} else {
/* what the ??. Didn't get the cert cache lock.
** Don't invalidate the SID cache entry, but don't find it.
*/
PORT_AssertNotReached("Didn't get cert Cache Lock!");
psce = 0;
pcce = 0;
}
}
if (psce && ((cndx = psce->u.ssl3.srvNameIndex) != -1)) {
PRUint32 gotLock = LockSidCacheLock(cache->srvNameCacheLock,
now);
if (gotLock) {
psnce = &cache->srvNameCacheData[cndx];
if (!PORT_Memcmp(psnce->nameHash, psce->u.ssl3.srvNameHash,
SHA256_LENGTH)) {
snce = *psnce;
} else {
/* The name doesen't match the SID cache entry,
** so invalidate the SID cache entry.
*/
psce->valid = 0;
psce = 0;
psnce = 0;
}
UnlockSidCacheLock(cache->srvNameCacheLock);
} else {
/* what the ??. Didn't get the cert cache lock.
** Don't invalidate the SID cache entry, but don't find it.
*/
PORT_AssertNotReached("Didn't get name Cache Lock!");
psce = 0;
psnce = 0;
}
}
if (psce) {
psce->lastAccessTime = sslNow;
sce = *psce; /* grab a copy while holding the lock */
}
}
UnlockSet(cache, set);
if (psce) {
/* sce conains a copy of the cache entry.
** Convert shared memory format to local format
*/
sid = ConvertToSID(&sce, pcce ? &cce : 0, psnce ? &snce : 0, dbHandle);
}
return sid;
}
/*
** Place a sid into the cache, if it isn't already there.
*/
void
ssl_ServerCacheSessionID(sslSessionID *sid, PRTime creationTime)
{
PORT_Assert(sid);
sidCacheEntry sce;
PRUint32 now = 0;
cacheDesc *cache = &globalCache;
if (sid->u.ssl3.sessionIDLength == 0) {
return;
}
if (sid->cached == never_cached || sid->cached == invalid_cache) {
PRUint32 set;
SECItem *name;
PORT_Assert(sid->creationTime != 0);
if (!sid->creationTime)
sid->lastAccessTime = sid->creationTime = creationTime;
/* override caller's expiration time, which uses client timeout
* duration, not server timeout duration.
*/
sid->expirationTime =
sid->creationTime + cache->ssl3Timeout * PR_USEC_PER_SEC;
SSL_TRC(8, ("%d: SSL: CacheMT: cached=%d addr=0x%08x%08x%08x%08x time=%x "
"cipherSuite=%d",
myPid, 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->creationTime / PR_USEC_PER_SEC,
sid->u.ssl3.cipherSuite));
PRINT_BUF(8, (0, "sessionID:", sid->u.ssl3.sessionID,
sid->u.ssl3.sessionIDLength));
ConvertFromSID(&sce, sid);
name = &sid->u.ssl3.srvName;
if (name->len && name->data) {
now = CacheSrvName(cache, name, &sce);
}
if (sid->peerCert != NULL) {
now = CacheCert(cache, sid->peerCert, &sce);
}
set = SIDindex(cache, &sce.addr, sce.sessionID, sce.sessionIDLength);
now = LockSet(cache, set, now);
if (now) {
PRUint32 next = cache->sidCacheSets[set].next;
PRUint32 ndx = set * SID_CACHE_ENTRIES_PER_SET + next;
/* Write out new cache entry */
cache->sidCacheData[ndx] = sce;
cache->sidCacheSets[set].next =
(next + 1) % SID_CACHE_ENTRIES_PER_SET;
UnlockSet(cache, set);
sid->cached = in_server_cache;
}
}
}
/*
** Although this is static, it is called from ssl via global function pointer
** ssl_sid_uncache. This invalidates the referenced cache entry.
*/
void
ssl_ServerUncacheSessionID(sslSessionID *sid)
{
cacheDesc *cache = &globalCache;
PRUint8 *sessionID;
unsigned int sessionIDLength;
PRErrorCode err;
PRUint32 set;
PRUint32 now;
sidCacheEntry *psce;
if (sid == NULL)
return;
/* Uncaching a SID should never change the error code.
** So save it here and restore it before exiting.
*/
err = PR_GetError();
sessionID = sid->u.ssl3.sessionID;
sessionIDLength = sid->u.ssl3.sessionIDLength;
SSL_TRC(8, ("%d: SSL3: UncacheMT: valid=%d addr=0x%08x%08x%08x%08x time=%x "
"cipherSuite=%d",
myPid, 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->creationTime / PR_USEC_PER_SEC,
sid->u.ssl3.cipherSuite));
PRINT_BUF(8, (0, "sessionID:", sessionID, sessionIDLength));
set = SIDindex(cache, &sid->addr, sessionID, sessionIDLength);
now = LockSet(cache, set, 0);
if (now) {
psce = FindSID(cache, set, now, &sid->addr, sessionID, sessionIDLength);
if (psce) {
psce->valid = 0;
}
UnlockSet(cache, set);
}
sid->cached = invalid_cache;
PORT_SetError(err);
}
#ifdef XP_OS2
#define INCL_DOSPROCESS
#include <os2.h>
long
gettid(void)
{
PTIB ptib;
PPIB ppib;
DosGetInfoBlocks(&ptib, &ppib);
return ((long)ptib->tib_ordinal); /* thread id */
}
#endif
static void
CloseCache(cacheDesc *cache)
{
int locks_initialized = cache->numSIDCacheLocksInitialized;
if (cache->cacheMem) {
if (cache->sharedCache) {
sidCacheLock *pLock = cache->sidCacheLocks;
for (; locks_initialized > 0; --locks_initialized, ++pLock) {
/* If everInherited is true, this shared cache was (and may
** still be) in use by multiple processes. We do not wish to
** destroy the mutexes while they are still in use, but we do
** want to free mutex resources associated with this process.
*/
sslMutex_Destroy(&pLock->mutex,
cache->sharedCache->everInherited);
}
}
if (cache->shared) {
PR_MemUnmap(cache->cacheMem, cache->cacheMemSize);
} else {
PORT_Free(cache->cacheMem);
}
cache->cacheMem = NULL;
}
if (cache->cacheMemMap) {
PR_CloseFileMap(cache->cacheMemMap);
cache->cacheMemMap = NULL;
}
memset(cache, 0, sizeof *cache);
}
static SECStatus
InitCache(cacheDesc *cache, int maxCacheEntries, int maxCertCacheEntries,
int maxSrvNameCacheEntries, PRUint32 ssl3_timeout,
const char *directory, PRBool shared)
{
ptrdiff_t ptr;
sidCacheLock *pLock;
char *cacheMem;
PRFileMap *cacheMemMap;
char *cfn = NULL; /* cache file name */
int locks_initialized = 0;
int locks_to_initialize = 0;
PRUint32 init_time;
if ((!cache) || (maxCacheEntries < 0) || (!directory)) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
if (cache->cacheMem) {
/* Already done */
return SECSuccess;
}
/* make sure loser can clean up properly */
cache->shared = shared;
cache->cacheMem = cacheMem = NULL;
cache->cacheMemMap = cacheMemMap = NULL;
cache->sharedCache = (cacheDesc *)0;
cache->numSIDCacheLocksInitialized = 0;
cache->nextCertCacheEntry = 0;
cache->stopPolling = PR_FALSE;
cache->everInherited = PR_FALSE;
cache->poller = NULL;
cache->mutexTimeout = 0;
cache->numSIDCacheEntries = maxCacheEntries ? maxCacheEntries
: DEF_SID_CACHE_ENTRIES;
cache->numSIDCacheSets =
SID_HOWMANY(cache->numSIDCacheEntries, SID_CACHE_ENTRIES_PER_SET);
cache->numSIDCacheEntries =
cache->numSIDCacheSets * SID_CACHE_ENTRIES_PER_SET;
cache->numSIDCacheLocks =
PR_MIN(cache->numSIDCacheSets, ssl_max_sid_cache_locks);
cache->numSIDCacheSetsPerLock =
SID_HOWMANY(cache->numSIDCacheSets, cache->numSIDCacheLocks);
cache->numCertCacheEntries = (maxCertCacheEntries > 0) ? maxCertCacheEntries
: 0;
cache->numSrvNameCacheEntries = (maxSrvNameCacheEntries >= 0) ? maxSrvNameCacheEntries
: DEF_NAME_CACHE_ENTRIES;
/* compute size of shared memory, and offsets of all pointers */
ptr = 0;
cache->cacheMem = (char *)ptr;
ptr += SID_ROUNDUP(sizeof(cacheDesc), SID_ALIGNMENT);
cache->sidCacheLocks = (sidCacheLock *)ptr;
cache->keyCacheLock = cache->sidCacheLocks + cache->numSIDCacheLocks;
cache->certCacheLock = cache->keyCacheLock + 1;
cache->srvNameCacheLock = cache->certCacheLock + 1;
ptr = (ptrdiff_t)(cache->srvNameCacheLock + 1);
ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
cache->sidCacheSets = (sidCacheSet *)ptr;
ptr = (ptrdiff_t)(cache->sidCacheSets + cache->numSIDCacheSets);
ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
cache->sidCacheData = (sidCacheEntry *)ptr;
ptr = (ptrdiff_t)(cache->sidCacheData + cache->numSIDCacheEntries);
ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
cache->certCacheData = (certCacheEntry *)ptr;
cache->sidCacheSize =
(char *)cache->certCacheData - (char *)cache->sidCacheData;
if (cache->numCertCacheEntries < MIN_CERT_CACHE_ENTRIES) {
/* This is really a poor way to computer this! */
cache->numCertCacheEntries = cache->sidCacheSize / sizeof(certCacheEntry);
if (cache->numCertCacheEntries < MIN_CERT_CACHE_ENTRIES)
cache->numCertCacheEntries = MIN_CERT_CACHE_ENTRIES;
}
ptr = (ptrdiff_t)(cache->certCacheData + cache->numCertCacheEntries);
ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
cache->keyCacheData = (SSLWrappedSymWrappingKey *)ptr;
cache->certCacheSize =
(char *)cache->keyCacheData - (char *)cache->certCacheData;
cache->numKeyCacheEntries = SSL_NUM_WRAP_KEYS * SSL_NUM_WRAP_MECHS;
ptr = (ptrdiff_t)(cache->keyCacheData + cache->numKeyCacheEntries);
ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
cache->keyCacheSize = (char *)ptr - (char *)cache->keyCacheData;
cache->ticketKeyNameSuffix = (PRUint8 *)ptr;
ptr = (ptrdiff_t)(cache->ticketKeyNameSuffix +
SELF_ENCRYPT_KEY_VAR_NAME_LEN);
ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
cache->ticketEncKey = (encKeyCacheEntry *)ptr;
ptr = (ptrdiff_t)(cache->ticketEncKey + 1);
ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
cache->ticketMacKey = (encKeyCacheEntry *)ptr;
ptr = (ptrdiff_t)(cache->ticketMacKey + 1);
ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
cache->ticketKeysValid = (PRUint32 *)ptr;
ptr = (ptrdiff_t)(cache->ticketKeysValid + 1);
ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
cache->srvNameCacheData = (srvNameCacheEntry *)ptr;
cache->srvNameCacheSize =
cache->numSrvNameCacheEntries * sizeof(srvNameCacheEntry);
ptr = (ptrdiff_t)(cache->srvNameCacheData + cache->numSrvNameCacheEntries);
ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
cache->cacheMemSize = ptr;
if (ssl3_timeout) {
if (ssl3_timeout > MAX_SSL3_TIMEOUT) {
ssl3_timeout = MAX_SSL3_TIMEOUT;
}
if (ssl3_timeout < MIN_SSL3_TIMEOUT) {
ssl3_timeout = MIN_SSL3_TIMEOUT;
}
cache->ssl3Timeout = ssl3_timeout;
} else {
cache->ssl3Timeout = DEF_SSL3_TIMEOUT;
}
if (shared) {
/* Create file names */
#if defined(XP_UNIX) || defined(XP_BEOS)
/* there's some confusion here about whether PR_OpenAnonFileMap wants
** a directory name or a file name for its first argument.
cfn = PR_smprintf("%s/.sslsvrcache.%d", directory, myPid);
*/
cfn = PR_smprintf("%s", directory);
#elif defined(XP_WIN32)
cfn = PR_smprintf("%s/svrcache_%d_%x.ssl", directory, myPid,
GetCurrentThreadId());
#elif defined(XP_OS2)
cfn = PR_smprintf("%s/svrcache_%d_%x.ssl", directory, myPid,
gettid());
#else
#error "Don't know how to create file name for this platform!"
#endif
if (!cfn) {
goto loser;
}
/* Create cache */
cacheMemMap = PR_OpenAnonFileMap(cfn, cache->cacheMemSize,
PR_PROT_READWRITE);
PR_smprintf_free(cfn);
if (!cacheMemMap) {
goto loser;
}
cacheMem = PR_MemMap(cacheMemMap, 0, cache->cacheMemSize);
} else {
cacheMem = PORT_Alloc(cache->cacheMemSize);
}
if (!cacheMem) {
goto loser;
}
/* Initialize shared memory. This may not be necessary on all platforms */
memset(cacheMem, 0, cache->cacheMemSize);
/* Copy cache descriptor header into shared memory */
memcpy(cacheMem, cache, sizeof *cache);
/* save private copies of these values */
cache->cacheMemMap = cacheMemMap;
cache->cacheMem = cacheMem;
cache->sharedCache = (cacheDesc *)cacheMem;
/* Fix pointers in our private copy of cache descriptor to point to
** spaces in shared memory
*/
cache->sidCacheLocks = (sidCacheLock *)(cache->cacheMem + (ptrdiff_t)cache->sidCacheLocks);
cache->keyCacheLock = (sidCacheLock *)(cache->cacheMem + (ptrdiff_t)cache->keyCacheLock);
cache->certCacheLock = (sidCacheLock *)(cache->cacheMem + (ptrdiff_t)cache->certCacheLock);
cache->srvNameCacheLock = (sidCacheLock *)(cache->cacheMem + (ptrdiff_t)cache->srvNameCacheLock);
cache->sidCacheSets = (sidCacheSet *)(cache->cacheMem + (ptrdiff_t)cache->sidCacheSets);
cache->sidCacheData = (sidCacheEntry *)(cache->cacheMem + (ptrdiff_t)cache->sidCacheData);
cache->certCacheData = (certCacheEntry *)(cache->cacheMem + (ptrdiff_t)cache->certCacheData);
cache->keyCacheData = (SSLWrappedSymWrappingKey *)(cache->cacheMem + (ptrdiff_t)cache->keyCacheData);
cache->ticketKeyNameSuffix = (PRUint8 *)(cache->cacheMem + (ptrdiff_t)cache->ticketKeyNameSuffix);
cache->ticketEncKey = (encKeyCacheEntry *)(cache->cacheMem + (ptrdiff_t)cache->ticketEncKey);
cache->ticketMacKey = (encKeyCacheEntry *)(cache->cacheMem + (ptrdiff_t)cache->ticketMacKey);
cache->ticketKeysValid = (PRUint32 *)(cache->cacheMem + (ptrdiff_t)cache->ticketKeysValid);
cache->srvNameCacheData = (srvNameCacheEntry *)(cache->cacheMem + (ptrdiff_t)cache->srvNameCacheData);
/* initialize the locks */
init_time = ssl_CacheNow();
pLock = cache->sidCacheLocks;
for (locks_to_initialize = cache->numSIDCacheLocks + 3;
locks_initialized < locks_to_initialize;
++locks_initialized, ++pLock) {
SECStatus err = sslMutex_Init(&pLock->mutex, shared);
if (err) {
cache->numSIDCacheLocksInitialized = locks_initialized;
goto loser;
}
pLock->timeStamp = init_time;
pLock->pid = 0;
}
cache->numSIDCacheLocksInitialized = locks_initialized;
return SECSuccess;
loser:
CloseCache(cache);
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
PRUint32
SSL_GetMaxServerCacheLocks(void)
{
return ssl_max_sid_cache_locks + 2;
/* The extra two are the cert cache lock and the key cache lock. */
}
SECStatus
SSL_SetMaxServerCacheLocks(PRUint32 maxLocks)
{
/* Minimum is 1 sid cache lock, 1 cert cache lock and 1 key cache lock.
** We'd like to test for a maximum value, but not all platforms' header
** files provide a symbol or function or other means of determining
** the maximum, other than trial and error.
*/
if (maxLocks < 3) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
ssl_max_sid_cache_locks = maxLocks - 2;
/* The extra two are the cert cache lock and the key cache lock. */
return SECSuccess;
}
PR_STATIC_ASSERT(sizeof(sidCacheEntry) % 16 == 0);
PR_STATIC_ASSERT(sizeof(certCacheEntry) == 4096);
PR_STATIC_ASSERT(sizeof(srvNameCacheEntry) == 1072);
static SECStatus
ssl_ConfigServerSessionIDCacheInstanceWithOpt(cacheDesc *cache,
PRUint32 ssl3_timeout,
const char *directory,
PRBool shared,
int maxCacheEntries,
int maxCertCacheEntries,
int maxSrvNameCacheEntries)
{
SECStatus rv;
rv = ssl_InitSessionCache();
if (rv != SECSuccess) {
return rv;
}
myPid = SSL_GETPID();
if (!directory) {
directory = DEFAULT_CACHE_DIRECTORY;
}
rv = InitCache(cache, maxCacheEntries, maxCertCacheEntries,
maxSrvNameCacheEntries, ssl3_timeout, directory, shared);
if (rv) {
return SECFailure;
}
ssl_sid_lookup = ServerSessionIDLookup;
return SECSuccess;
}
SECStatus
SSL_ConfigServerSessionIDCacheInstance(cacheDesc *cache,
int maxCacheEntries,
PRUint32 ssl2_timeout,
PRUint32 ssl3_timeout,
const char *directory, PRBool shared)
{
return ssl_ConfigServerSessionIDCacheInstanceWithOpt(cache,
ssl3_timeout,
directory,
shared,
maxCacheEntries,
-1, -1);
}
SECStatus
SSL_ConfigServerSessionIDCache(int maxCacheEntries,
PRUint32 ssl2_timeout,
PRUint32 ssl3_timeout,
const char *directory)
{
ssl_InitSessionCacheLocks(PR_FALSE);
return SSL_ConfigServerSessionIDCacheInstance(&globalCache,
maxCacheEntries, ssl2_timeout, ssl3_timeout, directory, PR_FALSE);
}
SECStatus
SSL_ShutdownServerSessionIDCacheInstance(cacheDesc *cache)
{
CloseCache(cache);
return SECSuccess;
}
SECStatus
SSL_ShutdownServerSessionIDCache(void)
{
#if defined(XP_UNIX) || defined(XP_BEOS)
/* Stop the thread that polls cache for expired locks on Unix */
StopLockPoller(&globalCache);
#endif
SSL3_ShutdownServerCache();
return SSL_ShutdownServerSessionIDCacheInstance(&globalCache);
}
/* Use this function, instead of SSL_ConfigServerSessionIDCache,
* if the cache will be shared by multiple processes.
*/
static SECStatus
ssl_ConfigMPServerSIDCacheWithOpt(PRUint32 ssl3_timeout,
const char *directory,
int maxCacheEntries,
int maxCertCacheEntries,
int maxSrvNameCacheEntries)
{
char *envValue;
char *inhValue;
cacheDesc *cache = &globalCache;
PRUint32 fmStrLen;
SECStatus result;
PRStatus prStatus;
SECStatus putEnvFailed;
inheritance inherit;
char fmString[PR_FILEMAP_STRING_BUFSIZE];
isMultiProcess = PR_TRUE;
result = ssl_ConfigServerSessionIDCacheInstanceWithOpt(cache,
ssl3_timeout, directory, PR_TRUE,
maxCacheEntries, maxCacheEntries, maxSrvNameCacheEntries);
if (result != SECSuccess)
return result;
prStatus = PR_ExportFileMapAsString(cache->cacheMemMap,
sizeof fmString, fmString);
if ((prStatus != PR_SUCCESS) || !(fmStrLen = strlen(fmString))) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
inherit.cacheMemSize = cache->cacheMemSize;
inherit.fmStrLen = fmStrLen;
inhValue = BTOA_DataToAscii((unsigned char *)&inherit, sizeof inherit);
if (!inhValue || !strlen(inhValue)) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
envValue = PR_smprintf("%s,%s", inhValue, fmString);
if (!envValue || !strlen(envValue)) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
PORT_Free(inhValue);
putEnvFailed = (SECStatus)NSS_PutEnv(envVarName, envValue);
PR_smprintf_free(envValue);
if (putEnvFailed) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
result = SECFailure;
}
#if defined(XP_UNIX) || defined(XP_BEOS)
/* Launch thread to poll cache for expired locks on Unix */
LaunchLockPoller(cache);
#endif
return result;
}
/* Use this function, instead of SSL_ConfigServerSessionIDCache,
* if the cache will be shared by multiple processes.
*/
SECStatus
SSL_ConfigMPServerSIDCache(int maxCacheEntries,
PRUint32 ssl2_timeout,
PRUint32 ssl3_timeout,
const char *directory)
{
return ssl_ConfigMPServerSIDCacheWithOpt(ssl3_timeout,
directory,
maxCacheEntries,
-1, -1);
}
SECStatus
SSL_ConfigServerSessionIDCacheWithOpt(
PRUint32 ssl2_timeout,
PRUint32 ssl3_timeout,
const char *directory,
int maxCacheEntries,
int maxCertCacheEntries,
int maxSrvNameCacheEntries,
PRBool enableMPCache)
{
if (!enableMPCache) {
ssl_InitSessionCacheLocks(PR_FALSE);
return ssl_ConfigServerSessionIDCacheInstanceWithOpt(&globalCache,
ssl3_timeout, directory, PR_FALSE,
maxCacheEntries, maxCertCacheEntries, maxSrvNameCacheEntries);
} else {
return ssl_ConfigMPServerSIDCacheWithOpt(ssl3_timeout, directory,
maxCacheEntries, maxCertCacheEntries, maxSrvNameCacheEntries);
}
}
SECStatus
SSL_InheritMPServerSIDCacheInstance(cacheDesc *cache, const char *envString)
{
unsigned char *decoString = NULL;
char *fmString = NULL;
char *myEnvString = NULL;
unsigned int decoLen;
inheritance inherit;
cacheDesc my;
#ifdef WINNT
sidCacheLock *newLocks;
int locks_initialized = 0;
int locks_to_initialize = 0;
#endif
SECStatus status = ssl_InitSessionCache();
if (status != SECSuccess) {
return status;
}
myPid = SSL_GETPID();
/* If this child was created by fork(), and not by exec() on unix,
** then isMultiProcess will already be set.
** If not, we'll set it below.
*/
if (isMultiProcess) {
if (cache && cache->sharedCache) {
cache->sharedCache->everInherited = PR_TRUE;
}
return SECSuccess; /* already done. */
}
ssl_InitSessionCacheLocks(PR_FALSE);
ssl_sid_lookup = ServerSessionIDLookup;
if (!envString) {
envString = PR_GetEnvSecure(envVarName);
if (!envString) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
}
myEnvString = PORT_Strdup(envString);
if (!myEnvString)
return SECFailure;
fmString = strchr(myEnvString, ',');
if (!fmString)
goto loser;
*fmString++ = 0;
decoString = ATOB_AsciiToData(myEnvString, &decoLen);
if (!decoString) {
goto loser;
}
if (decoLen != sizeof inherit) {
goto loser;
}
PORT_Memcpy(&inherit, decoString, sizeof inherit);
if (strlen(fmString) != inherit.fmStrLen) {
goto loser;
}
memset(cache, 0, sizeof *cache);
cache->cacheMemSize = inherit.cacheMemSize;
/* Create cache */
cache->cacheMemMap = PR_ImportFileMapFromString(fmString);
if (!cache->cacheMemMap) {
goto loser;
}
cache->cacheMem = PR_MemMap(cache->cacheMemMap, 0, cache->cacheMemSize);
if (!cache->cacheMem) {
goto loser;
}
cache->sharedCache = (cacheDesc *)cache->cacheMem;
if (cache->sharedCache->cacheMemSize != cache->cacheMemSize) {
goto loser;
}
/* We're now going to overwrite the local cache instance with the
** shared copy of the cache struct, then update several values in
** the local cache using the values for cache->cacheMemMap and
** cache->cacheMem computed just above. So, we copy cache into
** the automatic variable "my", to preserve the variables while
** cache is overwritten.
*/
my = *cache; /* save values computed above. */
memcpy(cache, cache->sharedCache, sizeof *cache); /* overwrite */
/* Fix pointers in our private copy of cache descriptor to point to
** spaces in shared memory, whose address is now in "my".
*/
cache->sidCacheLocks = (sidCacheLock *)(my.cacheMem + (ptrdiff_t)cache->sidCacheLocks);
cache->keyCacheLock = (sidCacheLock *)(my.cacheMem + (ptrdiff_t)cache->keyCacheLock);
cache->certCacheLock = (sidCacheLock *)(my.cacheMem + (ptrdiff_t)cache->certCacheLock);
cache->srvNameCacheLock = (sidCacheLock *)(my.cacheMem + (ptrdiff_t)cache->srvNameCacheLock);
cache->sidCacheSets = (sidCacheSet *)(my.cacheMem + (ptrdiff_t)cache->sidCacheSets);
cache->sidCacheData = (sidCacheEntry *)(my.cacheMem + (ptrdiff_t)cache->sidCacheData);
cache->certCacheData = (certCacheEntry *)(my.cacheMem + (ptrdiff_t)cache->certCacheData);
cache->keyCacheData = (SSLWrappedSymWrappingKey *)(my.cacheMem + (ptrdiff_t)cache->keyCacheData);
cache->ticketKeyNameSuffix = (PRUint8 *)(my.cacheMem + (ptrdiff_t)cache->ticketKeyNameSuffix);
cache->ticketEncKey = (encKeyCacheEntry *)(my.cacheMem + (ptrdiff_t)cache->ticketEncKey);
cache->ticketMacKey = (encKeyCacheEntry *)(my.cacheMem + (ptrdiff_t)cache->ticketMacKey);
cache->ticketKeysValid = (PRUint32 *)(my.cacheMem + (ptrdiff_t)cache->ticketKeysValid);
cache->srvNameCacheData = (srvNameCacheEntry *)(my.cacheMem + (ptrdiff_t)cache->srvNameCacheData);
cache->cacheMemMap = my.cacheMemMap;
cache->cacheMem = my.cacheMem;
cache->sharedCache = (cacheDesc *)cache->cacheMem;
#ifdef WINNT
/* On Windows NT we need to "fix" the sidCacheLocks here to support fibers
** When NT fibers are used in a multi-process server, a second level of
** locking is needed to prevent a deadlock, in case a fiber acquires the
** cross-process mutex, yields, and another fiber is later scheduled on
** the same native thread and tries to acquire the cross-process mutex.
** We do this by using a PRLock in the sslMutex. However, it is stored in
** shared memory as part of sidCacheLocks, and we don't want to overwrite
** the PRLock of the parent process. So we need to make new, private
** copies of sidCacheLocks before modifying the sslMutex with our own
** PRLock
*/
/* note from jpierre : this should be free'd in child processes when
** a function is added to delete the SSL session cache in the future.
*/
locks_to_initialize = cache->numSIDCacheLocks + 3;
newLocks = PORT_NewArray(sidCacheLock, locks_to_initialize);
if (!newLocks)
goto loser;
/* copy the old locks */
memcpy(newLocks, cache->sidCacheLocks,
locks_to_initialize * sizeof(sidCacheLock));
cache->sidCacheLocks = newLocks;
/* fix the locks */
for (; locks_initialized < locks_to_initialize; ++locks_initialized) {
/* now, make a local PRLock in this sslMutex for this child process */
SECStatus err;
err = sslMutex_2LevelInit(&newLocks[locks_initialized].mutex);
if (err != SECSuccess) {
cache->numSIDCacheLocksInitialized = locks_initialized;
goto loser;
}
}
cache->numSIDCacheLocksInitialized = locks_initialized;
/* also fix the key and cert cache which use the last 2 lock entries */
cache->keyCacheLock = cache->sidCacheLocks + cache->numSIDCacheLocks;
cache->certCacheLock = cache->keyCacheLock + 1;
cache->srvNameCacheLock = cache->certCacheLock + 1;
#endif
PORT_Free(myEnvString);
PORT_Free(decoString);
/* mark that we have inherited this. */
cache->sharedCache->everInherited = PR_TRUE;
isMultiProcess = PR_TRUE;
return SECSuccess;
loser:
PORT_Free(myEnvString);
if (decoString)
PORT_Free(decoString);
CloseCache(cache);
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
SECStatus
SSL_InheritMPServerSIDCache(const char *envString)
{
return SSL_InheritMPServerSIDCacheInstance(&globalCache, envString);
}
#if defined(XP_UNIX) || defined(XP_BEOS)
#define SID_LOCK_EXPIRATION_TIMEOUT 30 /* seconds */
static void
LockPoller(void *arg)
{
cacheDesc *cache = (cacheDesc *)arg;
cacheDesc *sharedCache = cache->sharedCache;
sidCacheLock *pLock;
PRIntervalTime timeout;
PRUint32 now;
PRUint32 then;
int locks_polled = 0;
int locks_to_poll = cache->numSIDCacheLocks + 2;
PRUint32 expiration = cache->mutexTimeout;
timeout = PR_SecondsToInterval(expiration);
while (!sharedCache->stopPolling) {
PR_Sleep(timeout);
if (sharedCache->stopPolling)
break;
now = ssl_CacheNow();
then = now - expiration;
for (pLock = cache->sidCacheLocks, locks_polled = 0;
locks_to_poll > locks_polled && !sharedCache->stopPolling;
++locks_polled, ++pLock) {
pid_t pid;
if (pLock->timeStamp < then &&
pLock->timeStamp != 0 &&
(pid = pLock->pid) != 0) {
/* maybe we should try the lock? */
int result = kill(pid, 0);
if (result < 0 && errno == ESRCH) {
SECStatus rv;
/* No process exists by that pid any more.
** Treat this mutex as abandoned.
*/
pLock->timeStamp = now;
pLock->pid = 0;
rv = sslMutex_Unlock(&pLock->mutex);
if (rv != SECSuccess) {
/* Now what? */
}
}
}
} /* end of loop over locks */
} /* end of entire polling loop */
}
/* Launch thread to poll cache for expired locks */
static SECStatus
LaunchLockPoller(cacheDesc *cache)
{
const char *timeoutString;
PRThread *pollerThread;
cache->mutexTimeout = SID_LOCK_EXPIRATION_TIMEOUT;
timeoutString = PR_GetEnvSecure("NSS_SSL_SERVER_CACHE_MUTEX_TIMEOUT");
if (timeoutString) {
long newTime = strtol(timeoutString, 0, 0);
if (newTime == 0)
return SECSuccess; /* application doesn't want poller thread */
if (newTime > 0)
cache->mutexTimeout = (PRUint32)newTime;
/* if error (newTime < 0) ignore it and use default */
}
pollerThread =
PR_CreateThread(PR_USER_THREAD, LockPoller, cache, PR_PRIORITY_NORMAL,
PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
if (!pollerThread) {
return SECFailure;
}
cache->poller = pollerThread;
return SECSuccess;
}
/* Stop the thread that polls cache for expired locks */
static SECStatus
StopLockPoller(cacheDesc *cache)
{
if (!cache->poller) {
return