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