Milán Major

netscape-revival

1 branch
Code

security/lib/ssl/sslsnce.c

/* XXX rename this file */

/*
 * NOTE:  The contents of this file are NOT used by the client.
 * (They are part of the security library as a whole, but they are
 * NOT USED BY THE CLIENT.)  Do not change things on behalf of the
 * client (like localizing strings), or add things that are only
 * for the client (put them elsewhere).  Clearly we need a better
 * way to organize things, but for now this will have to do.
 */

#if defined(XP_UNIX) || defined(XP_WIN32)
#ifndef NADA_VERISON

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

#ifdef XP_UNIX
#include <syslog.h>
#include <fcntl.h>
#include <unistd.h>
#else /* XP_WIN32 */
#ifdef MC_HTTPD
#include <ereport.h>
#endif /* MC_HTTPD */
#include <wtypes.h>
#endif /* XP_WIN32 */
#include <sys/types.h>

#if defined(SERVER_BUILD) && defined(USE_NSPR_MT)
#include "nspr/prmon.h"
static PRMonitor *cacheLock;
#endif

/*
** The server session-id cache uses a simple flat cache. The cache is
** sized during initialization. We hash the ip-address + session-id value
** into an index into the cache and do the lookup. No buckets, nothing
** fancy.
*/

#ifdef XP_UNIX
static int SIDCacheFD = -1;
static int certCacheFD = -1;
#else /* XP_WIN32 */
static HANDLE SIDCacheFD = INVALID_HANDLE_VALUE;
static HANDLE SIDCacheFDMAP = INVALID_HANDLE_VALUE;
static char *SIDCacheData = NULL;
static HANDLE SIDCacheSem = INVALID_HANDLE_VALUE;
static int SIDCacheOffset = 0;
static HANDLE certCacheFD = INVALID_HANDLE_VALUE;
static HANDLE certCacheFDMAP = INVALID_HANDLE_VALUE;
static char *certCacheData = NULL;
static HANDLE certCacheSem = INVALID_HANDLE_VALUE;
static int certCacheOffset = 0;
#endif /* XP_WIN32 */

static int numSIDCacheEntries = 10000;
static int numCertCacheEntries = 250;

#ifdef XP_UNIX
#define DEFAULT_CACHE_DIRECTORY "/tmp"
#else /* XP_WIN32 */
#define DEFAULT_CACHE_DIRECTORY "\\temp"
#endif /* XP_UNIX */

/*
** Format of a cache entry.
*/ 
typedef struct SIDCacheEntryStr SIDCacheEntry;
struct SIDCacheEntryStr {
    union {
	struct {
	    /* This is gross.  We have to have version and valid in both arms
	     * of the union for alignment reasons.  This probably won't work
	     * on a 64-bit machine. XXXX
	     */
	    uint16 version;
	    unsigned char valid;
	    unsigned char cipherType;
	    unsigned char sessionID[SSL_SESSIONID_BYTES];
	    unsigned char masterKey[SSL_MAX_MASTER_KEY_BYTES];
	    unsigned char cipherArg[SSL_MAX_CYPHER_ARG_BYTES];

	    unsigned char masterKeyLen;
	    unsigned char keyBits;

	    unsigned char secretKeyBits;
	    unsigned char cipherArgLen;
	} ssl2;
	struct {
	    uint16 version;
	    unsigned char valid;
	    uint8 sessionIDLength;
	    unsigned char sessionID[SSL3_SESSIONID_BYTES];
	    SSL3CipherSuite cipherSuite;
	    SSL3CompressionMethod compression;
	    SSL3MasterSecret masterSecret;
	    int16 certIndex;
	    PRBool resumable;
	} ssl3;
    } u;

    unsigned int addr : 32;
    unsigned int time : 32;

};


typedef struct CertCacheEntryStr CertCacheEntry;
struct CertCacheEntryStr {
    uint16 certLength;
    uint8 sessionIDLength;
    unsigned char sessionID[SSL3_SESSIONID_BYTES];
    unsigned char cert[SSL_MAX_CACHED_CERT_LEN];
};


static void IOError(int rv, char *type);
static unsigned long Offset(unsigned long addr, unsigned char *s, unsigned nl);
static void Invalidate(SIDCacheEntry *ce);


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

/*
** Reconstitute a cert from the cache
*/
static CERTCertificate *GetCertFromCache(SIDCacheEntry *sid)
{
    unsigned long offset = sid->u.ssl3.certIndex * sizeof(CertCacheEntry);
    int rv;
    CertCacheEntry ce;
    CERTCertificate *cert;
    SECItem derCert;

#ifdef XP_UNIX
    lseek(certCacheFD, offset, SEEK_SET);
    rv = read(certCacheFD, &ce, sizeof(CertCacheEntry));
    if (rv != sizeof(CertCacheEntry)) {
	/* ACK! File is damaged! */
	IOError(rv, "read");
	return NULL;
    }
#else /* XP_WIN32 */
    /* Use memory mapped I/O and just memcpy() the data */
    certCacheOffset = offset;
    CopyMemory(&ce, &certCacheData[offset], sizeof(CertCacheEntry));
    rv = sizeof(CertCacheEntry);
#endif /* XP_WIN32 */

    /* See if the session ID matches with that in the sid cache. */
    if((ce.sessionIDLength != sid->u.ssl3.sessionIDLength) ||
       PORT_Memcmp(ce.sessionID, sid->u.ssl3.sessionID, ce.sessionIDLength)) {
	return NULL;
    }

    derCert.len = ce.certLength;
    derCert.data = ce.cert;

    cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert, NULL,
				  PR_FALSE, PR_TRUE);

    return cert;
}

/* Put a certificate in the cache.  We assume that the certIndex in
** sid is valid.
*/
static void
CacheCert(CERTCertificate *cert, SIDCacheEntry *sid)
{
    CertCacheEntry ce;
    int16 index = sid->u.ssl3.certIndex;
    unsigned long offset = index * sizeof(CertCacheEntry);
    int rv;
    
    if (cert->derCert.len > SSL_MAX_CACHED_CERT_LEN)
	return;
    
    ce.sessionIDLength = sid->u.ssl3.sessionIDLength;
    PORT_Memcpy(ce.sessionID, sid->u.ssl3.sessionID, ce.sessionIDLength);
    ce.certLength = cert->derCert.len;
    PORT_Memcpy(ce.cert, cert->derCert.data, ce.certLength);

#ifdef XP_UNIX
    lseek(certCacheFD, offset, SEEK_SET);
    rv = write(certCacheFD, &ce, sizeof(CertCacheEntry));
    if (rv != sizeof(CertCacheEntry)) {
	/* ACK! File is damaged! */
	IOError(rv, "cert-write");
	Invalidate(sid);
    }
#else /* WIN32 */
    /* Use memory mapped I/O and just memcpy() the data */
    certCacheOffset = offset;
    CopyMemory(&certCacheData[offset], &ce, sizeof(CertCacheEntry));
#endif /* XP_UNIX */
    return;
}

/*
** Convert memory based SID to file based one
*/
static void ConvertFromSID(SIDCacheEntry *to, SSLSessionID *from)
{
    to->u.ssl2.valid = 1;
    to->u.ssl2.version = from->version;
    to->addr = from->addr;
    to->time = from->time;

    if (from->version == SSL_LIBRARY_VERSION_2) {
	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",
		     SSL_GETPID(), from->u.ssl2.masterKey.len,
		     from->u.ssl2.cipherArg.len));
	    to->u.ssl2.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;
	PORT_Memcpy(to->u.ssl2.sessionID, from->u.ssl2.sessionID,
		  sizeof(to->u.ssl2.sessionID));
	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%x cipherType=%d", SSL_GETPID(),
		    to->u.ssl2.masterKeyLen, to->u.ssl2.cipherArgLen,
		    to->time, to->addr, to->u.ssl2.cipherType));
    } else {
	to->u.ssl3.sessionIDLength = from->u.ssl3.sessionIDLength;
	to->u.ssl3.cipherSuite = from->u.ssl3.cipherSuite;
	to->u.ssl3.compression = from->u.ssl3.compression;
	to->u.ssl3.resumable = from->u.ssl3.resumable;
	PORT_Memcpy(to->u.ssl3.sessionID, from->u.ssl3.sessionID,
		  to->u.ssl3.sessionIDLength);
	PORT_Memcpy(to->u.ssl3.masterSecret, from->u.ssl3.masterSecret,
		  sizeof(to->u.ssl3.masterSecret));

	SSL_TRC(8, ("%d: SSL3: ConvertSID: time=%d addr=0x%x cipherSuite=%d",
		    SSL_GETPID(), to->time, to->addr, to->u.ssl3.cipherSuite));
    }
}

/*
** Convert file based cache-entry to memory based one
*/
static SSLSessionID *ConvertToSID(SIDCacheEntry *from)
{
    SSLSessionID *to;
    uint16 version = from->u.ssl2.version;

    to = (SSLSessionID*) PORT_ZAlloc(sizeof(SSLSessionID));
    if (!to) {
	return 0;
    }

    if (version == SSL_LIBRARY_VERSION_2) {
	/* 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;
	PORT_Memcpy(to->u.ssl2.sessionID, from->u.ssl2.sessionID,
		  sizeof(from->u.ssl2.sessionID));
	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%x cipherType=%d",
		    SSL_GETPID(), to->u.ssl2.masterKey.len,
		    to->u.ssl2.cipherArg.len, to->time, to->addr,
		    to->u.ssl2.cipherType));
    } else {
	/* This is an SSL v3 session */
	to->u.ssl3.sessionIDLength = from->u.ssl3.sessionIDLength;
	PORT_Memcpy(to->u.ssl3.sessionID, from->u.ssl3.sessionID,
		  to->u.ssl3.sessionIDLength);
	to->u.ssl3.cipherSuite = from->u.ssl3.cipherSuite;
	to->u.ssl3.compression = from->u.ssl3.compression;
	PORT_Memcpy(to->u.ssl3.masterSecret, from->u.ssl3.masterSecret,
		  sizeof(from->u.ssl3.masterSecret));
	to->u.ssl3.resumable = from->u.ssl3.resumable;

	if (from->u.ssl3.certIndex != -1) {
	    to->peerCert = GetCertFromCache(from);
	    if (to->peerCert == NULL)
		goto loser;
	}
    }

    to->version = from->u.ssl2.version;
    to->time = from->time;
    to->cached = in_cache;
    to->addr = from->addr;
    to->references = 1;
    
    return to;

  loser:
    Invalidate(from);
    if (to) {
	if (version == SSL_LIBRARY_VERSION_2) {
	    if (to->u.ssl2.masterKey.data)
		PORT_Free(to->u.ssl2.masterKey.data);
	    if (to->u.ssl2.cipherArg.data)
		PORT_Free(to->u.ssl2.cipherArg.data);
	}
	PORT_Free(to);
    }
    return NULL;
}

/* Invalidate a cache entry. */
static void Invalidate(SIDCacheEntry *ce)
{
    int rv;
    unsigned long offset;

    if (ce == NULL) return;

    if (ce->u.ssl2.version == SSL_LIBRARY_VERSION_2) {
	offset = Offset(ce->addr, ce->u.ssl2.sessionID,
			sizeof(ce->u.ssl2.sessionID)); 
    } else {
	offset = Offset(ce->addr, ce->u.ssl3.sessionID,
			ce->u.ssl3.sessionIDLength); 
    }
	
    ce->u.ssl2.valid = 0;
#ifdef XP_UNIX
    lseek(SIDCacheFD, -sizeof(ce), SEEK_CUR);
    rv = write(SIDCacheFD, ce, sizeof(SIDCacheEntry));
    if (rv != sizeof(SIDCacheEntry)) {
	/* ACK! File is damaged! */
	IOError(rv, "timeout-write");
    }
#else /* WIN32 */
    /* Use memory mapped I/O and just memcpy() the data */
    SIDCacheOffset = offset;
    CopyMemory(&SIDCacheData[offset], ce, sizeof(SIDCacheEntry));
#endif /* XP_UNIX */
}


static void IOError(int rv, char *type)
{
#ifdef XP_UNIX
    syslog(LOG_ALERT,
	   "SSL: %s error with session-id cache, pid=%d, rv=%d, error='%m'",
	   type, SSL_GETPID(), rv);
#else /* XP_WIN32 */
#ifdef MC_HTTPD 
	ereport(LOG_FAILURE, "%s error with session-id cache rv=%d\n",type, rv);
#endif /* MC_HTTPD */
#endif /* XP_UNIX */
}

static void lock_cache(void)
{
#if defined(SERVER_BUILD) && defined(USE_NSPR_MT)
    PR_EnterMonitor(cacheLock);
#endif
}

static void unlock_cache(void)
{
#if defined(SERVER_BUILD) && defined(USE_NSPR_MT)
    PR_ExitMonitor(cacheLock);
#endif
}

/*
** Perform some mumbo jumbo on the ip-address and the session-id value to
** compute a hash value.
*/
static unsigned long Offset(unsigned long addr, unsigned char *s, unsigned nl)
{
    unsigned long rv;

    rv = addr ^ (((unsigned long)s[0] << 24) + ((unsigned long)s[1] << 16)
		 + (s[2] << 8) + s[nl-1]);
    return (rv % ((unsigned long)numSIDCacheEntries)) * sizeof(SIDCacheEntry);
}

/*
** Look something up in the cache. This will invalidate old entries
** in the process. Caller has locked the cache!
*/
static PRBool FindSID(unsigned long addr, unsigned char *sessionID,
		      unsigned sessionIDLength, SIDCacheEntry *ce)
{
    unsigned long offset;
    time_t now;
    int rv;

    /* Read in cache entry after hashing ip address and session-id value */
    offset = Offset(addr, sessionID, sessionIDLength); 
    now = PORT_Time();
#ifdef XP_UNIX
    lseek(SIDCacheFD, offset, SEEK_SET);
    rv = read(SIDCacheFD, ce, sizeof(SIDCacheEntry));
    if (rv != sizeof(SIDCacheEntry)) {
	/* ACK! File is damaged! */
	IOError(rv, "read");
	return PR_FALSE;
    }
#else /* XP_WIN32 */
    /* Use memory mapped I/O and just memcpy() the data */
    SIDCacheOffset = offset;
    CopyMemory(ce, &SIDCacheData[offset], sizeof(SIDCacheEntry));
    rv = sizeof(SIDCacheEntry);
#endif /* XP_WIN32 */

    if (!ce->u.ssl2.valid) {
	/* Entry is not valid */
	return PR_FALSE;
    }

    if (((ce->u.ssl2.version == SSL_LIBRARY_VERSION_2) &&
	 (now > ce->time + ssl_sid_timeout)) ||
	((ce->u.ssl2.version == SSL_LIBRARY_VERSION_3_0) &&
	 (now > ce->time + ssl3_sid_timeout))) {
	/* SessionID has timed out. Invalidate the entry. */
	SSL_TRC(7, ("%d: timed out sid entry addr=%08x now=%x time+=%x",
		    SSL_GETPID(), ce->addr, now, ce->time + ssl_sid_timeout));
	ce->u.ssl2.valid = 0;
#ifdef XP_UNIX
	lseek(SIDCacheFD, offset, SEEK_SET);
	rv = write(SIDCacheFD, ce, sizeof(SIDCacheEntry));
	if (rv != sizeof(SIDCacheEntry)) {
	    /* ACK! File is damaged! */
	    IOError(rv, "timeout-write");
	}
#else /* WIN32 */
	/* Use memory mapped I/O and just memcpy() the data */
	SIDCacheOffset = offset;
	CopyMemory(&SIDCacheData[offset], ce, sizeof(SIDCacheEntry));
#endif /* XP_UNIX */
	return PR_FALSE;
    }

    /*
    ** Finally, examine specific session-id/addr data to see if the cache
    ** entry matches our addr+session-id value
    */
    if ((ce->addr == addr) &&
	(PORT_Memcmp(ce->u.ssl2.sessionID, sessionID, sessionIDLength) == 0)) {
	/* Found it */
	return PR_TRUE;
    }
    return PR_FALSE;
}

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


static SSLSessionID *ServerSessionIDLookup(unsigned long addr,
						     unsigned char *sessionID,
						     unsigned sessionIDLength)
{
    SIDCacheEntry ce;
    SSLSessionID *sid;

    sid = 0;
    lock_cache();
    if (FindSID(addr, sessionID, sessionIDLength, &ce)) {
	/* Found it. Convert file format to internal format */
	sid = ConvertToSID(&ce);
    }
    unlock_cache();
    return sid;
}

/*
** Place an sid into the cache, if it isn't already there. Note that if
** some other server process has replaced a session-id cache entry that has
** the same cache index as this sid, then all is ok. Somebody has to lose
** when this condition occurs, so it might as well be this sid.
*/
static void ServerSessionIDCache(SSLSessionID *sid)
{
    SIDCacheEntry ce;
    unsigned long offset;
    int rv;
    uint16 version = sid->version;

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

    if (sid->cached == never_cached) {
	lock_cache();

	sid->time = PORT_Time();
	if (version == SSL_LIBRARY_VERSION_2) {
	    SSL_TRC(8, ("%d: SSL: CacheMT: cached=%d addr=0x%08x time=%x "
			"cipher=%d", SSL_GETPID(), sid->cached, sid->addr,
			sid->time, sid->u.ssl2.cipherType));
	    PRINT_BUF(8, (0, "sessionID:", sid->u.ssl2.sessionID,
			  sizeof(sid->u.ssl2.sessionID)));
	    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));

	    /* Write out new cache entry */
	    offset = Offset(sid->addr, sid->u.ssl2.sessionID,
			    sizeof(sid->u.ssl2.sessionID)); 
	} else {
	    SSL_TRC(8, ("%d: SSL: CacheMT: cached=%d addr=0x%08x time=%x "
			"cipherSuite=%d", SSL_GETPID(), sid->cached, sid->addr,
			sid->time, sid->u.ssl3.cipherSuite));
	    PRINT_BUF(8, (0, "sessionID:", sid->u.ssl3.sessionID,
			  sid->u.ssl3.sessionIDLength));

	    offset = Offset(sid->addr, sid->u.ssl3.sessionID,
			    sid->u.ssl3.sessionIDLength);
	    
	}

	ConvertFromSID(&ce, sid);
	if (version == SSL_LIBRARY_VERSION_3_0) {
	    if (sid->peerCert == NULL) {
		ce.u.ssl3.certIndex = -1;
	    } else {
		ce.u.ssl3.certIndex = offset % numCertCacheEntries;
	    }
	}
	
#ifdef XP_UNIX
	lseek(SIDCacheFD, offset, SEEK_SET);
	rv = write(SIDCacheFD, &ce, sizeof(ce));
	if (rv != sizeof(ce)) {
	    IOError(rv, "update-write");
	}
#else /* WIN32 */
	SIDCacheOffset = offset;
	CopyMemory(&SIDCacheData[offset], &ce, sizeof(ce));
#endif /* XP_UNIX */

	if ((version == SSL_LIBRARY_VERSION_3_0) && (sid->peerCert != NULL)) {
	    CacheCert(sid->peerCert, &ce);
	}

	unlock_cache();
    }
}

static void ServerSessionIDUncache(SSLSessionID *sid)
{
    SIDCacheEntry ce;
    int rv;

    if (sid == NULL) return;
    
    lock_cache();
    if (sid->version == SSL_LIBRARY_VERSION_2) {
	SSL_TRC(8, ("%d: SSL: UncacheMT: valid=%d addr=0x%08x time=%x "
		    "cipher=%d", SSL_GETPID(), sid->cached, sid->addr,
		    sid->time, sid->u.ssl2.cipherType));
	PRINT_BUF(8, (0, "sessionID:", sid->u.ssl2.sessionID,
		      sizeof(sid->u.ssl2.sessionID)));
	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));
	rv = FindSID(sid->addr, sid->u.ssl2.sessionID,
		     sizeof(sid->u.ssl2.sessionID), &ce);
    } else {
	SSL_TRC(8, ("%d: SSL3: UncacheMT: valid=%d addr=0x%08x time=%x "
		    "cipherSuite=%d", SSL_GETPID(), sid->cached, sid->addr,
		    sid->time, sid->u.ssl3.cipherSuite));
	PRINT_BUF(8, (0, "sessionID:", sid->u.ssl3.sessionID,
		      sid->u.ssl3.sessionIDLength));
	rv = FindSID(sid->addr, sid->u.ssl3.sessionID,
		     sid->u.ssl3.sessionIDLength, &ce);
    }
    
    if (rv) {
	/* Back up one entry */
#ifdef XP_UNIX
	lseek(SIDCacheFD, -sizeof(ce), SEEK_CUR);
	SSL_TRC(7, ("%d: SSL: uncaching session-id at offset %d",
		    SSL_GETPID(), lseek(SIDCacheFD, 0, SEEK_CUR)));
	ce.u.ssl2.valid = 0;
	rv = write(SIDCacheFD, &ce, sizeof(ce));
	if (rv != sizeof(ce)) {
	    IOError(rv, "remove-write");
	}
#else /* WIN32 */
	CopyMemory(&SIDCacheData[SIDCacheOffset], &ce, sizeof(ce));
#endif /* XP_UNIX */
    }
    unlock_cache();
}

/*
** Zero a file out to nb bytes
*/
#ifdef XP_UNIX
static SECStatus ZeroFile(int fd, int nb)
{
    char buf[16384];
    int amount, rv;

    PORT_Memset(buf, 0, sizeof(buf));
    (void) lseek(fd, 0, SEEK_SET);

    while (nb) {
	amount = nb;
	if (amount > sizeof(buf)) {
	    amount = sizeof(buf);
	}
	rv = write(fd, buf, amount);
	if (rv != amount) {
	    IOError(rv, "zero-write");
	    return SECFailure;
	}
	nb -= amount;
    }
    return SECSuccess;
}
#endif /* XP_UNIX */

static SECStatus InitSessionIDCache(int maxCacheEntries, time_t timeout,
				   time_t ssl3_timeout, const char *directory)
{
    char *cfn;
    int rv;
#ifdef XP_UNIX
    if (SIDCacheFD >= 0) {
	/* Already done */
	return SECSuccess;
    }
#else /* WIN32 */
	if(SIDCacheFD != INVALID_HANDLE_VALUE) {
	/* Already done */
	return SECSuccess;
	}
#endif /* XP_UNIX */


    /* Create file names */
    cfn = (char*) PORT_Alloc(PORT_Strlen(directory) + 100);
    if (!cfn) {
	return SECFailure;
    }
#ifdef XP_UNIX
    sprintf(cfn, "%s/.sslsidc.%d", directory, getpid());
#else /* XP_WIN32 */
	sprintf(cfn, "%s\\ssl.sidc.%d.%d", directory,
			GetCurrentProcessId(), GetCurrentThreadId());
#endif /* XP_WIN32 */

    /* Create session-id cache file */
#ifdef XP_UNIX
    (void) unlink(cfn);
    SIDCacheFD = open(cfn, O_CREAT|O_RDWR, 0600);
    if (SIDCacheFD < 0) {
	IOError(SIDCacheFD, "create");
	goto loser;
    }
    rv = unlink(cfn);
    if (rv < 0) {
	IOError(rv, "unlink");
	goto loser;
    }
#else  /* WIN32 */
    SIDCacheFDMAP = CreateFileMapping((HANDLE)0xffffffff, NULL, PAGE_READWRITE,
				      0, numSIDCacheEntries *
				      sizeof(SIDCacheEntry), NULL);
    if(!(SIDCacheFDMAP))
	goto loser;
    SIDCacheData = (char *) MapViewOfFile(SIDCacheFDMAP, FILE_MAP_READ |
				       FILE_MAP_WRITE, 0, 0, 
				       numSIDCacheEntries *
				       sizeof(SIDCacheEntry));
    if(!(SIDCacheData))
	goto loser;
#endif /* XP_UNIX */

#if defined(SERVER_BUILD) && defined(USE_NSPR_MT)
    cacheLock = PR_NewMonitor();
#endif

    if (maxCacheEntries) {
	numSIDCacheEntries = maxCacheEntries;
    }
    if (timeout) {   
	if (timeout > 100) {
	    timeout = 100;
	}
	if (timeout < 5) {
	    timeout = 5;
	}
	ssl_sid_timeout = timeout;
    }

    if (ssl3_timeout) {   
	if (ssl3_timeout > 86400L) {
	    ssl3_timeout = 86400L;
	}
	if (ssl3_timeout < 5) {
	    ssl3_timeout = 5;
	}
	ssl3_sid_timeout = ssl3_timeout;
    }

#ifdef XP_UNIX
    /* Initialize the files */
    if (ZeroFile(SIDCacheFD, numSIDCacheEntries * sizeof(SIDCacheEntry))) {
	/* Bummer */
	close(SIDCacheFD);
	SIDCacheFD = -1;
	goto loser;
    }
#else /* XP_WIN32 */
	ZeroMemory(SIDCacheData, numSIDCacheEntries * sizeof(SIDCacheEntry));
#endif /* XP_UNIX */
    PORT_Free(cfn);
    return SECSuccess;

  loser:
    PORT_Free(cfn);
    return SECFailure;
}

static SECStatus InitCertCache(const char *directory)
{
    char *cfn;
    int rv;
#ifdef XP_UNIX
    if (certCacheFD >= 0) {
	/* Already done */
	return SECSuccess;
    }
#else /* WIN32 */
	if(certCacheFD != INVALID_HANDLE_VALUE) {
	/* Already done */
	return SECSuccess;
	}
#endif /* XP_UNIX */


    /* Create file names */
    cfn = (char*) PORT_Alloc(PORT_Strlen(directory) + 100);
    if (!cfn) {
	return SECFailure;
    }
#ifdef XP_UNIX
    sprintf(cfn, "%s/.sslcertc.%d", directory, getpid());
#else /* XP_WIN32 */
	sprintf(cfn, "%s\\ssl.certc.%d.%d", directory,
			GetCurrentProcessId(), GetCurrentThreadId());
#endif /* XP_WIN32 */

    /* Create certificate cache file */
#ifdef XP_UNIX
    (void) unlink(cfn);
    certCacheFD = open(cfn, O_CREAT|O_RDWR, 0600);
    if (certCacheFD < 0) {
	IOError(certCacheFD, "create");
	goto loser;
    }
    rv = unlink(cfn);
    if (rv < 0) {
	IOError(rv, "unlink");
	goto loser;
    }
#else  /* WIN32 */
    certCacheFDMAP = CreateFileMapping((HANDLE)0xffffffff, NULL, PAGE_READWRITE,
				       0, numCertCacheEntries *
				       sizeof(CertCacheEntry), NULL);
    if(!(certCacheFDMAP))
	goto loser;
    certCacheData = (char *) MapViewOfFile(certCacheFDMAP, FILE_MAP_READ |
					   FILE_MAP_WRITE, 0, 0, 
					   numCertCacheEntries *
					   sizeof(CertCacheEntry));
    if(!(certCacheData))
	goto loser;
#endif /* XP_UNIX */

#ifdef XP_UNIX
    /* Initialize the files */
    if (ZeroFile(certCacheFD, numCertCacheEntries * sizeof(CertCacheEntry))) {
	/* Bummer */
	close(certCacheFD);
	certCacheFD = -1;
	goto loser;
    }
#else /* XP_WIN32 */
	ZeroMemory(certCacheData, numCertCacheEntries * sizeof(CertCacheEntry));
#endif /* XP_UNIX */
    PORT_Free(cfn);
    return SECSuccess;

  loser:
    PORT_Free(cfn);
    return SECFailure;
}

int SSL_ConfigServerSessionIDCache(int maxCacheEntries, time_t timeout,
				   time_t ssl3_timeout, const char *directory)
{
    SECStatus rv;

    PORT_Assert(sizeof(SIDCacheEntry) == 128);

    if (!directory) {
	directory = DEFAULT_CACHE_DIRECTORY;
    }
    rv = InitSessionIDCache(maxCacheEntries, timeout, ssl3_timeout, directory);
    if (rv) return -1;
    rv = InitCertCache(directory);
    if (rv) return -1;

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

#endif /* NADA_VERISON */
#endif /* XP_UNIX || XP_WIN32 */