/* 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: sslsnce.c,v 1.8 2001/01/31 23:43:47 wtc%netscape.com Exp $ */ /* 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 "nssrenam.h" #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 #ifdef XP_UNIX #include #include #include #include #include "unix_err.h" #else /* XP_WIN32 */ #ifdef MC_HTTPD #include #endif /* MC_HTTPD */ #include #include "win32err.h" #endif /* XP_WIN32 */ #include #define SET_ERROR_CODE /* reminder */ #include "nspr.h" #include "nsslocks.h" static PZLock *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 { PRIPv6Addr 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[256 - sizeof(PRIPv6Addr) - sizeof(PRUint32)]; } 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(const PRIPv6Addr *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 if (ssl_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 if (ssl_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 if (ssl_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%08x%08x%08x%08x cipherType=%d", myPid, to->u.ssl2.masterKeyLen, to->u.ssl2.cipherArgLen, to->time, 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->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%08x%08x%08x%08x cipherSuite=%d", myPid, to->time, 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 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%08x%08x%08x%08x cipherType=%d", myPid, to->u.ssl2.masterKey.len, to->u.ssl2.cipherArg.len, to->time, 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->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) { PZ_Lock(cacheLock); } static void unlock_cache(void) { PZ_Unlock(cacheLock); } /* ** Perform some mumbo jumbo on the ip-address and the session-id value to ** compute a hash value. */ static PRUint32 Offset(const PRIPv6Addr *addr, unsigned char *s, unsigned nl) { PRUint32 rv; rv = addr->pr_s6_addr32[3] ^ (((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(const PRIPv6Addr *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%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->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 (!memcmp(&sce->addr, addr, sizeof(PRIPv6Addr)) && (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( const PRIPv6Addr *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%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->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%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->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; PRErrorCode err; int rv; 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(); lock_cache(); if (sid->version < SSL_LIBRARY_VERSION_3_0) { 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->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%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->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(); PORT_SetError(err); } 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, nssILockCache); 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) { PZ_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, nssILockCache); 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" int SSL_ConfigServerSessionIDCache( int maxCacheEntries, PRUint32 timeout, PRUint32 ssl3_timeout, const char * directory) { PR_ASSERT(!"SSL servers are not supported on the platform. (SSL_ConfigServerSessionIDCache)"); return SECFailure; } int SSL_ConfigMPServerSIDCache( int maxCacheEntries, PRUint32 timeout, PRUint32 ssl3_timeout, const char * directory) { PR_ASSERT(!"SSL servers are not supported on the platform. (SSL_ConfigMPServerSIDCache)"); return SECFailure; } SECStatus SSL_InheritMPServerSIDCache(const char * envString) { PR_ASSERT(!"SSL servers are not supported on the 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 the 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 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 platform. (ssl_SetWrappingKey)"); return rv; } #endif /* XP_UNIX || XP_WIN32 */