diff options
Diffstat (limited to 'security/nss/lib/ssl/sslsnce.c')
-rw-r--r-- | security/nss/lib/ssl/sslsnce.c | 1899 |
1 files changed, 1899 insertions, 0 deletions
diff --git a/security/nss/lib/ssl/sslsnce.c b/security/nss/lib/ssl/sslsnce.c new file mode 100644 index 000000000..84f3a99f0 --- /dev/null +++ b/security/nss/lib/ssl/sslsnce.c @@ -0,0 +1,1899 @@ +/* This file implements the SERVER Session ID cache. + * NOTE: The contents of this file are NOT used by the client. + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + * + * $Id$ + */ + +/* Note: ssl_FreeSID() in sslnonce.c gets used for both client and server + * cache sids! + * + * About record locking among different server processes: + * + * All processes that are part of the same conceptual server (serving on + * the same address and port) MUST share a common SSL session cache. + * This code makes the content of the shared cache accessible to all + * processes on the same "server". This code works on Unix and Win32 only, + * and is platform specific. + * + * Unix: Multiple processes share a single (inherited) FD for a disk + * file all share one single file position. If one lseeks, the position for + * all processes is changed. Since the set of platforms we support do not + * all share portable lseek-and-read or lseek-and-write functions, a global + * lock must be used to make the lseek call and the subsequent read or write + * call be one atomic operation. It is no longer necessary for cache element + * sizes to be a power of 2, or a multiple of a sector size. + * + * For Win32, where (a) disk I/O is not atomic, and (b) we use memory-mapped + * files and move data to & from memory instead of calling read or write, + * we must do explicit locking of the records for all reads and writes. + * We have just one lock, for the entire file, using an NT semaphore. + * We avoid blocking on "local threads" since it's bad to block on a local + * thread - If NSPR offered portable semaphores, it would handle this itself. + * + * Since this file has to do lots of platform specific I/O, the system + * dependent error codes need to be mapped back into NSPR error codes. + * Since NSPR's error mapping functions are private, the code is necessarily + * duplicated in libSSL. + * + * Note, now that NSPR provides portable anonymous shared memory, for all + * platforms except Mac, the implementation below should be replaced with + * one that uses anonymous shared memory ASAP. This will eliminate most + * platform dependent code in this file, and improve performance big time. + * + * Now that NSPR offers portable cross-process locking (semaphores) on Unix + * and Win32, semaphores should be used here for all platforms. + */ +#include "seccomon.h" + +#if defined(XP_UNIX) || defined(XP_WIN32) +#ifndef NADA_VERISON + +#include "cert.h" +#include "ssl.h" +#include "sslimpl.h" +#include "sslproto.h" +#include "pk11func.h" +#include "base64.h" + +#include <stdio.h> + +#ifdef XP_UNIX + +#include <syslog.h> +#include <fcntl.h> +#include <unistd.h> +#include "unix_err.h" + +#else /* XP_WIN32 */ +#ifdef MC_HTTPD +#include <ereport.h> +#endif /* MC_HTTPD */ +#include <wtypes.h> +#include "win32err.h" +#endif /* XP_WIN32 */ +#include <sys/types.h> + +#define SET_ERROR_CODE /* reminder */ + +#include "nspr.h" +#include "nsslocks.h" + +static PRLock *cacheLock; + +/* +** 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. +*/ + +static PRBool isMultiProcess = PR_FALSE; + +static PRUint32 numSIDCacheEntries = 10000; +static PRUint32 sidCacheFileSize; +static PRUint32 sidCacheWrapOffset; + +static PRUint32 numCertCacheEntries = 250; +static PRUint32 certCacheFileSize; + +#define MIN_CERT_CACHE_ENTRIES 125 /* the effective size in old releases. */ + + +/* +** Format of a cache entry. +*/ +typedef struct SIDCacheEntryStr SIDCacheEntry; +struct SIDCacheEntryStr { + PRUint32 addr; + PRUint32 time; + + 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 + */ +/* 2 */ uint16 version; +/* 1 */ unsigned char valid; +/* 1 */ unsigned char cipherType; + +/* 16 */ unsigned char sessionID[SSL_SESSIONID_BYTES]; +/* 64 */ unsigned char masterKey[SSL_MAX_MASTER_KEY_BYTES]; +/* 32 */ unsigned char cipherArg[SSL_MAX_CYPHER_ARG_BYTES]; + +/* 1 */ unsigned char masterKeyLen; +/* 1 */ unsigned char keyBits; + +/* 1 */ unsigned char secretKeyBits; +/* 1 */ unsigned char cipherArgLen; +/*120 */} ssl2; + + struct { +/* 2 */ uint16 version; +/* 1 */ unsigned char valid; +/* 1 */ uint8 sessionIDLength; + +/* 32 */ unsigned char sessionID[SSL3_SESSIONID_BYTES]; + +/* 2 */ ssl3CipherSuite cipherSuite; +/* 2 */ uint16 compression; /* SSL3CompressionMethod */ + +/*122 */ ssl3SidKeys keys; /* keys and ivs, wrapped as needed. */ +/* 4 */ PRUint32 masterWrapMech; +/* 4 */ SSL3KEAType exchKeyType; + +/* 2 */ int16 certIndex; +/* 1 */ uint8 hasFortezza; +/* 1 */ uint8 resumable; + } ssl3; + /* We can't make this struct fit in 128 bytes + * so, force the struct size up to the next power of two. + */ + struct { + unsigned char filler[248]; /* 248 + 4 + 4 == 256 */ + } force256; + } u; +}; + + +typedef struct CertCacheEntryStr CertCacheEntry; + +/* The length of this struct is supposed to be a power of 2, e.g. 4KB */ +struct CertCacheEntryStr { + uint16 certLength; /* 2 */ + uint16 sessionIDLength; /* 2 */ + unsigned char sessionID[SSL3_SESSIONID_BYTES]; /* 32 */ + unsigned char cert[SSL_MAX_CACHED_CERT_LEN]; /* 4060 */ +}; /* total 4096 */ + + +static void IOError(int rv, char *type); +static PRUint32 Offset(PRUint32 addr, unsigned char *s, unsigned nl); +static void Invalidate(SIDCacheEntry *sce); + +/************************************************************************/ + +static const char envVarName[] = { SSL_ENV_VAR_NAME }; + +#ifdef _WIN32 + +struct winInheritanceStr { + PRUint32 numSIDCacheEntries; + PRUint32 sidCacheFileSize; + PRUint32 sidCacheWrapOffset; + PRUint32 numCertCacheEntries; + PRUint32 certCacheFileSize; + + DWORD parentProcessID; + HANDLE parentProcessHandle; + HANDLE SIDCacheFDMAP; + HANDLE certCacheFDMAP; + HANDLE svrCacheSem; +}; + +typedef struct winInheritanceStr winInheritance; + +static HANDLE svrCacheSem = INVALID_HANDLE_VALUE; + +static char * SIDCacheData = NULL; +static HANDLE SIDCacheFD = INVALID_HANDLE_VALUE; +static HANDLE SIDCacheFDMAP = INVALID_HANDLE_VALUE; + +static char * certCacheData = NULL; +static HANDLE certCacheFD = INVALID_HANDLE_VALUE; +static HANDLE certCacheFDMAP = INVALID_HANDLE_VALUE; + +static PRUint32 myPid; + +/* The presence of the TRUE element in this struct makes the semaphore + * inheritable. The NULL means use process's default security descriptor. + */ +static SECURITY_ATTRIBUTES semaphoreAttributes = + { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; + +static SECURITY_ATTRIBUTES sidCacheFDMapAttributes = + { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; + +static SECURITY_ATTRIBUTES certCacheFDMapAttributes = + { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; + +#define DEFAULT_CACHE_DIRECTORY "\\temp" + +static SECStatus +createServerCacheSemaphore(void) +{ + PR_ASSERT(svrCacheSem == INVALID_HANDLE_VALUE); + + /* inheritable, starts signalled, 1 signal max, no file name. */ + svrCacheSem = CreateSemaphore(&semaphoreAttributes, 1, 1, NULL); + if (svrCacheSem == NULL) { + svrCacheSem = INVALID_HANDLE_VALUE; + /* We could get the error code, but what could be do with it ? */ + nss_MD_win32_map_default_error(GetLastError()); + return SECFailure; + } + return SECSuccess; +} + +static SECStatus +_getServerCacheSemaphore(void) +{ + DWORD event; + DWORD lastError; + SECStatus rv; + + PR_ASSERT(svrCacheSem != INVALID_HANDLE_VALUE); + if (svrCacheSem == INVALID_HANDLE_VALUE && + SECSuccess != createServerCacheSemaphore()) { + return SECFailure; /* what else ? */ + } + event = WaitForSingleObject(svrCacheSem, INFINITE); + switch (event) { + case WAIT_OBJECT_0: + case WAIT_ABANDONED: + rv = SECSuccess; + break; + + case WAIT_TIMEOUT: + case WAIT_IO_COMPLETION: + default: /* should never happen. nothing we can do. */ + PR_ASSERT(("WaitForSingleObject returned invalid value.", 0)); + /* fall thru */ + + case WAIT_FAILED: /* failure returns this */ + rv = SECFailure; + lastError = GetLastError(); /* for debugging */ + nss_MD_win32_map_default_error(lastError); + break; + } + return rv; +} + +static void +_doGetServerCacheSemaphore(void * arg) +{ + SECStatus * rv = (SECStatus *)arg; + *rv = _getServerCacheSemaphore(); +} + +static SECStatus +getServerCacheSemaphore(void) +{ + PRThread * selectThread; + PRThread * me = PR_GetCurrentThread(); + PRThreadScope scope = PR_GetThreadScope(me); + SECStatus rv = SECFailure; + + if (scope == PR_GLOBAL_THREAD) { + rv = _getServerCacheSemaphore(); + } else { + selectThread = PR_CreateThread(PR_USER_THREAD, + _doGetServerCacheSemaphore, &rv, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, 0); + if (selectThread != NULL) { + /* rv will be set by _doGetServerCacheSemaphore() */ + PR_JoinThread(selectThread); + } + } + return rv; +} + +static SECStatus +releaseServerCacheSemaphore(void) +{ + BOOL success = FALSE; + + PR_ASSERT(svrCacheSem != INVALID_HANDLE_VALUE); + if (svrCacheSem != INVALID_HANDLE_VALUE) { + /* Add 1, don't want previous value. */ + success = ReleaseSemaphore(svrCacheSem, 1, NULL); + } + if (!success) { + nss_MD_win32_map_default_error(GetLastError()); + return SECFailure; + } + return SECSuccess; +} + +static void +destroyServerCacheSemaphore(void) +{ + PR_ASSERT(svrCacheSem != INVALID_HANDLE_VALUE); + if (svrCacheSem != INVALID_HANDLE_VALUE) { + CloseHandle(svrCacheSem); + /* ignore error */ + svrCacheSem = INVALID_HANDLE_VALUE; + } +} + +#define GET_SERVER_CACHE_READ_LOCK(fd, offset, size) \ + if (isMultiProcess) getServerCacheSemaphore(); + +#define GET_SERVER_CACHE_WRITE_LOCK(fd, offset, size) \ + if (isMultiProcess) getServerCacheSemaphore(); + +#define RELEASE_SERVER_CACHE_LOCK(fd, offset, size) \ + if (isMultiProcess) releaseServerCacheSemaphore(); + +#endif /* _win32 */ + +/************************************************************************/ + +#ifdef XP_UNIX +static int SIDCacheFD = -1; +static int certCacheFD = -1; + +static pid_t myPid; + +struct unixInheritanceStr { + PRUint32 numSIDCacheEntries; + PRUint32 sidCacheFileSize; + PRUint32 sidCacheWrapOffset; + PRUint32 numCertCacheEntries; + PRUint32 certCacheFileSize; + + PRInt32 SIDCacheFD; + PRInt32 certCacheFD; +}; + +typedef struct unixInheritanceStr unixInheritance; + + +#define DEFAULT_CACHE_DIRECTORY "/tmp" + +#ifdef TRACE +static void +fcntlFailed(struct flock *lock) +{ + fprintf(stderr, + "fcntl failed, errno = %d, PR_GetError = %d, lock.l_type = %d\n", + errno, PR_GetError(), lock->l_type); + fflush(stderr); +} +#define FCNTL_FAILED(lock) fcntlFailed(lock) +#else +#define FCNTL_FAILED(lock) +#endif + +/* NOTES: Because there are no atomic seek-and-read and seek-and-write +** functions that are supported on all our UNIX platforms, we need +** to prevent all simultaeous seek-and-read operations. For that reason, +** we use mutually exclusive (write) locks for read and write operations, +** and use them all at the same offset (zero). +*/ +static SECStatus +_getServerCacheLock(int fd, short type, PRUint32 offset, PRUint32 size) +{ + int result; + struct flock lock; + + memset(&lock, 0, sizeof lock); + lock.l_type = /* type */ F_WRLCK; + lock.l_whence = SEEK_SET; /* absolute file offsets. */ + lock.l_start = 0; + lock.l_len = 128; + +#ifdef TRACE + fprintf(stderr, "%d: %s lock, offset %8x, size %4d\n", myPid, + (type == F_RDLCK) ? "read " : "write", offset, size); + fflush(stderr); +#endif + result = fcntl(fd, F_SETLKW, &lock); + if (result == -1) { + nss_MD_unix_map_default_error(errno); + FCNTL_FAILED(&lock); + return SECFailure; + } +#ifdef TRACE + fprintf(stderr, "%d: got lock, offset %8x, size %4d\n", + myPid, offset, size); + fflush(stderr); +#endif + return SECSuccess; +} + +typedef struct sslLockArgsStr { + PRUint32 offset; + PRUint32 size; + PRErrorCode err; + SECStatus rv; + int fd; + short type; +} sslLockArgs; + +static void +_doGetServerCacheLock(void * arg) +{ + sslLockArgs * args = (sslLockArgs *)arg; + args->rv = _getServerCacheLock(args->fd, args->type, args->offset, + args->size ); + if (args->rv != SECSuccess) { + args->err = PR_GetError(); + } +} + +static SECStatus +getServerCacheLock(int fd, short type, PRUint32 offset, PRUint32 size) +{ + PRThread * selectThread; + PRThread * me = PR_GetCurrentThread(); + PRThreadScope scope = PR_GetThreadScope(me); + SECStatus rv = SECFailure; + + if (scope == PR_GLOBAL_THREAD) { + rv = _getServerCacheLock(fd, type, offset, size); + } else { + /* Ib some platforms, one thread cannot read local/automatic + ** variables from another thread's stack. So, get this space + ** from the heap, not the stack. + */ + sslLockArgs * args = PORT_New(sslLockArgs); + + if (!args) + return rv; + + args->offset = offset; + args->size = size; + args->rv = SECFailure; + args->fd = fd; + args->type = type; + selectThread = PR_CreateThread(PR_USER_THREAD, + _doGetServerCacheLock, args, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, 0); + if (selectThread != NULL) { + /* rv will be set by _doGetServerCacheLock() */ + PR_JoinThread(selectThread); + rv = args->rv; + if (rv != SECSuccess) { + PORT_SetError(args->err); + } + } + PORT_Free(args); + } + return rv; +} + +static SECStatus +releaseServerCacheLock(int fd, PRUint32 offset, PRUint32 size) +{ + int result; + struct flock lock; + + memset(&lock, 0, sizeof lock); + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; /* absolute file offsets. */ + lock.l_start = 0; + lock.l_len = 128; + +#ifdef TRACE + fprintf(stderr, "%d: unlock, offset %8x, size %4d\n", + myPid, offset, size); + fflush(stderr); +#endif + result = fcntl(fd, F_SETLK, &lock); + if (result == -1) { + nss_MD_unix_map_default_error(errno); + FCNTL_FAILED(&lock); + return SECFailure; + } + return SECSuccess; +} + + +/* these defines take the arguments needed to do record locking, + * however the present implementation does only file locking. + */ + +#define GET_SERVER_CACHE_READ_LOCK( fd, offset, size) \ + if (isMultiProcess) getServerCacheLock(fd, F_RDLCK, offset, size); + +#define GET_SERVER_CACHE_WRITE_LOCK(fd, offset, size) \ + if (isMultiProcess) getServerCacheLock(fd, F_WRLCK, offset, size); + +#define RELEASE_SERVER_CACHE_LOCK( fd, offset, size) \ + if (isMultiProcess) releaseServerCacheLock(fd, offset, size); + +/* +** Zero a file out to nb bytes +*/ +static SECStatus +ZeroFile(int fd, int nb) +{ + off_t off; + int amount, rv; + char buf[16384]; + + PORT_Memset(buf, 0, sizeof(buf)); + off = lseek(fd, 0, SEEK_SET); + if (off != 0) { + if (off == -1) + nss_MD_unix_map_lseek_error(errno); + else + PORT_SetError(PR_FILE_SEEK_ERROR); + return SECFailure; + } + + while (nb > 0) { + amount = (nb > sizeof buf) ? sizeof buf : nb; + rv = write(fd, buf, amount); + if (rv <= 0) { + if (!rv) + PORT_SetError(PR_IO_ERROR); + else + nss_MD_unix_map_write_error(errno); + IOError(rv, "zero-write"); + return SECFailure; + } + nb -= rv; + } + return SECSuccess; +} + +#endif /* XP_UNIX */ + + +/************************************************************************/ + +/* +** Reconstitute a cert from the cache +** This is only called from ConvertToSID(). +** Caller must hold the cache lock before calling this. +*/ +static CERTCertificate * +GetCertFromCache(SIDCacheEntry *sce, CERTCertDBHandle *dbHandle) +{ + CERTCertificate *cert; + PRUint32 offset; + int rv; +#ifdef XP_UNIX + off_t off; +#endif + SECItem derCert; + CertCacheEntry cce; + + offset = (PRUint32)sce->u.ssl3.certIndex * sizeof(CertCacheEntry); + GET_SERVER_CACHE_READ_LOCK(certCacheFD, offset, sizeof(CertCacheEntry)); +#ifdef XP_UNIX + off = lseek(certCacheFD, offset, SEEK_SET); + rv = -1; + if (off != offset) { + if (off == -1) + nss_MD_unix_map_lseek_error(errno); + else + PORT_SetError(PR_FILE_SEEK_ERROR); + } else { + rv = read(certCacheFD, &cce, sizeof(CertCacheEntry)); + if (rv != sizeof(CertCacheEntry)) { + if (rv == -1) + nss_MD_unix_map_read_error(errno); + else + PORT_SetError(PR_IO_ERROR); + } + } +#else /* XP_WIN32 */ + /* Use memory mapped I/O and just memcpy() the data */ + CopyMemory(&cce, &certCacheData[offset], sizeof(CertCacheEntry)); + rv = sizeof cce; +#endif /* XP_WIN32 */ + RELEASE_SERVER_CACHE_LOCK(certCacheFD, offset, sizeof(CertCacheEntry)) + + if (rv != sizeof(CertCacheEntry)) { + IOError(rv, "read"); /* error set above */ + return NULL; + } + + /* See if the session ID matches with that in the sce cache. */ + if((cce.sessionIDLength != sce->u.ssl3.sessionIDLength) || + PORT_Memcmp(cce.sessionID, sce->u.ssl3.sessionID, cce.sessionIDLength)) { + /* this is a cache miss, not an error */ + PORT_SetError(SSL_ERROR_SESSION_NOT_FOUND); + return NULL; + } + + derCert.len = cce.certLength; + derCert.data = cce.cert; + + cert = CERT_NewTempCertificate(dbHandle, &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 *sce) +{ + PRUint32 offset; + CertCacheEntry cce; +#ifdef XP_UNIX + off_t off; + int rv; +#endif + + offset = (PRUint32)sce->u.ssl3.certIndex * sizeof(CertCacheEntry); + if (cert->derCert.len > SSL_MAX_CACHED_CERT_LEN) + return; + + cce.sessionIDLength = sce->u.ssl3.sessionIDLength; + PORT_Memcpy(cce.sessionID, sce->u.ssl3.sessionID, cce.sessionIDLength); + + cce.certLength = cert->derCert.len; + PORT_Memcpy(cce.cert, cert->derCert.data, cce.certLength); + + GET_SERVER_CACHE_WRITE_LOCK(certCacheFD, offset, sizeof cce); +#ifdef XP_UNIX + off = lseek(certCacheFD, offset, SEEK_SET); + if (off != offset) { + if (off == -1) + nss_MD_unix_map_lseek_error(errno); + else + PORT_SetError(PR_FILE_SEEK_ERROR); + } else { + rv = write(certCacheFD, &cce, sizeof cce); + if (rv != sizeof(CertCacheEntry)) { + if (rv == -1) + nss_MD_unix_map_write_error(errno); + else + PORT_SetError(PR_IO_ERROR); + IOError(rv, "cert-write"); + Invalidate(sce); + } + } +#else /* WIN32 */ + /* Use memory mapped I/O and just memcpy() the data */ + CopyMemory(&certCacheData[offset], &cce, sizeof cce); +#endif /* XP_UNIX */ + + RELEASE_SERVER_CACHE_LOCK(certCacheFD, offset, sizeof cce); + 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_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->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", myPid, + to->u.ssl2.masterKeyLen, to->u.ssl2.cipherArgLen, + to->time, to->addr, to->u.ssl2.cipherType)); + } else { + /* This is an SSL v3 session */ + + to->u.ssl3.sessionIDLength = from->u.ssl3.sessionIDLength; + to->u.ssl3.cipherSuite = from->u.ssl3.cipherSuite; + to->u.ssl3.compression = (uint16)from->u.ssl3.compression; + to->u.ssl3.resumable = from->u.ssl3.resumable; + to->u.ssl3.hasFortezza = from->u.ssl3.hasFortezza; + to->u.ssl3.keys = from->u.ssl3.keys; + to->u.ssl3.masterWrapMech = from->u.ssl3.masterWrapMech; + to->u.ssl3.exchKeyType = from->u.ssl3.exchKeyType; + + PORT_Memcpy(to->u.ssl3.sessionID, + from->u.ssl3.sessionID, + from->u.ssl3.sessionIDLength); + + SSL_TRC(8, ("%d: SSL3: ConvertSID: time=%d addr=0x%x cipherSuite=%d", + myPid, to->time, to->addr, to->u.ssl3.cipherSuite)); + } +} + +/* +** Convert file based cache-entry to memory based one +** This is only called from ServerSessionIDLookup(). +** Caller must hold cache lock when calling this. +*/ +static sslSessionID * +ConvertToSID(SIDCacheEntry *from, CERTCertDBHandle * dbHandle) +{ + sslSessionID *to; + uint16 version = from->u.ssl2.version; + + to = (sslSessionID*) PORT_ZAlloc(sizeof(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; + 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", + myPid, 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; + to->u.ssl3.cipherSuite = from->u.ssl3.cipherSuite; + to->u.ssl3.compression = (SSL3CompressionMethod)from->u.ssl3.compression; + to->u.ssl3.resumable = from->u.ssl3.resumable; + to->u.ssl3.hasFortezza = from->u.ssl3.hasFortezza; + to->u.ssl3.keys = from->u.ssl3.keys; + to->u.ssl3.masterWrapMech = from->u.ssl3.masterWrapMech; + to->u.ssl3.exchKeyType = from->u.ssl3.exchKeyType; + + PORT_Memcpy(to->u.ssl3.sessionID, + from->u.ssl3.sessionID, + from->u.ssl3.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->u.ssl3.tek = 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; + + to->u.ssl3.clientWriteSaveLen = 0; + + if (from->u.ssl3.certIndex != -1) { + to->peerCert = GetCertFromCache(from, dbHandle); + if (to->peerCert == NULL) + goto loser; + } + } + + to->version = from->u.ssl2.version; + to->time = from->time; + to->cached = in_server_cache; + to->addr = from->addr; + to->references = 1; + + return to; + + loser: + Invalidate(from); + 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); + } + PORT_Free(to); + } + return NULL; +} + + +/* Invalidate a SID cache entry. + * Called from CacheCert, ConvertToSid, and ServerSessionIDUncache. + */ +static void +Invalidate(SIDCacheEntry *sce) +{ + PRUint32 offset; +#ifdef XP_UNIX + off_t off; + int rv; +#endif + + if (sce == NULL) return; + + if (sce->u.ssl2.version < SSL_LIBRARY_VERSION_3_0) { + offset = Offset(sce->addr, sce->u.ssl2.sessionID, + sizeof sce->u.ssl2.sessionID); + } else { + offset = Offset(sce->addr, sce->u.ssl3.sessionID, + sce->u.ssl3.sessionIDLength); + } + + sce->u.ssl2.valid = 0; + SSL_TRC(7, ("%d: SSL: uncaching session-id at offset %ld", + myPid, offset)); + + GET_SERVER_CACHE_WRITE_LOCK(SIDCacheFD, offset, sizeof *sce); + +#ifdef XP_UNIX + off = lseek(SIDCacheFD, offset, SEEK_SET); + if (off != offset) { + if (off == -1) + nss_MD_unix_map_lseek_error(errno); + else + PORT_SetError(PR_FILE_SEEK_ERROR); + } else { + rv = write(SIDCacheFD, sce, sizeof *sce); + if (rv != sizeof *sce) { + if (rv == -1) + nss_MD_unix_map_write_error(errno); + else + PORT_SetError(PR_IO_ERROR); + IOError(rv, "invalidate-write"); + } + } +#else /* WIN32 */ + /* Use memory mapped I/O and just memcpy() the data */ + CopyMemory(&SIDCacheData[offset], sce, sizeof *sce); +#endif /* XP_UNIX */ + + RELEASE_SERVER_CACHE_LOCK(SIDCacheFD, offset, sizeof *sce); +} + + +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, myPid, 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) +{ + PR_Lock(cacheLock); +} + +static void +unlock_cache(void) +{ + PR_Unlock(cacheLock); +} + +/* +** Perform some mumbo jumbo on the ip-address and the session-id value to +** compute a hash value. +*/ +static PRUint32 +Offset(PRUint32 addr, unsigned char *s, unsigned nl) +{ + PRUint32 rv; + + rv = addr ^ (((PRUint32)s[0] << 24) | ((PRUint32)s[1] << 16) + | (s[2] << 8) | s[nl-1]); + return (rv % numSIDCacheEntries) * sizeof(SIDCacheEntry); +} + + + +/* +** Look something up in the cache. This will invalidate old entries +** in the process. Caller has locked the cache! +** Returns PR_TRUE if found a valid match. PR_FALSE otherwise. +*/ +static PRBool +FindSID(PRUint32 addr, unsigned char *sessionID, + unsigned sessionIDLength, SIDCacheEntry *sce) +{ + PRUint32 offset; + PRUint32 now; + int rv; +#ifdef XP_UNIX + off_t off; +#endif + + /* Read in cache entry after hashing ip address and session-id value */ + offset = Offset(addr, sessionID, sessionIDLength); + now = ssl_Time(); + GET_SERVER_CACHE_READ_LOCK(SIDCacheFD, offset, sizeof *sce); +#ifdef XP_UNIX + off = lseek(SIDCacheFD, offset, SEEK_SET); + rv = -1; + if (off != offset) { + if (off == -1) + nss_MD_unix_map_lseek_error(errno); + else + PORT_SetError(PR_FILE_SEEK_ERROR); + } else { + rv = read(SIDCacheFD, sce, sizeof *sce); + if (rv != sizeof *sce) { + if (rv == -1) + nss_MD_unix_map_read_error(errno); + else + PORT_SetError(PR_IO_ERROR); + } + } +#else /* XP_WIN32 */ + /* Use memory mapped I/O and just memcpy() the data */ + CopyMemory(sce, &SIDCacheData[offset], sizeof *sce); + rv = sizeof *sce; +#endif /* XP_WIN32 */ + RELEASE_SERVER_CACHE_LOCK(SIDCacheFD, offset, sizeof *sce); + + if (rv != sizeof *sce) { + IOError(rv, "server sid cache read"); + return PR_FALSE; + } + + if (!sce->u.ssl2.valid) { + /* Entry is not valid */ + PORT_SetError(SSL_ERROR_SESSION_NOT_FOUND); + return PR_FALSE; + } + + if (((sce->u.ssl2.version < SSL_LIBRARY_VERSION_3_0) && + (now > sce->time + ssl_sid_timeout)) || + ((sce->u.ssl2.version >= SSL_LIBRARY_VERSION_3_0) && + (now > sce->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", + myPid, sce->addr, now, sce->time + ssl_sid_timeout)); + sce->u.ssl2.valid = 0; + + GET_SERVER_CACHE_WRITE_LOCK(SIDCacheFD, offset, sizeof *sce); +#ifdef XP_UNIX + off = lseek(SIDCacheFD, offset, SEEK_SET); + rv = -1; + if (off != offset) { + if (off == -1) + nss_MD_unix_map_lseek_error(errno); + else + PORT_SetError(PR_IO_ERROR); + } else { + rv = write(SIDCacheFD, sce, sizeof *sce); + if (rv != sizeof *sce) { + if (rv == -1) + nss_MD_unix_map_write_error(errno); + else + PORT_SetError(PR_IO_ERROR); + IOError(rv, "timeout-write"); + } + } +#else /* WIN32 */ + /* Use memory mapped I/O and just memcpy() the data */ + CopyMemory(&SIDCacheData[offset], sce, sizeof *sce); + rv = sizeof *sce; +#endif /* XP_UNIX */ + RELEASE_SERVER_CACHE_LOCK(SIDCacheFD, offset, sizeof *sce); + if (rv == sizeof *sce) + PORT_SetError(SSL_ERROR_SESSION_NOT_FOUND); + return PR_FALSE; + } + + /* + ** Finally, examine specific session-id/addr data to see if the cache + ** entry matches our addr+session-id value + */ + if ((sce->addr == addr) && + (PORT_Memcmp(sce->u.ssl2.sessionID, sessionID, sessionIDLength) == 0)) { + /* Found it */ + return PR_TRUE; + } + PORT_SetError(SSL_ERROR_SESSION_NOT_FOUND); + return PR_FALSE; +} + +/************************************************************************/ + +/* 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( PRUint32 addr, + unsigned char *sessionID, + unsigned int sessionIDLength, + CERTCertDBHandle * dbHandle) +{ + SIDCacheEntry sce; + sslSessionID *sid; + + sid = 0; + lock_cache(); + if (FindSID(addr, sessionID, sessionIDLength, &sce)) { + /* Found it. Convert file format to internal format */ + sid = ConvertToSID(&sce, dbHandle); + } + 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 sce; + PRUint32 offset; +#ifdef XP_UNIX + off_t off; + int rv; +#endif + uint16 version = sid->version; + + if ((version >= SSL_LIBRARY_VERSION_3_0) && + (sid->u.ssl3.sessionIDLength == 0)) { + return; + } + + if (sid->cached == never_cached || sid->cached == invalid_cache) { + lock_cache(); + + sid->time = ssl_Time(); + if (version < SSL_LIBRARY_VERSION_3_0) { + SSL_TRC(8, ("%d: SSL: CacheMT: cached=%d addr=0x%08x time=%x " + "cipher=%d", myPid, 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", myPid, 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(&sce, sid); + if (version >= SSL_LIBRARY_VERSION_3_0) { + if (sid->peerCert == NULL) { + sce.u.ssl3.certIndex = -1; + } else { + sce.u.ssl3.certIndex = (int16) + ((offset / sizeof(SIDCacheEntry)) % numCertCacheEntries); + } + } + + GET_SERVER_CACHE_WRITE_LOCK(SIDCacheFD, offset, sizeof sce); +#ifdef XP_UNIX + off = lseek(SIDCacheFD, offset, SEEK_SET); + if (off != offset) { + if (off == -1) + nss_MD_unix_map_lseek_error(errno); + else + PORT_SetError(PR_IO_ERROR); + } else { + rv = write(SIDCacheFD, &sce, sizeof sce); + if (rv != sizeof(sce)) { + if (rv == -1) + nss_MD_unix_map_write_error(errno); + else + PORT_SetError(PR_IO_ERROR); + IOError(rv, "update-write"); + } + } +#else /* WIN32 */ + CopyMemory(&SIDCacheData[offset], &sce, sizeof sce); +#endif /* XP_UNIX */ + RELEASE_SERVER_CACHE_LOCK(SIDCacheFD, offset, sizeof sce); + + if ((version >= SSL_LIBRARY_VERSION_3_0) && + (sid->peerCert != NULL)) { + CacheCert(sid->peerCert, &sce); + } + + sid->cached = in_server_cache; + unlock_cache(); + } +} + +static void +ServerSessionIDUncache(sslSessionID *sid) +{ + SIDCacheEntry sce; + int rv; + + if (sid == NULL) return; + + lock_cache(); + if (sid->version < SSL_LIBRARY_VERSION_3_0) { + SSL_TRC(8, ("%d: SSL: UncacheMT: valid=%d addr=0x%08x time=%x " + "cipher=%d", myPid, 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), &sce); + } else { + SSL_TRC(8, ("%d: SSL3: UncacheMT: valid=%d addr=0x%08x time=%x " + "cipherSuite=%d", myPid, 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, &sce); + } + + if (rv) { + Invalidate(&sce); + } + sid->cached = invalid_cache; + unlock_cache(); +} + +static SECStatus +InitSessionIDCache(int maxCacheEntries, PRUint32 timeout, + PRUint32 ssl3_timeout, const char *directory) +{ + char *cfn; +#ifdef XP_UNIX + int rv; + if (SIDCacheFD >= 0) { + /* Already done */ + return SECSuccess; + } +#else /* WIN32 */ + if(SIDCacheFDMAP != INVALID_HANDLE_VALUE) { + /* Already done */ + return SECSuccess; + } +#endif /* XP_UNIX */ + + + if (maxCacheEntries) { + numSIDCacheEntries = maxCacheEntries; + } + sidCacheWrapOffset = numSIDCacheEntries * sizeof(SIDCacheEntry); + sidCacheFileSize = sidCacheWrapOffset + + (kt_kea_size * SSL_NUM_WRAP_MECHS * sizeof(SSLWrappedSymWrappingKey)); + + /* 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 + do { + (void) unlink(cfn); + SIDCacheFD = open(cfn, O_EXCL|O_CREAT|O_RDWR, 0600); + } while (SIDCacheFD < 0 && errno == EEXIST); + if (SIDCacheFD < 0) { + nss_MD_unix_map_open_error(errno); + IOError(SIDCacheFD, "create"); + goto loser; + } + rv = unlink(cfn); + if (rv < 0) { + nss_MD_unix_map_unlink_error(errno); + IOError(rv, "unlink"); + goto loser; + } +#else /* WIN32 */ + SIDCacheFDMAP = + CreateFileMapping(INVALID_HANDLE_VALUE, /* allocate in swap file */ + &sidCacheFDMapAttributes, /* inheritable. */ + PAGE_READWRITE, + 0, /* size, high word. */ + sidCacheFileSize, /* size, low word. */ + NULL); /* no map name in FS */ + if(! SIDCacheFDMAP) { + nss_MD_win32_map_default_error(GetLastError()); + goto loser; + } + SIDCacheData = (char *)MapViewOfFile(SIDCacheFDMAP, + FILE_MAP_ALL_ACCESS, /* R/W */ + 0, 0, /* offset */ + sidCacheFileSize); /* size */ + if (! SIDCacheData) { + nss_MD_win32_map_default_error(GetLastError()); + goto loser; + } +#endif /* XP_UNIX */ + + if (!cacheLock) + nss_InitLock(&cacheLock); + if (!cacheLock) { + SET_ERROR_CODE + goto loser; + } +#ifdef _WIN32 + if (isMultiProcess && (SECSuccess != createServerCacheSemaphore())) { + SET_ERROR_CODE + goto loser; + } +#endif + + 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; + } + + GET_SERVER_CACHE_WRITE_LOCK(SIDCacheFD, 0, sidCacheFileSize); +#ifdef XP_UNIX + /* Initialize the files */ + if (ZeroFile(SIDCacheFD, sidCacheFileSize)) { + /* Bummer */ + close(SIDCacheFD); + SIDCacheFD = -1; + goto loser; + } +#else /* XP_WIN32 */ + ZeroMemory(SIDCacheData, sidCacheFileSize); +#endif /* XP_UNIX */ + RELEASE_SERVER_CACHE_LOCK(SIDCacheFD, 0, sidCacheFileSize); + PORT_Free(cfn); + return SECSuccess; + + loser: +#ifdef _WIN32 + if (svrCacheSem) + destroyServerCacheSemaphore(); +#endif + if (cacheLock) { + PR_DestroyLock(cacheLock); + cacheLock = NULL; + } + PORT_Free(cfn); + return SECFailure; +} + +static SECStatus +InitCertCache(const char *directory) +{ + char *cfn; +#ifdef XP_UNIX + int rv; + if (certCacheFD >= 0) { + /* Already done */ + return SECSuccess; + } +#else /* WIN32 */ + if(certCacheFDMAP != INVALID_HANDLE_VALUE) { + /* Already done */ + return SECSuccess; + } +#endif /* XP_UNIX */ + + numCertCacheEntries = sidCacheFileSize / sizeof(CertCacheEntry); + if (numCertCacheEntries < MIN_CERT_CACHE_ENTRIES) + numCertCacheEntries = MIN_CERT_CACHE_ENTRIES; + certCacheFileSize = numCertCacheEntries * sizeof(CertCacheEntry); + + /* 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 + do { + (void) unlink(cfn); + certCacheFD = open(cfn, O_EXCL|O_CREAT|O_RDWR, 0600); + } while (certCacheFD < 0 && errno == EEXIST); + if (certCacheFD < 0) { + nss_MD_unix_map_open_error(errno); + IOError(certCacheFD, "create"); + goto loser; + } + rv = unlink(cfn); + if (rv < 0) { + nss_MD_unix_map_unlink_error(errno); + IOError(rv, "unlink"); + goto loser; + } +#else /* WIN32 */ + certCacheFDMAP = + CreateFileMapping(INVALID_HANDLE_VALUE, /* allocate in swap file */ + &certCacheFDMapAttributes, /* inheritable. */ + PAGE_READWRITE, + 0, /* size, high word. */ + certCacheFileSize, /* size, low word. */ + NULL); /* no map name in FS */ + if (! certCacheFDMAP) { + nss_MD_win32_map_default_error(GetLastError()); + goto loser; + } + certCacheData = (char *) MapViewOfFile(certCacheFDMAP, + FILE_MAP_ALL_ACCESS, /* R/W */ + 0, 0, /* offset */ + certCacheFileSize); /* size */ + if (! certCacheData) { + nss_MD_win32_map_default_error(GetLastError()); + goto loser; + } +#endif /* XP_UNIX */ + +/* GET_SERVER_CACHE_WRITE_LOCK(certCacheFD, 0, certCacheFileSize); */ +#ifdef XP_UNIX + /* Initialize the files */ + if (ZeroFile(certCacheFD, certCacheFileSize)) { + /* Bummer */ + close(certCacheFD); + certCacheFD = -1; + goto loser; + } +#else /* XP_WIN32 */ + ZeroMemory(certCacheData, certCacheFileSize); +#endif /* XP_UNIX */ +/* RELEASE_SERVER_CACHE_LOCK(certCacheFD, 0, certCacheFileSize); */ + PORT_Free(cfn); + return SECSuccess; + + loser: + PORT_Free(cfn); + return SECFailure; +} + +int +SSL_ConfigServerSessionIDCache( int maxCacheEntries, + PRUint32 timeout, + PRUint32 ssl3_timeout, + const char * directory) +{ + SECStatus rv; + + PORT_Assert(sizeof(SIDCacheEntry) == 256); + PORT_Assert(sizeof(CertCacheEntry) == 4096); + + myPid = SSL_GETPID(); + if (!directory) { + directory = DEFAULT_CACHE_DIRECTORY; + } + rv = InitSessionIDCache(maxCacheEntries, timeout, ssl3_timeout, directory); + if (rv) { + SET_ERROR_CODE + return SECFailure; + } + rv = InitCertCache(directory); + if (rv) { + SET_ERROR_CODE + return SECFailure; + } + + ssl_sid_lookup = ServerSessionIDLookup; + ssl_sid_cache = ServerSessionIDCache; + ssl_sid_uncache = ServerSessionIDUncache; + return SECSuccess; +} + +/* Use this function, instead of SSL_ConfigServerSessionIDCache, + * if the cache will be shared by multiple processes. + */ +int +SSL_ConfigMPServerSIDCache( int maxCacheEntries, + PRUint32 timeout, + PRUint32 ssl3_timeout, + const char * directory) +{ + char * envValue; + int result; + SECStatus putEnvFailed; + + isMultiProcess = PR_TRUE; + result = SSL_ConfigServerSessionIDCache(maxCacheEntries, timeout, + ssl3_timeout, directory); + if (result == SECSuccess) { +#ifdef _WIN32 + winInheritance winherit; + + winherit.numSIDCacheEntries = numSIDCacheEntries; + winherit.sidCacheFileSize = sidCacheFileSize; + winherit.sidCacheWrapOffset = sidCacheWrapOffset; + winherit.numCertCacheEntries = numCertCacheEntries; + winherit.certCacheFileSize = certCacheFileSize; + winherit.SIDCacheFDMAP = SIDCacheFDMAP; + winherit.certCacheFDMAP = certCacheFDMAP; + winherit.svrCacheSem = svrCacheSem; + winherit.parentProcessID = GetCurrentProcessId(); + winherit.parentProcessHandle = + OpenProcess(PROCESS_DUP_HANDLE, TRUE, winherit.parentProcessID); + if (winherit.parentProcessHandle == NULL) { + SET_ERROR_CODE + return SECFailure; + } + envValue = BTOA_DataToAscii((unsigned char *)&winherit, + sizeof winherit); + if (!envValue) { + SET_ERROR_CODE + return SECFailure; + } +#else + unixInheritance uinherit; + + uinherit.numSIDCacheEntries = numSIDCacheEntries; + uinherit.sidCacheFileSize = sidCacheFileSize; + uinherit.sidCacheWrapOffset = sidCacheWrapOffset; + uinherit.numCertCacheEntries = numCertCacheEntries; + uinherit.certCacheFileSize = certCacheFileSize; + uinherit.SIDCacheFD = SIDCacheFD; + uinherit.certCacheFD = certCacheFD; + + envValue = BTOA_DataToAscii((unsigned char *)&uinherit, + sizeof uinherit); + if (!envValue) { + SET_ERROR_CODE + return SECFailure; + } +#endif + } + putEnvFailed = (SECStatus)NSS_PutEnv(envVarName, envValue); + PORT_Free(envValue); + if (putEnvFailed) { + SET_ERROR_CODE + result = SECFailure; + } + return result; +} + +SECStatus +SSL_InheritMPServerSIDCache(const char * envString) +{ + unsigned char * decoString = NULL; + unsigned int decoLen; +#ifdef _WIN32 + winInheritance inherit; +#else + unixInheritance inherit; +#endif + + myPid = SSL_GETPID(); + if (isMultiProcess) + return SECSuccess; /* already done. */ + + ssl_sid_lookup = ServerSessionIDLookup; + ssl_sid_cache = ServerSessionIDCache; + ssl_sid_uncache = ServerSessionIDUncache; + + if (!envString) { + envString = getenv(envVarName); + if (!envString) { + SET_ERROR_CODE + return SECFailure; + } + } + + decoString = ATOB_AsciiToData(envString, &decoLen); + if (!decoString) { + SET_ERROR_CODE + return SECFailure; + } + if (decoLen != sizeof inherit) { + SET_ERROR_CODE + goto loser; + } + + PORT_Memcpy(&inherit, decoString, sizeof inherit); + PORT_Free(decoString); + + numSIDCacheEntries = inherit.numSIDCacheEntries; + sidCacheFileSize = inherit.sidCacheFileSize; + sidCacheWrapOffset = inherit.sidCacheWrapOffset; + numCertCacheEntries = inherit.numCertCacheEntries; + certCacheFileSize = inherit.certCacheFileSize; + +#ifdef _WIN32 + SIDCacheFDMAP = inherit.SIDCacheFDMAP; + certCacheFDMAP = inherit.certCacheFDMAP; + svrCacheSem = inherit.svrCacheSem; + +#if 0 + /* call DuplicateHandle ?? */ + inherit.parentProcessID; + inherit.parentProcessHandle; +#endif + + if(!SIDCacheFDMAP) { + SET_ERROR_CODE + goto loser; + } + SIDCacheData = (char *)MapViewOfFile(SIDCacheFDMAP, + FILE_MAP_ALL_ACCESS, /* R/W */ + 0, 0, /* offset */ + sidCacheFileSize); /* size */ + if(!SIDCacheData) { + nss_MD_win32_map_default_error(GetLastError()); + goto loser; + } + + if(!certCacheFDMAP) { + SET_ERROR_CODE + goto loser; + } + certCacheData = (char *) MapViewOfFile(certCacheFDMAP, + FILE_MAP_ALL_ACCESS, /* R/W */ + 0, 0, /* offset */ + certCacheFileSize); /* size */ + if(!certCacheData) { + nss_MD_win32_map_default_error(GetLastError()); + goto loser; + } + +#else /* must be unix */ + SIDCacheFD = inherit.SIDCacheFD; + certCacheFD = inherit.certCacheFD; + if (SIDCacheFD < 0 || certCacheFD < 0) { + SET_ERROR_CODE + goto loser; + } +#endif + + if (!cacheLock) { + nss_InitLock(&cacheLock); + if (!cacheLock) + goto loser; + } + isMultiProcess = PR_TRUE; + return SECSuccess; + +loser: + if (decoString) + PORT_Free(decoString); +#if _WIN32 + if (SIDCacheFDMAP) { + CloseHandle(SIDCacheFDMAP); + SIDCacheFDMAP = NULL; + } + if (certCacheFDMAP) { + CloseHandle(certCacheFDMAP); + certCacheFDMAP = NULL; + } +#else + if (SIDCacheFD >= 0) { + close(SIDCacheFD); + SIDCacheFD = -1; + } + if (certCacheFD >= 0) { + close(certCacheFD); + certCacheFD = -1; + } +#endif + return SECFailure; + +} + +/************************************************************************ + * Code dealing with shared wrapped symmetric wrapping keys below * + ************************************************************************/ + + +static PRBool +getWrappingKey(PRInt32 symWrapMechIndex, + SSL3KEAType exchKeyType, + SSLWrappedSymWrappingKey *wswk, + PRBool grabSharedLock) +{ + PRUint32 offset = sidCacheWrapOffset + + ((exchKeyType * SSL_NUM_WRAP_MECHS + symWrapMechIndex) * + sizeof(SSLWrappedSymWrappingKey)); + PRBool rv = PR_TRUE; +#ifdef XP_UNIX + off_t lrv; + ssize_t rrv; +#endif + + if (grabSharedLock) { + GET_SERVER_CACHE_READ_LOCK(SIDCacheFD, offset, sizeof *wswk); + } + +#ifdef XP_UNIX + lrv = lseek(SIDCacheFD, offset, SEEK_SET); + if (lrv != offset) { + if (lrv == -1) + nss_MD_unix_map_lseek_error(errno); + else + PORT_SetError(PR_IO_ERROR); + IOError(rv, "wrapping-read"); + rv = PR_FALSE; + } else { + rrv = read(SIDCacheFD, wswk, sizeof *wswk); + if (rrv != sizeof *wswk) { + if (rrv == -1) + nss_MD_unix_map_read_error(errno); + else + PORT_SetError(PR_IO_ERROR); + IOError(rv, "wrapping-read"); + rv = PR_FALSE; + } + } +#else /* XP_WIN32 */ + /* Use memory mapped I/O and just memcpy() the data */ + CopyMemory(wswk, &SIDCacheData[offset], sizeof *wswk); +#endif /* XP_WIN32 */ + if (grabSharedLock) { + RELEASE_SERVER_CACHE_LOCK(SIDCacheFD, offset, sizeof *wswk); + } + if (rv) { + if (wswk->exchKeyType != exchKeyType || + wswk->symWrapMechIndex != symWrapMechIndex || + wswk->wrappedSymKeyLen == 0) { + memset(wswk, 0, sizeof *wswk); + rv = PR_FALSE; + } + } + return rv; +} + +PRBool +ssl_GetWrappingKey( PRInt32 symWrapMechIndex, + SSL3KEAType exchKeyType, + SSLWrappedSymWrappingKey *wswk) +{ + PRBool rv; + + lock_cache(); + + 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 = getWrappingKey(symWrapMechIndex, exchKeyType, wswk, PR_TRUE); + } else { + rv = PR_FALSE; + } + unlock_cache(); + return rv; +} + +/* The caller passes in the new value it wants + * to set. This code tests the wrapped sym key entry in the file on disk. + * 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/semaphores necessary to make + * the operation atomic. + */ +PRBool +ssl_SetWrappingKey(SSLWrappedSymWrappingKey *wswk) +{ + PRBool rv; + SSL3KEAType exchKeyType = wswk->exchKeyType; + /* type of keys used to wrap SymWrapKey*/ + PRInt32 symWrapMechIndex = wswk->symWrapMechIndex; + PRUint32 offset; + SSLWrappedSymWrappingKey myWswk; + + 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; + + offset = sidCacheWrapOffset + + ((exchKeyType * SSL_NUM_WRAP_MECHS + symWrapMechIndex) * + sizeof(SSLWrappedSymWrappingKey)); + PORT_Memset(&myWswk, 0, sizeof myWswk); /* eliminate UMRs. */ + lock_cache(); + GET_SERVER_CACHE_WRITE_LOCK(SIDCacheFD, offset, sizeof *wswk); + + rv = getWrappingKey(wswk->symWrapMechIndex, wswk->exchKeyType, &myWswk, + PR_FALSE); + 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. */ + +#ifdef XP_UNIX + off_t lrv; + ssize_t rrv; + + lrv = lseek(SIDCacheFD, offset, SEEK_SET); + if (lrv != offset) { + if (lrv == -1) + nss_MD_unix_map_lseek_error(errno); + else + PORT_SetError(PR_IO_ERROR); + IOError(rv, "wrapping-read"); + rv = PR_FALSE; + } else { + rrv = write(SIDCacheFD, wswk, sizeof *wswk); + if (rrv != sizeof *wswk) { + if (rrv == -1) + nss_MD_unix_map_read_error(errno); + else + PORT_SetError(PR_IO_ERROR); + IOError(rv, "wrapping-read"); + rv = PR_FALSE; + } + } +#else /* XP_WIN32 */ + /* Use memory mapped I/O and just memcpy() the data */ + CopyMemory(&SIDCacheData[offset], wswk, sizeof *wswk); +#endif /* XP_WIN32 */ + } + RELEASE_SERVER_CACHE_LOCK(SIDCacheFD, offset, sizeof *wswk); + unlock_cache(); + return rv; +} + + +#endif /* NADA_VERISON */ +#else + +#include "seccomon.h" +#include "cert.h" +#include "ssl.h" +#include "sslimpl.h" + +PRBool +ssl_GetWrappingKey( PRInt32 symWrapMechIndex, + SSL3KEAType exchKeyType, + SSLWrappedSymWrappingKey *wswk) +{ + PRBool rv = PR_FALSE; + PR_ASSERT(!"SSL servers are not supported on the Mac. (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 file on disk. + * 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/semaphores necessary to make + * the operation atomic. + */ +PRBool +ssl_SetWrappingKey(SSLWrappedSymWrappingKey *wswk) +{ + PRBool rv = PR_FALSE; + PR_ASSERT(!"SSL servers are not supported on the Mac. (ssl_SetWrappingKey)"); + return rv; +} + +#endif /* XP_UNIX || XP_WIN32 */ |