gecko-dev/security/nss/lib/ssl/sslsnce.c
2000-05-16 17:28:31 +00:00

1906 lines
53 KiB
C

/* 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.2 2000/05/16 17:28:31 relyea%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 "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
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%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 */