Import 1.9b4 NSS tag from cvs
[mozilla-nss.git] / security / nss / lib / ssl / sslsnce.c
blobb5fd64d06b93adcd085d922c25c6b4b588663c7b
1 /* This file implements the SERVER Session ID cache.
2 * NOTE: The contents of this file are NOT used by the client.
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
17 * The Original Code is the Netscape security libraries.
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1994-2000
22 * the Initial Developer. All Rights Reserved.
24 * Contributor(s):
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
39 /* $Id: sslsnce.c,v 1.42 2008/02/16 04:38:09 julien.pierre.boogz%sun.com Exp $ */
41 /* Note: ssl_FreeSID() in sslnonce.c gets used for both client and server
42 * cache sids!
44 * About record locking among different server processes:
46 * All processes that are part of the same conceptual server (serving on
47 * the same address and port) MUST share a common SSL session cache.
48 * This code makes the content of the shared cache accessible to all
49 * processes on the same "server". This code works on Unix and Win32 only.
51 * We use NSPR anonymous shared memory and move data to & from shared memory.
52 * We must do explicit locking of the records for all reads and writes.
53 * The set of Cache entries are divided up into "sets" of 128 entries.
54 * Each set is protected by a lock. There may be one or more sets protected
55 * by each lock. That is, locks to sets are 1:N.
56 * There is one lock for the entire cert cache.
57 * There is one lock for the set of wrapped sym wrap keys.
59 * The anonymous shared memory is laid out as if it were declared like this:
61 * struct {
62 * cacheDescriptor desc;
63 * sidCacheLock sidCacheLocks[ numSIDCacheLocks];
64 * sidCacheLock keyCacheLock;
65 * sidCacheLock certCacheLock;
66 * sidCacheSet sidCacheSets[ numSIDCacheSets ];
67 * sidCacheEntry sidCacheData[ numSIDCacheEntries];
68 * certCacheEntry certCacheData[numCertCacheEntries];
69 * SSLWrappedSymWrappingKey keyCacheData[kt_kea_size][SSL_NUM_WRAP_MECHS];
70 * } cacheMemCacheData;
72 #include "nssrenam.h"
73 #include "seccomon.h"
75 #if (defined(XP_UNIX) || defined(XP_WIN32) || defined (XP_OS2) || defined(XP_BEOS)) && !defined(_WIN32_WCE)
77 #include "cert.h"
78 #include "ssl.h"
79 #include "sslimpl.h"
80 #include "sslproto.h"
81 #include "pk11func.h"
82 #include "base64.h"
84 #include <stdio.h>
86 #if defined(XP_UNIX) || defined(XP_BEOS)
88 #include <syslog.h>
89 #include <fcntl.h>
90 #include <unistd.h>
91 #include <errno.h>
92 #include <signal.h>
93 #include "unix_err.h"
95 #else
97 #ifdef XP_WIN32
98 #include <wtypes.h>
99 #include "win32err.h"
100 #endif
102 #endif
103 #include <sys/types.h>
105 #define SET_ERROR_CODE /* reminder */
107 #include "nspr.h"
108 #include "sslmutex.h"
111 ** Format of a cache entry in the shared memory.
113 struct sidCacheEntryStr {
114 /* 16 */ PRIPv6Addr addr; /* client's IP address */
115 /* 4 */ PRUint32 creationTime;
116 /* 4 */ PRUint32 lastAccessTime;
117 /* 4 */ PRUint32 expirationTime;
118 /* 2 */ PRUint16 version;
119 /* 1 */ PRUint8 valid;
120 /* 1 */ PRUint8 sessionIDLength;
121 /* 32 */ PRUint8 sessionID[SSL3_SESSIONID_BYTES];
122 /* 2 */ PRUint16 authAlgorithm;
123 /* 2 */ PRUint16 authKeyBits;
124 /* 2 */ PRUint16 keaType;
125 /* 2 */ PRUint16 keaKeyBits;
126 /* 72 - common header total */
128 union {
129 struct {
130 /* 64 */ PRUint8 masterKey[SSL_MAX_MASTER_KEY_BYTES];
131 /* 32 */ PRUint8 cipherArg[SSL_MAX_CYPHER_ARG_BYTES];
133 /* 1 */ PRUint8 cipherType;
134 /* 1 */ PRUint8 masterKeyLen;
135 /* 1 */ PRUint8 keyBits;
136 /* 1 */ PRUint8 secretKeyBits;
137 /* 1 */ PRUint8 cipherArgLen;
138 /*101 */} ssl2;
140 struct {
141 /* 2 */ ssl3CipherSuite cipherSuite;
142 /* 2 */ PRUint16 compression; /* SSL3CompressionMethod */
144 /*100 */ ssl3SidKeys keys; /* keys and ivs, wrapped as needed. */
146 /* 4 */ PRUint32 masterWrapMech;
147 /* 4 */ SSL3KEAType exchKeyType;
148 /* 4 */ PRInt32 certIndex;
149 /*116 */} ssl3;
150 /* force sizeof(sidCacheEntry) to be a multiple of cache line size */
151 struct {
152 /*120 */ PRUint8 filler[120]; /* 72+120==196, a multiple of 16 */
153 } forceSize;
154 } u;
156 typedef struct sidCacheEntryStr sidCacheEntry;
158 /* The length of this struct is supposed to be a power of 2, e.g. 4KB */
159 struct certCacheEntryStr {
160 PRUint16 certLength; /* 2 */
161 PRUint16 sessionIDLength; /* 2 */
162 PRUint8 sessionID[SSL3_SESSIONID_BYTES]; /* 32 */
163 PRUint8 cert[SSL_MAX_CACHED_CERT_LEN]; /* 4060 */
164 }; /* total 4096 */
165 typedef struct certCacheEntryStr certCacheEntry;
167 struct sidCacheLockStr {
168 PRUint32 timeStamp;
169 sslMutex mutex;
170 sslPID pid;
172 typedef struct sidCacheLockStr sidCacheLock;
174 struct sidCacheSetStr {
175 PRIntn next;
177 typedef struct sidCacheSetStr sidCacheSet;
179 struct cacheDescStr {
181 PRUint32 cacheMemSize;
183 PRUint32 numSIDCacheLocks;
184 PRUint32 numSIDCacheSets;
185 PRUint32 numSIDCacheSetsPerLock;
187 PRUint32 numSIDCacheEntries;
188 PRUint32 sidCacheSize;
190 PRUint32 numCertCacheEntries;
191 PRUint32 certCacheSize;
193 PRUint32 numKeyCacheEntries;
194 PRUint32 keyCacheSize;
196 PRUint32 ssl2Timeout;
197 PRUint32 ssl3Timeout;
199 PRUint32 numSIDCacheLocksInitialized;
201 /* These values are volatile, and are accessed through sharedCache-> */
202 PRUint32 nextCertCacheEntry; /* certCacheLock protects */
203 PRBool stopPolling;
204 PRBool everInherited;
206 /* The private copies of these values are pointers into shared mem */
207 /* The copies of these values in shared memory are merely offsets */
208 sidCacheLock * sidCacheLocks;
209 sidCacheLock * keyCacheLock;
210 sidCacheLock * certCacheLock;
211 sidCacheSet * sidCacheSets;
212 sidCacheEntry * sidCacheData;
213 certCacheEntry * certCacheData;
214 SSLWrappedSymWrappingKey * keyCacheData;
216 /* Only the private copies of these pointers are valid */
217 char * cacheMem;
218 struct cacheDescStr * sharedCache; /* shared copy of this struct */
219 PRFileMap * cacheMemMap;
220 PRThread * poller;
221 PRUint32 mutexTimeout;
222 PRBool shared;
224 typedef struct cacheDescStr cacheDesc;
226 static cacheDesc globalCache;
228 static const char envVarName[] = { SSL_ENV_VAR_NAME };
230 static PRBool isMultiProcess = PR_FALSE;
233 #define DEF_SID_CACHE_ENTRIES 10000
234 #define DEF_CERT_CACHE_ENTRIES 250
235 #define MIN_CERT_CACHE_ENTRIES 125 /* the effective size in old releases. */
236 #define DEF_KEY_CACHE_ENTRIES 250
238 #define SID_CACHE_ENTRIES_PER_SET 128
239 #define SID_ALIGNMENT 16
241 #define DEF_SSL2_TIMEOUT 100 /* seconds */
242 #define MAX_SSL2_TIMEOUT 100 /* seconds */
243 #define MIN_SSL2_TIMEOUT 5 /* seconds */
245 #define DEF_SSL3_TIMEOUT 86400L /* 24 hours */
246 #define MAX_SSL3_TIMEOUT 86400L /* 24 hours */
247 #define MIN_SSL3_TIMEOUT 5 /* seconds */
249 #if defined(AIX) || defined(LINUX) || defined(VMS)
250 #define MAX_SID_CACHE_LOCKS 8 /* two FDs per lock */
251 #elif defined(OSF1)
252 #define MAX_SID_CACHE_LOCKS 16 /* one FD per lock */
253 #else
254 #define MAX_SID_CACHE_LOCKS 256
255 #endif
257 #define SID_HOWMANY(val, size) (((val) + ((size) - 1)) / (size))
258 #define SID_ROUNDUP(val, size) ((size) * SID_HOWMANY((val), (size)))
261 static sslPID myPid;
262 static PRUint32 ssl_max_sid_cache_locks = MAX_SID_CACHE_LOCKS;
264 /* forward static function declarations */
265 static PRUint32 SIDindex(cacheDesc *cache, const PRIPv6Addr *addr, PRUint8 *s,
266 unsigned nl);
267 static SECStatus LaunchLockPoller(cacheDesc *cache);
268 static SECStatus StopLockPoller(cacheDesc *cache);
271 struct inheritanceStr {
272 PRUint32 cacheMemSize;
273 PRUint32 fmStrLen;
276 typedef struct inheritanceStr inheritance;
278 #if defined(_WIN32) || defined(XP_OS2)
280 #define DEFAULT_CACHE_DIRECTORY "\\temp"
282 #endif /* _win32 */
284 #if defined(XP_UNIX) || defined(XP_BEOS)
286 #define DEFAULT_CACHE_DIRECTORY "/tmp"
288 #endif /* XP_UNIX || XP_BEOS */
291 /************************************************************************/
293 static PRUint32
294 LockSidCacheLock(sidCacheLock *lock, PRUint32 now)
296 SECStatus rv = sslMutex_Lock(&lock->mutex);
297 if (rv != SECSuccess)
298 return 0;
299 if (!now)
300 now = ssl_Time();
301 lock->timeStamp = now;
302 lock->pid = myPid;
303 return now;
306 static SECStatus
307 UnlockSidCacheLock(sidCacheLock *lock)
309 SECStatus rv;
311 lock->pid = 0;
312 rv = sslMutex_Unlock(&lock->mutex);
313 return rv;
316 /* returns the value of ssl_Time on success, zero on failure. */
317 static PRUint32
318 LockSet(cacheDesc *cache, PRUint32 set, PRUint32 now)
320 PRUint32 lockNum = set % cache->numSIDCacheLocks;
321 sidCacheLock * lock = cache->sidCacheLocks + lockNum;
323 return LockSidCacheLock(lock, now);
326 static SECStatus
327 UnlockSet(cacheDesc *cache, PRUint32 set)
329 PRUint32 lockNum = set % cache->numSIDCacheLocks;
330 sidCacheLock * lock = cache->sidCacheLocks + lockNum;
332 return UnlockSidCacheLock(lock);
335 /************************************************************************/
338 /* Put a certificate in the cache. Update the cert index in the sce.
340 static PRUint32
341 CacheCert(cacheDesc * cache, CERTCertificate *cert, sidCacheEntry *sce)
343 PRUint32 now;
344 certCacheEntry cce;
346 if ((cert->derCert.len > SSL_MAX_CACHED_CERT_LEN) ||
347 (cert->derCert.len <= 0) ||
348 (cert->derCert.data == NULL)) {
349 PORT_SetError(SEC_ERROR_INVALID_ARGS);
350 return 0;
353 cce.sessionIDLength = sce->sessionIDLength;
354 PORT_Memcpy(cce.sessionID, sce->sessionID, cce.sessionIDLength);
356 cce.certLength = cert->derCert.len;
357 PORT_Memcpy(cce.cert, cert->derCert.data, cce.certLength);
359 /* get lock on cert cache */
360 now = LockSidCacheLock(cache->certCacheLock, 0);
361 if (now) {
363 /* Find where to place the next cert cache entry. */
364 cacheDesc * sharedCache = cache->sharedCache;
365 PRUint32 ndx = sharedCache->nextCertCacheEntry;
367 /* write the entry */
368 cache->certCacheData[ndx] = cce;
370 /* remember where we put it. */
371 sce->u.ssl3.certIndex = ndx;
373 /* update the "next" cache entry index */
374 sharedCache->nextCertCacheEntry =
375 (ndx + 1) % cache->numCertCacheEntries;
377 UnlockSidCacheLock(cache->certCacheLock);
379 return now;
384 ** Convert local SID to shared memory one
386 static void
387 ConvertFromSID(sidCacheEntry *to, sslSessionID *from)
389 to->valid = 1;
390 to->version = from->version;
391 to->addr = from->addr;
392 to->creationTime = from->creationTime;
393 to->lastAccessTime = from->lastAccessTime;
394 to->expirationTime = from->expirationTime;
395 to->authAlgorithm = from->authAlgorithm;
396 to->authKeyBits = from->authKeyBits;
397 to->keaType = from->keaType;
398 to->keaKeyBits = from->keaKeyBits;
400 if (from->version < SSL_LIBRARY_VERSION_3_0) {
401 if ((from->u.ssl2.masterKey.len > SSL_MAX_MASTER_KEY_BYTES) ||
402 (from->u.ssl2.cipherArg.len > SSL_MAX_CYPHER_ARG_BYTES)) {
403 SSL_DBG(("%d: SSL: masterKeyLen=%d cipherArgLen=%d",
404 myPid, from->u.ssl2.masterKey.len,
405 from->u.ssl2.cipherArg.len));
406 to->valid = 0;
407 return;
410 to->u.ssl2.cipherType = from->u.ssl2.cipherType;
411 to->u.ssl2.masterKeyLen = from->u.ssl2.masterKey.len;
412 to->u.ssl2.cipherArgLen = from->u.ssl2.cipherArg.len;
413 to->u.ssl2.keyBits = from->u.ssl2.keyBits;
414 to->u.ssl2.secretKeyBits = from->u.ssl2.secretKeyBits;
415 to->sessionIDLength = SSL2_SESSIONID_BYTES;
416 PORT_Memcpy(to->sessionID, from->u.ssl2.sessionID, SSL2_SESSIONID_BYTES);
417 PORT_Memcpy(to->u.ssl2.masterKey, from->u.ssl2.masterKey.data,
418 from->u.ssl2.masterKey.len);
419 PORT_Memcpy(to->u.ssl2.cipherArg, from->u.ssl2.cipherArg.data,
420 from->u.ssl2.cipherArg.len);
421 #ifdef DEBUG
422 PORT_Memset(to->u.ssl2.masterKey+from->u.ssl2.masterKey.len, 0,
423 sizeof(to->u.ssl2.masterKey) - from->u.ssl2.masterKey.len);
424 PORT_Memset(to->u.ssl2.cipherArg+from->u.ssl2.cipherArg.len, 0,
425 sizeof(to->u.ssl2.cipherArg) - from->u.ssl2.cipherArg.len);
426 #endif
427 SSL_TRC(8, ("%d: SSL: ConvertSID: masterKeyLen=%d cipherArgLen=%d "
428 "time=%d addr=0x%08x%08x%08x%08x cipherType=%d", myPid,
429 to->u.ssl2.masterKeyLen, to->u.ssl2.cipherArgLen,
430 to->creationTime, to->addr.pr_s6_addr32[0],
431 to->addr.pr_s6_addr32[1], to->addr.pr_s6_addr32[2],
432 to->addr.pr_s6_addr32[3], to->u.ssl2.cipherType));
433 } else {
434 /* This is an SSL v3 session */
436 to->u.ssl3.cipherSuite = from->u.ssl3.cipherSuite;
437 to->u.ssl3.compression = (uint16)from->u.ssl3.compression;
438 to->u.ssl3.keys = from->u.ssl3.keys;
439 to->u.ssl3.masterWrapMech = from->u.ssl3.masterWrapMech;
440 to->u.ssl3.exchKeyType = from->u.ssl3.exchKeyType;
441 to->sessionIDLength = from->u.ssl3.sessionIDLength;
442 to->u.ssl3.certIndex = -1;
444 PORT_Memcpy(to->sessionID, from->u.ssl3.sessionID,
445 to->sessionIDLength);
447 SSL_TRC(8, ("%d: SSL3: ConvertSID: time=%d addr=0x%08x%08x%08x%08x "
448 "cipherSuite=%d",
449 myPid, to->creationTime, to->addr.pr_s6_addr32[0],
450 to->addr.pr_s6_addr32[1], to->addr.pr_s6_addr32[2],
451 to->addr.pr_s6_addr32[3], to->u.ssl3.cipherSuite));
456 ** Convert shared memory cache-entry to local memory based one
457 ** This is only called from ServerSessionIDLookup().
458 ** Caller must hold cache lock when calling this.
460 static sslSessionID *
461 ConvertToSID(sidCacheEntry *from, certCacheEntry *pcce,
462 CERTCertDBHandle * dbHandle)
464 sslSessionID *to;
465 uint16 version = from->version;
467 to = (sslSessionID*) PORT_ZAlloc(sizeof(sslSessionID));
468 if (!to) {
469 return 0;
472 if (version < SSL_LIBRARY_VERSION_3_0) {
473 /* This is an SSL v2 session */
474 to->u.ssl2.masterKey.data =
475 (unsigned char*) PORT_Alloc(from->u.ssl2.masterKeyLen);
476 if (!to->u.ssl2.masterKey.data) {
477 goto loser;
479 if (from->u.ssl2.cipherArgLen) {
480 to->u.ssl2.cipherArg.data =
481 (unsigned char*)PORT_Alloc(from->u.ssl2.cipherArgLen);
482 if (!to->u.ssl2.cipherArg.data) {
483 goto loser;
485 PORT_Memcpy(to->u.ssl2.cipherArg.data, from->u.ssl2.cipherArg,
486 from->u.ssl2.cipherArgLen);
489 to->u.ssl2.cipherType = from->u.ssl2.cipherType;
490 to->u.ssl2.masterKey.len = from->u.ssl2.masterKeyLen;
491 to->u.ssl2.cipherArg.len = from->u.ssl2.cipherArgLen;
492 to->u.ssl2.keyBits = from->u.ssl2.keyBits;
493 to->u.ssl2.secretKeyBits = from->u.ssl2.secretKeyBits;
494 /* to->sessionIDLength = SSL2_SESSIONID_BYTES; */
495 PORT_Memcpy(to->u.ssl2.sessionID, from->sessionID, SSL2_SESSIONID_BYTES);
496 PORT_Memcpy(to->u.ssl2.masterKey.data, from->u.ssl2.masterKey,
497 from->u.ssl2.masterKeyLen);
499 SSL_TRC(8, ("%d: SSL: ConvertToSID: masterKeyLen=%d cipherArgLen=%d "
500 "time=%d addr=0x%08x%08x%08x%08x cipherType=%d",
501 myPid, to->u.ssl2.masterKey.len,
502 to->u.ssl2.cipherArg.len, to->creationTime,
503 to->addr.pr_s6_addr32[0], to->addr.pr_s6_addr32[1],
504 to->addr.pr_s6_addr32[2], to->addr.pr_s6_addr32[3],
505 to->u.ssl2.cipherType));
506 } else {
507 /* This is an SSL v3 session */
509 to->u.ssl3.sessionIDLength = from->sessionIDLength;
510 to->u.ssl3.cipherSuite = from->u.ssl3.cipherSuite;
511 to->u.ssl3.compression = (SSL3CompressionMethod)from->u.ssl3.compression;
512 to->u.ssl3.keys = from->u.ssl3.keys;
513 to->u.ssl3.masterWrapMech = from->u.ssl3.masterWrapMech;
514 to->u.ssl3.exchKeyType = from->u.ssl3.exchKeyType;
516 PORT_Memcpy(to->u.ssl3.sessionID, from->sessionID, from->sessionIDLength);
518 /* the portions of the SID that are only restored on the client
519 * are set to invalid values on the server.
521 to->u.ssl3.clientWriteKey = NULL;
522 to->u.ssl3.serverWriteKey = NULL;
524 to->urlSvrName = NULL;
526 to->u.ssl3.masterModuleID = (SECMODModuleID)-1; /* invalid value */
527 to->u.ssl3.masterSlotID = (CK_SLOT_ID)-1; /* invalid value */
528 to->u.ssl3.masterWrapIndex = 0;
529 to->u.ssl3.masterWrapSeries = 0;
530 to->u.ssl3.masterValid = PR_FALSE;
532 to->u.ssl3.clAuthModuleID = (SECMODModuleID)-1; /* invalid value */
533 to->u.ssl3.clAuthSlotID = (CK_SLOT_ID)-1; /* invalid value */
534 to->u.ssl3.clAuthSeries = 0;
535 to->u.ssl3.clAuthValid = PR_FALSE;
537 if (from->u.ssl3.certIndex != -1 && pcce) {
538 SECItem derCert;
540 derCert.len = pcce->certLength;
541 derCert.data = pcce->cert;
543 to->peerCert = CERT_NewTempCertificate(dbHandle, &derCert, NULL,
544 PR_FALSE, PR_TRUE);
545 if (to->peerCert == NULL)
546 goto loser;
550 to->version = from->version;
551 to->creationTime = from->creationTime;
552 to->lastAccessTime = from->lastAccessTime;
553 to->expirationTime = from->expirationTime;
554 to->cached = in_server_cache;
555 to->addr = from->addr;
556 to->references = 1;
557 to->authAlgorithm = from->authAlgorithm;
558 to->authKeyBits = from->authKeyBits;
559 to->keaType = from->keaType;
560 to->keaKeyBits = from->keaKeyBits;
562 return to;
564 loser:
565 if (to) {
566 if (version < SSL_LIBRARY_VERSION_3_0) {
567 if (to->u.ssl2.masterKey.data)
568 PORT_Free(to->u.ssl2.masterKey.data);
569 if (to->u.ssl2.cipherArg.data)
570 PORT_Free(to->u.ssl2.cipherArg.data);
572 PORT_Free(to);
574 return NULL;
580 ** Perform some mumbo jumbo on the ip-address and the session-id value to
581 ** compute a hash value.
583 static PRUint32
584 SIDindex(cacheDesc *cache, const PRIPv6Addr *addr, PRUint8 *s, unsigned nl)
586 PRUint32 rv;
587 PRUint32 x[8];
589 memset(x, 0, sizeof x);
590 if (nl > sizeof x)
591 nl = sizeof x;
592 memcpy(x, s, nl);
594 rv = (addr->pr_s6_addr32[0] ^ addr->pr_s6_addr32[1] ^
595 addr->pr_s6_addr32[2] ^ addr->pr_s6_addr32[3] ^
596 x[0] ^ x[1] ^ x[2] ^ x[3] ^ x[4] ^ x[5] ^ x[6] ^ x[7])
597 % cache->numSIDCacheSets;
598 return rv;
604 ** Look something up in the cache. This will invalidate old entries
605 ** in the process. Caller has locked the cache set!
606 ** Returns PR_TRUE if found a valid match. PR_FALSE otherwise.
608 static sidCacheEntry *
609 FindSID(cacheDesc *cache, PRUint32 setNum, PRUint32 now,
610 const PRIPv6Addr *addr, unsigned char *sessionID,
611 unsigned sessionIDLength)
613 PRUint32 ndx = cache->sidCacheSets[setNum].next;
614 int i;
616 sidCacheEntry * set = cache->sidCacheData +
617 (setNum * SID_CACHE_ENTRIES_PER_SET);
619 for (i = SID_CACHE_ENTRIES_PER_SET; i > 0; --i) {
620 sidCacheEntry * sce;
622 ndx = (ndx - 1) % SID_CACHE_ENTRIES_PER_SET;
623 sce = set + ndx;
625 if (!sce->valid)
626 continue;
628 if (now > sce->expirationTime) {
629 /* SessionID has timed out. Invalidate the entry. */
630 SSL_TRC(7, ("%d: timed out sid entry addr=%08x%08x%08x%08x now=%x "
631 "time+=%x",
632 myPid, sce->addr.pr_s6_addr32[0],
633 sce->addr.pr_s6_addr32[1], sce->addr.pr_s6_addr32[2],
634 sce->addr.pr_s6_addr32[3], now,
635 sce->expirationTime ));
636 sce->valid = 0;
637 continue;
641 ** Next, examine specific session-id/addr data to see if the cache
642 ** entry matches our addr+session-id value
644 if (sessionIDLength == sce->sessionIDLength &&
645 !memcmp(&sce->addr, addr, sizeof(PRIPv6Addr)) &&
646 !memcmp(sce->sessionID, sessionID, sessionIDLength)) {
647 /* Found it */
648 return sce;
652 PORT_SetError(SSL_ERROR_SESSION_NOT_FOUND);
653 return NULL;
656 /************************************************************************/
658 /* This is the primary function for finding entries in the server's sid cache.
659 * Although it is static, this function is called via the global function
660 * pointer ssl_sid_lookup.
662 static sslSessionID *
663 ServerSessionIDLookup(const PRIPv6Addr *addr,
664 unsigned char *sessionID,
665 unsigned int sessionIDLength,
666 CERTCertDBHandle * dbHandle)
668 sslSessionID * sid = 0;
669 sidCacheEntry * psce;
670 certCacheEntry *pcce = 0;
671 cacheDesc * cache = &globalCache;
672 PRUint32 now;
673 PRUint32 set;
674 PRInt32 cndx;
675 sidCacheEntry sce;
676 certCacheEntry cce;
678 set = SIDindex(cache, addr, sessionID, sessionIDLength);
679 now = LockSet(cache, set, 0);
680 if (!now)
681 return NULL;
683 psce = FindSID(cache, set, now, addr, sessionID, sessionIDLength);
684 if (psce) {
685 if (psce->version >= SSL_LIBRARY_VERSION_3_0 &&
686 (cndx = psce->u.ssl3.certIndex) != -1) {
688 PRUint32 gotLock = LockSidCacheLock(cache->certCacheLock, now);
689 if (gotLock) {
690 pcce = &cache->certCacheData[cndx];
692 /* See if the cert's session ID matches the sce cache. */
693 if ((pcce->sessionIDLength == psce->sessionIDLength) &&
694 !PORT_Memcmp(pcce->sessionID, psce->sessionID,
695 pcce->sessionIDLength)) {
696 cce = *pcce;
697 } else {
698 /* The cert doesen't match the SID cache entry,
699 ** so invalidate the SID cache entry.
701 psce->valid = 0;
702 psce = 0;
703 pcce = 0;
705 UnlockSidCacheLock(cache->certCacheLock);
706 } else {
707 /* what the ??. Didn't get the cert cache lock.
708 ** Don't invalidate the SID cache entry, but don't find it.
710 PORT_Assert(!("Didn't get cert Cache Lock!"));
711 psce = 0;
712 pcce = 0;
715 if (psce) {
716 psce->lastAccessTime = now;
717 sce = *psce; /* grab a copy while holding the lock */
720 UnlockSet(cache, set);
721 if (psce) {
722 /* sce conains a copy of the cache entry.
723 ** Convert shared memory format to local format
725 sid = ConvertToSID(&sce, pcce ? &cce : 0, dbHandle);
727 return sid;
731 ** Place a sid into the cache, if it isn't already there.
733 static void
734 ServerSessionIDCache(sslSessionID *sid)
736 sidCacheEntry sce;
737 PRUint32 now = 0;
738 uint16 version = sid->version;
739 cacheDesc * cache = &globalCache;
741 if ((version >= SSL_LIBRARY_VERSION_3_0) &&
742 (sid->u.ssl3.sessionIDLength == 0)) {
743 return;
746 if (sid->cached == never_cached || sid->cached == invalid_cache) {
747 PRUint32 set;
749 PORT_Assert(sid->creationTime != 0);
750 if (!sid->creationTime)
751 sid->lastAccessTime = sid->creationTime = ssl_Time();
752 if (version < SSL_LIBRARY_VERSION_3_0) {
753 /* override caller's expiration time, which uses client timeout
754 * duration, not server timeout duration.
756 sid->expirationTime = sid->creationTime + cache->ssl2Timeout;
757 SSL_TRC(8, ("%d: SSL: CacheMT: cached=%d addr=0x%08x%08x%08x%08x time=%x "
758 "cipher=%d", myPid, sid->cached,
759 sid->addr.pr_s6_addr32[0], sid->addr.pr_s6_addr32[1],
760 sid->addr.pr_s6_addr32[2], sid->addr.pr_s6_addr32[3],
761 sid->creationTime, sid->u.ssl2.cipherType));
762 PRINT_BUF(8, (0, "sessionID:", sid->u.ssl2.sessionID,
763 SSL2_SESSIONID_BYTES));
764 PRINT_BUF(8, (0, "masterKey:", sid->u.ssl2.masterKey.data,
765 sid->u.ssl2.masterKey.len));
766 PRINT_BUF(8, (0, "cipherArg:", sid->u.ssl2.cipherArg.data,
767 sid->u.ssl2.cipherArg.len));
769 } else {
770 /* override caller's expiration time, which uses client timeout
771 * duration, not server timeout duration.
773 sid->expirationTime = sid->creationTime + cache->ssl3Timeout;
774 SSL_TRC(8, ("%d: SSL: CacheMT: cached=%d addr=0x%08x%08x%08x%08x time=%x "
775 "cipherSuite=%d", myPid, sid->cached,
776 sid->addr.pr_s6_addr32[0], sid->addr.pr_s6_addr32[1],
777 sid->addr.pr_s6_addr32[2], sid->addr.pr_s6_addr32[3],
778 sid->creationTime, sid->u.ssl3.cipherSuite));
779 PRINT_BUF(8, (0, "sessionID:", sid->u.ssl3.sessionID,
780 sid->u.ssl3.sessionIDLength));
783 ConvertFromSID(&sce, sid);
785 if ((version >= SSL_LIBRARY_VERSION_3_0) &&
786 (sid->peerCert != NULL)) {
787 now = CacheCert(cache, sid->peerCert, &sce);
790 set = SIDindex(cache, &sce.addr, sce.sessionID, sce.sessionIDLength);
791 now = LockSet(cache, set, now);
792 if (now) {
793 PRUint32 next = cache->sidCacheSets[set].next;
794 PRUint32 ndx = set * SID_CACHE_ENTRIES_PER_SET + next;
796 /* Write out new cache entry */
797 cache->sidCacheData[ndx] = sce;
799 cache->sidCacheSets[set].next =
800 (next + 1) % SID_CACHE_ENTRIES_PER_SET;
802 UnlockSet(cache, set);
803 sid->cached = in_server_cache;
809 ** Although this is static, it is called from ssl via global function pointer
810 ** ssl_sid_uncache. This invalidates the referenced cache entry.
812 static void
813 ServerSessionIDUncache(sslSessionID *sid)
815 cacheDesc * cache = &globalCache;
816 PRUint8 * sessionID;
817 unsigned int sessionIDLength;
818 PRErrorCode err;
819 PRUint32 set;
820 PRUint32 now;
821 sidCacheEntry *psce;
823 if (sid == NULL)
824 return;
826 /* Uncaching a SID should never change the error code.
827 ** So save it here and restore it before exiting.
829 err = PR_GetError();
831 if (sid->version < SSL_LIBRARY_VERSION_3_0) {
832 sessionID = sid->u.ssl2.sessionID;
833 sessionIDLength = SSL2_SESSIONID_BYTES;
834 SSL_TRC(8, ("%d: SSL: UncacheMT: valid=%d addr=0x%08x%08x%08x%08x time=%x "
835 "cipher=%d", myPid, sid->cached,
836 sid->addr.pr_s6_addr32[0], sid->addr.pr_s6_addr32[1],
837 sid->addr.pr_s6_addr32[2], sid->addr.pr_s6_addr32[3],
838 sid->creationTime, sid->u.ssl2.cipherType));
839 PRINT_BUF(8, (0, "sessionID:", sessionID, sessionIDLength));
840 PRINT_BUF(8, (0, "masterKey:", sid->u.ssl2.masterKey.data,
841 sid->u.ssl2.masterKey.len));
842 PRINT_BUF(8, (0, "cipherArg:", sid->u.ssl2.cipherArg.data,
843 sid->u.ssl2.cipherArg.len));
844 } else {
845 sessionID = sid->u.ssl3.sessionID;
846 sessionIDLength = sid->u.ssl3.sessionIDLength;
847 SSL_TRC(8, ("%d: SSL3: UncacheMT: valid=%d addr=0x%08x%08x%08x%08x time=%x "
848 "cipherSuite=%d", myPid, sid->cached,
849 sid->addr.pr_s6_addr32[0], sid->addr.pr_s6_addr32[1],
850 sid->addr.pr_s6_addr32[2], sid->addr.pr_s6_addr32[3],
851 sid->creationTime, sid->u.ssl3.cipherSuite));
852 PRINT_BUF(8, (0, "sessionID:", sessionID, sessionIDLength));
854 set = SIDindex(cache, &sid->addr, sessionID, sessionIDLength);
855 now = LockSet(cache, set, 0);
856 if (now) {
857 psce = FindSID(cache, set, now, &sid->addr, sessionID, sessionIDLength);
858 if (psce) {
859 psce->valid = 0;
861 UnlockSet(cache, set);
863 sid->cached = invalid_cache;
864 PORT_SetError(err);
867 #ifdef XP_OS2
869 #define INCL_DOSPROCESS
870 #include <os2.h>
872 long gettid(void)
874 PTIB ptib;
875 PPIB ppib;
876 DosGetInfoBlocks(&ptib, &ppib);
877 return ((long)ptib->tib_ordinal); /* thread id */
879 #endif
881 static void
882 CloseCache(cacheDesc *cache)
884 int locks_initialized = cache->numSIDCacheLocksInitialized;
886 if (cache->cacheMem) {
887 /* If everInherited is true, this shared cache was (and may still
888 ** be) in use by multiple processes. We do not wish to destroy
889 ** the mutexes while they are still in use.
891 if (cache->sharedCache &&
892 PR_FALSE == cache->sharedCache->everInherited) {
893 sidCacheLock *pLock = cache->sidCacheLocks;
894 for (; locks_initialized > 0; --locks_initialized, ++pLock ) {
895 sslMutex_Destroy(&pLock->mutex);
898 if (cache->shared) {
899 PR_MemUnmap(cache->cacheMem, cache->cacheMemSize);
900 } else {
901 PORT_Free(cache->cacheMem);
903 cache->cacheMem = NULL;
905 if (cache->cacheMemMap) {
906 PR_CloseFileMap(cache->cacheMemMap);
907 cache->cacheMemMap = NULL;
909 memset(cache, 0, sizeof *cache);
912 static SECStatus
913 InitCache(cacheDesc *cache, int maxCacheEntries, PRUint32 ssl2_timeout,
914 PRUint32 ssl3_timeout, const char *directory, PRBool shared)
916 ptrdiff_t ptr;
917 sidCacheLock *pLock;
918 char * cacheMem;
919 PRFileMap * cacheMemMap;
920 char * cfn = NULL; /* cache file name */
921 int locks_initialized = 0;
922 int locks_to_initialize = 0;
923 PRUint32 init_time;
925 if ( (!cache) || (maxCacheEntries < 0) || (!directory) ) {
926 PORT_SetError(SEC_ERROR_INVALID_ARGS);
927 return SECFailure;
930 if (cache->cacheMem) {
931 /* Already done */
932 return SECSuccess;
935 /* make sure loser can clean up properly */
936 cache->shared = shared;
937 cache->cacheMem = cacheMem = NULL;
938 cache->cacheMemMap = cacheMemMap = NULL;
939 cache->sharedCache = (cacheDesc *)0;
941 cache->numSIDCacheLocksInitialized = 0;
942 cache->nextCertCacheEntry = 0;
943 cache->stopPolling = PR_FALSE;
944 cache->everInherited = PR_FALSE;
945 cache->poller = NULL;
946 cache->mutexTimeout = 0;
948 cache->numSIDCacheEntries = maxCacheEntries ? maxCacheEntries
949 : DEF_SID_CACHE_ENTRIES;
950 cache->numSIDCacheSets =
951 SID_HOWMANY(cache->numSIDCacheEntries, SID_CACHE_ENTRIES_PER_SET);
953 cache->numSIDCacheEntries =
954 cache->numSIDCacheSets * SID_CACHE_ENTRIES_PER_SET;
956 cache->numSIDCacheLocks =
957 PR_MIN(cache->numSIDCacheSets, ssl_max_sid_cache_locks);
959 cache->numSIDCacheSetsPerLock =
960 SID_HOWMANY(cache->numSIDCacheSets, cache->numSIDCacheLocks);
962 /* compute size of shared memory, and offsets of all pointers */
963 ptr = 0;
964 cache->cacheMem = (char *)ptr;
965 ptr += SID_ROUNDUP(sizeof(cacheDesc), SID_ALIGNMENT);
967 cache->sidCacheLocks = (sidCacheLock *)ptr;
968 cache->keyCacheLock = cache->sidCacheLocks + cache->numSIDCacheLocks;
969 cache->certCacheLock = cache->keyCacheLock + 1;
970 ptr = (ptrdiff_t)(cache->certCacheLock + 1);
971 ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
973 cache->sidCacheSets = (sidCacheSet *)ptr;
974 ptr = (ptrdiff_t)(cache->sidCacheSets + cache->numSIDCacheSets);
975 ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
977 cache->sidCacheData = (sidCacheEntry *)ptr;
978 ptr = (ptrdiff_t)(cache->sidCacheData + cache->numSIDCacheEntries);
979 ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
981 cache->certCacheData = (certCacheEntry *)ptr;
982 cache->sidCacheSize =
983 (char *)cache->certCacheData - (char *)cache->sidCacheData;
985 /* This is really a poor way to computer this! */
986 cache->numCertCacheEntries = cache->sidCacheSize / sizeof(certCacheEntry);
987 if (cache->numCertCacheEntries < MIN_CERT_CACHE_ENTRIES)
988 cache->numCertCacheEntries = MIN_CERT_CACHE_ENTRIES;
989 ptr = (ptrdiff_t)(cache->certCacheData + cache->numCertCacheEntries);
990 ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
992 cache->keyCacheData = (SSLWrappedSymWrappingKey *)ptr;
993 cache->certCacheSize =
994 (char *)cache->keyCacheData - (char *)cache->certCacheData;
996 cache->numKeyCacheEntries = kt_kea_size * SSL_NUM_WRAP_MECHS;
997 ptr = (ptrdiff_t)(cache->keyCacheData + cache->numKeyCacheEntries);
998 ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
1000 cache->cacheMemSize = ptr;
1002 cache->keyCacheSize = (char *)ptr - (char *)cache->keyCacheData;
1004 if (ssl2_timeout) {
1005 if (ssl2_timeout > MAX_SSL2_TIMEOUT) {
1006 ssl2_timeout = MAX_SSL2_TIMEOUT;
1008 if (ssl2_timeout < MIN_SSL2_TIMEOUT) {
1009 ssl2_timeout = MIN_SSL2_TIMEOUT;
1011 cache->ssl2Timeout = ssl2_timeout;
1012 } else {
1013 cache->ssl2Timeout = DEF_SSL2_TIMEOUT;
1016 if (ssl3_timeout) {
1017 if (ssl3_timeout > MAX_SSL3_TIMEOUT) {
1018 ssl3_timeout = MAX_SSL3_TIMEOUT;
1020 if (ssl3_timeout < MIN_SSL3_TIMEOUT) {
1021 ssl3_timeout = MIN_SSL3_TIMEOUT;
1023 cache->ssl3Timeout = ssl3_timeout;
1024 } else {
1025 cache->ssl3Timeout = DEF_SSL3_TIMEOUT;
1028 if (shared) {
1029 /* Create file names */
1030 #if defined(XP_UNIX) || defined(XP_BEOS)
1031 /* there's some confusion here about whether PR_OpenAnonFileMap wants
1032 ** a directory name or a file name for its first argument.
1033 cfn = PR_smprintf("%s/.sslsvrcache.%d", directory, myPid);
1035 cfn = PR_smprintf("%s", directory);
1036 #elif defined(XP_WIN32)
1037 cfn = PR_smprintf("%s/svrcache_%d_%x.ssl", directory, myPid,
1038 GetCurrentThreadId());
1039 #elif defined(XP_OS2)
1040 cfn = PR_smprintf("%s/svrcache_%d_%x.ssl", directory, myPid,
1041 gettid());
1042 #else
1043 #error "Don't know how to create file name for this platform!"
1044 #endif
1045 if (!cfn) {
1046 goto loser;
1049 /* Create cache */
1050 cacheMemMap = PR_OpenAnonFileMap(cfn, cache->cacheMemSize,
1051 PR_PROT_READWRITE);
1053 PR_smprintf_free(cfn);
1054 if(!cacheMemMap) {
1055 goto loser;
1058 cacheMem = PR_MemMap(cacheMemMap, 0, cache->cacheMemSize);
1059 } else {
1060 cacheMem = PORT_Alloc(cache->cacheMemSize);
1063 if (! cacheMem) {
1064 goto loser;
1067 /* Initialize shared memory. This may not be necessary on all platforms */
1068 memset(cacheMem, 0, cache->cacheMemSize);
1070 /* Copy cache descriptor header into shared memory */
1071 memcpy(cacheMem, cache, sizeof *cache);
1073 /* save private copies of these values */
1074 cache->cacheMemMap = cacheMemMap;
1075 cache->cacheMem = cacheMem;
1076 cache->sharedCache = (cacheDesc *)cacheMem;
1078 /* Fix pointers in our private copy of cache descriptor to point to
1079 ** spaces in shared memory
1081 ptr = (ptrdiff_t)cache->cacheMem;
1082 *(ptrdiff_t *)(&cache->sidCacheLocks) += ptr;
1083 *(ptrdiff_t *)(&cache->keyCacheLock ) += ptr;
1084 *(ptrdiff_t *)(&cache->certCacheLock) += ptr;
1085 *(ptrdiff_t *)(&cache->sidCacheSets ) += ptr;
1086 *(ptrdiff_t *)(&cache->sidCacheData ) += ptr;
1087 *(ptrdiff_t *)(&cache->certCacheData) += ptr;
1088 *(ptrdiff_t *)(&cache->keyCacheData ) += ptr;
1090 /* initialize the locks */
1091 init_time = ssl_Time();
1092 pLock = cache->sidCacheLocks;
1093 for (locks_to_initialize = cache->numSIDCacheLocks + 2;
1094 locks_initialized < locks_to_initialize;
1095 ++locks_initialized, ++pLock ) {
1097 SECStatus err = sslMutex_Init(&pLock->mutex, shared);
1098 if (err) {
1099 cache->numSIDCacheLocksInitialized = locks_initialized;
1100 goto loser;
1102 pLock->timeStamp = init_time;
1103 pLock->pid = 0;
1105 cache->numSIDCacheLocksInitialized = locks_initialized;
1107 return SECSuccess;
1109 loser:
1110 CloseCache(cache);
1111 return SECFailure;
1114 PRUint32
1115 SSL_GetMaxServerCacheLocks(void)
1117 return ssl_max_sid_cache_locks + 2;
1118 /* The extra two are the cert cache lock and the key cache lock. */
1121 SECStatus
1122 SSL_SetMaxServerCacheLocks(PRUint32 maxLocks)
1124 /* Minimum is 1 sid cache lock, 1 cert cache lock and 1 key cache lock.
1125 ** We'd like to test for a maximum value, but not all platforms' header
1126 ** files provide a symbol or function or other means of determining
1127 ** the maximum, other than trial and error.
1129 if (maxLocks < 3) {
1130 PORT_SetError(SEC_ERROR_INVALID_ARGS);
1131 return SECFailure;
1133 ssl_max_sid_cache_locks = maxLocks - 2;
1134 /* The extra two are the cert cache lock and the key cache lock. */
1135 return SECSuccess;
1138 SECStatus
1139 SSL_ConfigServerSessionIDCacheInstance( cacheDesc *cache,
1140 int maxCacheEntries,
1141 PRUint32 ssl2_timeout,
1142 PRUint32 ssl3_timeout,
1143 const char * directory, PRBool shared)
1145 SECStatus rv;
1147 #if defined(DEBUG_nelsonb)
1148 printf("sizeof(sidCacheEntry) == %u\n", sizeof(sidCacheEntry));
1149 #endif
1150 PORT_Assert(sizeof(sidCacheEntry) == 192);
1151 PORT_Assert(sizeof(certCacheEntry) == 4096);
1153 myPid = SSL_GETPID();
1154 if (!directory) {
1155 directory = DEFAULT_CACHE_DIRECTORY;
1157 rv = InitCache(cache, maxCacheEntries, ssl2_timeout, ssl3_timeout,
1158 directory, shared);
1159 if (rv) {
1160 SET_ERROR_CODE
1161 return SECFailure;
1164 ssl_sid_lookup = ServerSessionIDLookup;
1165 ssl_sid_cache = ServerSessionIDCache;
1166 ssl_sid_uncache = ServerSessionIDUncache;
1167 return SECSuccess;
1170 SECStatus
1171 SSL_ConfigServerSessionIDCache( int maxCacheEntries,
1172 PRUint32 ssl2_timeout,
1173 PRUint32 ssl3_timeout,
1174 const char * directory)
1176 ssl_InitLocks(PR_FALSE);
1177 return SSL_ConfigServerSessionIDCacheInstance(&globalCache,
1178 maxCacheEntries, ssl2_timeout, ssl3_timeout, directory, PR_FALSE);
1181 SECStatus
1182 SSL_ShutdownServerSessionIDCacheInstance(cacheDesc *cache)
1184 CloseCache(cache);
1185 return SECSuccess;
1188 SECStatus
1189 SSL_ShutdownServerSessionIDCache(void)
1191 #if defined(XP_UNIX) || defined(XP_BEOS)
1192 /* Stop the thread that polls cache for expired locks on Unix */
1193 StopLockPoller(&globalCache);
1194 #endif
1195 SSL3_ShutdownServerCache();
1196 return SSL_ShutdownServerSessionIDCacheInstance(&globalCache);
1199 /* Use this function, instead of SSL_ConfigServerSessionIDCache,
1200 * if the cache will be shared by multiple processes.
1202 SECStatus
1203 SSL_ConfigMPServerSIDCache( int maxCacheEntries,
1204 PRUint32 ssl2_timeout,
1205 PRUint32 ssl3_timeout,
1206 const char * directory)
1208 char * envValue;
1209 char * inhValue;
1210 cacheDesc * cache = &globalCache;
1211 PRUint32 fmStrLen;
1212 SECStatus result;
1213 PRStatus prStatus;
1214 SECStatus putEnvFailed;
1215 inheritance inherit;
1216 char fmString[PR_FILEMAP_STRING_BUFSIZE];
1218 isMultiProcess = PR_TRUE;
1219 result = SSL_ConfigServerSessionIDCacheInstance(cache, maxCacheEntries,
1220 ssl2_timeout, ssl3_timeout, directory, PR_TRUE);
1221 if (result != SECSuccess)
1222 return result;
1224 prStatus = PR_ExportFileMapAsString(cache->cacheMemMap,
1225 sizeof fmString, fmString);
1226 if ((prStatus != PR_SUCCESS) || !(fmStrLen = strlen(fmString))) {
1227 SET_ERROR_CODE
1228 return SECFailure;
1231 inherit.cacheMemSize = cache->cacheMemSize;
1232 inherit.fmStrLen = fmStrLen;
1234 inhValue = BTOA_DataToAscii((unsigned char *)&inherit, sizeof inherit);
1235 if (!inhValue || !strlen(inhValue)) {
1236 SET_ERROR_CODE
1237 return SECFailure;
1239 envValue = PR_smprintf("%s,%s", inhValue, fmString);
1240 if (!envValue || !strlen(envValue)) {
1241 SET_ERROR_CODE
1242 return SECFailure;
1244 PORT_Free(inhValue);
1246 putEnvFailed = (SECStatus)NSS_PutEnv(envVarName, envValue);
1247 PR_smprintf_free(envValue);
1248 if (putEnvFailed) {
1249 SET_ERROR_CODE
1250 result = SECFailure;
1253 #if defined(XP_UNIX) || defined(XP_BEOS)
1254 /* Launch thread to poll cache for expired locks on Unix */
1255 LaunchLockPoller(cache);
1256 #endif
1257 return result;
1260 SECStatus
1261 SSL_InheritMPServerSIDCacheInstance(cacheDesc *cache, const char * envString)
1263 unsigned char * decoString = NULL;
1264 char * fmString = NULL;
1265 char * myEnvString = NULL;
1266 unsigned int decoLen;
1267 ptrdiff_t ptr;
1268 inheritance inherit;
1269 cacheDesc my;
1270 #ifdef WINNT
1271 sidCacheLock* newLocks;
1272 int locks_initialized = 0;
1273 int locks_to_initialize = 0;
1274 #endif
1276 myPid = SSL_GETPID();
1278 /* If this child was created by fork(), and not by exec() on unix,
1279 ** then isMultiProcess will already be set.
1280 ** If not, we'll set it below.
1282 if (isMultiProcess) {
1283 if (cache && cache->sharedCache) {
1284 cache->sharedCache->everInherited = PR_TRUE;
1286 return SECSuccess; /* already done. */
1289 ssl_InitLocks(PR_FALSE);
1291 ssl_sid_lookup = ServerSessionIDLookup;
1292 ssl_sid_cache = ServerSessionIDCache;
1293 ssl_sid_uncache = ServerSessionIDUncache;
1295 if (!envString) {
1296 envString = getenv(envVarName);
1297 if (!envString) {
1298 SET_ERROR_CODE
1299 return SECFailure;
1302 myEnvString = PORT_Strdup(envString);
1303 if (!myEnvString)
1304 return SECFailure;
1305 fmString = strchr(myEnvString, ',');
1306 if (!fmString)
1307 goto loser;
1308 *fmString++ = 0;
1310 decoString = ATOB_AsciiToData(myEnvString, &decoLen);
1311 if (!decoString) {
1312 SET_ERROR_CODE
1313 goto loser;
1315 if (decoLen != sizeof inherit) {
1316 SET_ERROR_CODE
1317 goto loser;
1320 PORT_Memcpy(&inherit, decoString, sizeof inherit);
1322 if (strlen(fmString) != inherit.fmStrLen ) {
1323 goto loser;
1326 memset(cache, 0, sizeof *cache);
1327 cache->cacheMemSize = inherit.cacheMemSize;
1329 /* Create cache */
1330 cache->cacheMemMap = PR_ImportFileMapFromString(fmString);
1331 if(! cache->cacheMemMap) {
1332 goto loser;
1334 cache->cacheMem = PR_MemMap(cache->cacheMemMap, 0, cache->cacheMemSize);
1335 if (! cache->cacheMem) {
1336 goto loser;
1338 cache->sharedCache = (cacheDesc *)cache->cacheMem;
1340 if (cache->sharedCache->cacheMemSize != cache->cacheMemSize) {
1341 SET_ERROR_CODE
1342 goto loser;
1345 /* We're now going to overwrite the local cache instance with the
1346 ** shared copy of the cache struct, then update several values in
1347 ** the local cache using the values for cache->cacheMemMap and
1348 ** cache->cacheMem computed just above. So, we copy cache into
1349 ** the automatic variable "my", to preserve the variables while
1350 ** cache is overwritten.
1352 my = *cache; /* save values computed above. */
1353 memcpy(cache, cache->sharedCache, sizeof *cache); /* overwrite */
1355 /* Fix pointers in our private copy of cache descriptor to point to
1356 ** spaces in shared memory, whose address is now in "my".
1358 ptr = (ptrdiff_t)my.cacheMem;
1359 *(ptrdiff_t *)(&cache->sidCacheLocks) += ptr;
1360 *(ptrdiff_t *)(&cache->keyCacheLock ) += ptr;
1361 *(ptrdiff_t *)(&cache->certCacheLock) += ptr;
1362 *(ptrdiff_t *)(&cache->sidCacheSets ) += ptr;
1363 *(ptrdiff_t *)(&cache->sidCacheData ) += ptr;
1364 *(ptrdiff_t *)(&cache->certCacheData) += ptr;
1365 *(ptrdiff_t *)(&cache->keyCacheData ) += ptr;
1367 cache->cacheMemMap = my.cacheMemMap;
1368 cache->cacheMem = my.cacheMem;
1369 cache->sharedCache = (cacheDesc *)cache->cacheMem;
1371 #ifdef WINNT
1372 /* On Windows NT we need to "fix" the sidCacheLocks here to support fibers
1373 ** When NT fibers are used in a multi-process server, a second level of
1374 ** locking is needed to prevent a deadlock, in case a fiber acquires the
1375 ** cross-process mutex, yields, and another fiber is later scheduled on
1376 ** the same native thread and tries to acquire the cross-process mutex.
1377 ** We do this by using a PRLock in the sslMutex. However, it is stored in
1378 ** shared memory as part of sidCacheLocks, and we don't want to overwrite
1379 ** the PRLock of the parent process. So we need to make new, private
1380 ** copies of sidCacheLocks before modifying the sslMutex with our own
1381 ** PRLock
1384 /* note from jpierre : this should be free'd in child processes when
1385 ** a function is added to delete the SSL session cache in the future.
1387 locks_to_initialize = cache->numSIDCacheLocks + 2;
1388 newLocks = PORT_NewArray(sidCacheLock, locks_to_initialize);
1389 if (!newLocks)
1390 goto loser;
1391 /* copy the old locks */
1392 memcpy(newLocks, cache->sidCacheLocks,
1393 locks_to_initialize * sizeof(sidCacheLock));
1394 cache->sidCacheLocks = newLocks;
1395 /* fix the locks */
1396 for (; locks_initialized < locks_to_initialize; ++locks_initialized) {
1397 /* now, make a local PRLock in this sslMutex for this child process */
1398 SECStatus err;
1399 err = sslMutex_2LevelInit(&newLocks[locks_initialized].mutex);
1400 if (err != SECSuccess) {
1401 cache->numSIDCacheLocksInitialized = locks_initialized;
1402 goto loser;
1405 cache->numSIDCacheLocksInitialized = locks_initialized;
1407 /* also fix the key and cert cache which use the last 2 lock entries */
1408 cache->keyCacheLock = cache->sidCacheLocks + cache->numSIDCacheLocks;
1409 cache->certCacheLock = cache->keyCacheLock + 1;
1410 #endif
1412 PORT_Free(myEnvString);
1413 PORT_Free(decoString);
1415 /* mark that we have inherited this. */
1416 cache->sharedCache->everInherited = PR_TRUE;
1417 isMultiProcess = PR_TRUE;
1419 return SECSuccess;
1421 loser:
1422 PORT_Free(myEnvString);
1423 if (decoString)
1424 PORT_Free(decoString);
1425 CloseCache(cache);
1426 return SECFailure;
1429 SECStatus
1430 SSL_InheritMPServerSIDCache(const char * envString)
1432 return SSL_InheritMPServerSIDCacheInstance(&globalCache, envString);
1435 #if defined(XP_UNIX) || defined(XP_BEOS)
1437 #define SID_LOCK_EXPIRATION_TIMEOUT 30 /* seconds */
1439 static void
1440 LockPoller(void * arg)
1442 cacheDesc * cache = (cacheDesc *)arg;
1443 cacheDesc * sharedCache = cache->sharedCache;
1444 sidCacheLock * pLock;
1445 PRIntervalTime timeout;
1446 PRUint32 now;
1447 PRUint32 then;
1448 int locks_polled = 0;
1449 int locks_to_poll = cache->numSIDCacheLocks + 2;
1450 PRUint32 expiration = cache->mutexTimeout;
1452 timeout = PR_SecondsToInterval(expiration);
1453 while(!sharedCache->stopPolling) {
1454 PR_Sleep(timeout);
1455 if (sharedCache->stopPolling)
1456 break;
1458 now = ssl_Time();
1459 then = now - expiration;
1460 for (pLock = cache->sidCacheLocks, locks_polled = 0;
1461 locks_to_poll > locks_polled && !sharedCache->stopPolling;
1462 ++locks_polled, ++pLock ) {
1463 pid_t pid;
1465 if (pLock->timeStamp < then &&
1466 pLock->timeStamp != 0 &&
1467 (pid = pLock->pid) != 0) {
1469 /* maybe we should try the lock? */
1470 int result = kill(pid, 0);
1471 if (result < 0 && errno == ESRCH) {
1472 SECStatus rv;
1473 /* No process exists by that pid any more.
1474 ** Treat this mutex as abandoned.
1476 pLock->timeStamp = now;
1477 pLock->pid = 0;
1478 rv = sslMutex_Unlock(&pLock->mutex);
1479 if (rv != SECSuccess) {
1480 /* Now what? */
1484 } /* end of loop over locks */
1485 } /* end of entire polling loop */
1488 /* Launch thread to poll cache for expired locks */
1489 static SECStatus
1490 LaunchLockPoller(cacheDesc *cache)
1492 const char * timeoutString;
1493 PRThread * pollerThread;
1495 cache->mutexTimeout = SID_LOCK_EXPIRATION_TIMEOUT;
1496 timeoutString = getenv("NSS_SSL_SERVER_CACHE_MUTEX_TIMEOUT");
1497 if (timeoutString) {
1498 long newTime = strtol(timeoutString, 0, 0);
1499 if (newTime == 0)
1500 return SECSuccess; /* application doesn't want poller thread */
1501 if (newTime > 0)
1502 cache->mutexTimeout = (PRUint32)newTime;
1503 /* if error (newTime < 0) ignore it and use default */
1506 pollerThread =
1507 PR_CreateThread(PR_USER_THREAD, LockPoller, cache, PR_PRIORITY_NORMAL,
1508 PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
1509 if (!pollerThread) {
1510 return SECFailure;
1512 cache->poller = pollerThread;
1513 return SECSuccess;
1516 /* Stop the thread that polls cache for expired locks */
1517 static SECStatus
1518 StopLockPoller(cacheDesc *cache)
1520 if (!cache->poller) {
1521 return SECSuccess;
1523 cache->sharedCache->stopPolling = PR_TRUE;
1524 if (PR_Interrupt(cache->poller) != PR_SUCCESS) {
1525 return SECFailure;
1527 if (PR_JoinThread(cache->poller) != PR_SUCCESS) {
1528 return SECFailure;
1530 cache->poller = NULL;
1531 return SECSuccess;
1533 #endif
1535 /************************************************************************
1536 * Code dealing with shared wrapped symmetric wrapping keys below *
1537 ************************************************************************/
1539 /* If now is zero, it implies that the lock is not held, and must be
1540 ** aquired here.
1542 static PRBool
1543 getSvrWrappingKey(PRInt32 symWrapMechIndex,
1544 SSL3KEAType exchKeyType,
1545 SSLWrappedSymWrappingKey *wswk,
1546 cacheDesc * cache,
1547 PRUint32 lockTime)
1549 PRUint32 ndx = (exchKeyType * SSL_NUM_WRAP_MECHS) + symWrapMechIndex;
1550 SSLWrappedSymWrappingKey * pwswk = cache->keyCacheData + ndx;
1551 PRUint32 now = 0;
1552 PRBool rv = PR_FALSE;
1554 if (!cache->cacheMem) { /* cache is uninitialized */
1555 PORT_SetError(SSL_ERROR_SERVER_CACHE_NOT_CONFIGURED);
1556 return rv;
1558 if (!lockTime) {
1559 lockTime = now = LockSidCacheLock(cache->keyCacheLock, now);
1560 if (!lockTime) {
1561 return rv;
1564 if (pwswk->exchKeyType == exchKeyType &&
1565 pwswk->symWrapMechIndex == symWrapMechIndex &&
1566 pwswk->wrappedSymKeyLen != 0) {
1567 *wswk = *pwswk;
1568 rv = PR_TRUE;
1570 if (now) {
1571 UnlockSidCacheLock(cache->keyCacheLock);
1573 return rv;
1576 PRBool
1577 ssl_GetWrappingKey( PRInt32 symWrapMechIndex,
1578 SSL3KEAType exchKeyType,
1579 SSLWrappedSymWrappingKey *wswk)
1581 PRBool rv;
1583 PORT_Assert( (unsigned)exchKeyType < kt_kea_size);
1584 PORT_Assert( (unsigned)symWrapMechIndex < SSL_NUM_WRAP_MECHS);
1585 if ((unsigned)exchKeyType < kt_kea_size &&
1586 (unsigned)symWrapMechIndex < SSL_NUM_WRAP_MECHS) {
1587 rv = getSvrWrappingKey(symWrapMechIndex, exchKeyType, wswk,
1588 &globalCache, 0);
1589 } else {
1590 rv = PR_FALSE;
1593 return rv;
1596 /* The caller passes in the new value it wants
1597 * to set. This code tests the wrapped sym key entry in the shared memory.
1598 * If it is uninitialized, this function writes the caller's value into
1599 * the disk entry, and returns false.
1600 * Otherwise, it overwrites the caller's wswk with the value obtained from
1601 * the disk, and returns PR_TRUE.
1602 * This is all done while holding the locks/mutexes necessary to make
1603 * the operation atomic.
1605 PRBool
1606 ssl_SetWrappingKey(SSLWrappedSymWrappingKey *wswk)
1608 cacheDesc * cache = &globalCache;
1609 PRBool rv = PR_FALSE;
1610 SSL3KEAType exchKeyType = wswk->exchKeyType;
1611 /* type of keys used to wrap SymWrapKey*/
1612 PRInt32 symWrapMechIndex = wswk->symWrapMechIndex;
1613 PRUint32 ndx;
1614 PRUint32 now = 0;
1615 SSLWrappedSymWrappingKey myWswk;
1617 if (!cache->cacheMem) { /* cache is uninitialized */
1618 PORT_SetError(SSL_ERROR_SERVER_CACHE_NOT_CONFIGURED);
1619 return 0;
1622 PORT_Assert( (unsigned)exchKeyType < kt_kea_size);
1623 if ((unsigned)exchKeyType >= kt_kea_size)
1624 return 0;
1626 PORT_Assert( (unsigned)symWrapMechIndex < SSL_NUM_WRAP_MECHS);
1627 if ((unsigned)symWrapMechIndex >= SSL_NUM_WRAP_MECHS)
1628 return 0;
1630 ndx = (exchKeyType * SSL_NUM_WRAP_MECHS) + symWrapMechIndex;
1631 PORT_Memset(&myWswk, 0, sizeof myWswk); /* eliminate UMRs. */
1633 now = LockSidCacheLock(cache->keyCacheLock, now);
1634 if (now) {
1635 rv = getSvrWrappingKey(wswk->symWrapMechIndex, wswk->exchKeyType,
1636 &myWswk, cache, now);
1637 if (rv) {
1638 /* we found it on disk, copy it out to the caller. */
1639 PORT_Memcpy(wswk, &myWswk, sizeof *wswk);
1640 } else {
1641 /* Wasn't on disk, and we're still holding the lock, so write it. */
1642 cache->keyCacheData[ndx] = *wswk;
1644 UnlockSidCacheLock(cache->keyCacheLock);
1646 return rv;
1649 #else /* MAC version or other platform */
1651 #include "seccomon.h"
1652 #include "cert.h"
1653 #include "ssl.h"
1654 #include "sslimpl.h"
1656 SECStatus
1657 SSL_ConfigServerSessionIDCache( int maxCacheEntries,
1658 PRUint32 ssl2_timeout,
1659 PRUint32 ssl3_timeout,
1660 const char * directory)
1662 PR_ASSERT(!"SSL servers are not supported on this platform. (SSL_ConfigServerSessionIDCache)");
1663 return SECFailure;
1666 SECStatus
1667 SSL_ConfigMPServerSIDCache( int maxCacheEntries,
1668 PRUint32 ssl2_timeout,
1669 PRUint32 ssl3_timeout,
1670 const char * directory)
1672 PR_ASSERT(!"SSL servers are not supported on this platform. (SSL_ConfigMPServerSIDCache)");
1673 return SECFailure;
1676 SECStatus
1677 SSL_InheritMPServerSIDCache(const char * envString)
1679 PR_ASSERT(!"SSL servers are not supported on this platform. (SSL_InheritMPServerSIDCache)");
1680 return SECFailure;
1683 PRBool
1684 ssl_GetWrappingKey( PRInt32 symWrapMechIndex,
1685 SSL3KEAType exchKeyType,
1686 SSLWrappedSymWrappingKey *wswk)
1688 PRBool rv = PR_FALSE;
1689 PR_ASSERT(!"SSL servers are not supported on this platform. (ssl_GetWrappingKey)");
1690 return rv;
1693 /* This is a kind of test-and-set. The caller passes in the new value it wants
1694 * to set. This code tests the wrapped sym key entry in the shared memory.
1695 * If it is uninitialized, this function writes the caller's value into
1696 * the disk entry, and returns false.
1697 * Otherwise, it overwrites the caller's wswk with the value obtained from
1698 * the disk, and returns PR_TRUE.
1699 * This is all done while holding the locks/mutexes necessary to make
1700 * the operation atomic.
1702 PRBool
1703 ssl_SetWrappingKey(SSLWrappedSymWrappingKey *wswk)
1705 PRBool rv = PR_FALSE;
1706 PR_ASSERT(!"SSL servers are not supported on this platform. (ssl_SetWrappingKey)");
1707 return rv;
1710 PRUint32
1711 SSL_GetMaxServerCacheLocks(void)
1713 PR_ASSERT(!"SSL servers are not supported on this platform. (SSL_GetMaxServerCacheLocks)");
1714 return -1;
1717 SECStatus
1718 SSL_SetMaxServerCacheLocks(PRUint32 maxLocks)
1720 PR_ASSERT(!"SSL servers are not supported on this platform. (SSL_SetMaxServerCacheLocks)");
1721 return SECFailure;
1724 #endif /* XP_UNIX || XP_WIN32 */