Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/security/nss/lib/ssl/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 72 kB image not shown  

Quelle  sslsnce.c   Sprache: C

 
/* -*- 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)

#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)

#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);
}

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());
#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 now = 0;
    PRBool rv = SECFailure;

    if (!cache->cacheMem) { /* cache is uninitialized */
        PORT_SetError(SSL_ERROR_SERVER_CACHE_NOT_CONFIGURED);
        return SECFailure;
    }

    PRUint32 ndx = (wrapKeyIndex * SSL_NUM_WRAP_MECHS) + symWrapMechIndex;
    SSLWrappedSymWrappingKey *pwswk = cache->keyCacheData + ndx;

    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,
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=91 G=91

¤ Dauer der Verarbeitung: 0.46 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.