From 3ecd967b2a9e23403935e2bc932597f7e03e7f24 Mon Sep 17 00:00:00 2001 From: Kai Engert Date: Thu, 28 Feb 2013 12:44:50 +0100 Subject: Bug 845556, reorganize NSS directory layout, moving files, very large changeset! r=wtc --- lib/ssl/sslsnce.c | 2214 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2214 insertions(+) create mode 100644 lib/ssl/sslsnce.c (limited to 'lib/ssl/sslsnce.c') diff --git a/lib/ssl/sslsnce.c b/lib/ssl/sslsnce.c new file mode 100644 index 000000000..46e944cb1 --- /dev/null +++ b/lib/ssl/sslsnce.c @@ -0,0 +1,2214 @@ +/* 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/. */ +/* $Id$ */ + +/* 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; + * 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" +#ifdef NO_PKCS11_BYPASS +#include "blapit.h" +#include "sechash.h" +#else +#include "blapi.h" +#endif + +#include + +#if defined(XP_UNIX) || defined(XP_BEOS) + +#include +#include +#include +#include +#include +#include "unix_err.h" + +#else + +#ifdef XP_WIN32 +#include +#include "win32err.h" +#endif + +#endif +#include + +#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; /* SSLCompressionMethod */ + +/* 52 */ ssl3SidKeys keys; /* keys, wrapped as needed. */ + +/* 4 */ PRUint32 masterWrapMech; +/* 4 */ SSL3KEAType exchKeyType; +/* 4 */ PRInt32 certIndex; +/* 4 */ PRInt32 srvNameIndex; +/* 32 */ PRUint8 srvNameHash[SHA256_LENGTH]; /* SHA256 name hash */ +/*104 */} ssl3; +/* force sizeof(sidCacheEntry) to be a multiple of cache line size */ + struct { +/*120 */ PRUint8 filler[120]; /* 72+120==192, 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 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; + sidCacheLock * srvNameCacheLock; + sidCacheSet * sidCacheSets; + sidCacheEntry * sidCacheData; + certCacheEntry * certCacheData; + SSLWrappedSymWrappingKey * keyCacheData; + uint8 * 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_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(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 */ + + +/************************************************************************/ + +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; + +} + +/* 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); +#ifdef NO_PKCS11_BYPASS + HASH_HashBuf(HASH_AlgSHA256, snce.nameHash, name->data, name->len); +#else + SHA256_HashBuf(snce.nameHash, (unsigned char*)name->data, + name->len); +#endif + /* 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->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; + to->u.ssl3.srvNameIndex = -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, + srvNameCacheEntry *psnce, + CERTCertDBHandle * dbHandle) +{ + sslSessionID *to; + uint16 version = from->version; + + to = PORT_ZNew(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 = (SSLCompressionMethod)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; + 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); + + /* 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); + } else { + 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. + */ +static sslSessionID * +ServerSessionIDLookup(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 (psce->version >= SSL_LIBRARY_VERSION_3_0) { + 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_Assert(!("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_Assert(!("Didn't get name Cache Lock!")); + psce = 0; + psnce = 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, psnce ? &snce : 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) { + SECItem *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. +*/ +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 + +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 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); + + 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 = 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->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 (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->srvNameCacheLock) += 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; + *(ptrdiff_t *)(&cache->srvNameCacheData) += ptr; + + /* initialize the locks */ + init_time = ssl_Time(); + 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); + 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; +} + +static SECStatus +ssl_ConfigServerSessionIDCacheInstanceWithOpt(cacheDesc *cache, + PRUint32 ssl2_timeout, + PRUint32 ssl3_timeout, + const char * directory, + PRBool shared, + int maxCacheEntries, + int maxCertCacheEntries, + int maxSrvNameCacheEntries) +{ + SECStatus rv; + + PORT_Assert(sizeof(sidCacheEntry) == 192); + PORT_Assert(sizeof(certCacheEntry) == 4096); + PORT_Assert(sizeof(srvNameCacheEntry) == 1072); + + rv = ssl_Init(); + if (rv != SECSuccess) { + return rv; + } + + myPid = SSL_GETPID(); + if (!directory) { + directory = DEFAULT_CACHE_DIRECTORY; + } + rv = InitCache(cache, maxCacheEntries, maxCertCacheEntries, + maxSrvNameCacheEntries, 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_ConfigServerSessionIDCacheInstance( cacheDesc *cache, + int maxCacheEntries, + PRUint32 ssl2_timeout, + PRUint32 ssl3_timeout, + const char * directory, PRBool shared) +{ + return ssl_ConfigServerSessionIDCacheInstanceWithOpt(cache, + ssl2_timeout, + 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 ssl2_timeout, + 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, + ssl2_timeout, 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))) { + 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; +} + +/* 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(ssl2_timeout, + 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, + ssl2_timeout, ssl3_timeout, directory, PR_FALSE, + maxCacheEntries, maxCertCacheEntries, maxSrvNameCacheEntries); + } else { + return ssl_ConfigMPServerSIDCacheWithOpt(ssl2_timeout, 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; + ptrdiff_t ptr; + inheritance inherit; + cacheDesc my; +#ifdef WINNT + sidCacheLock* newLocks; + int locks_initialized = 0; + int locks_to_initialize = 0; +#endif + SECStatus status = ssl_Init(); + + 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; + 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->srvNameCacheLock) += 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; + *(ptrdiff_t *)(&cache->srvNameCacheData) += 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 + 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); + 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 +GenerateTicketKeys(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; + uint8 ticketKeyNameSuffixLocal[SESS_TICKET_KEY_VAR_NAME_LEN]; + uint8 *ticketKeyNameSuffix; + + if (!cache->cacheMem) { + /* cache is not initalized. Use stack buffer */ + ticketKeyNameSuffix = ticketKeyNameSuffixLocal; + } else { + ticketKeyNameSuffix = cache->ticketKeyNameSuffix; + } + + if (PK11_GenerateRandom(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, + AES_256_KEY_LENGTH, 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; + } + PORT_Memcpy(keyName, 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 +GenerateAndWrapTicketKeys(SECKEYPublicKey *svrPubKey, void *pwArg, + unsigned char *keyName, PK11SymKey **aesKey, + PK11SymKey **macKey) +{ + PK11SymKey *aesKeyTmp = NULL; + PK11SymKey *macKeyTmp = NULL; + cacheDesc *cache = &globalCache; + + if (!GenerateTicketKeys(pwArg, keyName, &aesKeyTmp, &macKeyTmp)) { + goto loser; + } + + if (cache->cacheMem) { + /* 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; + } + *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; + + if (!cache->cacheMem) { + /* cache is uninitialized. Generate keys and return them + * without caching. */ + return GenerateTicketKeys(pwArg, keyName, aesKey, macKey); + } + + 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; + uint8 ticketMacKey[AES_256_KEY_LENGTH], ticketEncKey[SHA256_LENGTH]; + uint8 ticketKeyNameSuffixLocal[SESS_TICKET_KEY_VAR_NAME_LEN]; + uint8 *ticketMacKeyPtr, *ticketEncKeyPtr, *ticketKeyNameSuffix; + PRBool cacheIsEnabled = PR_TRUE; + + if (!cache->cacheMem) { /* cache is uninitialized */ + cacheIsEnabled = PR_FALSE; + ticketKeyNameSuffix = ticketKeyNameSuffixLocal; + ticketEncKeyPtr = ticketEncKey; + ticketMacKeyPtr = ticketMacKey; + } else { + /* these values have constant memory locations in the cache. + * Ok to reference them without holding the lock. */ + ticketKeyNameSuffix = cache->ticketKeyNameSuffix; + ticketEncKeyPtr = cache->ticketEncKey->bytes; + ticketMacKeyPtr = cache->ticketMacKey->bytes; + } + + if (cacheIsEnabled) { + /* Grab lock if initialized. */ + now = LockSidCacheLock(cache->keyCacheLock, now); + if (!now) + return rv; + } + /* Going to regenerate keys on every call if cache was not + * initialized. */ + if (!cacheIsEnabled || !*(cache->ticketKeysValid)) { + if (PK11_GenerateRandom(ticketKeyNameSuffix, + SESS_TICKET_KEY_VAR_NAME_LEN) != SECSuccess) + goto loser; + if (PK11_GenerateRandom(ticketEncKeyPtr, + AES_256_KEY_LENGTH) != SECSuccess) + goto loser; + if (PK11_GenerateRandom(ticketMacKeyPtr, + SHA256_LENGTH) != SECSuccess) + goto loser; + if (cacheIsEnabled) { + *(cache->ticketKeysValid) = 1; + } + } + + rv = PR_TRUE; + + loser: + if (cacheIsEnabled) { + UnlockSidCacheLock(cache->keyCacheLock); + } + if (rv) { + PORT_Memcpy(keyName, ticketKeyNameSuffix, + SESS_TICKET_KEY_VAR_NAME_LEN); + PORT_Memcpy(encKey, ticketEncKeyPtr, AES_256_KEY_LENGTH); + PORT_Memcpy(macKey, ticketMacKeyPtr, 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 */ -- cgit v1.2.1