gecko/security/nss/lib/ssl/sslsnce.c

1953 lines
59 KiB
C
Raw Normal View History

2008-06-06 05:40:11 -07:00
/* This file implements the SERVER Session ID cache.
* NOTE: The contents of this file are NOT used by the client.
*
* ***** 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: sslsnce.c,v 1.49 2008/12/02 06:36:59 nelson%bolyard.com Exp $ */
2008-06-06 05:40:11 -07:00
/* 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[kt_kea_size][SSL_NUM_WRAP_MECHS];
* uint8 keyNameSuffix[SESS_TICKET_KEY_VAR_NAME_LEN]
* encKeyCacheEntry ticketEncKey; // Wrapped in non-bypass mode
* encKeyCacheEntry ticketMacKey; // Wrapped in non-bypass mode
* PRBool ticketKeysValid;
* } cacheMemCacheData;
*/
#include "seccomon.h"
#if defined(XP_UNIX) || defined(XP_WIN32) || defined (XP_OS2) || defined(XP_BEOS)
2008-06-06 05:40:11 -07:00
#include "cert.h"
#include "ssl.h"
#include "sslimpl.h"
#include "sslproto.h"
#include "pk11func.h"
#include "base64.h"
#include "keyhi.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>
#define SET_ERROR_CODE /* reminder */
#include "nspr.h"
#include "sslmutex.h"
/*
** Format of a cache entry in the shared memory.
*/
struct sidCacheEntryStr {
/* 16 */ PRIPv6Addr addr; /* client's IP address */
/* 4 */ PRUint32 creationTime;
/* 4 */ PRUint32 lastAccessTime;
/* 4 */ PRUint32 expirationTime;
/* 2 */ PRUint16 version;
/* 1 */ PRUint8 valid;
/* 1 */ PRUint8 sessionIDLength;
/* 32 */ PRUint8 sessionID[SSL3_SESSIONID_BYTES];
/* 2 */ PRUint16 authAlgorithm;
/* 2 */ PRUint16 authKeyBits;
/* 2 */ PRUint16 keaType;
/* 2 */ PRUint16 keaKeyBits;
/* 72 - common header total */
union {
struct {
/* 64 */ PRUint8 masterKey[SSL_MAX_MASTER_KEY_BYTES];
/* 32 */ PRUint8 cipherArg[SSL_MAX_CYPHER_ARG_BYTES];
/* 1 */ PRUint8 cipherType;
/* 1 */ PRUint8 masterKeyLen;
/* 1 */ PRUint8 keyBits;
/* 1 */ PRUint8 secretKeyBits;
/* 1 */ PRUint8 cipherArgLen;
/*101 */} ssl2;
struct {
/* 2 */ ssl3CipherSuite cipherSuite;
/* 2 */ PRUint16 compression; /* SSL3CompressionMethod */
/*100 */ ssl3SidKeys keys; /* keys and ivs, wrapped as needed. */
/* 4 */ PRUint32 masterWrapMech;
/* 4 */ SSL3KEAType exchKeyType;
/* 4 */ PRInt32 certIndex;
/*116 */} ssl3;
/* force sizeof(sidCacheEntry) to be a multiple of cache line size */
struct {
/*120 */ PRUint8 filler[120]; /* 72+120==196, 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;
struct cacheDescStr {
PRUint32 cacheMemSize;
PRUint32 numSIDCacheLocks;
PRUint32 numSIDCacheSets;
PRUint32 numSIDCacheSetsPerLock;
PRUint32 numSIDCacheEntries;
PRUint32 sidCacheSize;
PRUint32 numCertCacheEntries;
PRUint32 certCacheSize;
PRUint32 numKeyCacheEntries;
PRUint32 keyCacheSize;
PRUint32 ssl2Timeout;
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;
sidCacheSet * sidCacheSets;
sidCacheEntry * sidCacheData;
certCacheEntry * certCacheData;
SSLWrappedSymWrappingKey * keyCacheData;
uint8 * ticketKeyNameSuffix;
encKeyCacheEntry * ticketEncKey;
encKeyCacheEntry * ticketMacKey;
PRUint32 * ticketKeysValid;
/* 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 SID_CACHE_ENTRIES_PER_SET 128
#define SID_ALIGNMENT 16
#define DEF_SSL2_TIMEOUT 100 /* seconds */
#define MAX_SSL2_TIMEOUT 100 /* seconds */
#define MIN_SSL2_TIMEOUT 5 /* seconds */
#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(VMS) || defined(NETBSD) || defined(OPENBSD)
2008-06-06 05:40:11 -07:00
#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 */
/************************************************************************/
static PRUint32
LockSidCacheLock(sidCacheLock *lock, PRUint32 now)
{
SECStatus rv = sslMutex_Lock(&lock->mutex);
if (rv != SECSuccess)
return 0;
if (!now)
now = ssl_Time();
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 the value of ssl_Time 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;
}
/*
** 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->authAlgorithm = from->authAlgorithm;
to->authKeyBits = from->authKeyBits;
to->keaType = from->keaType;
to->keaKeyBits = from->keaKeyBits;
if (from->version < SSL_LIBRARY_VERSION_3_0) {
if ((from->u.ssl2.masterKey.len > SSL_MAX_MASTER_KEY_BYTES) ||
(from->u.ssl2.cipherArg.len > SSL_MAX_CYPHER_ARG_BYTES)) {
SSL_DBG(("%d: SSL: masterKeyLen=%d cipherArgLen=%d",
myPid, from->u.ssl2.masterKey.len,
from->u.ssl2.cipherArg.len));
to->valid = 0;
return;
}
to->u.ssl2.cipherType = from->u.ssl2.cipherType;
to->u.ssl2.masterKeyLen = from->u.ssl2.masterKey.len;
to->u.ssl2.cipherArgLen = from->u.ssl2.cipherArg.len;
to->u.ssl2.keyBits = from->u.ssl2.keyBits;
to->u.ssl2.secretKeyBits = from->u.ssl2.secretKeyBits;
to->sessionIDLength = SSL2_SESSIONID_BYTES;
PORT_Memcpy(to->sessionID, from->u.ssl2.sessionID, SSL2_SESSIONID_BYTES);
PORT_Memcpy(to->u.ssl2.masterKey, from->u.ssl2.masterKey.data,
from->u.ssl2.masterKey.len);
PORT_Memcpy(to->u.ssl2.cipherArg, from->u.ssl2.cipherArg.data,
from->u.ssl2.cipherArg.len);
#ifdef DEBUG
PORT_Memset(to->u.ssl2.masterKey+from->u.ssl2.masterKey.len, 0,
sizeof(to->u.ssl2.masterKey) - from->u.ssl2.masterKey.len);
PORT_Memset(to->u.ssl2.cipherArg+from->u.ssl2.cipherArg.len, 0,
sizeof(to->u.ssl2.cipherArg) - from->u.ssl2.cipherArg.len);
#endif
SSL_TRC(8, ("%d: SSL: ConvertSID: masterKeyLen=%d cipherArgLen=%d "
"time=%d addr=0x%08x%08x%08x%08x cipherType=%d", myPid,
to->u.ssl2.masterKeyLen, to->u.ssl2.cipherArgLen,
to->creationTime, 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.ssl2.cipherType));
} else {
/* This is an SSL v3 session */
to->u.ssl3.cipherSuite = from->u.ssl3.cipherSuite;
to->u.ssl3.compression = (uint16)from->u.ssl3.compression;
to->u.ssl3.keys = from->u.ssl3.keys;
to->u.ssl3.masterWrapMech = from->u.ssl3.masterWrapMech;
to->u.ssl3.exchKeyType = from->u.ssl3.exchKeyType;
to->sessionIDLength = from->u.ssl3.sessionIDLength;
to->u.ssl3.certIndex = -1;
PORT_Memcpy(to->sessionID, from->u.ssl3.sessionID,
to->sessionIDLength);
SSL_TRC(8, ("%d: SSL3: ConvertSID: time=%d addr=0x%08x%08x%08x%08x "
"cipherSuite=%d",
myPid, to->creationTime, 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().
** Caller must hold cache lock when calling this.
*/
static sslSessionID *
ConvertToSID(sidCacheEntry *from, certCacheEntry *pcce,
CERTCertDBHandle * dbHandle)
{
sslSessionID *to;
uint16 version = from->version;
to = (sslSessionID*) PORT_ZAlloc(sizeof(sslSessionID));
if (!to) {
return 0;
}
if (version < SSL_LIBRARY_VERSION_3_0) {
/* This is an SSL v2 session */
to->u.ssl2.masterKey.data =
(unsigned char*) PORT_Alloc(from->u.ssl2.masterKeyLen);
if (!to->u.ssl2.masterKey.data) {
goto loser;
}
if (from->u.ssl2.cipherArgLen) {
to->u.ssl2.cipherArg.data =
(unsigned char*)PORT_Alloc(from->u.ssl2.cipherArgLen);
if (!to->u.ssl2.cipherArg.data) {
goto loser;
}
PORT_Memcpy(to->u.ssl2.cipherArg.data, from->u.ssl2.cipherArg,
from->u.ssl2.cipherArgLen);
}
to->u.ssl2.cipherType = from->u.ssl2.cipherType;
to->u.ssl2.masterKey.len = from->u.ssl2.masterKeyLen;
to->u.ssl2.cipherArg.len = from->u.ssl2.cipherArgLen;
to->u.ssl2.keyBits = from->u.ssl2.keyBits;
to->u.ssl2.secretKeyBits = from->u.ssl2.secretKeyBits;
/* to->sessionIDLength = SSL2_SESSIONID_BYTES; */
PORT_Memcpy(to->u.ssl2.sessionID, from->sessionID, SSL2_SESSIONID_BYTES);
PORT_Memcpy(to->u.ssl2.masterKey.data, from->u.ssl2.masterKey,
from->u.ssl2.masterKeyLen);
SSL_TRC(8, ("%d: SSL: ConvertToSID: masterKeyLen=%d cipherArgLen=%d "
"time=%d addr=0x%08x%08x%08x%08x cipherType=%d",
myPid, to->u.ssl2.masterKey.len,
to->u.ssl2.cipherArg.len, to->creationTime,
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.ssl2.cipherType));
} else {
/* This is an SSL v3 session */
to->u.ssl3.sessionIDLength = from->sessionIDLength;
to->u.ssl3.cipherSuite = from->u.ssl3.cipherSuite;
to->u.ssl3.compression = (SSL3CompressionMethod)from->u.ssl3.compression;
to->u.ssl3.keys = from->u.ssl3.keys;
to->u.ssl3.masterWrapMech = from->u.ssl3.masterWrapMech;
to->u.ssl3.exchKeyType = from->u.ssl3.exchKeyType;
PORT_Memcpy(to->u.ssl3.sessionID, from->sessionID, from->sessionIDLength);
/* the portions of the SID that are only restored on the client
* are set to invalid values on the server.
*/
to->u.ssl3.clientWriteKey = NULL;
to->u.ssl3.serverWriteKey = NULL;
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;
}
}
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->authAlgorithm = from->authAlgorithm;
to->authKeyBits = from->authKeyBits;
to->keaType = from->keaType;
to->keaKeyBits = from->keaKeyBits;
return to;
loser:
if (to) {
if (version < SSL_LIBRARY_VERSION_3_0) {
if (to->u.ssl2.masterKey.data)
PORT_Free(to->u.ssl2.masterKey.data);
if (to->u.ssl2.cipherArg.data)
PORT_Free(to->u.ssl2.cipherArg.data);
}
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.
*/
static sslSessionID *
ServerSessionIDLookup(const PRIPv6Addr *addr,
unsigned char *sessionID,
unsigned int sessionIDLength,
CERTCertDBHandle * dbHandle)
{
sslSessionID * sid = 0;
sidCacheEntry * psce;
certCacheEntry *pcce = 0;
cacheDesc * cache = &globalCache;
PRUint32 now;
PRUint32 set;
PRInt32 cndx;
sidCacheEntry sce;
certCacheEntry cce;
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 (psce->version >= SSL_LIBRARY_VERSION_3_0 &&
(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_Assert(!("Didn't get cert Cache Lock!"));
psce = 0;
pcce = 0;
}
}
if (psce) {
psce->lastAccessTime = now;
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, dbHandle);
}
return sid;
}
/*
** Place a sid into the cache, if it isn't already there.
*/
static void
ServerSessionIDCache(sslSessionID *sid)
{
sidCacheEntry sce;
PRUint32 now = 0;
uint16 version = sid->version;
cacheDesc * cache = &globalCache;
if ((version >= SSL_LIBRARY_VERSION_3_0) &&
(sid->u.ssl3.sessionIDLength == 0)) {
return;
}
if (sid->cached == never_cached || sid->cached == invalid_cache) {
PRUint32 set;
PORT_Assert(sid->creationTime != 0);
if (!sid->creationTime)
sid->lastAccessTime = sid->creationTime = ssl_Time();
if (version < SSL_LIBRARY_VERSION_3_0) {
/* override caller's expiration time, which uses client timeout
* duration, not server timeout duration.
*/
sid->expirationTime = sid->creationTime + cache->ssl2Timeout;
SSL_TRC(8, ("%d: SSL: CacheMT: cached=%d addr=0x%08x%08x%08x%08x time=%x "
"cipher=%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, sid->u.ssl2.cipherType));
PRINT_BUF(8, (0, "sessionID:", sid->u.ssl2.sessionID,
SSL2_SESSIONID_BYTES));
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 {
/* override caller's expiration time, which uses client timeout
* duration, not server timeout duration.
*/
sid->expirationTime = sid->creationTime + cache->ssl3Timeout;
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, sid->u.ssl3.cipherSuite));
PRINT_BUF(8, (0, "sessionID:", sid->u.ssl3.sessionID,
sid->u.ssl3.sessionIDLength));
}
ConvertFromSID(&sce, sid);
if ((version >= SSL_LIBRARY_VERSION_3_0) &&
(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.
*/
static void
ServerSessionIDUncache(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();
if (sid->version < SSL_LIBRARY_VERSION_3_0) {
sessionID = sid->u.ssl2.sessionID;
sessionIDLength = SSL2_SESSIONID_BYTES;
SSL_TRC(8, ("%d: SSL: UncacheMT: valid=%d addr=0x%08x%08x%08x%08x time=%x "
"cipher=%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, sid->u.ssl2.cipherType));
PRINT_BUF(8, (0, "sessionID:", sessionID, sessionIDLength));
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 {
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, 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 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.
*/
if (cache->sharedCache &&
PR_FALSE == cache->sharedCache->everInherited) {
sidCacheLock *pLock = cache->sidCacheLocks;
for (; locks_initialized > 0; --locks_initialized, ++pLock ) {
sslMutex_Destroy(&pLock->mutex);
}
}
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, PRUint32 ssl2_timeout,
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);
/* 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;
ptr = (ptrdiff_t)(cache->certCacheLock + 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;
/* 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 = kt_kea_size * 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 = (uint8 *)ptr;
ptr = (ptrdiff_t)(cache->ticketKeyNameSuffix +
SESS_TICKET_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->cacheMemSize = ptr;
if (ssl2_timeout) {
if (ssl2_timeout > MAX_SSL2_TIMEOUT) {
ssl2_timeout = MAX_SSL2_TIMEOUT;
}
if (ssl2_timeout < MIN_SSL2_TIMEOUT) {
ssl2_timeout = MIN_SSL2_TIMEOUT;
}
cache->ssl2Timeout = ssl2_timeout;
} else {
cache->ssl2Timeout = DEF_SSL2_TIMEOUT;
}
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
*/
ptr = (ptrdiff_t)cache->cacheMem;
*(ptrdiff_t *)(&cache->sidCacheLocks) += ptr;
*(ptrdiff_t *)(&cache->keyCacheLock ) += ptr;
*(ptrdiff_t *)(&cache->certCacheLock) += ptr;
*(ptrdiff_t *)(&cache->sidCacheSets ) += ptr;
*(ptrdiff_t *)(&cache->sidCacheData ) += ptr;
*(ptrdiff_t *)(&cache->certCacheData) += ptr;
*(ptrdiff_t *)(&cache->keyCacheData ) += ptr;
*(ptrdiff_t *)(&cache->ticketKeyNameSuffix) += ptr;
*(ptrdiff_t *)(&cache->ticketEncKey ) += ptr;
*(ptrdiff_t *)(&cache->ticketMacKey ) += ptr;
*(ptrdiff_t *)(&cache->ticketKeysValid) += ptr;
/* initialize the locks */
init_time = ssl_Time();
pLock = cache->sidCacheLocks;
for (locks_to_initialize = cache->numSIDCacheLocks + 2;
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);
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;
}
SECStatus
SSL_ConfigServerSessionIDCacheInstance( cacheDesc *cache,
int maxCacheEntries,
PRUint32 ssl2_timeout,
PRUint32 ssl3_timeout,
const char * directory, PRBool shared)
{
SECStatus rv;
PORT_Assert(sizeof(sidCacheEntry) == 192);
PORT_Assert(sizeof(certCacheEntry) == 4096);
myPid = SSL_GETPID();
if (!directory) {
directory = DEFAULT_CACHE_DIRECTORY;
}
rv = InitCache(cache, maxCacheEntries, ssl2_timeout, ssl3_timeout,
directory, shared);
if (rv) {
SET_ERROR_CODE
return SECFailure;
}
ssl_sid_lookup = ServerSessionIDLookup;
ssl_sid_cache = ServerSessionIDCache;
ssl_sid_uncache = ServerSessionIDUncache;
return SECSuccess;
}
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.
*/
SECStatus
SSL_ConfigMPServerSIDCache( int maxCacheEntries,
PRUint32 ssl2_timeout,
PRUint32 ssl3_timeout,
const char * directory)
{
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_ConfigServerSessionIDCacheInstance(cache, maxCacheEntries,
ssl2_timeout, ssl3_timeout, directory, PR_TRUE);
if (result != SECSuccess)
return result;
prStatus = PR_ExportFileMapAsString(cache->cacheMemMap,
sizeof fmString, fmString);
if ((prStatus != PR_SUCCESS) || !(fmStrLen = strlen(fmString))) {
SET_ERROR_CODE
return SECFailure;
}
inherit.cacheMemSize = cache->cacheMemSize;
inherit.fmStrLen = fmStrLen;
inhValue = BTOA_DataToAscii((unsigned char *)&inherit, sizeof inherit);
if (!inhValue || !strlen(inhValue)) {
SET_ERROR_CODE
return SECFailure;
}
envValue = PR_smprintf("%s,%s", inhValue, fmString);
if (!envValue || !strlen(envValue)) {
SET_ERROR_CODE
return SECFailure;
}
PORT_Free(inhValue);
putEnvFailed = (SECStatus)NSS_PutEnv(envVarName, envValue);
PR_smprintf_free(envValue);
if (putEnvFailed) {
SET_ERROR_CODE
result = SECFailure;
}
#if defined(XP_UNIX) || defined(XP_BEOS)
/* Launch thread to poll cache for expired locks on Unix */
LaunchLockPoller(cache);
#endif
return result;
}
SECStatus
SSL_InheritMPServerSIDCacheInstance(cacheDesc *cache, const char * envString)
{
unsigned char * decoString = NULL;
char * fmString = NULL;
char * myEnvString = NULL;
unsigned int decoLen;
ptrdiff_t ptr;
inheritance inherit;
cacheDesc my;
#ifdef WINNT
sidCacheLock* newLocks;
int locks_initialized = 0;
int locks_to_initialize = 0;
#endif
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;
ssl_sid_cache = ServerSessionIDCache;
ssl_sid_uncache = ServerSessionIDUncache;
if (!envString) {
envString = getenv(envVarName);
if (!envString) {
SET_ERROR_CODE
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) {
SET_ERROR_CODE
goto loser;
}
if (decoLen != sizeof inherit) {
SET_ERROR_CODE
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) {
SET_ERROR_CODE
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".
*/
ptr = (ptrdiff_t)my.cacheMem;
*(ptrdiff_t *)(&cache->sidCacheLocks) += ptr;
*(ptrdiff_t *)(&cache->keyCacheLock ) += ptr;
*(ptrdiff_t *)(&cache->certCacheLock) += ptr;
*(ptrdiff_t *)(&cache->sidCacheSets ) += ptr;
*(ptrdiff_t *)(&cache->sidCacheData ) += ptr;
*(ptrdiff_t *)(&cache->certCacheData) += ptr;
*(ptrdiff_t *)(&cache->keyCacheData ) += ptr;
*(ptrdiff_t *)(&cache->ticketKeyNameSuffix) += ptr;
*(ptrdiff_t *)(&cache->ticketEncKey ) += ptr;
*(ptrdiff_t *)(&cache->ticketMacKey ) += ptr;
*(ptrdiff_t *)(&cache->ticketKeysValid) += ptr;
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 + 2;
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;
#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);
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_Time();
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 = getenv("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 SECSuccess;
}
cache->sharedCache->stopPolling = PR_TRUE;
if (PR_Interrupt(cache->poller) != PR_SUCCESS) {
return SECFailure;
}
if (PR_JoinThread(cache->poller) != PR_SUCCESS) {
return SECFailure;
}
cache->poller = NULL;
return SECSuccess;
}
#endif
/************************************************************************
* Code dealing with shared wrapped symmetric wrapping keys below *
************************************************************************/
/* If now is zero, it implies that the lock is not held, and must be
** aquired here.
*/
static PRBool
getSvrWrappingKey(PRInt32 symWrapMechIndex,
SSL3KEAType exchKeyType,
SSLWrappedSymWrappingKey *wswk,
cacheDesc * cache,
PRUint32 lockTime)
{
PRUint32 ndx = (exchKeyType * SSL_NUM_WRAP_MECHS) + symWrapMechIndex;
SSLWrappedSymWrappingKey * pwswk = cache->keyCacheData + ndx;
PRUint32 now = 0;
PRBool rv = PR_FALSE;
if (!cache->cacheMem) { /* cache is uninitialized */
PORT_SetError(SSL_ERROR_SERVER_CACHE_NOT_CONFIGURED);
return rv;
}
if (!lockTime) {
lockTime = now = LockSidCacheLock(cache->keyCacheLock, now);
if (!lockTime) {
return rv;
}
}
if (pwswk->exchKeyType == exchKeyType &&
pwswk->symWrapMechIndex == symWrapMechIndex &&
pwswk->wrappedSymKeyLen != 0) {
*wswk = *pwswk;
rv = PR_TRUE;
}
if (now) {
UnlockSidCacheLock(cache->keyCacheLock);
}
return rv;
}
PRBool
ssl_GetWrappingKey( PRInt32 symWrapMechIndex,
SSL3KEAType exchKeyType,
SSLWrappedSymWrappingKey *wswk)
{
PRBool rv;
PORT_Assert( (unsigned)exchKeyType < kt_kea_size);
PORT_Assert( (unsigned)symWrapMechIndex < SSL_NUM_WRAP_MECHS);
if ((unsigned)exchKeyType < kt_kea_size &&
(unsigned)symWrapMechIndex < SSL_NUM_WRAP_MECHS) {
rv = getSvrWrappingKey(symWrapMechIndex, exchKeyType, wswk,
&globalCache, 0);
} else {
rv = PR_FALSE;
}
return rv;
}
/* Wrap and cache a session ticket key. */
static PRBool
WrapTicketKey(SECKEYPublicKey *svrPubKey, PK11SymKey *symKey,
const char *keyName, encKeyCacheEntry* cacheEntry)
{
SECItem wrappedKey = {siBuffer, NULL, 0};
wrappedKey.len = SECKEY_PublicKeyStrength(svrPubKey);
PORT_Assert(wrappedKey.len <= sizeof(cacheEntry->bytes));
if (wrappedKey.len > sizeof(cacheEntry->bytes))
return PR_FALSE;
wrappedKey.data = cacheEntry->bytes;
if (PK11_PubWrapSymKey(CKM_RSA_PKCS, svrPubKey, symKey, &wrappedKey)
!= SECSuccess) {
SSL_DBG(("%d: SSL[%s]: Unable to wrap session ticket %s.",
SSL_GETPID(), "unknown", keyName));
return PR_FALSE;
}
cacheEntry->length = wrappedKey.len;
return PR_TRUE;
}
static PRBool
GenerateAndWrapTicketKeys(SECKEYPublicKey *svrPubKey, void *pwArg,
unsigned char *keyName, PK11SymKey **aesKey,
PK11SymKey **macKey)
{
PK11SlotInfo *slot;
CK_MECHANISM_TYPE mechanismArray[2];
PK11SymKey *aesKeyTmp = NULL;
PK11SymKey *macKeyTmp = NULL;
cacheDesc *cache = &globalCache;
if (PK11_GenerateRandom(cache->ticketKeyNameSuffix,
SESS_TICKET_KEY_VAR_NAME_LEN) != SECSuccess) {
SSL_DBG(("%d: SSL[%s]: Unable to generate random key name bytes.",
SSL_GETPID(), "unknown"));
goto loser;
}
mechanismArray[0] = CKM_AES_CBC;
mechanismArray[1] = CKM_SHA256_HMAC;
slot = PK11_GetBestSlotMultiple(mechanismArray, 2, pwArg);
if (slot) {
aesKeyTmp = PK11_KeyGen(slot, mechanismArray[0], NULL, 32, pwArg);
macKeyTmp = PK11_KeyGen(slot, mechanismArray[1], NULL, SHA256_LENGTH,
pwArg);
PK11_FreeSlot(slot);
}
if (aesKeyTmp == NULL || macKeyTmp == NULL) {
SSL_DBG(("%d: SSL[%s]: Unable to generate session ticket keys.",
SSL_GETPID(), "unknown"));
goto loser;
}
/* Export the keys to the shared cache in wrapped form. */
if (!WrapTicketKey(svrPubKey, aesKeyTmp, "enc key", cache->ticketEncKey))
goto loser;
if (!WrapTicketKey(svrPubKey, macKeyTmp, "mac key", cache->ticketMacKey))
goto loser;
PORT_Memcpy(keyName, cache->ticketKeyNameSuffix,
SESS_TICKET_KEY_VAR_NAME_LEN);
*aesKey = aesKeyTmp;
*macKey = macKeyTmp;
return PR_TRUE;
loser:
if (aesKeyTmp)
PK11_FreeSymKey(aesKeyTmp);
if (macKeyTmp)
PK11_FreeSymKey(macKeyTmp);
return PR_FALSE;
}
static PRBool
UnwrapCachedTicketKeys(SECKEYPrivateKey *svrPrivKey, unsigned char *keyName,
PK11SymKey **aesKey, PK11SymKey **macKey)
{
SECItem wrappedKey = {siBuffer, NULL, 0};
PK11SymKey *aesKeyTmp = NULL;
PK11SymKey *macKeyTmp = NULL;
cacheDesc *cache = &globalCache;
wrappedKey.data = cache->ticketEncKey->bytes;
wrappedKey.len = cache->ticketEncKey->length;
PORT_Assert(wrappedKey.len <= sizeof(cache->ticketEncKey->bytes));
aesKeyTmp = PK11_PubUnwrapSymKey(svrPrivKey, &wrappedKey,
CKM_AES_CBC, CKA_DECRYPT, 0);
wrappedKey.data = cache->ticketMacKey->bytes;
wrappedKey.len = cache->ticketMacKey->length;
PORT_Assert(wrappedKey.len <= sizeof(cache->ticketMacKey->bytes));
macKeyTmp = PK11_PubUnwrapSymKey(svrPrivKey, &wrappedKey,
CKM_SHA256_HMAC, CKA_SIGN, 0);
if (aesKeyTmp == NULL || macKeyTmp == NULL) {
SSL_DBG(("%d: SSL[%s]: Unable to unwrap session ticket keys.",
SSL_GETPID(), "unknown"));
goto loser;
}
SSL_DBG(("%d: SSL[%s]: Successfully unwrapped session ticket keys.",
SSL_GETPID(), "unknown"));
PORT_Memcpy(keyName, cache->ticketKeyNameSuffix,
SESS_TICKET_KEY_VAR_NAME_LEN);
*aesKey = aesKeyTmp;
*macKey = macKeyTmp;
return PR_TRUE;
loser:
if (aesKeyTmp)
PK11_FreeSymKey(aesKeyTmp);
if (macKeyTmp)
PK11_FreeSymKey(macKeyTmp);
return PR_FALSE;
}
PRBool
ssl_GetSessionTicketKeysPKCS11(SECKEYPrivateKey *svrPrivKey,
SECKEYPublicKey *svrPubKey, void *pwArg,
unsigned char *keyName, PK11SymKey **aesKey,
PK11SymKey **macKey)
{
PRUint32 now = 0;
PRBool rv = PR_FALSE;
PRBool keysGenerated = PR_FALSE;
cacheDesc *cache = &globalCache;
now = LockSidCacheLock(cache->keyCacheLock, now);
if (!now)
return rv;
if (!*(cache->ticketKeysValid)) {
/* Keys do not exist, create them. */
if (!GenerateAndWrapTicketKeys(svrPubKey, pwArg, keyName,
aesKey, macKey))
goto loser;
keysGenerated = PR_TRUE;
*(cache->ticketKeysValid) = 1;
}
rv = PR_TRUE;
loser:
UnlockSidCacheLock(cache->keyCacheLock);
if (rv && !keysGenerated)
rv = UnwrapCachedTicketKeys(svrPrivKey, keyName, aesKey, macKey);
return rv;
}
PRBool
ssl_GetSessionTicketKeys(unsigned char *keyName, unsigned char *encKey,
unsigned char *macKey)
{
PRBool rv = PR_FALSE;
PRUint32 now = 0;
cacheDesc *cache = &globalCache;
/* Grab lock. */
now = LockSidCacheLock(cache->keyCacheLock, now);
if (!now)
return rv;
if (!*(cache->ticketKeysValid)) {
if (PK11_GenerateRandom(cache->ticketKeyNameSuffix,
SESS_TICKET_KEY_VAR_NAME_LEN) != SECSuccess)
goto loser;
if (PK11_GenerateRandom(cache->ticketEncKey->bytes, 32) != SECSuccess)
goto loser;
if (PK11_GenerateRandom(cache->ticketMacKey->bytes,
SHA256_LENGTH) != SECSuccess)
goto loser;
*(cache->ticketKeysValid) = 1;
}
rv = PR_TRUE;
loser:
UnlockSidCacheLock(cache->keyCacheLock);
if (rv) {
PORT_Memcpy(keyName, cache->ticketKeyNameSuffix,
SESS_TICKET_KEY_VAR_NAME_LEN);
PORT_Memcpy(encKey, cache->ticketEncKey->bytes, 32);
PORT_Memcpy(macKey, cache->ticketMacKey->bytes, SHA256_LENGTH);
}
return rv;
}
/* The caller passes in the new value it wants
* to set. This code tests the wrapped sym key entry in the shared memory.
* If it is uninitialized, this function writes the caller's value into
* the disk entry, and returns false.
* Otherwise, it overwrites the caller's wswk with the value obtained from
* the disk, and returns PR_TRUE.
* This is all done while holding the locks/mutexes necessary to make
* the operation atomic.
*/
PRBool
ssl_SetWrappingKey(SSLWrappedSymWrappingKey *wswk)
{
cacheDesc * cache = &globalCache;
PRBool rv = PR_FALSE;
SSL3KEAType exchKeyType = wswk->exchKeyType;
/* type of keys used to wrap SymWrapKey*/
PRInt32 symWrapMechIndex = wswk->symWrapMechIndex;
PRUint32 ndx;
PRUint32 now = 0;
SSLWrappedSymWrappingKey myWswk;
if (!cache->cacheMem) { /* cache is uninitialized */
PORT_SetError(SSL_ERROR_SERVER_CACHE_NOT_CONFIGURED);
return 0;
}
PORT_Assert( (unsigned)exchKeyType < kt_kea_size);
if ((unsigned)exchKeyType >= kt_kea_size)
return 0;
PORT_Assert( (unsigned)symWrapMechIndex < SSL_NUM_WRAP_MECHS);
if ((unsigned)symWrapMechIndex >= SSL_NUM_WRAP_MECHS)
return 0;
ndx = (exchKeyType * SSL_NUM_WRAP_MECHS) + symWrapMechIndex;
PORT_Memset(&myWswk, 0, sizeof myWswk); /* eliminate UMRs. */
now = LockSidCacheLock(cache->keyCacheLock, now);
if (now) {
rv = getSvrWrappingKey(wswk->symWrapMechIndex, wswk->exchKeyType,
&myWswk, cache, now);
if (rv) {
/* we found it on disk, copy it out to the caller. */
PORT_Memcpy(wswk, &myWswk, sizeof *wswk);
} else {
/* Wasn't on disk, and we're still holding the lock, so write it. */
cache->keyCacheData[ndx] = *wswk;
}
UnlockSidCacheLock(cache->keyCacheLock);
}
return rv;
}
#else /* MAC version or other platform */
#include "seccomon.h"
#include "cert.h"
#include "ssl.h"
#include "sslimpl.h"
SECStatus
SSL_ConfigServerSessionIDCache( int maxCacheEntries,
PRUint32 ssl2_timeout,
PRUint32 ssl3_timeout,
const char * directory)
{
PR_ASSERT(!"SSL servers are not supported on this platform. (SSL_ConfigServerSessionIDCache)");
return SECFailure;
}
SECStatus
SSL_ConfigMPServerSIDCache( int maxCacheEntries,
PRUint32 ssl2_timeout,
PRUint32 ssl3_timeout,
const char * directory)
{
PR_ASSERT(!"SSL servers are not supported on this platform. (SSL_ConfigMPServerSIDCache)");
return SECFailure;
}
SECStatus
SSL_InheritMPServerSIDCache(const char * envString)
{
PR_ASSERT(!"SSL servers are not supported on this platform. (SSL_InheritMPServerSIDCache)");
return SECFailure;
}
PRBool
ssl_GetWrappingKey( PRInt32 symWrapMechIndex,
SSL3KEAType exchKeyType,
SSLWrappedSymWrappingKey *wswk)
{
PRBool rv = PR_FALSE;
PR_ASSERT(!"SSL servers are not supported on this platform. (ssl_GetWrappingKey)");
return rv;
}
/* This is a kind of test-and-set. The caller passes in the new value it wants
* to set. This code tests the wrapped sym key entry in the shared memory.
* If it is uninitialized, this function writes the caller's value into
* the disk entry, and returns false.
* Otherwise, it overwrites the caller's wswk with the value obtained from
* the disk, and returns PR_TRUE.
* This is all done while holding the locks/mutexes necessary to make
* the operation atomic.
*/
PRBool
ssl_SetWrappingKey(SSLWrappedSymWrappingKey *wswk)
{
PRBool rv = PR_FALSE;
PR_ASSERT(!"SSL servers are not supported on this platform. (ssl_SetWrappingKey)");
return rv;
}
PRUint32
SSL_GetMaxServerCacheLocks(void)
{
PR_ASSERT(!"SSL servers are not supported on this platform. (SSL_GetMaxServerCacheLocks)");
return -1;
}
SECStatus
SSL_SetMaxServerCacheLocks(PRUint32 maxLocks)
{
PR_ASSERT(!"SSL servers are not supported on this platform. (SSL_SetMaxServerCacheLocks)");
return SECFailure;
}
#endif /* XP_UNIX || XP_WIN32 */