root/net/third_party/nss/ssl/sslsnce.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. LockSidCacheLock
  2. UnlockSidCacheLock
  3. LockSet
  4. UnlockSet
  5. CacheCert
  6. Get32BitNameHash
  7. CacheSrvName
  8. ConvertFromSID
  9. ConvertToSID
  10. SIDindex
  11. FindSID
  12. ServerSessionIDLookup
  13. ServerSessionIDCache
  14. ServerSessionIDUncache
  15. gettid
  16. CloseCache
  17. InitCache
  18. SSL_GetMaxServerCacheLocks
  19. SSL_SetMaxServerCacheLocks
  20. ssl_ConfigServerSessionIDCacheInstanceWithOpt
  21. SSL_ConfigServerSessionIDCacheInstance
  22. SSL_ConfigServerSessionIDCache
  23. SSL_ShutdownServerSessionIDCacheInstance
  24. SSL_ShutdownServerSessionIDCache
  25. ssl_ConfigMPServerSIDCacheWithOpt
  26. SSL_ConfigMPServerSIDCache
  27. SSL_ConfigServerSessionIDCacheWithOpt
  28. SSL_InheritMPServerSIDCacheInstance
  29. SSL_InheritMPServerSIDCache
  30. LockPoller
  31. LaunchLockPoller
  32. StopLockPoller
  33. getSvrWrappingKey
  34. ssl_GetWrappingKey
  35. WrapTicketKey
  36. GenerateTicketKeys
  37. GenerateAndWrapTicketKeys
  38. UnwrapCachedTicketKeys
  39. ssl_GetSessionTicketKeysPKCS11
  40. ssl_GetSessionTicketKeys
  41. ssl_SetWrappingKey
  42. SSL_ConfigServerSessionIDCache
  43. SSL_ConfigMPServerSIDCache
  44. SSL_InheritMPServerSIDCache
  45. ssl_GetWrappingKey
  46. ssl_SetWrappingKey
  47. SSL_GetMaxServerCacheLocks
  48. SSL_SetMaxServerCacheLocks

/* 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[kt_kea_size][SSL_NUM_WRAP_MECHS];
 *     PRUint8                  keyNameSuffix[SESS_TICKET_KEY_VAR_NAME_LEN]
 *     encKeyCacheEntry         ticketEncKey; // Wrapped in non-bypass mode
 *     encKeyCacheEntry         ticketMacKey; // Wrapped in non-bypass mode
 *     PRBool                   ticketKeysValid;
 *     sidCacheLock             srvNameCacheLock;
 *     srvNameCacheEntry        srvNameData[ numSrvNameCacheEntries ];
 * } cacheMemCacheData;
 */
#include "seccomon.h"

#if defined(XP_UNIX) || defined(XP_WIN32) || defined (XP_OS2) || defined(XP_BEOS)

#include "cert.h"
#include "ssl.h"
#include "sslimpl.h"
#include "sslproto.h"
#include "pk11func.h"
#include "base64.h"
#include "keyhi.h"
#ifdef NO_PKCS11_BYPASS
#include "blapit.h"
#include "sechash.h"
#else
#include "blapi.h"
#endif

#include <stdio.h>

#if defined(XP_UNIX) || defined(XP_BEOS)

#include <syslog.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include "unix_err.h"

#else

#ifdef XP_WIN32
#include <wtypes.h>
#include "win32err.h"
#endif

#endif 
#include <sys/types.h>

#define SET_ERROR_CODE /* reminder */

#include "nspr.h"
#include "sslmutex.h"

/*
** Format of a cache entry in the shared memory.
*/ 
struct sidCacheEntryStr {
/* 16 */    PRIPv6Addr  addr;   /* client's IP address */
/*  4 */    PRUint32    creationTime;
/*  4 */    PRUint32    lastAccessTime; 
/*  4 */    PRUint32    expirationTime;
/*  2 */    PRUint16    version;
/*  1 */    PRUint8     valid;
/*  1 */    PRUint8     sessionIDLength;
/* 32 */    PRUint8     sessionID[SSL3_SESSIONID_BYTES];
/*  2 */    PRUint16    authAlgorithm;
/*  2 */    PRUint16    authKeyBits;
/*  2 */    PRUint16    keaType;
/*  2 */    PRUint16    keaKeyBits;
/* 72  - common header total */

    union {
        struct {
/* 64 */    PRUint8     masterKey[SSL_MAX_MASTER_KEY_BYTES];
/* 32 */    PRUint8     cipherArg[SSL_MAX_CYPHER_ARG_BYTES];

/*  1 */    PRUint8     cipherType;
/*  1 */    PRUint8     masterKeyLen;
/*  1 */    PRUint8     keyBits;
/*  1 */    PRUint8     secretKeyBits;
/*  1 */    PRUint8     cipherArgLen;
/*101 */} ssl2;

        struct {
/*  2 */    ssl3CipherSuite  cipherSuite;
/*  2 */    PRUint16    compression;    /* SSLCompressionMethod */

/* 52 */    ssl3SidKeys keys;   /* keys, wrapped as needed. */

/*  4 */    PRUint32    masterWrapMech; 
/*  4 */    SSL3KEAType exchKeyType;
/*  4 */    PRInt32     certIndex;
/*  4 */    PRInt32     srvNameIndex;
/* 32 */    PRUint8     srvNameHash[SHA256_LENGTH]; /* SHA256 name hash */
/*104 */} ssl3;
/* force sizeof(sidCacheEntry) to be a multiple of cache line size */
        struct {
/*120 */    PRUint8     filler[120]; /* 72+120==192, a multiple of 16 */
        } forceSize;
    } u;
};
typedef struct sidCacheEntryStr sidCacheEntry;

/* The length of this struct is supposed to be a power of 2, e.g. 4KB */
struct certCacheEntryStr {
    PRUint16    certLength;                             /*    2 */
    PRUint16    sessionIDLength;                        /*    2 */
    PRUint8     sessionID[SSL3_SESSIONID_BYTES];        /*   32 */
    PRUint8     cert[SSL_MAX_CACHED_CERT_LEN];          /* 4060 */
};                                              /* total   4096 */
typedef struct certCacheEntryStr certCacheEntry;

struct sidCacheLockStr {
    PRUint32    timeStamp;
    sslMutex    mutex;
    sslPID      pid;
};
typedef struct sidCacheLockStr sidCacheLock;

struct sidCacheSetStr {
    PRIntn      next;
};
typedef struct sidCacheSetStr sidCacheSet;

struct encKeyCacheEntryStr {
    PRUint8     bytes[512];
    PRInt32     length;
};
typedef struct encKeyCacheEntryStr encKeyCacheEntry;

#define SSL_MAX_DNS_HOST_NAME  1024

struct srvNameCacheEntryStr {
    PRUint16    type;                                   /*    2 */
    PRUint16    nameLen;                                /*    2 */
    PRUint8     name[SSL_MAX_DNS_HOST_NAME + 12];       /* 1034 */
    PRUint8     nameHash[SHA256_LENGTH];                /*   32 */
                                                        /* 1072 */
};
typedef struct srvNameCacheEntryStr srvNameCacheEntry;


struct cacheDescStr {

    PRUint32            cacheMemSize;

    PRUint32            numSIDCacheLocks;
    PRUint32            numSIDCacheSets;
    PRUint32            numSIDCacheSetsPerLock;

    PRUint32            numSIDCacheEntries; 
    PRUint32            sidCacheSize;

    PRUint32            numCertCacheEntries;
    PRUint32            certCacheSize;

    PRUint32            numKeyCacheEntries;
    PRUint32            keyCacheSize;

    PRUint32            numSrvNameCacheEntries;
    PRUint32            srvNameCacheSize;

    PRUint32            ssl2Timeout;
    PRUint32            ssl3Timeout;

    PRUint32            numSIDCacheLocksInitialized;

    /* These values are volatile, and are accessed through sharedCache-> */
    PRUint32            nextCertCacheEntry;     /* certCacheLock protects */
    PRBool              stopPolling;
    PRBool              everInherited;

    /* The private copies of these values are pointers into shared mem */
    /* The copies of these values in shared memory are merely offsets */
    sidCacheLock    *          sidCacheLocks;
    sidCacheLock    *          keyCacheLock;
    sidCacheLock    *          certCacheLock;
    sidCacheLock    *          srvNameCacheLock;
    sidCacheSet     *          sidCacheSets;
    sidCacheEntry   *          sidCacheData;
    certCacheEntry  *          certCacheData;
    SSLWrappedSymWrappingKey * keyCacheData;
    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_SSL2_TIMEOUT        100   /* seconds */
#define MAX_SSL2_TIMEOUT        100   /* seconds */
#define MIN_SSL2_TIMEOUT          5   /* seconds */

#define DEF_SSL3_TIMEOUT      86400L  /* 24 hours */
#define MAX_SSL3_TIMEOUT      86400L  /* 24 hours */
#define MIN_SSL3_TIMEOUT          5   /* seconds  */

#if defined(AIX) || defined(LINUX) || defined(NETBSD) || defined(OPENBSD)
#define MAX_SID_CACHE_LOCKS 8   /* two FDs per lock */
#elif defined(OSF1)
#define MAX_SID_CACHE_LOCKS 16  /* one FD per lock */
#else
#define MAX_SID_CACHE_LOCKS 256
#endif

#define SID_HOWMANY(val, size) (((val) + ((size) - 1)) / (size))
#define SID_ROUNDUP(val, size) ((size) * SID_HOWMANY((val), (size)))


static sslPID myPid;
static PRUint32  ssl_max_sid_cache_locks = MAX_SID_CACHE_LOCKS;

/* forward static function declarations */
static PRUint32 SIDindex(cacheDesc *cache, const PRIPv6Addr *addr, PRUint8 *s, 
                         unsigned nl);
static SECStatus LaunchLockPoller(cacheDesc *cache);
static SECStatus StopLockPoller(cacheDesc *cache);


struct inheritanceStr {
    PRUint32 cacheMemSize;
    PRUint32 fmStrLen;
};

typedef struct inheritanceStr inheritance;

#if defined(_WIN32) || defined(XP_OS2)

#define DEFAULT_CACHE_DIRECTORY "\\temp"

#endif /* _win32 */

#if defined(XP_UNIX) || defined(XP_BEOS)

#define DEFAULT_CACHE_DIRECTORY "/tmp"

#endif /* XP_UNIX || XP_BEOS */


/************************************************************************/

static PRUint32
LockSidCacheLock(sidCacheLock *lock, PRUint32 now)
{
    SECStatus      rv      = sslMutex_Lock(&lock->mutex);
    if (rv != SECSuccess)
        return 0;
    if (!now)
        now  = ssl_Time();
    lock->timeStamp = now;
    lock->pid       = myPid;
    return now;
}

static SECStatus
UnlockSidCacheLock(sidCacheLock *lock)
{
    SECStatus      rv;

    lock->pid = 0;
    rv        = sslMutex_Unlock(&lock->mutex);
    return rv;
}

/* returns the value of ssl_Time on success, zero on failure. */
static PRUint32
LockSet(cacheDesc *cache, PRUint32 set, PRUint32 now)
{
    PRUint32       lockNum = set % cache->numSIDCacheLocks;
    sidCacheLock * lock    = cache->sidCacheLocks + lockNum;

    return LockSidCacheLock(lock, now);
}

static SECStatus
UnlockSet(cacheDesc *cache, PRUint32 set)
{
    PRUint32       lockNum = set % cache->numSIDCacheLocks;
    sidCacheLock * lock    = cache->sidCacheLocks + lockNum;

    return UnlockSidCacheLock(lock);
}

/************************************************************************/


/* Put a certificate in the cache.  Update the cert index in the sce.
*/
static PRUint32
CacheCert(cacheDesc * cache, CERTCertificate *cert, sidCacheEntry *sce)
{
    PRUint32        now;
    certCacheEntry  cce;

    if ((cert->derCert.len > SSL_MAX_CACHED_CERT_LEN) ||
        (cert->derCert.len <= 0) ||
        (cert->derCert.data == NULL)) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return 0;
    }

    cce.sessionIDLength = sce->sessionIDLength;
    PORT_Memcpy(cce.sessionID, sce->sessionID, cce.sessionIDLength);

    cce.certLength = cert->derCert.len;
    PORT_Memcpy(cce.cert, cert->derCert.data, cce.certLength);

    /* get lock on cert cache */
    now = LockSidCacheLock(cache->certCacheLock, 0);
    if (now) {

        /* Find where to place the next cert cache entry. */
        cacheDesc * sharedCache = cache->sharedCache;
        PRUint32    ndx         = sharedCache->nextCertCacheEntry;

        /* write the entry */
        cache->certCacheData[ndx] = cce;

        /* remember where we put it. */
        sce->u.ssl3.certIndex = ndx;

        /* update the "next" cache entry index */
        sharedCache->nextCertCacheEntry = 
                                        (ndx + 1) % cache->numCertCacheEntries;

        UnlockSidCacheLock(cache->certCacheLock);
    }
    return now;

}

/* Server configuration hash tables need to account the SECITEM.type
 * field as well. These functions accomplish that. */
static PLHashNumber
Get32BitNameHash(const SECItem *name)
{
    PLHashNumber rv = SECITEM_Hash(name);
    
    PRUint8 *rvc = (PRUint8 *)&rv;
    rvc[ name->len % sizeof(rv) ] ^= name->type;

    return rv;
}

/* Put a name in the cache.  Update the cert index in the sce.
*/
static PRUint32
CacheSrvName(cacheDesc * cache, SECItem *name, sidCacheEntry *sce)
{
    PRUint32           now;
    PRUint32           ndx;
    srvNameCacheEntry  snce;

    if (!name || name->len <= 0 ||
        name->len > SSL_MAX_DNS_HOST_NAME) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return 0;
    }

    snce.type = name->type;
    snce.nameLen = name->len;
    PORT_Memcpy(snce.name, name->data, snce.nameLen);
#ifdef NO_PKCS11_BYPASS
    HASH_HashBuf(HASH_AlgSHA256, snce.nameHash, name->data, name->len);
#else
    SHA256_HashBuf(snce.nameHash, (unsigned char*)name->data,
                   name->len);
#endif
    /* get index of the next name */
    ndx = Get32BitNameHash(name);
    /* get lock on cert cache */
    now = LockSidCacheLock(cache->srvNameCacheLock, 0);
    if (now) {
        if (cache->numSrvNameCacheEntries > 0) {
            /* Fit the index into array */
            ndx %= cache->numSrvNameCacheEntries;
            /* write the entry */
            cache->srvNameCacheData[ndx] = snce;
            /* remember where we put it. */
            sce->u.ssl3.srvNameIndex = ndx;
            /* Copy hash into sid hash */
            PORT_Memcpy(sce->u.ssl3.srvNameHash, snce.nameHash, SHA256_LENGTH);
        }
        UnlockSidCacheLock(cache->srvNameCacheLock);
    }
    return now;
}

/*
** Convert local SID to shared memory one
*/
static void 
ConvertFromSID(sidCacheEntry *to, sslSessionID *from)
{
    to->valid   = 1;
    to->version = from->version;
    to->addr    = from->addr;
    to->creationTime    = from->creationTime;
    to->lastAccessTime  = from->lastAccessTime;
    to->expirationTime  = from->expirationTime;
    to->authAlgorithm   = from->authAlgorithm;
    to->authKeyBits     = from->authKeyBits;
    to->keaType         = from->keaType;
    to->keaKeyBits      = from->keaKeyBits;

    if (from->version < SSL_LIBRARY_VERSION_3_0) {
        if ((from->u.ssl2.masterKey.len > SSL_MAX_MASTER_KEY_BYTES) ||
            (from->u.ssl2.cipherArg.len > SSL_MAX_CYPHER_ARG_BYTES)) {
            SSL_DBG(("%d: SSL: masterKeyLen=%d cipherArgLen=%d",
                     myPid, from->u.ssl2.masterKey.len,
                     from->u.ssl2.cipherArg.len));
            to->valid = 0;
            return;
        }

        to->u.ssl2.cipherType    = from->u.ssl2.cipherType;
        to->u.ssl2.masterKeyLen  = from->u.ssl2.masterKey.len;
        to->u.ssl2.cipherArgLen  = from->u.ssl2.cipherArg.len;
        to->u.ssl2.keyBits       = from->u.ssl2.keyBits;
        to->u.ssl2.secretKeyBits = from->u.ssl2.secretKeyBits;
        to->sessionIDLength      = SSL2_SESSIONID_BYTES;
        PORT_Memcpy(to->sessionID, from->u.ssl2.sessionID, SSL2_SESSIONID_BYTES);
        PORT_Memcpy(to->u.ssl2.masterKey, from->u.ssl2.masterKey.data,
                  from->u.ssl2.masterKey.len);
        PORT_Memcpy(to->u.ssl2.cipherArg, from->u.ssl2.cipherArg.data,
                  from->u.ssl2.cipherArg.len);
#ifdef DEBUG
        PORT_Memset(to->u.ssl2.masterKey+from->u.ssl2.masterKey.len, 0,
                  sizeof(to->u.ssl2.masterKey) - from->u.ssl2.masterKey.len);
        PORT_Memset(to->u.ssl2.cipherArg+from->u.ssl2.cipherArg.len, 0,
                  sizeof(to->u.ssl2.cipherArg) - from->u.ssl2.cipherArg.len);
#endif
        SSL_TRC(8, ("%d: SSL: ConvertSID: masterKeyLen=%d cipherArgLen=%d "
                    "time=%d addr=0x%08x%08x%08x%08x cipherType=%d", myPid,
                    to->u.ssl2.masterKeyLen, to->u.ssl2.cipherArgLen,
                    to->creationTime, to->addr.pr_s6_addr32[0],
                    to->addr.pr_s6_addr32[1], to->addr.pr_s6_addr32[2],
                    to->addr.pr_s6_addr32[3], to->u.ssl2.cipherType));
    } else {
        /* This is an SSL v3 session */

        to->u.ssl3.cipherSuite      = from->u.ssl3.cipherSuite;
        to->u.ssl3.compression      = (PRUint16)from->u.ssl3.compression;
        to->u.ssl3.keys             = from->u.ssl3.keys;
        to->u.ssl3.masterWrapMech   = from->u.ssl3.masterWrapMech;
        to->u.ssl3.exchKeyType      = from->u.ssl3.exchKeyType;
        to->sessionIDLength         = from->u.ssl3.sessionIDLength;
        to->u.ssl3.certIndex        = -1;
        to->u.ssl3.srvNameIndex     = -1;

        PORT_Memcpy(to->sessionID, from->u.ssl3.sessionID,
                    to->sessionIDLength);

        SSL_TRC(8, ("%d: SSL3: ConvertSID: time=%d addr=0x%08x%08x%08x%08x "
                    "cipherSuite=%d",
                    myPid, to->creationTime, to->addr.pr_s6_addr32[0],
                    to->addr.pr_s6_addr32[1], to->addr.pr_s6_addr32[2],
                    to->addr.pr_s6_addr32[3], to->u.ssl3.cipherSuite));
    }
}

/*
** Convert shared memory cache-entry to local memory based one
** This is only called from ServerSessionIDLookup().
** Caller must hold cache lock when calling this.
*/
static sslSessionID *
ConvertToSID(sidCacheEntry *    from,
             certCacheEntry *   pcce,
             srvNameCacheEntry *psnce,
             CERTCertDBHandle * dbHandle)
{
    sslSessionID *to;
    PRUint16 version = from->version;

    to = PORT_ZNew(sslSessionID);
    if (!to) {
        return 0;
    }

    if (version < SSL_LIBRARY_VERSION_3_0) {
        /* This is an SSL v2 session */
        to->u.ssl2.masterKey.data =
            (unsigned char*) PORT_Alloc(from->u.ssl2.masterKeyLen);
        if (!to->u.ssl2.masterKey.data) {
            goto loser;
        }
        if (from->u.ssl2.cipherArgLen) {
            to->u.ssl2.cipherArg.data = 
                (unsigned char*)PORT_Alloc(from->u.ssl2.cipherArgLen);
            if (!to->u.ssl2.cipherArg.data) {
                goto loser;
            }
            PORT_Memcpy(to->u.ssl2.cipherArg.data, from->u.ssl2.cipherArg,
                        from->u.ssl2.cipherArgLen);
        }

        to->u.ssl2.cipherType    = from->u.ssl2.cipherType;
        to->u.ssl2.masterKey.len = from->u.ssl2.masterKeyLen;
        to->u.ssl2.cipherArg.len = from->u.ssl2.cipherArgLen;
        to->u.ssl2.keyBits       = from->u.ssl2.keyBits;
        to->u.ssl2.secretKeyBits = from->u.ssl2.secretKeyBits;
/*      to->sessionIDLength      = SSL2_SESSIONID_BYTES; */
        PORT_Memcpy(to->u.ssl2.sessionID, from->sessionID, SSL2_SESSIONID_BYTES);
        PORT_Memcpy(to->u.ssl2.masterKey.data, from->u.ssl2.masterKey,
                    from->u.ssl2.masterKeyLen);

        SSL_TRC(8, ("%d: SSL: ConvertToSID: masterKeyLen=%d cipherArgLen=%d "
                    "time=%d addr=0x%08x%08x%08x%08x cipherType=%d",
                    myPid, to->u.ssl2.masterKey.len,
                    to->u.ssl2.cipherArg.len, to->creationTime,
                    to->addr.pr_s6_addr32[0], to->addr.pr_s6_addr32[1],
                    to->addr.pr_s6_addr32[2], to->addr.pr_s6_addr32[3],
                    to->u.ssl2.cipherType));
    } else {
        /* This is an SSL v3 session */

        to->u.ssl3.sessionIDLength  = from->sessionIDLength;
        to->u.ssl3.cipherSuite      = from->u.ssl3.cipherSuite;
        to->u.ssl3.compression      = (SSLCompressionMethod)from->u.ssl3.compression;
        to->u.ssl3.keys             = from->u.ssl3.keys;
        to->u.ssl3.masterWrapMech   = from->u.ssl3.masterWrapMech;
        to->u.ssl3.exchKeyType      = from->u.ssl3.exchKeyType;
        if (from->u.ssl3.srvNameIndex != -1 && psnce) {
            SECItem name;
            SECStatus rv;
            name.type                   = psnce->type;
            name.len                    = psnce->nameLen;
            name.data                   = psnce->name;
            rv = SECITEM_CopyItem(NULL, &to->u.ssl3.srvName, &name);
            if (rv != SECSuccess) {
                goto loser;
            }
        }

        PORT_Memcpy(to->u.ssl3.sessionID, from->sessionID, from->sessionIDLength);

        /* the portions of the SID that are only restored on the client
         * are set to invalid values on the server.
         */
        to->u.ssl3.clientWriteKey   = NULL;
        to->u.ssl3.serverWriteKey   = NULL;

        to->urlSvrName              = NULL;

        to->u.ssl3.masterModuleID   = (SECMODModuleID)-1; /* invalid value */
        to->u.ssl3.masterSlotID     = (CK_SLOT_ID)-1;     /* invalid value */
        to->u.ssl3.masterWrapIndex  = 0;
        to->u.ssl3.masterWrapSeries = 0;
        to->u.ssl3.masterValid      = PR_FALSE;

        to->u.ssl3.clAuthModuleID   = (SECMODModuleID)-1; /* invalid value */
        to->u.ssl3.clAuthSlotID     = (CK_SLOT_ID)-1;     /* invalid value */
        to->u.ssl3.clAuthSeries     = 0;
        to->u.ssl3.clAuthValid      = PR_FALSE;

        if (from->u.ssl3.certIndex != -1 && pcce) {
            SECItem          derCert;

            derCert.len  = pcce->certLength;
            derCert.data = pcce->cert;

            to->peerCert = CERT_NewTempCertificate(dbHandle, &derCert, NULL,
                                                   PR_FALSE, PR_TRUE);
            if (to->peerCert == NULL)
                goto loser;
        }
    }

    to->version         = from->version;
    to->creationTime    = from->creationTime;
    to->lastAccessTime  = from->lastAccessTime;
    to->expirationTime  = from->expirationTime;
    to->cached          = in_server_cache;
    to->addr            = from->addr;
    to->references      = 1;
    to->authAlgorithm   = from->authAlgorithm;
    to->authKeyBits     = from->authKeyBits;
    to->keaType         = from->keaType;
    to->keaKeyBits      = from->keaKeyBits;
    
    return to;

  loser:
    if (to) {
        if (version < SSL_LIBRARY_VERSION_3_0) {
            if (to->u.ssl2.masterKey.data)
                PORT_Free(to->u.ssl2.masterKey.data);
            if (to->u.ssl2.cipherArg.data)
                PORT_Free(to->u.ssl2.cipherArg.data);
        } else {
            SECITEM_FreeItem(&to->u.ssl3.srvName, PR_FALSE);
        }
        PORT_Free(to);
    }
    return NULL;
}



/*
** Perform some mumbo jumbo on the ip-address and the session-id value to
** compute a hash value.
*/
static PRUint32 
SIDindex(cacheDesc *cache, const PRIPv6Addr *addr, PRUint8 *s, unsigned nl)
{
    PRUint32 rv;
    PRUint32 x[8];

    memset(x, 0, sizeof x);
    if (nl > sizeof x)
        nl = sizeof x;
    memcpy(x, s, nl);

    rv = (addr->pr_s6_addr32[0] ^ addr->pr_s6_addr32[1] ^
          addr->pr_s6_addr32[2] ^ addr->pr_s6_addr32[3] ^
          x[0] ^ x[1] ^ x[2] ^ x[3] ^ x[4] ^ x[5] ^ x[6] ^ x[7])
          % cache->numSIDCacheSets;
    return rv;
}



/*
** Look something up in the cache. This will invalidate old entries
** in the process. Caller has locked the cache set!
** Returns PR_TRUE if found a valid match.  PR_FALSE otherwise.
*/
static sidCacheEntry *
FindSID(cacheDesc *cache, PRUint32 setNum, PRUint32 now,
        const PRIPv6Addr *addr, unsigned char *sessionID,
        unsigned sessionIDLength)
{
    PRUint32      ndx   = cache->sidCacheSets[setNum].next;
    int           i;

    sidCacheEntry * set = cache->sidCacheData + 
                         (setNum * SID_CACHE_ENTRIES_PER_SET);

    for (i = SID_CACHE_ENTRIES_PER_SET; i > 0; --i) {
        sidCacheEntry * sce;

        ndx  = (ndx - 1) % SID_CACHE_ENTRIES_PER_SET;
        sce = set + ndx;

        if (!sce->valid)
            continue;

        if (now > sce->expirationTime) {
            /* SessionID has timed out. Invalidate the entry. */
            SSL_TRC(7, ("%d: timed out sid entry addr=%08x%08x%08x%08x now=%x "
                        "time+=%x",
                        myPid, sce->addr.pr_s6_addr32[0],
                        sce->addr.pr_s6_addr32[1], sce->addr.pr_s6_addr32[2],
                        sce->addr.pr_s6_addr32[3], now,
                        sce->expirationTime ));
            sce->valid = 0;
            continue;
        }

        /*
        ** Next, examine specific session-id/addr data to see if the cache
        ** entry matches our addr+session-id value
        */
        if (sessionIDLength == sce->sessionIDLength      &&
            !memcmp(&sce->addr, addr, sizeof(PRIPv6Addr)) &&
            !memcmp(sce->sessionID, sessionID, sessionIDLength)) {
            /* Found it */
            return sce;
        }
    }

    PORT_SetError(SSL_ERROR_SESSION_NOT_FOUND);
    return NULL;
}

/************************************************************************/

/* This is the primary function for finding entries in the server's sid cache.
 * Although it is static, this function is called via the global function 
 * pointer ssl_sid_lookup.
 */
static sslSessionID *
ServerSessionIDLookup(const PRIPv6Addr *addr,
                        unsigned char *sessionID,
                        unsigned int   sessionIDLength,
                        CERTCertDBHandle * dbHandle)
{
    sslSessionID *  sid      = 0;
    sidCacheEntry * psce;
    certCacheEntry *pcce     = 0;
    srvNameCacheEntry *psnce = 0;
    cacheDesc *     cache    = &globalCache;
    PRUint32        now;
    PRUint32        set;
    PRInt32         cndx;
    sidCacheEntry   sce;
    certCacheEntry  cce;
    srvNameCacheEntry snce;

    set = SIDindex(cache, addr, sessionID, sessionIDLength);
    now = LockSet(cache, set, 0);
    if (!now)
        return NULL;

    psce = FindSID(cache, set, now, addr, sessionID, sessionIDLength);
    if (psce) {
        if (psce->version >= SSL_LIBRARY_VERSION_3_0) {
            if ((cndx = psce->u.ssl3.certIndex) != -1) {
                
                PRUint32 gotLock = LockSidCacheLock(cache->certCacheLock, now);
                if (gotLock) {
                    pcce = &cache->certCacheData[cndx];
                    
                    /* See if the cert's session ID matches the sce cache. */
                    if ((pcce->sessionIDLength == psce->sessionIDLength) &&
                        !PORT_Memcmp(pcce->sessionID, psce->sessionID, 
                                     pcce->sessionIDLength)) {
                        cce = *pcce;
                    } else {
                        /* The cert doesen't match the SID cache entry, 
                        ** so invalidate the SID cache entry. 
                        */
                        psce->valid = 0;
                        psce = 0;
                        pcce = 0;
                    }
                    UnlockSidCacheLock(cache->certCacheLock);
                } else {
                    /* what the ??.  Didn't get the cert cache lock.
                    ** Don't invalidate the SID cache entry, but don't find it.
                    */
                    PORT_Assert(!("Didn't get cert Cache Lock!"));
                    psce = 0;
                    pcce = 0;
                }
            }
            if (psce && ((cndx = psce->u.ssl3.srvNameIndex) != -1)) {
                PRUint32 gotLock = LockSidCacheLock(cache->srvNameCacheLock,
                                                    now);
                if (gotLock) {
                    psnce = &cache->srvNameCacheData[cndx];
                    
                    if (!PORT_Memcmp(psnce->nameHash, psce->u.ssl3.srvNameHash, 
                                     SHA256_LENGTH)) {
                        snce = *psnce;
                    } else {
                        /* The name doesen't match the SID cache entry, 
                        ** so invalidate the SID cache entry. 
                        */
                        psce->valid = 0;
                        psce = 0;
                        psnce = 0;
                    }
                    UnlockSidCacheLock(cache->srvNameCacheLock);
                } else {
                    /* what the ??.  Didn't get the cert cache lock.
                    ** Don't invalidate the SID cache entry, but don't find it.
                    */
                    PORT_Assert(!("Didn't get name Cache Lock!"));
                    psce = 0;
                    psnce = 0;
                }
                
            }
        }
        if (psce) {
            psce->lastAccessTime = now;
            sce = *psce;        /* grab a copy while holding the lock */
        }
    }
    UnlockSet(cache, set);
    if (psce) {
        /* sce conains a copy of the cache entry.
        ** Convert shared memory format to local format 
        */
        sid = ConvertToSID(&sce, pcce ? &cce : 0, psnce ? &snce : 0, dbHandle);
    }
    return sid;
}

/*
** Place a sid into the cache, if it isn't already there. 
*/
static void 
ServerSessionIDCache(sslSessionID *sid)
{
    sidCacheEntry sce;
    PRUint32      now     = 0;
    PRUint16      version = sid->version;
    cacheDesc *   cache   = &globalCache;

    if ((version >= SSL_LIBRARY_VERSION_3_0) &&
        (sid->u.ssl3.sessionIDLength == 0)) {
        return;
    }

    if (sid->cached == never_cached || sid->cached == invalid_cache) {
        PRUint32 set;

        PORT_Assert(sid->creationTime != 0);
        if (!sid->creationTime)
            sid->lastAccessTime = sid->creationTime = ssl_Time();
        if (version < SSL_LIBRARY_VERSION_3_0) {
            /* override caller's expiration time, which uses client timeout
             * duration, not server timeout duration.
             */
            sid->expirationTime = sid->creationTime + cache->ssl2Timeout;
            SSL_TRC(8, ("%d: SSL: CacheMT: cached=%d addr=0x%08x%08x%08x%08x time=%x "
                        "cipher=%d", myPid, sid->cached,
                        sid->addr.pr_s6_addr32[0], sid->addr.pr_s6_addr32[1],
                        sid->addr.pr_s6_addr32[2], sid->addr.pr_s6_addr32[3],
                        sid->creationTime, sid->u.ssl2.cipherType));
            PRINT_BUF(8, (0, "sessionID:", sid->u.ssl2.sessionID,
                          SSL2_SESSIONID_BYTES));
            PRINT_BUF(8, (0, "masterKey:", sid->u.ssl2.masterKey.data,
                          sid->u.ssl2.masterKey.len));
            PRINT_BUF(8, (0, "cipherArg:", sid->u.ssl2.cipherArg.data,
                          sid->u.ssl2.cipherArg.len));

        } else {
            /* override caller's expiration time, which uses client timeout
             * duration, not server timeout duration.
             */
            sid->expirationTime = sid->creationTime + cache->ssl3Timeout;
            SSL_TRC(8, ("%d: SSL: CacheMT: cached=%d addr=0x%08x%08x%08x%08x time=%x "
                        "cipherSuite=%d", myPid, sid->cached,
                        sid->addr.pr_s6_addr32[0], sid->addr.pr_s6_addr32[1],
                        sid->addr.pr_s6_addr32[2], sid->addr.pr_s6_addr32[3],
                        sid->creationTime, sid->u.ssl3.cipherSuite));
            PRINT_BUF(8, (0, "sessionID:", sid->u.ssl3.sessionID,
                          sid->u.ssl3.sessionIDLength));
        }

        ConvertFromSID(&sce, sid);

        if (version >= SSL_LIBRARY_VERSION_3_0) {
            SECItem *name = &sid->u.ssl3.srvName;
            if (name->len && name->data) {
                now = CacheSrvName(cache, name, &sce);
            }
            if (sid->peerCert != NULL) {
                now = CacheCert(cache, sid->peerCert, &sce);
            }
        }

        set = SIDindex(cache, &sce.addr, sce.sessionID, sce.sessionIDLength);
        now = LockSet(cache, set, now);
        if (now) {
            PRUint32  next = cache->sidCacheSets[set].next;
            PRUint32  ndx  = set * SID_CACHE_ENTRIES_PER_SET + next;

            /* Write out new cache entry */
            cache->sidCacheData[ndx] = sce;

            cache->sidCacheSets[set].next = 
                                        (next + 1) % SID_CACHE_ENTRIES_PER_SET;

            UnlockSet(cache, set);
            sid->cached = in_server_cache;
        }
    }
}

/*
** Although this is static, it is called from ssl via global function pointer
**      ssl_sid_uncache.  This invalidates the referenced cache entry.
*/
static void 
ServerSessionIDUncache(sslSessionID *sid)
{
    cacheDesc *    cache   = &globalCache;
    PRUint8 *      sessionID;
    unsigned int   sessionIDLength;
    PRErrorCode    err;
    PRUint32       set;
    PRUint32       now;
    sidCacheEntry *psce;

    if (sid == NULL) 
        return;
    
    /* Uncaching a SID should never change the error code. 
    ** So save it here and restore it before exiting.
    */
    err = PR_GetError();

    if (sid->version < SSL_LIBRARY_VERSION_3_0) {
        sessionID       = sid->u.ssl2.sessionID;
        sessionIDLength = SSL2_SESSIONID_BYTES;
        SSL_TRC(8, ("%d: SSL: UncacheMT: valid=%d addr=0x%08x%08x%08x%08x time=%x "
                    "cipher=%d", myPid, sid->cached,
                    sid->addr.pr_s6_addr32[0], sid->addr.pr_s6_addr32[1],
                    sid->addr.pr_s6_addr32[2], sid->addr.pr_s6_addr32[3],
                    sid->creationTime, sid->u.ssl2.cipherType));
        PRINT_BUF(8, (0, "sessionID:", sessionID, sessionIDLength));
        PRINT_BUF(8, (0, "masterKey:", sid->u.ssl2.masterKey.data,
                      sid->u.ssl2.masterKey.len));
        PRINT_BUF(8, (0, "cipherArg:", sid->u.ssl2.cipherArg.data,
                      sid->u.ssl2.cipherArg.len));
    } else {
        sessionID       = sid->u.ssl3.sessionID;
        sessionIDLength = sid->u.ssl3.sessionIDLength;
        SSL_TRC(8, ("%d: SSL3: UncacheMT: valid=%d addr=0x%08x%08x%08x%08x time=%x "
                    "cipherSuite=%d", myPid, sid->cached,
                    sid->addr.pr_s6_addr32[0], sid->addr.pr_s6_addr32[1],
                    sid->addr.pr_s6_addr32[2], sid->addr.pr_s6_addr32[3],
                    sid->creationTime, sid->u.ssl3.cipherSuite));
        PRINT_BUF(8, (0, "sessionID:", sessionID, sessionIDLength));
    }
    set = SIDindex(cache, &sid->addr, sessionID, sessionIDLength);
    now = LockSet(cache, set, 0);
    if (now) {
        psce = FindSID(cache, set, now, &sid->addr, sessionID, sessionIDLength);
        if (psce) {
            psce->valid = 0;
        }
        UnlockSet(cache, set);
    }
    sid->cached = invalid_cache;
    PORT_SetError(err);
}

#ifdef XP_OS2

#define INCL_DOSPROCESS
#include <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 ssl2_timeout, 
          PRUint32 ssl3_timeout, const char *directory, PRBool shared)
{
    ptrdiff_t     ptr;
    sidCacheLock *pLock;
    char *        cacheMem;
    PRFileMap *   cacheMemMap;
    char *        cfn = NULL;   /* cache file name */
    int           locks_initialized = 0;
    int           locks_to_initialize = 0;
    PRUint32      init_time;

    if ( (!cache) || (maxCacheEntries < 0) || (!directory) ) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

    if (cache->cacheMem) {
        /* Already done */
        return SECSuccess;
    }

    /* make sure loser can clean up properly */
    cache->shared = shared;
    cache->cacheMem    = cacheMem    = NULL;
    cache->cacheMemMap = cacheMemMap = NULL;
    cache->sharedCache = (cacheDesc *)0;

    cache->numSIDCacheLocksInitialized = 0;
    cache->nextCertCacheEntry = 0;
    cache->stopPolling = PR_FALSE;
    cache->everInherited = PR_FALSE;
    cache->poller = NULL;
    cache->mutexTimeout = 0;

    cache->numSIDCacheEntries = maxCacheEntries ? maxCacheEntries 
                                                : DEF_SID_CACHE_ENTRIES;
    cache->numSIDCacheSets    = 
        SID_HOWMANY(cache->numSIDCacheEntries, SID_CACHE_ENTRIES_PER_SET);

    cache->numSIDCacheEntries = 
        cache->numSIDCacheSets * SID_CACHE_ENTRIES_PER_SET;

    cache->numSIDCacheLocks   = 
        PR_MIN(cache->numSIDCacheSets, ssl_max_sid_cache_locks);

    cache->numSIDCacheSetsPerLock = 
        SID_HOWMANY(cache->numSIDCacheSets, cache->numSIDCacheLocks);

    cache->numCertCacheEntries = (maxCertCacheEntries > 0) ?
                                             maxCertCacheEntries : 0;
    cache->numSrvNameCacheEntries = (maxSrvNameCacheEntries >= 0) ?
                                             maxSrvNameCacheEntries : DEF_NAME_CACHE_ENTRIES;

    /* compute size of shared memory, and offsets of all pointers */
    ptr = 0;
    cache->cacheMem     = (char *)ptr;
    ptr += SID_ROUNDUP(sizeof(cacheDesc), SID_ALIGNMENT);

    cache->sidCacheLocks = (sidCacheLock *)ptr;
    cache->keyCacheLock  = cache->sidCacheLocks + cache->numSIDCacheLocks;
    cache->certCacheLock = cache->keyCacheLock  + 1;
    cache->srvNameCacheLock = cache->certCacheLock  + 1;
    ptr = (ptrdiff_t)(cache->srvNameCacheLock + 1);
    ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);

    cache->sidCacheSets  = (sidCacheSet *)ptr;
    ptr = (ptrdiff_t)(cache->sidCacheSets + cache->numSIDCacheSets);
    ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);

    cache->sidCacheData  = (sidCacheEntry *)ptr;
    ptr = (ptrdiff_t)(cache->sidCacheData + cache->numSIDCacheEntries);
    ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);

    cache->certCacheData = (certCacheEntry *)ptr;
    cache->sidCacheSize  = 
        (char *)cache->certCacheData - (char *)cache->sidCacheData;

    if (cache->numCertCacheEntries < MIN_CERT_CACHE_ENTRIES) {
        /* This is really a poor way to computer this! */
        cache->numCertCacheEntries = cache->sidCacheSize / sizeof(certCacheEntry);
        if (cache->numCertCacheEntries < MIN_CERT_CACHE_ENTRIES)
        cache->numCertCacheEntries = MIN_CERT_CACHE_ENTRIES;
    }
    ptr = (ptrdiff_t)(cache->certCacheData + cache->numCertCacheEntries);
    ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);

    cache->keyCacheData  = (SSLWrappedSymWrappingKey *)ptr;
    cache->certCacheSize = 
        (char *)cache->keyCacheData - (char *)cache->certCacheData;

    cache->numKeyCacheEntries = kt_kea_size * SSL_NUM_WRAP_MECHS;
    ptr = (ptrdiff_t)(cache->keyCacheData + cache->numKeyCacheEntries);
    ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);

    cache->keyCacheSize  = (char *)ptr - (char *)cache->keyCacheData;

    cache->ticketKeyNameSuffix = (PRUint8 *)ptr;
    ptr = (ptrdiff_t)(cache->ticketKeyNameSuffix +
        SESS_TICKET_KEY_VAR_NAME_LEN);
    ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);

    cache->ticketEncKey = (encKeyCacheEntry *)ptr;
    ptr = (ptrdiff_t)(cache->ticketEncKey + 1);
    ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);

    cache->ticketMacKey = (encKeyCacheEntry *)ptr;
    ptr = (ptrdiff_t)(cache->ticketMacKey + 1);
    ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);

    cache->ticketKeysValid = (PRUint32 *)ptr;
    ptr = (ptrdiff_t)(cache->ticketKeysValid + 1);
    ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);

    cache->srvNameCacheData = (srvNameCacheEntry *)ptr;
    cache->srvNameCacheSize =
        cache->numSrvNameCacheEntries * sizeof(srvNameCacheEntry);
    ptr = (ptrdiff_t)(cache->srvNameCacheData + cache->numSrvNameCacheEntries);
    ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);

    cache->cacheMemSize = ptr;

    if (ssl2_timeout) {   
        if (ssl2_timeout > MAX_SSL2_TIMEOUT) {
            ssl2_timeout = MAX_SSL2_TIMEOUT;
        }
        if (ssl2_timeout < MIN_SSL2_TIMEOUT) {
            ssl2_timeout = MIN_SSL2_TIMEOUT;
        }
        cache->ssl2Timeout = ssl2_timeout;
    } else {
        cache->ssl2Timeout = DEF_SSL2_TIMEOUT;
    }

    if (ssl3_timeout) {   
        if (ssl3_timeout > MAX_SSL3_TIMEOUT) {
            ssl3_timeout = MAX_SSL3_TIMEOUT;
        }
        if (ssl3_timeout < MIN_SSL3_TIMEOUT) {
            ssl3_timeout = MIN_SSL3_TIMEOUT;
        }
        cache->ssl3Timeout = ssl3_timeout;
    } else {
        cache->ssl3Timeout = DEF_SSL3_TIMEOUT;
    }

    if (shared) {
        /* Create file names */
#if defined(XP_UNIX) || defined(XP_BEOS)
        /* there's some confusion here about whether PR_OpenAnonFileMap wants
        ** a directory name or a file name for its first argument.
        cfn = PR_smprintf("%s/.sslsvrcache.%d", directory, myPid);
        */
        cfn = PR_smprintf("%s", directory);
#elif defined(XP_WIN32)
        cfn = PR_smprintf("%s/svrcache_%d_%x.ssl", directory, myPid, 
                            GetCurrentThreadId());
#elif defined(XP_OS2)
        cfn = PR_smprintf("%s/svrcache_%d_%x.ssl", directory, myPid, 
                            gettid());
#else
#error "Don't know how to create file name for this platform!"
#endif
        if (!cfn) {
            goto loser;
        }

        /* Create cache */
        cacheMemMap = PR_OpenAnonFileMap(cfn, cache->cacheMemSize, 
                                         PR_PROT_READWRITE);

        PR_smprintf_free(cfn);
        if(!cacheMemMap) {
            goto loser;
        }

        cacheMem = PR_MemMap(cacheMemMap, 0, cache->cacheMemSize);
    } else {
        cacheMem = PORT_Alloc(cache->cacheMemSize);
    }
    
    if (! cacheMem) {
        goto loser;
    }

    /* Initialize shared memory. This may not be necessary on all platforms */
    memset(cacheMem, 0, cache->cacheMemSize);

    /* Copy cache descriptor header into shared memory */
    memcpy(cacheMem, cache, sizeof *cache);

    /* save private copies of these values */
    cache->cacheMemMap = cacheMemMap;
    cache->cacheMem    = cacheMem;
    cache->sharedCache = (cacheDesc *)cacheMem;

    /* Fix pointers in our private copy of cache descriptor to point to 
    ** spaces in shared memory 
    */
    ptr = (ptrdiff_t)cache->cacheMem;
    *(ptrdiff_t *)(&cache->sidCacheLocks) += ptr;
    *(ptrdiff_t *)(&cache->keyCacheLock ) += ptr;
    *(ptrdiff_t *)(&cache->certCacheLock) += ptr;
    *(ptrdiff_t *)(&cache->srvNameCacheLock) += ptr;
    *(ptrdiff_t *)(&cache->sidCacheSets ) += ptr;
    *(ptrdiff_t *)(&cache->sidCacheData ) += ptr;
    *(ptrdiff_t *)(&cache->certCacheData) += ptr;
    *(ptrdiff_t *)(&cache->keyCacheData ) += ptr;
    *(ptrdiff_t *)(&cache->ticketKeyNameSuffix) += ptr;
    *(ptrdiff_t *)(&cache->ticketEncKey ) += ptr;
    *(ptrdiff_t *)(&cache->ticketMacKey ) += ptr;
    *(ptrdiff_t *)(&cache->ticketKeysValid) += ptr;
    *(ptrdiff_t *)(&cache->srvNameCacheData) += ptr;

    /* initialize the locks */
    init_time = ssl_Time();
    pLock = cache->sidCacheLocks;
    for (locks_to_initialize = cache->numSIDCacheLocks + 3;
         locks_initialized < locks_to_initialize; 
         ++locks_initialized, ++pLock ) {

        SECStatus err = sslMutex_Init(&pLock->mutex, shared);
        if (err) {
            cache->numSIDCacheLocksInitialized = locks_initialized;
            goto loser;
        }
        pLock->timeStamp = init_time;
        pLock->pid       = 0;
    }
    cache->numSIDCacheLocksInitialized = locks_initialized;

    return SECSuccess;

loser:
    CloseCache(cache);
    return SECFailure;
}

PRUint32
SSL_GetMaxServerCacheLocks(void)
{
    return ssl_max_sid_cache_locks + 2;
    /* The extra two are the cert cache lock and the key cache lock. */
}

SECStatus
SSL_SetMaxServerCacheLocks(PRUint32 maxLocks)
{
    /* Minimum is 1 sid cache lock, 1 cert cache lock and 1 key cache lock.
    ** We'd like to test for a maximum value, but not all platforms' header
    ** files provide a symbol or function or other means of determining
    ** the maximum, other than trial and error.
    */
    if (maxLocks < 3) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }
    ssl_max_sid_cache_locks = maxLocks - 2;
    /* The extra two are the cert cache lock and the key cache lock. */
    return SECSuccess;
}

static SECStatus
ssl_ConfigServerSessionIDCacheInstanceWithOpt(cacheDesc *cache,
                                              PRUint32 ssl2_timeout,
                                              PRUint32 ssl3_timeout, 
                                              const char *   directory,
                                              PRBool shared,
                                              int      maxCacheEntries, 
                                              int      maxCertCacheEntries,
                                              int      maxSrvNameCacheEntries)
{
    SECStatus rv;

    PORT_Assert(sizeof(sidCacheEntry) == 192);
    PORT_Assert(sizeof(certCacheEntry) == 4096);
    PORT_Assert(sizeof(srvNameCacheEntry) == 1072);

    rv = ssl_Init();
    if (rv != SECSuccess) {
        return rv;
    }

    myPid = SSL_GETPID();
    if (!directory) {
        directory = DEFAULT_CACHE_DIRECTORY;
    }
    rv = InitCache(cache, maxCacheEntries, maxCertCacheEntries,
                   maxSrvNameCacheEntries, ssl2_timeout, ssl3_timeout, 
                   directory, shared);
    if (rv) {
        SET_ERROR_CODE
        return SECFailure;
    }

    ssl_sid_lookup  = ServerSessionIDLookup;
    ssl_sid_cache   = ServerSessionIDCache;
    ssl_sid_uncache = ServerSessionIDUncache;
    return SECSuccess;
}

SECStatus
SSL_ConfigServerSessionIDCacheInstance( cacheDesc *cache,
                                int      maxCacheEntries, 
                                PRUint32 ssl2_timeout,
                                PRUint32 ssl3_timeout, 
                                const char *   directory, PRBool shared)
{
    return ssl_ConfigServerSessionIDCacheInstanceWithOpt(cache,
                                                         ssl2_timeout,
                                                         ssl3_timeout,
                                                         directory,
                                                         shared,
                                                         maxCacheEntries, 
                                                         -1, -1);
}

SECStatus
SSL_ConfigServerSessionIDCache( int      maxCacheEntries, 
                                PRUint32 ssl2_timeout,
                                PRUint32 ssl3_timeout, 
                          const char *   directory)
{
    ssl_InitSessionCacheLocks();
    return SSL_ConfigServerSessionIDCacheInstance(&globalCache, 
                maxCacheEntries, ssl2_timeout, ssl3_timeout, directory, PR_FALSE);
}

SECStatus
SSL_ShutdownServerSessionIDCacheInstance(cacheDesc *cache)
{
    CloseCache(cache);
    return SECSuccess;
}

SECStatus
SSL_ShutdownServerSessionIDCache(void)
{
#if defined(XP_UNIX) || defined(XP_BEOS)
    /* Stop the thread that polls cache for expired locks on Unix */
    StopLockPoller(&globalCache);
#endif
    SSL3_ShutdownServerCache();
    return SSL_ShutdownServerSessionIDCacheInstance(&globalCache);
}

/* Use this function, instead of SSL_ConfigServerSessionIDCache,
 * if the cache will be shared by multiple processes.
 */
static SECStatus
ssl_ConfigMPServerSIDCacheWithOpt(      PRUint32 ssl2_timeout,
                                        PRUint32 ssl3_timeout, 
                                        const char *   directory,
                                        int maxCacheEntries,
                                        int maxCertCacheEntries,
                                        int maxSrvNameCacheEntries)
{
    char *      envValue;
    char *      inhValue;
    cacheDesc * cache         = &globalCache;
    PRUint32    fmStrLen;
    SECStatus   result;
    PRStatus    prStatus;
    SECStatus   putEnvFailed;
    inheritance inherit;
    char        fmString[PR_FILEMAP_STRING_BUFSIZE];

    isMultiProcess = PR_TRUE;
    result = ssl_ConfigServerSessionIDCacheInstanceWithOpt(cache,
                  ssl2_timeout, ssl3_timeout, directory, PR_TRUE,
        maxCacheEntries, maxCacheEntries, maxSrvNameCacheEntries);
    if (result != SECSuccess) 
        return result;

    prStatus = PR_ExportFileMapAsString(cache->cacheMemMap, 
                                        sizeof fmString, fmString);
    if ((prStatus != PR_SUCCESS) || !(fmStrLen = strlen(fmString))) {
        SET_ERROR_CODE
        return SECFailure;
    }

    inherit.cacheMemSize        = cache->cacheMemSize;
    inherit.fmStrLen            = fmStrLen;

    inhValue = BTOA_DataToAscii((unsigned char *)&inherit, sizeof inherit);
    if (!inhValue || !strlen(inhValue)) {
        SET_ERROR_CODE
        return SECFailure;
    }
    envValue = PR_smprintf("%s,%s", inhValue, fmString);
    if (!envValue || !strlen(envValue)) {
        SET_ERROR_CODE
        return SECFailure;
    }
    PORT_Free(inhValue);

    putEnvFailed = (SECStatus)NSS_PutEnv(envVarName, envValue);
    PR_smprintf_free(envValue);
    if (putEnvFailed) {
        SET_ERROR_CODE
        result = SECFailure;
    }

#if defined(XP_UNIX) || defined(XP_BEOS)
    /* Launch thread to poll cache for expired locks on Unix */
    LaunchLockPoller(cache);
#endif
    return result;
}

/* Use this function, instead of SSL_ConfigServerSessionIDCache,
 * if the cache will be shared by multiple processes.
 */
SECStatus
SSL_ConfigMPServerSIDCache(     int      maxCacheEntries, 
                                PRUint32 ssl2_timeout,
                                PRUint32 ssl3_timeout, 
                          const char *   directory)
{
    return ssl_ConfigMPServerSIDCacheWithOpt(ssl2_timeout,
                                             ssl3_timeout,
                                             directory,
                                             maxCacheEntries,
                                             -1, -1);
}

SECStatus
SSL_ConfigServerSessionIDCacheWithOpt(
                                PRUint32 ssl2_timeout,
                                PRUint32 ssl3_timeout, 
                                const char *   directory,
                                int maxCacheEntries,
                                int maxCertCacheEntries,
                                int maxSrvNameCacheEntries,
                                PRBool enableMPCache)
{
    if (!enableMPCache) {
        ssl_InitSessionCacheLocks();
        return ssl_ConfigServerSessionIDCacheInstanceWithOpt(&globalCache, 
           ssl2_timeout, ssl3_timeout, directory, PR_FALSE,
           maxCacheEntries, maxCertCacheEntries, maxSrvNameCacheEntries);
    } else {
        return ssl_ConfigMPServerSIDCacheWithOpt(ssl2_timeout, ssl3_timeout,
                directory, maxCacheEntries, maxCertCacheEntries,
                                                 maxSrvNameCacheEntries);
    }
}

SECStatus
SSL_InheritMPServerSIDCacheInstance(cacheDesc *cache, const char * envString)
{
    unsigned char * decoString = NULL;
    char *          fmString   = NULL;
    char *          myEnvString = NULL;
    unsigned int    decoLen;
    ptrdiff_t       ptr;
    inheritance     inherit;
    cacheDesc       my;
#ifdef WINNT
    sidCacheLock* newLocks;
    int           locks_initialized = 0;
    int           locks_to_initialize = 0;
#endif
    SECStatus     status = ssl_Init();

    if (status != SECSuccess) {
        return status;
    }

    myPid = SSL_GETPID();

    /* If this child was created by fork(), and not by exec() on unix,
    ** then isMultiProcess will already be set.
    ** If not, we'll set it below.
    */
    if (isMultiProcess) {
        if (cache && cache->sharedCache) {
            cache->sharedCache->everInherited = PR_TRUE;
        }
        return SECSuccess;      /* already done. */
    }

    ssl_InitSessionCacheLocks();

    ssl_sid_lookup  = ServerSessionIDLookup;
    ssl_sid_cache   = ServerSessionIDCache;
    ssl_sid_uncache = ServerSessionIDUncache;

    if (!envString) {
        envString  = getenv(envVarName);
        if (!envString) {
            SET_ERROR_CODE
            return SECFailure;
        }
    }
    myEnvString = PORT_Strdup(envString);
    if (!myEnvString) 
        return SECFailure;
    fmString = strchr(myEnvString, ',');
    if (!fmString) 
        goto loser;
    *fmString++ = 0;

    decoString = ATOB_AsciiToData(myEnvString, &decoLen);
    if (!decoString) {
        SET_ERROR_CODE
        goto loser;
    }
    if (decoLen != sizeof inherit) {
        SET_ERROR_CODE
        goto loser;
    }

    PORT_Memcpy(&inherit, decoString, sizeof inherit);

    if (strlen(fmString)  != inherit.fmStrLen ) {
        goto loser;
    }

    memset(cache, 0, sizeof *cache);
    cache->cacheMemSize = inherit.cacheMemSize;

    /* Create cache */
    cache->cacheMemMap = PR_ImportFileMapFromString(fmString);
    if(! cache->cacheMemMap) {
        goto loser;
    }
    cache->cacheMem = PR_MemMap(cache->cacheMemMap, 0, cache->cacheMemSize);
    if (! cache->cacheMem) {
        goto loser;
    }
    cache->sharedCache   = (cacheDesc *)cache->cacheMem;

    if (cache->sharedCache->cacheMemSize != cache->cacheMemSize) {
        SET_ERROR_CODE
        goto loser;
    }

    /* We're now going to overwrite the local cache instance with the 
    ** shared copy of the cache struct, then update several values in 
    ** the local cache using the values for cache->cacheMemMap and 
    ** cache->cacheMem computed just above.  So, we copy cache into 
    ** the automatic variable "my", to preserve the variables while
    ** cache is overwritten.
    */
    my = *cache;  /* save values computed above. */
    memcpy(cache, cache->sharedCache, sizeof *cache); /* overwrite */

    /* Fix pointers in our private copy of cache descriptor to point to 
    ** spaces in shared memory, whose address is now in "my".
    */
    ptr = (ptrdiff_t)my.cacheMem;
    *(ptrdiff_t *)(&cache->sidCacheLocks) += ptr;
    *(ptrdiff_t *)(&cache->keyCacheLock ) += ptr;
    *(ptrdiff_t *)(&cache->certCacheLock) += ptr;
    *(ptrdiff_t *)(&cache->srvNameCacheLock) += ptr;
    *(ptrdiff_t *)(&cache->sidCacheSets ) += ptr;
    *(ptrdiff_t *)(&cache->sidCacheData ) += ptr;
    *(ptrdiff_t *)(&cache->certCacheData) += ptr;
    *(ptrdiff_t *)(&cache->keyCacheData ) += ptr;
    *(ptrdiff_t *)(&cache->ticketKeyNameSuffix) += ptr;
    *(ptrdiff_t *)(&cache->ticketEncKey ) += ptr;
    *(ptrdiff_t *)(&cache->ticketMacKey ) += ptr;
    *(ptrdiff_t *)(&cache->ticketKeysValid) += ptr;
    *(ptrdiff_t *)(&cache->srvNameCacheData) += ptr;

    cache->cacheMemMap = my.cacheMemMap;
    cache->cacheMem    = my.cacheMem;
    cache->sharedCache = (cacheDesc *)cache->cacheMem;

#ifdef WINNT
    /*  On Windows NT we need to "fix" the sidCacheLocks here to support fibers
    **  When NT fibers are used in a multi-process server, a second level of
    **  locking is needed to prevent a deadlock, in case a fiber acquires the
    **  cross-process mutex, yields, and another fiber is later scheduled on
    **  the same native thread and tries to acquire the cross-process mutex.
    **  We do this by using a PRLock in the sslMutex. However, it is stored in
    **  shared memory as part of sidCacheLocks, and we don't want to overwrite
    **  the PRLock of the parent process. So we need to make new, private
    **  copies of sidCacheLocks before modifying the sslMutex with our own
    **  PRLock
    */
    
    /* note from jpierre : this should be free'd in child processes when
    ** a function is added to delete the SSL session cache in the future. 
    */
    locks_to_initialize = cache->numSIDCacheLocks + 3;
    newLocks = PORT_NewArray(sidCacheLock, locks_to_initialize);
    if (!newLocks)
        goto loser;
    /* copy the old locks */
    memcpy(newLocks, cache->sidCacheLocks, 
           locks_to_initialize * sizeof(sidCacheLock));
    cache->sidCacheLocks = newLocks;
    /* fix the locks */         
    for (; locks_initialized < locks_to_initialize; ++locks_initialized) {
        /* now, make a local PRLock in this sslMutex for this child process */
        SECStatus err;
        err = sslMutex_2LevelInit(&newLocks[locks_initialized].mutex);
        if (err != SECSuccess) {
            cache->numSIDCacheLocksInitialized = locks_initialized;
            goto loser;
        }
    }
    cache->numSIDCacheLocksInitialized = locks_initialized;

    /* also fix the key and cert cache which use the last 2 lock entries */
    cache->keyCacheLock  = cache->sidCacheLocks + cache->numSIDCacheLocks;
    cache->certCacheLock = cache->keyCacheLock  + 1;
    cache->srvNameCacheLock = cache->certCacheLock  + 1;
#endif

    PORT_Free(myEnvString);
    PORT_Free(decoString);

    /* mark that we have inherited this. */
    cache->sharedCache->everInherited = PR_TRUE;
    isMultiProcess = PR_TRUE;

    return SECSuccess;

loser:
    PORT_Free(myEnvString);
    if (decoString) 
        PORT_Free(decoString);
    CloseCache(cache);
    return SECFailure;
}

SECStatus
SSL_InheritMPServerSIDCache(const char * envString)
{
    return SSL_InheritMPServerSIDCacheInstance(&globalCache, envString);
}

#if defined(XP_UNIX) || defined(XP_BEOS)

#define SID_LOCK_EXPIRATION_TIMEOUT  30 /* seconds */

static void
LockPoller(void * arg)
{
    cacheDesc *    cache         = (cacheDesc *)arg;
    cacheDesc *    sharedCache   = cache->sharedCache;
    sidCacheLock * pLock;
    PRIntervalTime timeout;
    PRUint32       now;
    PRUint32       then;
    int            locks_polled  = 0;
    int            locks_to_poll = cache->numSIDCacheLocks + 2;
    PRUint32       expiration    = cache->mutexTimeout;

    timeout = PR_SecondsToInterval(expiration);
    while(!sharedCache->stopPolling) {
        PR_Sleep(timeout);
        if (sharedCache->stopPolling)
            break;

        now   = ssl_Time();
        then  = now - expiration;
        for (pLock = cache->sidCacheLocks, locks_polled = 0;
             locks_to_poll > locks_polled && !sharedCache->stopPolling; 
             ++locks_polled, ++pLock ) {
            pid_t pid;

            if (pLock->timeStamp   < then && 
                pLock->timeStamp   != 0 && 
                (pid = pLock->pid) != 0) {

                /* maybe we should try the lock? */
                int result = kill(pid, 0);
                if (result < 0 && errno == ESRCH) {
                    SECStatus rv;
                    /* No process exists by that pid any more.
                    ** Treat this mutex as abandoned.
                    */
                    pLock->timeStamp = now;
                    pLock->pid       = 0;
                    rv = sslMutex_Unlock(&pLock->mutex);
                    if (rv != SECSuccess) {
                        /* Now what? */
                    }
                }
            }
        } /* end of loop over locks */
    } /* end of entire polling loop */
}

/* Launch thread to poll cache for expired locks */
static SECStatus 
LaunchLockPoller(cacheDesc *cache)
{
    const char * timeoutString;
    PRThread *   pollerThread;

    cache->mutexTimeout = SID_LOCK_EXPIRATION_TIMEOUT;
    timeoutString       = getenv("NSS_SSL_SERVER_CACHE_MUTEX_TIMEOUT");
    if (timeoutString) {
        long newTime = strtol(timeoutString, 0, 0);
        if (newTime == 0) 
            return SECSuccess;  /* application doesn't want poller thread */
        if (newTime > 0)
            cache->mutexTimeout = (PRUint32)newTime;
        /* if error (newTime < 0) ignore it and use default */
    }

    pollerThread = 
        PR_CreateThread(PR_USER_THREAD, LockPoller, cache, PR_PRIORITY_NORMAL, 
                        PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
    if (!pollerThread) {
        return SECFailure;
    }
    cache->poller = pollerThread;
    return SECSuccess;
}

/* Stop the thread that polls cache for expired locks */
static SECStatus 
StopLockPoller(cacheDesc *cache)
{
    if (!cache->poller) {
        return SECSuccess;
    }
    cache->sharedCache->stopPolling = PR_TRUE;
    if (PR_Interrupt(cache->poller) != PR_SUCCESS) {
        return SECFailure;
    }
    if (PR_JoinThread(cache->poller) != PR_SUCCESS) {
        return SECFailure;
    }
    cache->poller = NULL;
    return SECSuccess;
}
#endif

/************************************************************************
 *  Code dealing with shared wrapped symmetric wrapping keys below      *
 ************************************************************************/

/* If now is zero, it implies that the lock is not held, and must be 
** aquired here.  
*/
static PRBool
getSvrWrappingKey(PRInt32                symWrapMechIndex,
               SSL3KEAType               exchKeyType, 
               SSLWrappedSymWrappingKey *wswk, 
               cacheDesc *               cache,
               PRUint32                  lockTime)
{
    PRUint32  ndx = (exchKeyType * SSL_NUM_WRAP_MECHS) + symWrapMechIndex;
    SSLWrappedSymWrappingKey * pwswk = cache->keyCacheData + ndx;
    PRUint32  now = 0;
    PRBool    rv  = PR_FALSE;

    if (!cache->cacheMem) { /* cache is uninitialized */
        PORT_SetError(SSL_ERROR_SERVER_CACHE_NOT_CONFIGURED);
        return rv;
    }
    if (!lockTime) {
        lockTime = now = LockSidCacheLock(cache->keyCacheLock, now);
        if (!lockTime) {
            return rv;
        }
    }
    if (pwswk->exchKeyType      == exchKeyType && 
        pwswk->symWrapMechIndex == symWrapMechIndex &&
        pwswk->wrappedSymKeyLen != 0) {
        *wswk = *pwswk;
        rv = PR_TRUE;
    }
    if (now) {
        UnlockSidCacheLock(cache->keyCacheLock);
    }
    return rv;
}

PRBool
ssl_GetWrappingKey( PRInt32                   symWrapMechIndex,
                    SSL3KEAType               exchKeyType, 
                    SSLWrappedSymWrappingKey *wswk)
{
    PRBool rv;

    PORT_Assert( (unsigned)exchKeyType < kt_kea_size);
    PORT_Assert( (unsigned)symWrapMechIndex < SSL_NUM_WRAP_MECHS);
    if ((unsigned)exchKeyType < kt_kea_size &&
        (unsigned)symWrapMechIndex < SSL_NUM_WRAP_MECHS) {
        rv = getSvrWrappingKey(symWrapMechIndex, exchKeyType, wswk, 
                               &globalCache, 0);
    } else {
        rv = PR_FALSE;
    }

    return rv;
}

/* Wrap and cache a session ticket key. */
static PRBool
WrapTicketKey(SECKEYPublicKey *svrPubKey, PK11SymKey *symKey,
              const char *keyName, encKeyCacheEntry* cacheEntry)
{
    SECItem wrappedKey = {siBuffer, NULL, 0};

    wrappedKey.len = SECKEY_PublicKeyStrength(svrPubKey);
    PORT_Assert(wrappedKey.len <= sizeof(cacheEntry->bytes));
    if (wrappedKey.len > sizeof(cacheEntry->bytes))
        return PR_FALSE;
    wrappedKey.data = cacheEntry->bytes;

    if (PK11_PubWrapSymKey(CKM_RSA_PKCS, svrPubKey, symKey, &wrappedKey)
            != SECSuccess) {
        SSL_DBG(("%d: SSL[%s]: Unable to wrap session ticket %s.",
                    SSL_GETPID(), "unknown", keyName));
        return PR_FALSE;
    }
    cacheEntry->length = wrappedKey.len;
    return PR_TRUE;
}

static PRBool
GenerateTicketKeys(void *pwArg, unsigned char *keyName, PK11SymKey **aesKey,
                   PK11SymKey **macKey)
{
    PK11SlotInfo *slot;
    CK_MECHANISM_TYPE mechanismArray[2];
    PK11SymKey *aesKeyTmp = NULL;
    PK11SymKey *macKeyTmp = NULL;
    cacheDesc *cache = &globalCache;
    PRUint8 ticketKeyNameSuffixLocal[SESS_TICKET_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,
            SESS_TICKET_KEY_VAR_NAME_LEN) != SECSuccess) {
        SSL_DBG(("%d: SSL[%s]: Unable to generate random key name bytes.",
                    SSL_GETPID(), "unknown"));
        goto loser;
    }

    mechanismArray[0] = CKM_AES_CBC;
    mechanismArray[1] = CKM_SHA256_HMAC;

    slot = PK11_GetBestSlotMultiple(mechanismArray, 2, pwArg);
    if (slot) {
        aesKeyTmp = PK11_KeyGen(slot, mechanismArray[0], NULL,
                                AES_256_KEY_LENGTH, pwArg);
        macKeyTmp = PK11_KeyGen(slot, mechanismArray[1], NULL,
                                SHA256_LENGTH, pwArg);
        PK11_FreeSlot(slot);
    }

    if (aesKeyTmp == NULL || macKeyTmp == NULL) {
        SSL_DBG(("%d: SSL[%s]: Unable to generate session ticket keys.",
                    SSL_GETPID(), "unknown"));
        goto loser;
    }
    PORT_Memcpy(keyName, ticketKeyNameSuffix, SESS_TICKET_KEY_VAR_NAME_LEN);
    *aesKey = aesKeyTmp;
    *macKey = macKeyTmp;
    return PR_TRUE;

loser:
    if (aesKeyTmp)
        PK11_FreeSymKey(aesKeyTmp);
    if (macKeyTmp)
        PK11_FreeSymKey(macKeyTmp);
    return PR_FALSE;
}

static PRBool
GenerateAndWrapTicketKeys(SECKEYPublicKey *svrPubKey, void *pwArg,
                          unsigned char *keyName, PK11SymKey **aesKey,
                          PK11SymKey **macKey)
{
    PK11SymKey *aesKeyTmp = NULL;
    PK11SymKey *macKeyTmp = NULL;
    cacheDesc *cache = &globalCache;

    if (!GenerateTicketKeys(pwArg, keyName, &aesKeyTmp, &macKeyTmp)) {
        goto loser;
    }

    if (cache->cacheMem) {
        /* Export the keys to the shared cache in wrapped form. */
        if (!WrapTicketKey(svrPubKey, aesKeyTmp, "enc key", cache->ticketEncKey))
            goto loser;
        if (!WrapTicketKey(svrPubKey, macKeyTmp, "mac key", cache->ticketMacKey))
            goto loser;
    }
    *aesKey = aesKeyTmp;
    *macKey = macKeyTmp;
    return PR_TRUE;

loser:
    if (aesKeyTmp)
        PK11_FreeSymKey(aesKeyTmp);
    if (macKeyTmp)
        PK11_FreeSymKey(macKeyTmp);
    return PR_FALSE;
}

static PRBool
UnwrapCachedTicketKeys(SECKEYPrivateKey *svrPrivKey, unsigned char *keyName,
                       PK11SymKey **aesKey, PK11SymKey **macKey)
{
    SECItem wrappedKey = {siBuffer, NULL, 0};
    PK11SymKey *aesKeyTmp = NULL;
    PK11SymKey *macKeyTmp = NULL;
    cacheDesc *cache = &globalCache;

    wrappedKey.data = cache->ticketEncKey->bytes;
    wrappedKey.len = cache->ticketEncKey->length;
    PORT_Assert(wrappedKey.len <= sizeof(cache->ticketEncKey->bytes));
    aesKeyTmp = PK11_PubUnwrapSymKey(svrPrivKey, &wrappedKey,
        CKM_AES_CBC, CKA_DECRYPT, 0);

    wrappedKey.data = cache->ticketMacKey->bytes;
    wrappedKey.len = cache->ticketMacKey->length;
    PORT_Assert(wrappedKey.len <= sizeof(cache->ticketMacKey->bytes));
    macKeyTmp = PK11_PubUnwrapSymKey(svrPrivKey, &wrappedKey,
        CKM_SHA256_HMAC, CKA_SIGN, 0);

    if (aesKeyTmp == NULL || macKeyTmp == NULL) {
        SSL_DBG(("%d: SSL[%s]: Unable to unwrap session ticket keys.",
                    SSL_GETPID(), "unknown"));
        goto loser;
    }
    SSL_DBG(("%d: SSL[%s]: Successfully unwrapped session ticket keys.",
                SSL_GETPID(), "unknown"));

    PORT_Memcpy(keyName, cache->ticketKeyNameSuffix,
        SESS_TICKET_KEY_VAR_NAME_LEN);
    *aesKey = aesKeyTmp;
    *macKey = macKeyTmp;
    return PR_TRUE;

loser:
    if (aesKeyTmp)
        PK11_FreeSymKey(aesKeyTmp);
    if (macKeyTmp)
        PK11_FreeSymKey(macKeyTmp);
    return PR_FALSE;
}

PRBool
ssl_GetSessionTicketKeysPKCS11(SECKEYPrivateKey *svrPrivKey,
                               SECKEYPublicKey *svrPubKey, void *pwArg,
                               unsigned char *keyName, PK11SymKey **aesKey,
                               PK11SymKey **macKey)
{
    PRUint32 now = 0;
    PRBool rv = PR_FALSE;
    PRBool keysGenerated = PR_FALSE;
    cacheDesc *cache = &globalCache;

    if (!cache->cacheMem) {
        /* cache is uninitialized. Generate keys and return them
         * without caching. */
        return GenerateTicketKeys(pwArg, keyName, aesKey, macKey);
    }

    now = LockSidCacheLock(cache->keyCacheLock, now);
    if (!now)
        return rv;

    if (!*(cache->ticketKeysValid)) {
        /* Keys do not exist, create them. */
        if (!GenerateAndWrapTicketKeys(svrPubKey, pwArg, keyName,
                aesKey, macKey))
            goto loser;
        keysGenerated = PR_TRUE;
        *(cache->ticketKeysValid) = 1;
    }

    rv = PR_TRUE;

 loser:
    UnlockSidCacheLock(cache->keyCacheLock);
    if (rv && !keysGenerated)
        rv = UnwrapCachedTicketKeys(svrPrivKey, keyName, aesKey, macKey);
    return rv;
}

PRBool
ssl_GetSessionTicketKeys(unsigned char *keyName, unsigned char *encKey,
                         unsigned char *macKey)
{
    PRBool rv = PR_FALSE;
    PRUint32 now = 0;
    cacheDesc *cache = &globalCache;
    PRUint8 ticketMacKey[SHA256_LENGTH], ticketEncKey[AES_256_KEY_LENGTH];
    PRUint8 ticketKeyNameSuffixLocal[SESS_TICKET_KEY_VAR_NAME_LEN];
    PRUint8 *ticketMacKeyPtr, *ticketEncKeyPtr, *ticketKeyNameSuffix;
    PRBool cacheIsEnabled = PR_TRUE;

    if (!cache->cacheMem) { /* cache is uninitialized */
        cacheIsEnabled = PR_FALSE;
        ticketKeyNameSuffix = ticketKeyNameSuffixLocal;
        ticketEncKeyPtr = ticketEncKey;
        ticketMacKeyPtr = ticketMacKey;
    } else {
        /* these values have constant memory locations in the cache.
         * Ok to reference them without holding the lock. */
        ticketKeyNameSuffix = cache->ticketKeyNameSuffix;
        ticketEncKeyPtr = cache->ticketEncKey->bytes;
        ticketMacKeyPtr = cache->ticketMacKey->bytes;
    }

    if (cacheIsEnabled) {
        /* Grab lock if initialized. */
        now = LockSidCacheLock(cache->keyCacheLock, now);
        if (!now)
            return rv;
    }
    /* Going to regenerate keys on every call if cache was not
     * initialized. */
    if (!cacheIsEnabled || !*(cache->ticketKeysValid)) {
        if (PK11_GenerateRandom(ticketKeyNameSuffix,
                SESS_TICKET_KEY_VAR_NAME_LEN) != SECSuccess)
            goto loser;
        if (PK11_GenerateRandom(ticketEncKeyPtr,
                                AES_256_KEY_LENGTH) != SECSuccess)
            goto loser;
        if (PK11_GenerateRandom(ticketMacKeyPtr,
                                SHA256_LENGTH) != SECSuccess)
            goto loser;
        if (cacheIsEnabled) {
            *(cache->ticketKeysValid) = 1;
        }
    }

    rv = PR_TRUE;

 loser:
    if (cacheIsEnabled) {
        UnlockSidCacheLock(cache->keyCacheLock);
    }
    if (rv) {
        PORT_Memcpy(keyName, ticketKeyNameSuffix,
                    SESS_TICKET_KEY_VAR_NAME_LEN);
        PORT_Memcpy(encKey, ticketEncKeyPtr, AES_256_KEY_LENGTH);
        PORT_Memcpy(macKey, ticketMacKeyPtr, SHA256_LENGTH);
    }
    return rv;
}

/* The caller passes in the new value it wants
 * to set.  This code tests the wrapped sym key entry in the shared memory.
 * If it is uninitialized, this function writes the caller's value into 
 * the disk entry, and returns false.  
 * Otherwise, it overwrites the caller's wswk with the value obtained from 
 * the disk, and returns PR_TRUE.  
 * This is all done while holding the locks/mutexes necessary to make 
 * the operation atomic.
 */
PRBool
ssl_SetWrappingKey(SSLWrappedSymWrappingKey *wswk)
{
    cacheDesc *   cache            = &globalCache;
    PRBool        rv               = PR_FALSE;
    SSL3KEAType   exchKeyType      = wswk->exchKeyType;   
                                /* type of keys used to wrap SymWrapKey*/
    PRInt32       symWrapMechIndex = wswk->symWrapMechIndex;
    PRUint32      ndx;
    PRUint32      now = 0;
    SSLWrappedSymWrappingKey myWswk;

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

    PORT_Assert( (unsigned)exchKeyType < kt_kea_size);
    if ((unsigned)exchKeyType >= kt_kea_size)
        return 0;

    PORT_Assert( (unsigned)symWrapMechIndex < SSL_NUM_WRAP_MECHS);
    if ((unsigned)symWrapMechIndex >=  SSL_NUM_WRAP_MECHS)
        return 0;

    ndx = (exchKeyType * SSL_NUM_WRAP_MECHS) + symWrapMechIndex;
    PORT_Memset(&myWswk, 0, sizeof myWswk);     /* eliminate UMRs. */

    now = LockSidCacheLock(cache->keyCacheLock, now);
    if (now) {
        rv = getSvrWrappingKey(wswk->symWrapMechIndex, wswk->exchKeyType, 
                               &myWswk, cache, now);
        if (rv) {
            /* we found it on disk, copy it out to the caller. */
            PORT_Memcpy(wswk, &myWswk, sizeof *wswk);
        } else {
            /* Wasn't on disk, and we're still holding the lock, so write it. */
            cache->keyCacheData[ndx] = *wswk;
        }
        UnlockSidCacheLock(cache->keyCacheLock);
    }
    return rv;
}

#else  /* MAC version or other platform */

#include "seccomon.h"
#include "cert.h"
#include "ssl.h"
#include "sslimpl.h"

SECStatus
SSL_ConfigServerSessionIDCache( int      maxCacheEntries, 
                                PRUint32 ssl2_timeout,
                                PRUint32 ssl3_timeout, 
                          const char *   directory)
{
    PR_ASSERT(!"SSL servers are not supported on this platform. (SSL_ConfigServerSessionIDCache)");    
    return SECFailure;
}

SECStatus
SSL_ConfigMPServerSIDCache(     int      maxCacheEntries, 
                                PRUint32 ssl2_timeout,
                                PRUint32 ssl3_timeout, 
                          const char *   directory)
{
    PR_ASSERT(!"SSL servers are not supported on this platform. (SSL_ConfigMPServerSIDCache)");    
    return SECFailure;
}

SECStatus
SSL_InheritMPServerSIDCache(const char * envString)
{
    PR_ASSERT(!"SSL servers are not supported on this platform. (SSL_InheritMPServerSIDCache)");    
    return SECFailure;
}

PRBool
ssl_GetWrappingKey( PRInt32                   symWrapMechIndex,
                    SSL3KEAType               exchKeyType, 
                    SSLWrappedSymWrappingKey *wswk)
{
    PRBool rv = PR_FALSE;
    PR_ASSERT(!"SSL servers are not supported on this platform. (ssl_GetWrappingKey)");    
    return rv;
}

/* This is a kind of test-and-set.  The caller passes in the new value it wants
 * to set.  This code tests the wrapped sym key entry in the shared memory.
 * If it is uninitialized, this function writes the caller's value into 
 * the disk entry, and returns false.  
 * Otherwise, it overwrites the caller's wswk with the value obtained from 
 * the disk, and returns PR_TRUE.  
 * This is all done while holding the locks/mutexes necessary to make 
 * the operation atomic.
 */
PRBool
ssl_SetWrappingKey(SSLWrappedSymWrappingKey *wswk)
{
    PRBool        rv = PR_FALSE;
    PR_ASSERT(!"SSL servers are not supported on this platform. (ssl_SetWrappingKey)");
    return rv;
}

PRUint32  
SSL_GetMaxServerCacheLocks(void)
{
    PR_ASSERT(!"SSL servers are not supported on this platform. (SSL_GetMaxServerCacheLocks)");
    return -1;
}

SECStatus 
SSL_SetMaxServerCacheLocks(PRUint32 maxLocks)
{
    PR_ASSERT(!"SSL servers are not supported on this platform. (SSL_SetMaxServerCacheLocks)");
    return SECFailure;
}

#endif /* XP_UNIX || XP_WIN32 */

/* [<][>][^][v][top][bottom][index][help] */