security/nss/lib/ssl/sslsnce.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This file implements the SERVER Session ID cache.
michael@0 2 * NOTE: The contents of this file are NOT used by the client.
michael@0 3 *
michael@0 4 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 5 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 7
michael@0 8 /* Note: ssl_FreeSID() in sslnonce.c gets used for both client and server
michael@0 9 * cache sids!
michael@0 10 *
michael@0 11 * About record locking among different server processes:
michael@0 12 *
michael@0 13 * All processes that are part of the same conceptual server (serving on
michael@0 14 * the same address and port) MUST share a common SSL session cache.
michael@0 15 * This code makes the content of the shared cache accessible to all
michael@0 16 * processes on the same "server". This code works on Unix and Win32 only.
michael@0 17 *
michael@0 18 * We use NSPR anonymous shared memory and move data to & from shared memory.
michael@0 19 * We must do explicit locking of the records for all reads and writes.
michael@0 20 * The set of Cache entries are divided up into "sets" of 128 entries.
michael@0 21 * Each set is protected by a lock. There may be one or more sets protected
michael@0 22 * by each lock. That is, locks to sets are 1:N.
michael@0 23 * There is one lock for the entire cert cache.
michael@0 24 * There is one lock for the set of wrapped sym wrap keys.
michael@0 25 *
michael@0 26 * The anonymous shared memory is laid out as if it were declared like this:
michael@0 27 *
michael@0 28 * struct {
michael@0 29 * cacheDescriptor desc;
michael@0 30 * sidCacheLock sidCacheLocks[ numSIDCacheLocks];
michael@0 31 * sidCacheLock keyCacheLock;
michael@0 32 * sidCacheLock certCacheLock;
michael@0 33 * sidCacheSet sidCacheSets[ numSIDCacheSets ];
michael@0 34 * sidCacheEntry sidCacheData[ numSIDCacheEntries];
michael@0 35 * certCacheEntry certCacheData[numCertCacheEntries];
michael@0 36 * SSLWrappedSymWrappingKey keyCacheData[kt_kea_size][SSL_NUM_WRAP_MECHS];
michael@0 37 * PRUint8 keyNameSuffix[SESS_TICKET_KEY_VAR_NAME_LEN]
michael@0 38 * encKeyCacheEntry ticketEncKey; // Wrapped in non-bypass mode
michael@0 39 * encKeyCacheEntry ticketMacKey; // Wrapped in non-bypass mode
michael@0 40 * PRBool ticketKeysValid;
michael@0 41 * sidCacheLock srvNameCacheLock;
michael@0 42 * srvNameCacheEntry srvNameData[ numSrvNameCacheEntries ];
michael@0 43 * } cacheMemCacheData;
michael@0 44 */
michael@0 45 #include "seccomon.h"
michael@0 46
michael@0 47 #if defined(XP_UNIX) || defined(XP_WIN32) || defined (XP_OS2) || defined(XP_BEOS)
michael@0 48
michael@0 49 #include "cert.h"
michael@0 50 #include "ssl.h"
michael@0 51 #include "sslimpl.h"
michael@0 52 #include "sslproto.h"
michael@0 53 #include "pk11func.h"
michael@0 54 #include "base64.h"
michael@0 55 #include "keyhi.h"
michael@0 56 #ifdef NO_PKCS11_BYPASS
michael@0 57 #include "blapit.h"
michael@0 58 #include "sechash.h"
michael@0 59 #else
michael@0 60 #include "blapi.h"
michael@0 61 #endif
michael@0 62
michael@0 63 #include <stdio.h>
michael@0 64
michael@0 65 #if defined(XP_UNIX) || defined(XP_BEOS)
michael@0 66
michael@0 67 #include <syslog.h>
michael@0 68 #include <fcntl.h>
michael@0 69 #include <unistd.h>
michael@0 70 #include <errno.h>
michael@0 71 #include <signal.h>
michael@0 72 #include "unix_err.h"
michael@0 73
michael@0 74 #else
michael@0 75
michael@0 76 #ifdef XP_WIN32
michael@0 77 #include <wtypes.h>
michael@0 78 #include "win32err.h"
michael@0 79 #endif
michael@0 80
michael@0 81 #endif
michael@0 82 #include <sys/types.h>
michael@0 83
michael@0 84 #define SET_ERROR_CODE /* reminder */
michael@0 85
michael@0 86 #include "nspr.h"
michael@0 87 #include "sslmutex.h"
michael@0 88
michael@0 89 /*
michael@0 90 ** Format of a cache entry in the shared memory.
michael@0 91 */
michael@0 92 struct sidCacheEntryStr {
michael@0 93 /* 16 */ PRIPv6Addr addr; /* client's IP address */
michael@0 94 /* 4 */ PRUint32 creationTime;
michael@0 95 /* 4 */ PRUint32 lastAccessTime;
michael@0 96 /* 4 */ PRUint32 expirationTime;
michael@0 97 /* 2 */ PRUint16 version;
michael@0 98 /* 1 */ PRUint8 valid;
michael@0 99 /* 1 */ PRUint8 sessionIDLength;
michael@0 100 /* 32 */ PRUint8 sessionID[SSL3_SESSIONID_BYTES];
michael@0 101 /* 2 */ PRUint16 authAlgorithm;
michael@0 102 /* 2 */ PRUint16 authKeyBits;
michael@0 103 /* 2 */ PRUint16 keaType;
michael@0 104 /* 2 */ PRUint16 keaKeyBits;
michael@0 105 /* 72 - common header total */
michael@0 106
michael@0 107 union {
michael@0 108 struct {
michael@0 109 /* 64 */ PRUint8 masterKey[SSL_MAX_MASTER_KEY_BYTES];
michael@0 110 /* 32 */ PRUint8 cipherArg[SSL_MAX_CYPHER_ARG_BYTES];
michael@0 111
michael@0 112 /* 1 */ PRUint8 cipherType;
michael@0 113 /* 1 */ PRUint8 masterKeyLen;
michael@0 114 /* 1 */ PRUint8 keyBits;
michael@0 115 /* 1 */ PRUint8 secretKeyBits;
michael@0 116 /* 1 */ PRUint8 cipherArgLen;
michael@0 117 /*101 */} ssl2;
michael@0 118
michael@0 119 struct {
michael@0 120 /* 2 */ ssl3CipherSuite cipherSuite;
michael@0 121 /* 2 */ PRUint16 compression; /* SSLCompressionMethod */
michael@0 122
michael@0 123 /* 52 */ ssl3SidKeys keys; /* keys, wrapped as needed. */
michael@0 124
michael@0 125 /* 4 */ PRUint32 masterWrapMech;
michael@0 126 /* 4 */ SSL3KEAType exchKeyType;
michael@0 127 /* 4 */ PRInt32 certIndex;
michael@0 128 /* 4 */ PRInt32 srvNameIndex;
michael@0 129 /* 32 */ PRUint8 srvNameHash[SHA256_LENGTH]; /* SHA256 name hash */
michael@0 130 /*104 */} ssl3;
michael@0 131 /* force sizeof(sidCacheEntry) to be a multiple of cache line size */
michael@0 132 struct {
michael@0 133 /*120 */ PRUint8 filler[120]; /* 72+120==192, a multiple of 16 */
michael@0 134 } forceSize;
michael@0 135 } u;
michael@0 136 };
michael@0 137 typedef struct sidCacheEntryStr sidCacheEntry;
michael@0 138
michael@0 139 /* The length of this struct is supposed to be a power of 2, e.g. 4KB */
michael@0 140 struct certCacheEntryStr {
michael@0 141 PRUint16 certLength; /* 2 */
michael@0 142 PRUint16 sessionIDLength; /* 2 */
michael@0 143 PRUint8 sessionID[SSL3_SESSIONID_BYTES]; /* 32 */
michael@0 144 PRUint8 cert[SSL_MAX_CACHED_CERT_LEN]; /* 4060 */
michael@0 145 }; /* total 4096 */
michael@0 146 typedef struct certCacheEntryStr certCacheEntry;
michael@0 147
michael@0 148 struct sidCacheLockStr {
michael@0 149 PRUint32 timeStamp;
michael@0 150 sslMutex mutex;
michael@0 151 sslPID pid;
michael@0 152 };
michael@0 153 typedef struct sidCacheLockStr sidCacheLock;
michael@0 154
michael@0 155 struct sidCacheSetStr {
michael@0 156 PRIntn next;
michael@0 157 };
michael@0 158 typedef struct sidCacheSetStr sidCacheSet;
michael@0 159
michael@0 160 struct encKeyCacheEntryStr {
michael@0 161 PRUint8 bytes[512];
michael@0 162 PRInt32 length;
michael@0 163 };
michael@0 164 typedef struct encKeyCacheEntryStr encKeyCacheEntry;
michael@0 165
michael@0 166 #define SSL_MAX_DNS_HOST_NAME 1024
michael@0 167
michael@0 168 struct srvNameCacheEntryStr {
michael@0 169 PRUint16 type; /* 2 */
michael@0 170 PRUint16 nameLen; /* 2 */
michael@0 171 PRUint8 name[SSL_MAX_DNS_HOST_NAME + 12]; /* 1034 */
michael@0 172 PRUint8 nameHash[SHA256_LENGTH]; /* 32 */
michael@0 173 /* 1072 */
michael@0 174 };
michael@0 175 typedef struct srvNameCacheEntryStr srvNameCacheEntry;
michael@0 176
michael@0 177
michael@0 178 struct cacheDescStr {
michael@0 179
michael@0 180 PRUint32 cacheMemSize;
michael@0 181
michael@0 182 PRUint32 numSIDCacheLocks;
michael@0 183 PRUint32 numSIDCacheSets;
michael@0 184 PRUint32 numSIDCacheSetsPerLock;
michael@0 185
michael@0 186 PRUint32 numSIDCacheEntries;
michael@0 187 PRUint32 sidCacheSize;
michael@0 188
michael@0 189 PRUint32 numCertCacheEntries;
michael@0 190 PRUint32 certCacheSize;
michael@0 191
michael@0 192 PRUint32 numKeyCacheEntries;
michael@0 193 PRUint32 keyCacheSize;
michael@0 194
michael@0 195 PRUint32 numSrvNameCacheEntries;
michael@0 196 PRUint32 srvNameCacheSize;
michael@0 197
michael@0 198 PRUint32 ssl2Timeout;
michael@0 199 PRUint32 ssl3Timeout;
michael@0 200
michael@0 201 PRUint32 numSIDCacheLocksInitialized;
michael@0 202
michael@0 203 /* These values are volatile, and are accessed through sharedCache-> */
michael@0 204 PRUint32 nextCertCacheEntry; /* certCacheLock protects */
michael@0 205 PRBool stopPolling;
michael@0 206 PRBool everInherited;
michael@0 207
michael@0 208 /* The private copies of these values are pointers into shared mem */
michael@0 209 /* The copies of these values in shared memory are merely offsets */
michael@0 210 sidCacheLock * sidCacheLocks;
michael@0 211 sidCacheLock * keyCacheLock;
michael@0 212 sidCacheLock * certCacheLock;
michael@0 213 sidCacheLock * srvNameCacheLock;
michael@0 214 sidCacheSet * sidCacheSets;
michael@0 215 sidCacheEntry * sidCacheData;
michael@0 216 certCacheEntry * certCacheData;
michael@0 217 SSLWrappedSymWrappingKey * keyCacheData;
michael@0 218 PRUint8 * ticketKeyNameSuffix;
michael@0 219 encKeyCacheEntry * ticketEncKey;
michael@0 220 encKeyCacheEntry * ticketMacKey;
michael@0 221 PRUint32 * ticketKeysValid;
michael@0 222 srvNameCacheEntry * srvNameCacheData;
michael@0 223
michael@0 224 /* Only the private copies of these pointers are valid */
michael@0 225 char * cacheMem;
michael@0 226 struct cacheDescStr * sharedCache; /* shared copy of this struct */
michael@0 227 PRFileMap * cacheMemMap;
michael@0 228 PRThread * poller;
michael@0 229 PRUint32 mutexTimeout;
michael@0 230 PRBool shared;
michael@0 231 };
michael@0 232 typedef struct cacheDescStr cacheDesc;
michael@0 233
michael@0 234 static cacheDesc globalCache;
michael@0 235
michael@0 236 static const char envVarName[] = { SSL_ENV_VAR_NAME };
michael@0 237
michael@0 238 static PRBool isMultiProcess = PR_FALSE;
michael@0 239
michael@0 240
michael@0 241 #define DEF_SID_CACHE_ENTRIES 10000
michael@0 242 #define DEF_CERT_CACHE_ENTRIES 250
michael@0 243 #define MIN_CERT_CACHE_ENTRIES 125 /* the effective size in old releases. */
michael@0 244 #define DEF_KEY_CACHE_ENTRIES 250
michael@0 245 #define DEF_NAME_CACHE_ENTRIES 1000
michael@0 246
michael@0 247 #define SID_CACHE_ENTRIES_PER_SET 128
michael@0 248 #define SID_ALIGNMENT 16
michael@0 249
michael@0 250 #define DEF_SSL2_TIMEOUT 100 /* seconds */
michael@0 251 #define MAX_SSL2_TIMEOUT 100 /* seconds */
michael@0 252 #define MIN_SSL2_TIMEOUT 5 /* seconds */
michael@0 253
michael@0 254 #define DEF_SSL3_TIMEOUT 86400L /* 24 hours */
michael@0 255 #define MAX_SSL3_TIMEOUT 86400L /* 24 hours */
michael@0 256 #define MIN_SSL3_TIMEOUT 5 /* seconds */
michael@0 257
michael@0 258 #if defined(AIX) || defined(LINUX) || defined(NETBSD) || defined(OPENBSD)
michael@0 259 #define MAX_SID_CACHE_LOCKS 8 /* two FDs per lock */
michael@0 260 #elif defined(OSF1)
michael@0 261 #define MAX_SID_CACHE_LOCKS 16 /* one FD per lock */
michael@0 262 #else
michael@0 263 #define MAX_SID_CACHE_LOCKS 256
michael@0 264 #endif
michael@0 265
michael@0 266 #define SID_HOWMANY(val, size) (((val) + ((size) - 1)) / (size))
michael@0 267 #define SID_ROUNDUP(val, size) ((size) * SID_HOWMANY((val), (size)))
michael@0 268
michael@0 269
michael@0 270 static sslPID myPid;
michael@0 271 static PRUint32 ssl_max_sid_cache_locks = MAX_SID_CACHE_LOCKS;
michael@0 272
michael@0 273 /* forward static function declarations */
michael@0 274 static PRUint32 SIDindex(cacheDesc *cache, const PRIPv6Addr *addr, PRUint8 *s,
michael@0 275 unsigned nl);
michael@0 276 static SECStatus LaunchLockPoller(cacheDesc *cache);
michael@0 277 static SECStatus StopLockPoller(cacheDesc *cache);
michael@0 278
michael@0 279
michael@0 280 struct inheritanceStr {
michael@0 281 PRUint32 cacheMemSize;
michael@0 282 PRUint32 fmStrLen;
michael@0 283 };
michael@0 284
michael@0 285 typedef struct inheritanceStr inheritance;
michael@0 286
michael@0 287 #if defined(_WIN32) || defined(XP_OS2)
michael@0 288
michael@0 289 #define DEFAULT_CACHE_DIRECTORY "\\temp"
michael@0 290
michael@0 291 #endif /* _win32 */
michael@0 292
michael@0 293 #if defined(XP_UNIX) || defined(XP_BEOS)
michael@0 294
michael@0 295 #define DEFAULT_CACHE_DIRECTORY "/tmp"
michael@0 296
michael@0 297 #endif /* XP_UNIX || XP_BEOS */
michael@0 298
michael@0 299
michael@0 300 /************************************************************************/
michael@0 301
michael@0 302 static PRUint32
michael@0 303 LockSidCacheLock(sidCacheLock *lock, PRUint32 now)
michael@0 304 {
michael@0 305 SECStatus rv = sslMutex_Lock(&lock->mutex);
michael@0 306 if (rv != SECSuccess)
michael@0 307 return 0;
michael@0 308 if (!now)
michael@0 309 now = ssl_Time();
michael@0 310 lock->timeStamp = now;
michael@0 311 lock->pid = myPid;
michael@0 312 return now;
michael@0 313 }
michael@0 314
michael@0 315 static SECStatus
michael@0 316 UnlockSidCacheLock(sidCacheLock *lock)
michael@0 317 {
michael@0 318 SECStatus rv;
michael@0 319
michael@0 320 lock->pid = 0;
michael@0 321 rv = sslMutex_Unlock(&lock->mutex);
michael@0 322 return rv;
michael@0 323 }
michael@0 324
michael@0 325 /* returns the value of ssl_Time on success, zero on failure. */
michael@0 326 static PRUint32
michael@0 327 LockSet(cacheDesc *cache, PRUint32 set, PRUint32 now)
michael@0 328 {
michael@0 329 PRUint32 lockNum = set % cache->numSIDCacheLocks;
michael@0 330 sidCacheLock * lock = cache->sidCacheLocks + lockNum;
michael@0 331
michael@0 332 return LockSidCacheLock(lock, now);
michael@0 333 }
michael@0 334
michael@0 335 static SECStatus
michael@0 336 UnlockSet(cacheDesc *cache, PRUint32 set)
michael@0 337 {
michael@0 338 PRUint32 lockNum = set % cache->numSIDCacheLocks;
michael@0 339 sidCacheLock * lock = cache->sidCacheLocks + lockNum;
michael@0 340
michael@0 341 return UnlockSidCacheLock(lock);
michael@0 342 }
michael@0 343
michael@0 344 /************************************************************************/
michael@0 345
michael@0 346
michael@0 347 /* Put a certificate in the cache. Update the cert index in the sce.
michael@0 348 */
michael@0 349 static PRUint32
michael@0 350 CacheCert(cacheDesc * cache, CERTCertificate *cert, sidCacheEntry *sce)
michael@0 351 {
michael@0 352 PRUint32 now;
michael@0 353 certCacheEntry cce;
michael@0 354
michael@0 355 if ((cert->derCert.len > SSL_MAX_CACHED_CERT_LEN) ||
michael@0 356 (cert->derCert.len <= 0) ||
michael@0 357 (cert->derCert.data == NULL)) {
michael@0 358 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 359 return 0;
michael@0 360 }
michael@0 361
michael@0 362 cce.sessionIDLength = sce->sessionIDLength;
michael@0 363 PORT_Memcpy(cce.sessionID, sce->sessionID, cce.sessionIDLength);
michael@0 364
michael@0 365 cce.certLength = cert->derCert.len;
michael@0 366 PORT_Memcpy(cce.cert, cert->derCert.data, cce.certLength);
michael@0 367
michael@0 368 /* get lock on cert cache */
michael@0 369 now = LockSidCacheLock(cache->certCacheLock, 0);
michael@0 370 if (now) {
michael@0 371
michael@0 372 /* Find where to place the next cert cache entry. */
michael@0 373 cacheDesc * sharedCache = cache->sharedCache;
michael@0 374 PRUint32 ndx = sharedCache->nextCertCacheEntry;
michael@0 375
michael@0 376 /* write the entry */
michael@0 377 cache->certCacheData[ndx] = cce;
michael@0 378
michael@0 379 /* remember where we put it. */
michael@0 380 sce->u.ssl3.certIndex = ndx;
michael@0 381
michael@0 382 /* update the "next" cache entry index */
michael@0 383 sharedCache->nextCertCacheEntry =
michael@0 384 (ndx + 1) % cache->numCertCacheEntries;
michael@0 385
michael@0 386 UnlockSidCacheLock(cache->certCacheLock);
michael@0 387 }
michael@0 388 return now;
michael@0 389
michael@0 390 }
michael@0 391
michael@0 392 /* Server configuration hash tables need to account the SECITEM.type
michael@0 393 * field as well. These functions accomplish that. */
michael@0 394 static PLHashNumber
michael@0 395 Get32BitNameHash(const SECItem *name)
michael@0 396 {
michael@0 397 PLHashNumber rv = SECITEM_Hash(name);
michael@0 398
michael@0 399 PRUint8 *rvc = (PRUint8 *)&rv;
michael@0 400 rvc[ name->len % sizeof(rv) ] ^= name->type;
michael@0 401
michael@0 402 return rv;
michael@0 403 }
michael@0 404
michael@0 405 /* Put a name in the cache. Update the cert index in the sce.
michael@0 406 */
michael@0 407 static PRUint32
michael@0 408 CacheSrvName(cacheDesc * cache, SECItem *name, sidCacheEntry *sce)
michael@0 409 {
michael@0 410 PRUint32 now;
michael@0 411 PRUint32 ndx;
michael@0 412 srvNameCacheEntry snce;
michael@0 413
michael@0 414 if (!name || name->len <= 0 ||
michael@0 415 name->len > SSL_MAX_DNS_HOST_NAME) {
michael@0 416 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 417 return 0;
michael@0 418 }
michael@0 419
michael@0 420 snce.type = name->type;
michael@0 421 snce.nameLen = name->len;
michael@0 422 PORT_Memcpy(snce.name, name->data, snce.nameLen);
michael@0 423 #ifdef NO_PKCS11_BYPASS
michael@0 424 HASH_HashBuf(HASH_AlgSHA256, snce.nameHash, name->data, name->len);
michael@0 425 #else
michael@0 426 SHA256_HashBuf(snce.nameHash, (unsigned char*)name->data,
michael@0 427 name->len);
michael@0 428 #endif
michael@0 429 /* get index of the next name */
michael@0 430 ndx = Get32BitNameHash(name);
michael@0 431 /* get lock on cert cache */
michael@0 432 now = LockSidCacheLock(cache->srvNameCacheLock, 0);
michael@0 433 if (now) {
michael@0 434 if (cache->numSrvNameCacheEntries > 0) {
michael@0 435 /* Fit the index into array */
michael@0 436 ndx %= cache->numSrvNameCacheEntries;
michael@0 437 /* write the entry */
michael@0 438 cache->srvNameCacheData[ndx] = snce;
michael@0 439 /* remember where we put it. */
michael@0 440 sce->u.ssl3.srvNameIndex = ndx;
michael@0 441 /* Copy hash into sid hash */
michael@0 442 PORT_Memcpy(sce->u.ssl3.srvNameHash, snce.nameHash, SHA256_LENGTH);
michael@0 443 }
michael@0 444 UnlockSidCacheLock(cache->srvNameCacheLock);
michael@0 445 }
michael@0 446 return now;
michael@0 447 }
michael@0 448
michael@0 449 /*
michael@0 450 ** Convert local SID to shared memory one
michael@0 451 */
michael@0 452 static void
michael@0 453 ConvertFromSID(sidCacheEntry *to, sslSessionID *from)
michael@0 454 {
michael@0 455 to->valid = 1;
michael@0 456 to->version = from->version;
michael@0 457 to->addr = from->addr;
michael@0 458 to->creationTime = from->creationTime;
michael@0 459 to->lastAccessTime = from->lastAccessTime;
michael@0 460 to->expirationTime = from->expirationTime;
michael@0 461 to->authAlgorithm = from->authAlgorithm;
michael@0 462 to->authKeyBits = from->authKeyBits;
michael@0 463 to->keaType = from->keaType;
michael@0 464 to->keaKeyBits = from->keaKeyBits;
michael@0 465
michael@0 466 if (from->version < SSL_LIBRARY_VERSION_3_0) {
michael@0 467 if ((from->u.ssl2.masterKey.len > SSL_MAX_MASTER_KEY_BYTES) ||
michael@0 468 (from->u.ssl2.cipherArg.len > SSL_MAX_CYPHER_ARG_BYTES)) {
michael@0 469 SSL_DBG(("%d: SSL: masterKeyLen=%d cipherArgLen=%d",
michael@0 470 myPid, from->u.ssl2.masterKey.len,
michael@0 471 from->u.ssl2.cipherArg.len));
michael@0 472 to->valid = 0;
michael@0 473 return;
michael@0 474 }
michael@0 475
michael@0 476 to->u.ssl2.cipherType = from->u.ssl2.cipherType;
michael@0 477 to->u.ssl2.masterKeyLen = from->u.ssl2.masterKey.len;
michael@0 478 to->u.ssl2.cipherArgLen = from->u.ssl2.cipherArg.len;
michael@0 479 to->u.ssl2.keyBits = from->u.ssl2.keyBits;
michael@0 480 to->u.ssl2.secretKeyBits = from->u.ssl2.secretKeyBits;
michael@0 481 to->sessionIDLength = SSL2_SESSIONID_BYTES;
michael@0 482 PORT_Memcpy(to->sessionID, from->u.ssl2.sessionID, SSL2_SESSIONID_BYTES);
michael@0 483 PORT_Memcpy(to->u.ssl2.masterKey, from->u.ssl2.masterKey.data,
michael@0 484 from->u.ssl2.masterKey.len);
michael@0 485 PORT_Memcpy(to->u.ssl2.cipherArg, from->u.ssl2.cipherArg.data,
michael@0 486 from->u.ssl2.cipherArg.len);
michael@0 487 #ifdef DEBUG
michael@0 488 PORT_Memset(to->u.ssl2.masterKey+from->u.ssl2.masterKey.len, 0,
michael@0 489 sizeof(to->u.ssl2.masterKey) - from->u.ssl2.masterKey.len);
michael@0 490 PORT_Memset(to->u.ssl2.cipherArg+from->u.ssl2.cipherArg.len, 0,
michael@0 491 sizeof(to->u.ssl2.cipherArg) - from->u.ssl2.cipherArg.len);
michael@0 492 #endif
michael@0 493 SSL_TRC(8, ("%d: SSL: ConvertSID: masterKeyLen=%d cipherArgLen=%d "
michael@0 494 "time=%d addr=0x%08x%08x%08x%08x cipherType=%d", myPid,
michael@0 495 to->u.ssl2.masterKeyLen, to->u.ssl2.cipherArgLen,
michael@0 496 to->creationTime, to->addr.pr_s6_addr32[0],
michael@0 497 to->addr.pr_s6_addr32[1], to->addr.pr_s6_addr32[2],
michael@0 498 to->addr.pr_s6_addr32[3], to->u.ssl2.cipherType));
michael@0 499 } else {
michael@0 500 /* This is an SSL v3 session */
michael@0 501
michael@0 502 to->u.ssl3.cipherSuite = from->u.ssl3.cipherSuite;
michael@0 503 to->u.ssl3.compression = (PRUint16)from->u.ssl3.compression;
michael@0 504 to->u.ssl3.keys = from->u.ssl3.keys;
michael@0 505 to->u.ssl3.masterWrapMech = from->u.ssl3.masterWrapMech;
michael@0 506 to->u.ssl3.exchKeyType = from->u.ssl3.exchKeyType;
michael@0 507 to->sessionIDLength = from->u.ssl3.sessionIDLength;
michael@0 508 to->u.ssl3.certIndex = -1;
michael@0 509 to->u.ssl3.srvNameIndex = -1;
michael@0 510
michael@0 511 PORT_Memcpy(to->sessionID, from->u.ssl3.sessionID,
michael@0 512 to->sessionIDLength);
michael@0 513
michael@0 514 SSL_TRC(8, ("%d: SSL3: ConvertSID: time=%d addr=0x%08x%08x%08x%08x "
michael@0 515 "cipherSuite=%d",
michael@0 516 myPid, to->creationTime, to->addr.pr_s6_addr32[0],
michael@0 517 to->addr.pr_s6_addr32[1], to->addr.pr_s6_addr32[2],
michael@0 518 to->addr.pr_s6_addr32[3], to->u.ssl3.cipherSuite));
michael@0 519 }
michael@0 520 }
michael@0 521
michael@0 522 /*
michael@0 523 ** Convert shared memory cache-entry to local memory based one
michael@0 524 ** This is only called from ServerSessionIDLookup().
michael@0 525 */
michael@0 526 static sslSessionID *
michael@0 527 ConvertToSID(sidCacheEntry * from,
michael@0 528 certCacheEntry * pcce,
michael@0 529 srvNameCacheEntry *psnce,
michael@0 530 CERTCertDBHandle * dbHandle)
michael@0 531 {
michael@0 532 sslSessionID *to;
michael@0 533 PRUint16 version = from->version;
michael@0 534
michael@0 535 to = PORT_ZNew(sslSessionID);
michael@0 536 if (!to) {
michael@0 537 return 0;
michael@0 538 }
michael@0 539
michael@0 540 if (version < SSL_LIBRARY_VERSION_3_0) {
michael@0 541 /* This is an SSL v2 session */
michael@0 542 to->u.ssl2.masterKey.data =
michael@0 543 (unsigned char*) PORT_Alloc(from->u.ssl2.masterKeyLen);
michael@0 544 if (!to->u.ssl2.masterKey.data) {
michael@0 545 goto loser;
michael@0 546 }
michael@0 547 if (from->u.ssl2.cipherArgLen) {
michael@0 548 to->u.ssl2.cipherArg.data =
michael@0 549 (unsigned char*)PORT_Alloc(from->u.ssl2.cipherArgLen);
michael@0 550 if (!to->u.ssl2.cipherArg.data) {
michael@0 551 goto loser;
michael@0 552 }
michael@0 553 PORT_Memcpy(to->u.ssl2.cipherArg.data, from->u.ssl2.cipherArg,
michael@0 554 from->u.ssl2.cipherArgLen);
michael@0 555 }
michael@0 556
michael@0 557 to->u.ssl2.cipherType = from->u.ssl2.cipherType;
michael@0 558 to->u.ssl2.masterKey.len = from->u.ssl2.masterKeyLen;
michael@0 559 to->u.ssl2.cipherArg.len = from->u.ssl2.cipherArgLen;
michael@0 560 to->u.ssl2.keyBits = from->u.ssl2.keyBits;
michael@0 561 to->u.ssl2.secretKeyBits = from->u.ssl2.secretKeyBits;
michael@0 562 /* to->sessionIDLength = SSL2_SESSIONID_BYTES; */
michael@0 563 PORT_Memcpy(to->u.ssl2.sessionID, from->sessionID, SSL2_SESSIONID_BYTES);
michael@0 564 PORT_Memcpy(to->u.ssl2.masterKey.data, from->u.ssl2.masterKey,
michael@0 565 from->u.ssl2.masterKeyLen);
michael@0 566
michael@0 567 SSL_TRC(8, ("%d: SSL: ConvertToSID: masterKeyLen=%d cipherArgLen=%d "
michael@0 568 "time=%d addr=0x%08x%08x%08x%08x cipherType=%d",
michael@0 569 myPid, to->u.ssl2.masterKey.len,
michael@0 570 to->u.ssl2.cipherArg.len, to->creationTime,
michael@0 571 to->addr.pr_s6_addr32[0], to->addr.pr_s6_addr32[1],
michael@0 572 to->addr.pr_s6_addr32[2], to->addr.pr_s6_addr32[3],
michael@0 573 to->u.ssl2.cipherType));
michael@0 574 } else {
michael@0 575 /* This is an SSL v3 session */
michael@0 576
michael@0 577 to->u.ssl3.sessionIDLength = from->sessionIDLength;
michael@0 578 to->u.ssl3.cipherSuite = from->u.ssl3.cipherSuite;
michael@0 579 to->u.ssl3.compression = (SSLCompressionMethod)from->u.ssl3.compression;
michael@0 580 to->u.ssl3.keys = from->u.ssl3.keys;
michael@0 581 to->u.ssl3.masterWrapMech = from->u.ssl3.masterWrapMech;
michael@0 582 to->u.ssl3.exchKeyType = from->u.ssl3.exchKeyType;
michael@0 583 if (from->u.ssl3.srvNameIndex != -1 && psnce) {
michael@0 584 SECItem name;
michael@0 585 SECStatus rv;
michael@0 586 name.type = psnce->type;
michael@0 587 name.len = psnce->nameLen;
michael@0 588 name.data = psnce->name;
michael@0 589 rv = SECITEM_CopyItem(NULL, &to->u.ssl3.srvName, &name);
michael@0 590 if (rv != SECSuccess) {
michael@0 591 goto loser;
michael@0 592 }
michael@0 593 }
michael@0 594
michael@0 595 PORT_Memcpy(to->u.ssl3.sessionID, from->sessionID, from->sessionIDLength);
michael@0 596
michael@0 597 /* the portions of the SID that are only restored on the client
michael@0 598 * are set to invalid values on the server.
michael@0 599 */
michael@0 600 to->u.ssl3.clientWriteKey = NULL;
michael@0 601 to->u.ssl3.serverWriteKey = NULL;
michael@0 602
michael@0 603 to->urlSvrName = NULL;
michael@0 604
michael@0 605 to->u.ssl3.masterModuleID = (SECMODModuleID)-1; /* invalid value */
michael@0 606 to->u.ssl3.masterSlotID = (CK_SLOT_ID)-1; /* invalid value */
michael@0 607 to->u.ssl3.masterWrapIndex = 0;
michael@0 608 to->u.ssl3.masterWrapSeries = 0;
michael@0 609 to->u.ssl3.masterValid = PR_FALSE;
michael@0 610
michael@0 611 to->u.ssl3.clAuthModuleID = (SECMODModuleID)-1; /* invalid value */
michael@0 612 to->u.ssl3.clAuthSlotID = (CK_SLOT_ID)-1; /* invalid value */
michael@0 613 to->u.ssl3.clAuthSeries = 0;
michael@0 614 to->u.ssl3.clAuthValid = PR_FALSE;
michael@0 615
michael@0 616 if (from->u.ssl3.certIndex != -1 && pcce) {
michael@0 617 SECItem derCert;
michael@0 618
michael@0 619 derCert.len = pcce->certLength;
michael@0 620 derCert.data = pcce->cert;
michael@0 621
michael@0 622 to->peerCert = CERT_NewTempCertificate(dbHandle, &derCert, NULL,
michael@0 623 PR_FALSE, PR_TRUE);
michael@0 624 if (to->peerCert == NULL)
michael@0 625 goto loser;
michael@0 626 }
michael@0 627 }
michael@0 628
michael@0 629 to->version = from->version;
michael@0 630 to->creationTime = from->creationTime;
michael@0 631 to->lastAccessTime = from->lastAccessTime;
michael@0 632 to->expirationTime = from->expirationTime;
michael@0 633 to->cached = in_server_cache;
michael@0 634 to->addr = from->addr;
michael@0 635 to->references = 1;
michael@0 636 to->authAlgorithm = from->authAlgorithm;
michael@0 637 to->authKeyBits = from->authKeyBits;
michael@0 638 to->keaType = from->keaType;
michael@0 639 to->keaKeyBits = from->keaKeyBits;
michael@0 640
michael@0 641 return to;
michael@0 642
michael@0 643 loser:
michael@0 644 if (to) {
michael@0 645 if (version < SSL_LIBRARY_VERSION_3_0) {
michael@0 646 if (to->u.ssl2.masterKey.data)
michael@0 647 PORT_Free(to->u.ssl2.masterKey.data);
michael@0 648 if (to->u.ssl2.cipherArg.data)
michael@0 649 PORT_Free(to->u.ssl2.cipherArg.data);
michael@0 650 } else {
michael@0 651 SECITEM_FreeItem(&to->u.ssl3.srvName, PR_FALSE);
michael@0 652 }
michael@0 653 PORT_Free(to);
michael@0 654 }
michael@0 655 return NULL;
michael@0 656 }
michael@0 657
michael@0 658
michael@0 659
michael@0 660 /*
michael@0 661 ** Perform some mumbo jumbo on the ip-address and the session-id value to
michael@0 662 ** compute a hash value.
michael@0 663 */
michael@0 664 static PRUint32
michael@0 665 SIDindex(cacheDesc *cache, const PRIPv6Addr *addr, PRUint8 *s, unsigned nl)
michael@0 666 {
michael@0 667 PRUint32 rv;
michael@0 668 PRUint32 x[8];
michael@0 669
michael@0 670 memset(x, 0, sizeof x);
michael@0 671 if (nl > sizeof x)
michael@0 672 nl = sizeof x;
michael@0 673 memcpy(x, s, nl);
michael@0 674
michael@0 675 rv = (addr->pr_s6_addr32[0] ^ addr->pr_s6_addr32[1] ^
michael@0 676 addr->pr_s6_addr32[2] ^ addr->pr_s6_addr32[3] ^
michael@0 677 x[0] ^ x[1] ^ x[2] ^ x[3] ^ x[4] ^ x[5] ^ x[6] ^ x[7])
michael@0 678 % cache->numSIDCacheSets;
michael@0 679 return rv;
michael@0 680 }
michael@0 681
michael@0 682
michael@0 683
michael@0 684 /*
michael@0 685 ** Look something up in the cache. This will invalidate old entries
michael@0 686 ** in the process. Caller has locked the cache set!
michael@0 687 ** Returns PR_TRUE if found a valid match. PR_FALSE otherwise.
michael@0 688 */
michael@0 689 static sidCacheEntry *
michael@0 690 FindSID(cacheDesc *cache, PRUint32 setNum, PRUint32 now,
michael@0 691 const PRIPv6Addr *addr, unsigned char *sessionID,
michael@0 692 unsigned sessionIDLength)
michael@0 693 {
michael@0 694 PRUint32 ndx = cache->sidCacheSets[setNum].next;
michael@0 695 int i;
michael@0 696
michael@0 697 sidCacheEntry * set = cache->sidCacheData +
michael@0 698 (setNum * SID_CACHE_ENTRIES_PER_SET);
michael@0 699
michael@0 700 for (i = SID_CACHE_ENTRIES_PER_SET; i > 0; --i) {
michael@0 701 sidCacheEntry * sce;
michael@0 702
michael@0 703 ndx = (ndx - 1) % SID_CACHE_ENTRIES_PER_SET;
michael@0 704 sce = set + ndx;
michael@0 705
michael@0 706 if (!sce->valid)
michael@0 707 continue;
michael@0 708
michael@0 709 if (now > sce->expirationTime) {
michael@0 710 /* SessionID has timed out. Invalidate the entry. */
michael@0 711 SSL_TRC(7, ("%d: timed out sid entry addr=%08x%08x%08x%08x now=%x "
michael@0 712 "time+=%x",
michael@0 713 myPid, sce->addr.pr_s6_addr32[0],
michael@0 714 sce->addr.pr_s6_addr32[1], sce->addr.pr_s6_addr32[2],
michael@0 715 sce->addr.pr_s6_addr32[3], now,
michael@0 716 sce->expirationTime ));
michael@0 717 sce->valid = 0;
michael@0 718 continue;
michael@0 719 }
michael@0 720
michael@0 721 /*
michael@0 722 ** Next, examine specific session-id/addr data to see if the cache
michael@0 723 ** entry matches our addr+session-id value
michael@0 724 */
michael@0 725 if (sessionIDLength == sce->sessionIDLength &&
michael@0 726 !memcmp(&sce->addr, addr, sizeof(PRIPv6Addr)) &&
michael@0 727 !memcmp(sce->sessionID, sessionID, sessionIDLength)) {
michael@0 728 /* Found it */
michael@0 729 return sce;
michael@0 730 }
michael@0 731 }
michael@0 732
michael@0 733 PORT_SetError(SSL_ERROR_SESSION_NOT_FOUND);
michael@0 734 return NULL;
michael@0 735 }
michael@0 736
michael@0 737 /************************************************************************/
michael@0 738
michael@0 739 /* This is the primary function for finding entries in the server's sid cache.
michael@0 740 * Although it is static, this function is called via the global function
michael@0 741 * pointer ssl_sid_lookup.
michael@0 742 */
michael@0 743 static sslSessionID *
michael@0 744 ServerSessionIDLookup(const PRIPv6Addr *addr,
michael@0 745 unsigned char *sessionID,
michael@0 746 unsigned int sessionIDLength,
michael@0 747 CERTCertDBHandle * dbHandle)
michael@0 748 {
michael@0 749 sslSessionID * sid = 0;
michael@0 750 sidCacheEntry * psce;
michael@0 751 certCacheEntry *pcce = 0;
michael@0 752 srvNameCacheEntry *psnce = 0;
michael@0 753 cacheDesc * cache = &globalCache;
michael@0 754 PRUint32 now;
michael@0 755 PRUint32 set;
michael@0 756 PRInt32 cndx;
michael@0 757 sidCacheEntry sce;
michael@0 758 certCacheEntry cce;
michael@0 759 srvNameCacheEntry snce;
michael@0 760
michael@0 761 set = SIDindex(cache, addr, sessionID, sessionIDLength);
michael@0 762 now = LockSet(cache, set, 0);
michael@0 763 if (!now)
michael@0 764 return NULL;
michael@0 765
michael@0 766 psce = FindSID(cache, set, now, addr, sessionID, sessionIDLength);
michael@0 767 if (psce) {
michael@0 768 if (psce->version >= SSL_LIBRARY_VERSION_3_0) {
michael@0 769 if ((cndx = psce->u.ssl3.certIndex) != -1) {
michael@0 770
michael@0 771 PRUint32 gotLock = LockSidCacheLock(cache->certCacheLock, now);
michael@0 772 if (gotLock) {
michael@0 773 pcce = &cache->certCacheData[cndx];
michael@0 774
michael@0 775 /* See if the cert's session ID matches the sce cache. */
michael@0 776 if ((pcce->sessionIDLength == psce->sessionIDLength) &&
michael@0 777 !PORT_Memcmp(pcce->sessionID, psce->sessionID,
michael@0 778 pcce->sessionIDLength)) {
michael@0 779 cce = *pcce;
michael@0 780 } else {
michael@0 781 /* The cert doesen't match the SID cache entry,
michael@0 782 ** so invalidate the SID cache entry.
michael@0 783 */
michael@0 784 psce->valid = 0;
michael@0 785 psce = 0;
michael@0 786 pcce = 0;
michael@0 787 }
michael@0 788 UnlockSidCacheLock(cache->certCacheLock);
michael@0 789 } else {
michael@0 790 /* what the ??. Didn't get the cert cache lock.
michael@0 791 ** Don't invalidate the SID cache entry, but don't find it.
michael@0 792 */
michael@0 793 PORT_Assert(!("Didn't get cert Cache Lock!"));
michael@0 794 psce = 0;
michael@0 795 pcce = 0;
michael@0 796 }
michael@0 797 }
michael@0 798 if (psce && ((cndx = psce->u.ssl3.srvNameIndex) != -1)) {
michael@0 799 PRUint32 gotLock = LockSidCacheLock(cache->srvNameCacheLock,
michael@0 800 now);
michael@0 801 if (gotLock) {
michael@0 802 psnce = &cache->srvNameCacheData[cndx];
michael@0 803
michael@0 804 if (!PORT_Memcmp(psnce->nameHash, psce->u.ssl3.srvNameHash,
michael@0 805 SHA256_LENGTH)) {
michael@0 806 snce = *psnce;
michael@0 807 } else {
michael@0 808 /* The name doesen't match the SID cache entry,
michael@0 809 ** so invalidate the SID cache entry.
michael@0 810 */
michael@0 811 psce->valid = 0;
michael@0 812 psce = 0;
michael@0 813 psnce = 0;
michael@0 814 }
michael@0 815 UnlockSidCacheLock(cache->srvNameCacheLock);
michael@0 816 } else {
michael@0 817 /* what the ??. Didn't get the cert cache lock.
michael@0 818 ** Don't invalidate the SID cache entry, but don't find it.
michael@0 819 */
michael@0 820 PORT_Assert(!("Didn't get name Cache Lock!"));
michael@0 821 psce = 0;
michael@0 822 psnce = 0;
michael@0 823 }
michael@0 824
michael@0 825 }
michael@0 826 }
michael@0 827 if (psce) {
michael@0 828 psce->lastAccessTime = now;
michael@0 829 sce = *psce; /* grab a copy while holding the lock */
michael@0 830 }
michael@0 831 }
michael@0 832 UnlockSet(cache, set);
michael@0 833 if (psce) {
michael@0 834 /* sce conains a copy of the cache entry.
michael@0 835 ** Convert shared memory format to local format
michael@0 836 */
michael@0 837 sid = ConvertToSID(&sce, pcce ? &cce : 0, psnce ? &snce : 0, dbHandle);
michael@0 838 }
michael@0 839 return sid;
michael@0 840 }
michael@0 841
michael@0 842 /*
michael@0 843 ** Place a sid into the cache, if it isn't already there.
michael@0 844 */
michael@0 845 static void
michael@0 846 ServerSessionIDCache(sslSessionID *sid)
michael@0 847 {
michael@0 848 sidCacheEntry sce;
michael@0 849 PRUint32 now = 0;
michael@0 850 PRUint16 version = sid->version;
michael@0 851 cacheDesc * cache = &globalCache;
michael@0 852
michael@0 853 if ((version >= SSL_LIBRARY_VERSION_3_0) &&
michael@0 854 (sid->u.ssl3.sessionIDLength == 0)) {
michael@0 855 return;
michael@0 856 }
michael@0 857
michael@0 858 if (sid->cached == never_cached || sid->cached == invalid_cache) {
michael@0 859 PRUint32 set;
michael@0 860
michael@0 861 PORT_Assert(sid->creationTime != 0);
michael@0 862 if (!sid->creationTime)
michael@0 863 sid->lastAccessTime = sid->creationTime = ssl_Time();
michael@0 864 if (version < SSL_LIBRARY_VERSION_3_0) {
michael@0 865 /* override caller's expiration time, which uses client timeout
michael@0 866 * duration, not server timeout duration.
michael@0 867 */
michael@0 868 sid->expirationTime = sid->creationTime + cache->ssl2Timeout;
michael@0 869 SSL_TRC(8, ("%d: SSL: CacheMT: cached=%d addr=0x%08x%08x%08x%08x time=%x "
michael@0 870 "cipher=%d", myPid, sid->cached,
michael@0 871 sid->addr.pr_s6_addr32[0], sid->addr.pr_s6_addr32[1],
michael@0 872 sid->addr.pr_s6_addr32[2], sid->addr.pr_s6_addr32[3],
michael@0 873 sid->creationTime, sid->u.ssl2.cipherType));
michael@0 874 PRINT_BUF(8, (0, "sessionID:", sid->u.ssl2.sessionID,
michael@0 875 SSL2_SESSIONID_BYTES));
michael@0 876 PRINT_BUF(8, (0, "masterKey:", sid->u.ssl2.masterKey.data,
michael@0 877 sid->u.ssl2.masterKey.len));
michael@0 878 PRINT_BUF(8, (0, "cipherArg:", sid->u.ssl2.cipherArg.data,
michael@0 879 sid->u.ssl2.cipherArg.len));
michael@0 880
michael@0 881 } else {
michael@0 882 /* override caller's expiration time, which uses client timeout
michael@0 883 * duration, not server timeout duration.
michael@0 884 */
michael@0 885 sid->expirationTime = sid->creationTime + cache->ssl3Timeout;
michael@0 886 SSL_TRC(8, ("%d: SSL: CacheMT: cached=%d addr=0x%08x%08x%08x%08x time=%x "
michael@0 887 "cipherSuite=%d", myPid, sid->cached,
michael@0 888 sid->addr.pr_s6_addr32[0], sid->addr.pr_s6_addr32[1],
michael@0 889 sid->addr.pr_s6_addr32[2], sid->addr.pr_s6_addr32[3],
michael@0 890 sid->creationTime, sid->u.ssl3.cipherSuite));
michael@0 891 PRINT_BUF(8, (0, "sessionID:", sid->u.ssl3.sessionID,
michael@0 892 sid->u.ssl3.sessionIDLength));
michael@0 893 }
michael@0 894
michael@0 895 ConvertFromSID(&sce, sid);
michael@0 896
michael@0 897 if (version >= SSL_LIBRARY_VERSION_3_0) {
michael@0 898 SECItem *name = &sid->u.ssl3.srvName;
michael@0 899 if (name->len && name->data) {
michael@0 900 now = CacheSrvName(cache, name, &sce);
michael@0 901 }
michael@0 902 if (sid->peerCert != NULL) {
michael@0 903 now = CacheCert(cache, sid->peerCert, &sce);
michael@0 904 }
michael@0 905 }
michael@0 906
michael@0 907 set = SIDindex(cache, &sce.addr, sce.sessionID, sce.sessionIDLength);
michael@0 908 now = LockSet(cache, set, now);
michael@0 909 if (now) {
michael@0 910 PRUint32 next = cache->sidCacheSets[set].next;
michael@0 911 PRUint32 ndx = set * SID_CACHE_ENTRIES_PER_SET + next;
michael@0 912
michael@0 913 /* Write out new cache entry */
michael@0 914 cache->sidCacheData[ndx] = sce;
michael@0 915
michael@0 916 cache->sidCacheSets[set].next =
michael@0 917 (next + 1) % SID_CACHE_ENTRIES_PER_SET;
michael@0 918
michael@0 919 UnlockSet(cache, set);
michael@0 920 sid->cached = in_server_cache;
michael@0 921 }
michael@0 922 }
michael@0 923 }
michael@0 924
michael@0 925 /*
michael@0 926 ** Although this is static, it is called from ssl via global function pointer
michael@0 927 ** ssl_sid_uncache. This invalidates the referenced cache entry.
michael@0 928 */
michael@0 929 static void
michael@0 930 ServerSessionIDUncache(sslSessionID *sid)
michael@0 931 {
michael@0 932 cacheDesc * cache = &globalCache;
michael@0 933 PRUint8 * sessionID;
michael@0 934 unsigned int sessionIDLength;
michael@0 935 PRErrorCode err;
michael@0 936 PRUint32 set;
michael@0 937 PRUint32 now;
michael@0 938 sidCacheEntry *psce;
michael@0 939
michael@0 940 if (sid == NULL)
michael@0 941 return;
michael@0 942
michael@0 943 /* Uncaching a SID should never change the error code.
michael@0 944 ** So save it here and restore it before exiting.
michael@0 945 */
michael@0 946 err = PR_GetError();
michael@0 947
michael@0 948 if (sid->version < SSL_LIBRARY_VERSION_3_0) {
michael@0 949 sessionID = sid->u.ssl2.sessionID;
michael@0 950 sessionIDLength = SSL2_SESSIONID_BYTES;
michael@0 951 SSL_TRC(8, ("%d: SSL: UncacheMT: valid=%d addr=0x%08x%08x%08x%08x time=%x "
michael@0 952 "cipher=%d", myPid, sid->cached,
michael@0 953 sid->addr.pr_s6_addr32[0], sid->addr.pr_s6_addr32[1],
michael@0 954 sid->addr.pr_s6_addr32[2], sid->addr.pr_s6_addr32[3],
michael@0 955 sid->creationTime, sid->u.ssl2.cipherType));
michael@0 956 PRINT_BUF(8, (0, "sessionID:", sessionID, sessionIDLength));
michael@0 957 PRINT_BUF(8, (0, "masterKey:", sid->u.ssl2.masterKey.data,
michael@0 958 sid->u.ssl2.masterKey.len));
michael@0 959 PRINT_BUF(8, (0, "cipherArg:", sid->u.ssl2.cipherArg.data,
michael@0 960 sid->u.ssl2.cipherArg.len));
michael@0 961 } else {
michael@0 962 sessionID = sid->u.ssl3.sessionID;
michael@0 963 sessionIDLength = sid->u.ssl3.sessionIDLength;
michael@0 964 SSL_TRC(8, ("%d: SSL3: UncacheMT: valid=%d addr=0x%08x%08x%08x%08x time=%x "
michael@0 965 "cipherSuite=%d", myPid, sid->cached,
michael@0 966 sid->addr.pr_s6_addr32[0], sid->addr.pr_s6_addr32[1],
michael@0 967 sid->addr.pr_s6_addr32[2], sid->addr.pr_s6_addr32[3],
michael@0 968 sid->creationTime, sid->u.ssl3.cipherSuite));
michael@0 969 PRINT_BUF(8, (0, "sessionID:", sessionID, sessionIDLength));
michael@0 970 }
michael@0 971 set = SIDindex(cache, &sid->addr, sessionID, sessionIDLength);
michael@0 972 now = LockSet(cache, set, 0);
michael@0 973 if (now) {
michael@0 974 psce = FindSID(cache, set, now, &sid->addr, sessionID, sessionIDLength);
michael@0 975 if (psce) {
michael@0 976 psce->valid = 0;
michael@0 977 }
michael@0 978 UnlockSet(cache, set);
michael@0 979 }
michael@0 980 sid->cached = invalid_cache;
michael@0 981 PORT_SetError(err);
michael@0 982 }
michael@0 983
michael@0 984 #ifdef XP_OS2
michael@0 985
michael@0 986 #define INCL_DOSPROCESS
michael@0 987 #include <os2.h>
michael@0 988
michael@0 989 long gettid(void)
michael@0 990 {
michael@0 991 PTIB ptib;
michael@0 992 PPIB ppib;
michael@0 993 DosGetInfoBlocks(&ptib, &ppib);
michael@0 994 return ((long)ptib->tib_ordinal); /* thread id */
michael@0 995 }
michael@0 996 #endif
michael@0 997
michael@0 998 static void
michael@0 999 CloseCache(cacheDesc *cache)
michael@0 1000 {
michael@0 1001 int locks_initialized = cache->numSIDCacheLocksInitialized;
michael@0 1002
michael@0 1003 if (cache->cacheMem) {
michael@0 1004 if (cache->sharedCache) {
michael@0 1005 sidCacheLock *pLock = cache->sidCacheLocks;
michael@0 1006 for (; locks_initialized > 0; --locks_initialized, ++pLock ) {
michael@0 1007 /* If everInherited is true, this shared cache was (and may
michael@0 1008 ** still be) in use by multiple processes. We do not wish to
michael@0 1009 ** destroy the mutexes while they are still in use, but we do
michael@0 1010 ** want to free mutex resources associated with this process.
michael@0 1011 */
michael@0 1012 sslMutex_Destroy(&pLock->mutex,
michael@0 1013 cache->sharedCache->everInherited);
michael@0 1014 }
michael@0 1015 }
michael@0 1016 if (cache->shared) {
michael@0 1017 PR_MemUnmap(cache->cacheMem, cache->cacheMemSize);
michael@0 1018 } else {
michael@0 1019 PORT_Free(cache->cacheMem);
michael@0 1020 }
michael@0 1021 cache->cacheMem = NULL;
michael@0 1022 }
michael@0 1023 if (cache->cacheMemMap) {
michael@0 1024 PR_CloseFileMap(cache->cacheMemMap);
michael@0 1025 cache->cacheMemMap = NULL;
michael@0 1026 }
michael@0 1027 memset(cache, 0, sizeof *cache);
michael@0 1028 }
michael@0 1029
michael@0 1030 static SECStatus
michael@0 1031 InitCache(cacheDesc *cache, int maxCacheEntries, int maxCertCacheEntries,
michael@0 1032 int maxSrvNameCacheEntries, PRUint32 ssl2_timeout,
michael@0 1033 PRUint32 ssl3_timeout, const char *directory, PRBool shared)
michael@0 1034 {
michael@0 1035 ptrdiff_t ptr;
michael@0 1036 sidCacheLock *pLock;
michael@0 1037 char * cacheMem;
michael@0 1038 PRFileMap * cacheMemMap;
michael@0 1039 char * cfn = NULL; /* cache file name */
michael@0 1040 int locks_initialized = 0;
michael@0 1041 int locks_to_initialize = 0;
michael@0 1042 PRUint32 init_time;
michael@0 1043
michael@0 1044 if ( (!cache) || (maxCacheEntries < 0) || (!directory) ) {
michael@0 1045 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 1046 return SECFailure;
michael@0 1047 }
michael@0 1048
michael@0 1049 if (cache->cacheMem) {
michael@0 1050 /* Already done */
michael@0 1051 return SECSuccess;
michael@0 1052 }
michael@0 1053
michael@0 1054 /* make sure loser can clean up properly */
michael@0 1055 cache->shared = shared;
michael@0 1056 cache->cacheMem = cacheMem = NULL;
michael@0 1057 cache->cacheMemMap = cacheMemMap = NULL;
michael@0 1058 cache->sharedCache = (cacheDesc *)0;
michael@0 1059
michael@0 1060 cache->numSIDCacheLocksInitialized = 0;
michael@0 1061 cache->nextCertCacheEntry = 0;
michael@0 1062 cache->stopPolling = PR_FALSE;
michael@0 1063 cache->everInherited = PR_FALSE;
michael@0 1064 cache->poller = NULL;
michael@0 1065 cache->mutexTimeout = 0;
michael@0 1066
michael@0 1067 cache->numSIDCacheEntries = maxCacheEntries ? maxCacheEntries
michael@0 1068 : DEF_SID_CACHE_ENTRIES;
michael@0 1069 cache->numSIDCacheSets =
michael@0 1070 SID_HOWMANY(cache->numSIDCacheEntries, SID_CACHE_ENTRIES_PER_SET);
michael@0 1071
michael@0 1072 cache->numSIDCacheEntries =
michael@0 1073 cache->numSIDCacheSets * SID_CACHE_ENTRIES_PER_SET;
michael@0 1074
michael@0 1075 cache->numSIDCacheLocks =
michael@0 1076 PR_MIN(cache->numSIDCacheSets, ssl_max_sid_cache_locks);
michael@0 1077
michael@0 1078 cache->numSIDCacheSetsPerLock =
michael@0 1079 SID_HOWMANY(cache->numSIDCacheSets, cache->numSIDCacheLocks);
michael@0 1080
michael@0 1081 cache->numCertCacheEntries = (maxCertCacheEntries > 0) ?
michael@0 1082 maxCertCacheEntries : 0;
michael@0 1083 cache->numSrvNameCacheEntries = (maxSrvNameCacheEntries >= 0) ?
michael@0 1084 maxSrvNameCacheEntries : DEF_NAME_CACHE_ENTRIES;
michael@0 1085
michael@0 1086 /* compute size of shared memory, and offsets of all pointers */
michael@0 1087 ptr = 0;
michael@0 1088 cache->cacheMem = (char *)ptr;
michael@0 1089 ptr += SID_ROUNDUP(sizeof(cacheDesc), SID_ALIGNMENT);
michael@0 1090
michael@0 1091 cache->sidCacheLocks = (sidCacheLock *)ptr;
michael@0 1092 cache->keyCacheLock = cache->sidCacheLocks + cache->numSIDCacheLocks;
michael@0 1093 cache->certCacheLock = cache->keyCacheLock + 1;
michael@0 1094 cache->srvNameCacheLock = cache->certCacheLock + 1;
michael@0 1095 ptr = (ptrdiff_t)(cache->srvNameCacheLock + 1);
michael@0 1096 ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
michael@0 1097
michael@0 1098 cache->sidCacheSets = (sidCacheSet *)ptr;
michael@0 1099 ptr = (ptrdiff_t)(cache->sidCacheSets + cache->numSIDCacheSets);
michael@0 1100 ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
michael@0 1101
michael@0 1102 cache->sidCacheData = (sidCacheEntry *)ptr;
michael@0 1103 ptr = (ptrdiff_t)(cache->sidCacheData + cache->numSIDCacheEntries);
michael@0 1104 ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
michael@0 1105
michael@0 1106 cache->certCacheData = (certCacheEntry *)ptr;
michael@0 1107 cache->sidCacheSize =
michael@0 1108 (char *)cache->certCacheData - (char *)cache->sidCacheData;
michael@0 1109
michael@0 1110 if (cache->numCertCacheEntries < MIN_CERT_CACHE_ENTRIES) {
michael@0 1111 /* This is really a poor way to computer this! */
michael@0 1112 cache->numCertCacheEntries = cache->sidCacheSize / sizeof(certCacheEntry);
michael@0 1113 if (cache->numCertCacheEntries < MIN_CERT_CACHE_ENTRIES)
michael@0 1114 cache->numCertCacheEntries = MIN_CERT_CACHE_ENTRIES;
michael@0 1115 }
michael@0 1116 ptr = (ptrdiff_t)(cache->certCacheData + cache->numCertCacheEntries);
michael@0 1117 ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
michael@0 1118
michael@0 1119 cache->keyCacheData = (SSLWrappedSymWrappingKey *)ptr;
michael@0 1120 cache->certCacheSize =
michael@0 1121 (char *)cache->keyCacheData - (char *)cache->certCacheData;
michael@0 1122
michael@0 1123 cache->numKeyCacheEntries = kt_kea_size * SSL_NUM_WRAP_MECHS;
michael@0 1124 ptr = (ptrdiff_t)(cache->keyCacheData + cache->numKeyCacheEntries);
michael@0 1125 ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
michael@0 1126
michael@0 1127 cache->keyCacheSize = (char *)ptr - (char *)cache->keyCacheData;
michael@0 1128
michael@0 1129 cache->ticketKeyNameSuffix = (PRUint8 *)ptr;
michael@0 1130 ptr = (ptrdiff_t)(cache->ticketKeyNameSuffix +
michael@0 1131 SESS_TICKET_KEY_VAR_NAME_LEN);
michael@0 1132 ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
michael@0 1133
michael@0 1134 cache->ticketEncKey = (encKeyCacheEntry *)ptr;
michael@0 1135 ptr = (ptrdiff_t)(cache->ticketEncKey + 1);
michael@0 1136 ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
michael@0 1137
michael@0 1138 cache->ticketMacKey = (encKeyCacheEntry *)ptr;
michael@0 1139 ptr = (ptrdiff_t)(cache->ticketMacKey + 1);
michael@0 1140 ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
michael@0 1141
michael@0 1142 cache->ticketKeysValid = (PRUint32 *)ptr;
michael@0 1143 ptr = (ptrdiff_t)(cache->ticketKeysValid + 1);
michael@0 1144 ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
michael@0 1145
michael@0 1146 cache->srvNameCacheData = (srvNameCacheEntry *)ptr;
michael@0 1147 cache->srvNameCacheSize =
michael@0 1148 cache->numSrvNameCacheEntries * sizeof(srvNameCacheEntry);
michael@0 1149 ptr = (ptrdiff_t)(cache->srvNameCacheData + cache->numSrvNameCacheEntries);
michael@0 1150 ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
michael@0 1151
michael@0 1152 cache->cacheMemSize = ptr;
michael@0 1153
michael@0 1154 if (ssl2_timeout) {
michael@0 1155 if (ssl2_timeout > MAX_SSL2_TIMEOUT) {
michael@0 1156 ssl2_timeout = MAX_SSL2_TIMEOUT;
michael@0 1157 }
michael@0 1158 if (ssl2_timeout < MIN_SSL2_TIMEOUT) {
michael@0 1159 ssl2_timeout = MIN_SSL2_TIMEOUT;
michael@0 1160 }
michael@0 1161 cache->ssl2Timeout = ssl2_timeout;
michael@0 1162 } else {
michael@0 1163 cache->ssl2Timeout = DEF_SSL2_TIMEOUT;
michael@0 1164 }
michael@0 1165
michael@0 1166 if (ssl3_timeout) {
michael@0 1167 if (ssl3_timeout > MAX_SSL3_TIMEOUT) {
michael@0 1168 ssl3_timeout = MAX_SSL3_TIMEOUT;
michael@0 1169 }
michael@0 1170 if (ssl3_timeout < MIN_SSL3_TIMEOUT) {
michael@0 1171 ssl3_timeout = MIN_SSL3_TIMEOUT;
michael@0 1172 }
michael@0 1173 cache->ssl3Timeout = ssl3_timeout;
michael@0 1174 } else {
michael@0 1175 cache->ssl3Timeout = DEF_SSL3_TIMEOUT;
michael@0 1176 }
michael@0 1177
michael@0 1178 if (shared) {
michael@0 1179 /* Create file names */
michael@0 1180 #if defined(XP_UNIX) || defined(XP_BEOS)
michael@0 1181 /* there's some confusion here about whether PR_OpenAnonFileMap wants
michael@0 1182 ** a directory name or a file name for its first argument.
michael@0 1183 cfn = PR_smprintf("%s/.sslsvrcache.%d", directory, myPid);
michael@0 1184 */
michael@0 1185 cfn = PR_smprintf("%s", directory);
michael@0 1186 #elif defined(XP_WIN32)
michael@0 1187 cfn = PR_smprintf("%s/svrcache_%d_%x.ssl", directory, myPid,
michael@0 1188 GetCurrentThreadId());
michael@0 1189 #elif defined(XP_OS2)
michael@0 1190 cfn = PR_smprintf("%s/svrcache_%d_%x.ssl", directory, myPid,
michael@0 1191 gettid());
michael@0 1192 #else
michael@0 1193 #error "Don't know how to create file name for this platform!"
michael@0 1194 #endif
michael@0 1195 if (!cfn) {
michael@0 1196 goto loser;
michael@0 1197 }
michael@0 1198
michael@0 1199 /* Create cache */
michael@0 1200 cacheMemMap = PR_OpenAnonFileMap(cfn, cache->cacheMemSize,
michael@0 1201 PR_PROT_READWRITE);
michael@0 1202
michael@0 1203 PR_smprintf_free(cfn);
michael@0 1204 if(!cacheMemMap) {
michael@0 1205 goto loser;
michael@0 1206 }
michael@0 1207
michael@0 1208 cacheMem = PR_MemMap(cacheMemMap, 0, cache->cacheMemSize);
michael@0 1209 } else {
michael@0 1210 cacheMem = PORT_Alloc(cache->cacheMemSize);
michael@0 1211 }
michael@0 1212
michael@0 1213 if (! cacheMem) {
michael@0 1214 goto loser;
michael@0 1215 }
michael@0 1216
michael@0 1217 /* Initialize shared memory. This may not be necessary on all platforms */
michael@0 1218 memset(cacheMem, 0, cache->cacheMemSize);
michael@0 1219
michael@0 1220 /* Copy cache descriptor header into shared memory */
michael@0 1221 memcpy(cacheMem, cache, sizeof *cache);
michael@0 1222
michael@0 1223 /* save private copies of these values */
michael@0 1224 cache->cacheMemMap = cacheMemMap;
michael@0 1225 cache->cacheMem = cacheMem;
michael@0 1226 cache->sharedCache = (cacheDesc *)cacheMem;
michael@0 1227
michael@0 1228 /* Fix pointers in our private copy of cache descriptor to point to
michael@0 1229 ** spaces in shared memory
michael@0 1230 */
michael@0 1231 ptr = (ptrdiff_t)cache->cacheMem;
michael@0 1232 *(ptrdiff_t *)(&cache->sidCacheLocks) += ptr;
michael@0 1233 *(ptrdiff_t *)(&cache->keyCacheLock ) += ptr;
michael@0 1234 *(ptrdiff_t *)(&cache->certCacheLock) += ptr;
michael@0 1235 *(ptrdiff_t *)(&cache->srvNameCacheLock) += ptr;
michael@0 1236 *(ptrdiff_t *)(&cache->sidCacheSets ) += ptr;
michael@0 1237 *(ptrdiff_t *)(&cache->sidCacheData ) += ptr;
michael@0 1238 *(ptrdiff_t *)(&cache->certCacheData) += ptr;
michael@0 1239 *(ptrdiff_t *)(&cache->keyCacheData ) += ptr;
michael@0 1240 *(ptrdiff_t *)(&cache->ticketKeyNameSuffix) += ptr;
michael@0 1241 *(ptrdiff_t *)(&cache->ticketEncKey ) += ptr;
michael@0 1242 *(ptrdiff_t *)(&cache->ticketMacKey ) += ptr;
michael@0 1243 *(ptrdiff_t *)(&cache->ticketKeysValid) += ptr;
michael@0 1244 *(ptrdiff_t *)(&cache->srvNameCacheData) += ptr;
michael@0 1245
michael@0 1246 /* initialize the locks */
michael@0 1247 init_time = ssl_Time();
michael@0 1248 pLock = cache->sidCacheLocks;
michael@0 1249 for (locks_to_initialize = cache->numSIDCacheLocks + 3;
michael@0 1250 locks_initialized < locks_to_initialize;
michael@0 1251 ++locks_initialized, ++pLock ) {
michael@0 1252
michael@0 1253 SECStatus err = sslMutex_Init(&pLock->mutex, shared);
michael@0 1254 if (err) {
michael@0 1255 cache->numSIDCacheLocksInitialized = locks_initialized;
michael@0 1256 goto loser;
michael@0 1257 }
michael@0 1258 pLock->timeStamp = init_time;
michael@0 1259 pLock->pid = 0;
michael@0 1260 }
michael@0 1261 cache->numSIDCacheLocksInitialized = locks_initialized;
michael@0 1262
michael@0 1263 return SECSuccess;
michael@0 1264
michael@0 1265 loser:
michael@0 1266 CloseCache(cache);
michael@0 1267 return SECFailure;
michael@0 1268 }
michael@0 1269
michael@0 1270 PRUint32
michael@0 1271 SSL_GetMaxServerCacheLocks(void)
michael@0 1272 {
michael@0 1273 return ssl_max_sid_cache_locks + 2;
michael@0 1274 /* The extra two are the cert cache lock and the key cache lock. */
michael@0 1275 }
michael@0 1276
michael@0 1277 SECStatus
michael@0 1278 SSL_SetMaxServerCacheLocks(PRUint32 maxLocks)
michael@0 1279 {
michael@0 1280 /* Minimum is 1 sid cache lock, 1 cert cache lock and 1 key cache lock.
michael@0 1281 ** We'd like to test for a maximum value, but not all platforms' header
michael@0 1282 ** files provide a symbol or function or other means of determining
michael@0 1283 ** the maximum, other than trial and error.
michael@0 1284 */
michael@0 1285 if (maxLocks < 3) {
michael@0 1286 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 1287 return SECFailure;
michael@0 1288 }
michael@0 1289 ssl_max_sid_cache_locks = maxLocks - 2;
michael@0 1290 /* The extra two are the cert cache lock and the key cache lock. */
michael@0 1291 return SECSuccess;
michael@0 1292 }
michael@0 1293
michael@0 1294 static SECStatus
michael@0 1295 ssl_ConfigServerSessionIDCacheInstanceWithOpt(cacheDesc *cache,
michael@0 1296 PRUint32 ssl2_timeout,
michael@0 1297 PRUint32 ssl3_timeout,
michael@0 1298 const char * directory,
michael@0 1299 PRBool shared,
michael@0 1300 int maxCacheEntries,
michael@0 1301 int maxCertCacheEntries,
michael@0 1302 int maxSrvNameCacheEntries)
michael@0 1303 {
michael@0 1304 SECStatus rv;
michael@0 1305
michael@0 1306 PORT_Assert(sizeof(sidCacheEntry) == 192);
michael@0 1307 PORT_Assert(sizeof(certCacheEntry) == 4096);
michael@0 1308 PORT_Assert(sizeof(srvNameCacheEntry) == 1072);
michael@0 1309
michael@0 1310 rv = ssl_Init();
michael@0 1311 if (rv != SECSuccess) {
michael@0 1312 return rv;
michael@0 1313 }
michael@0 1314
michael@0 1315 myPid = SSL_GETPID();
michael@0 1316 if (!directory) {
michael@0 1317 directory = DEFAULT_CACHE_DIRECTORY;
michael@0 1318 }
michael@0 1319 rv = InitCache(cache, maxCacheEntries, maxCertCacheEntries,
michael@0 1320 maxSrvNameCacheEntries, ssl2_timeout, ssl3_timeout,
michael@0 1321 directory, shared);
michael@0 1322 if (rv) {
michael@0 1323 SET_ERROR_CODE
michael@0 1324 return SECFailure;
michael@0 1325 }
michael@0 1326
michael@0 1327 ssl_sid_lookup = ServerSessionIDLookup;
michael@0 1328 ssl_sid_cache = ServerSessionIDCache;
michael@0 1329 ssl_sid_uncache = ServerSessionIDUncache;
michael@0 1330 return SECSuccess;
michael@0 1331 }
michael@0 1332
michael@0 1333 SECStatus
michael@0 1334 SSL_ConfigServerSessionIDCacheInstance( cacheDesc *cache,
michael@0 1335 int maxCacheEntries,
michael@0 1336 PRUint32 ssl2_timeout,
michael@0 1337 PRUint32 ssl3_timeout,
michael@0 1338 const char * directory, PRBool shared)
michael@0 1339 {
michael@0 1340 return ssl_ConfigServerSessionIDCacheInstanceWithOpt(cache,
michael@0 1341 ssl2_timeout,
michael@0 1342 ssl3_timeout,
michael@0 1343 directory,
michael@0 1344 shared,
michael@0 1345 maxCacheEntries,
michael@0 1346 -1, -1);
michael@0 1347 }
michael@0 1348
michael@0 1349 SECStatus
michael@0 1350 SSL_ConfigServerSessionIDCache( int maxCacheEntries,
michael@0 1351 PRUint32 ssl2_timeout,
michael@0 1352 PRUint32 ssl3_timeout,
michael@0 1353 const char * directory)
michael@0 1354 {
michael@0 1355 ssl_InitSessionCacheLocks(PR_FALSE);
michael@0 1356 return SSL_ConfigServerSessionIDCacheInstance(&globalCache,
michael@0 1357 maxCacheEntries, ssl2_timeout, ssl3_timeout, directory, PR_FALSE);
michael@0 1358 }
michael@0 1359
michael@0 1360 SECStatus
michael@0 1361 SSL_ShutdownServerSessionIDCacheInstance(cacheDesc *cache)
michael@0 1362 {
michael@0 1363 CloseCache(cache);
michael@0 1364 return SECSuccess;
michael@0 1365 }
michael@0 1366
michael@0 1367 SECStatus
michael@0 1368 SSL_ShutdownServerSessionIDCache(void)
michael@0 1369 {
michael@0 1370 #if defined(XP_UNIX) || defined(XP_BEOS)
michael@0 1371 /* Stop the thread that polls cache for expired locks on Unix */
michael@0 1372 StopLockPoller(&globalCache);
michael@0 1373 #endif
michael@0 1374 SSL3_ShutdownServerCache();
michael@0 1375 return SSL_ShutdownServerSessionIDCacheInstance(&globalCache);
michael@0 1376 }
michael@0 1377
michael@0 1378 /* Use this function, instead of SSL_ConfigServerSessionIDCache,
michael@0 1379 * if the cache will be shared by multiple processes.
michael@0 1380 */
michael@0 1381 static SECStatus
michael@0 1382 ssl_ConfigMPServerSIDCacheWithOpt( PRUint32 ssl2_timeout,
michael@0 1383 PRUint32 ssl3_timeout,
michael@0 1384 const char * directory,
michael@0 1385 int maxCacheEntries,
michael@0 1386 int maxCertCacheEntries,
michael@0 1387 int maxSrvNameCacheEntries)
michael@0 1388 {
michael@0 1389 char * envValue;
michael@0 1390 char * inhValue;
michael@0 1391 cacheDesc * cache = &globalCache;
michael@0 1392 PRUint32 fmStrLen;
michael@0 1393 SECStatus result;
michael@0 1394 PRStatus prStatus;
michael@0 1395 SECStatus putEnvFailed;
michael@0 1396 inheritance inherit;
michael@0 1397 char fmString[PR_FILEMAP_STRING_BUFSIZE];
michael@0 1398
michael@0 1399 isMultiProcess = PR_TRUE;
michael@0 1400 result = ssl_ConfigServerSessionIDCacheInstanceWithOpt(cache,
michael@0 1401 ssl2_timeout, ssl3_timeout, directory, PR_TRUE,
michael@0 1402 maxCacheEntries, maxCacheEntries, maxSrvNameCacheEntries);
michael@0 1403 if (result != SECSuccess)
michael@0 1404 return result;
michael@0 1405
michael@0 1406 prStatus = PR_ExportFileMapAsString(cache->cacheMemMap,
michael@0 1407 sizeof fmString, fmString);
michael@0 1408 if ((prStatus != PR_SUCCESS) || !(fmStrLen = strlen(fmString))) {
michael@0 1409 SET_ERROR_CODE
michael@0 1410 return SECFailure;
michael@0 1411 }
michael@0 1412
michael@0 1413 inherit.cacheMemSize = cache->cacheMemSize;
michael@0 1414 inherit.fmStrLen = fmStrLen;
michael@0 1415
michael@0 1416 inhValue = BTOA_DataToAscii((unsigned char *)&inherit, sizeof inherit);
michael@0 1417 if (!inhValue || !strlen(inhValue)) {
michael@0 1418 SET_ERROR_CODE
michael@0 1419 return SECFailure;
michael@0 1420 }
michael@0 1421 envValue = PR_smprintf("%s,%s", inhValue, fmString);
michael@0 1422 if (!envValue || !strlen(envValue)) {
michael@0 1423 SET_ERROR_CODE
michael@0 1424 return SECFailure;
michael@0 1425 }
michael@0 1426 PORT_Free(inhValue);
michael@0 1427
michael@0 1428 putEnvFailed = (SECStatus)NSS_PutEnv(envVarName, envValue);
michael@0 1429 PR_smprintf_free(envValue);
michael@0 1430 if (putEnvFailed) {
michael@0 1431 SET_ERROR_CODE
michael@0 1432 result = SECFailure;
michael@0 1433 }
michael@0 1434
michael@0 1435 #if defined(XP_UNIX) || defined(XP_BEOS)
michael@0 1436 /* Launch thread to poll cache for expired locks on Unix */
michael@0 1437 LaunchLockPoller(cache);
michael@0 1438 #endif
michael@0 1439 return result;
michael@0 1440 }
michael@0 1441
michael@0 1442 /* Use this function, instead of SSL_ConfigServerSessionIDCache,
michael@0 1443 * if the cache will be shared by multiple processes.
michael@0 1444 */
michael@0 1445 SECStatus
michael@0 1446 SSL_ConfigMPServerSIDCache( int maxCacheEntries,
michael@0 1447 PRUint32 ssl2_timeout,
michael@0 1448 PRUint32 ssl3_timeout,
michael@0 1449 const char * directory)
michael@0 1450 {
michael@0 1451 return ssl_ConfigMPServerSIDCacheWithOpt(ssl2_timeout,
michael@0 1452 ssl3_timeout,
michael@0 1453 directory,
michael@0 1454 maxCacheEntries,
michael@0 1455 -1, -1);
michael@0 1456 }
michael@0 1457
michael@0 1458 SECStatus
michael@0 1459 SSL_ConfigServerSessionIDCacheWithOpt(
michael@0 1460 PRUint32 ssl2_timeout,
michael@0 1461 PRUint32 ssl3_timeout,
michael@0 1462 const char * directory,
michael@0 1463 int maxCacheEntries,
michael@0 1464 int maxCertCacheEntries,
michael@0 1465 int maxSrvNameCacheEntries,
michael@0 1466 PRBool enableMPCache)
michael@0 1467 {
michael@0 1468 if (!enableMPCache) {
michael@0 1469 ssl_InitSessionCacheLocks(PR_FALSE);
michael@0 1470 return ssl_ConfigServerSessionIDCacheInstanceWithOpt(&globalCache,
michael@0 1471 ssl2_timeout, ssl3_timeout, directory, PR_FALSE,
michael@0 1472 maxCacheEntries, maxCertCacheEntries, maxSrvNameCacheEntries);
michael@0 1473 } else {
michael@0 1474 return ssl_ConfigMPServerSIDCacheWithOpt(ssl2_timeout, ssl3_timeout,
michael@0 1475 directory, maxCacheEntries, maxCertCacheEntries,
michael@0 1476 maxSrvNameCacheEntries);
michael@0 1477 }
michael@0 1478 }
michael@0 1479
michael@0 1480 SECStatus
michael@0 1481 SSL_InheritMPServerSIDCacheInstance(cacheDesc *cache, const char * envString)
michael@0 1482 {
michael@0 1483 unsigned char * decoString = NULL;
michael@0 1484 char * fmString = NULL;
michael@0 1485 char * myEnvString = NULL;
michael@0 1486 unsigned int decoLen;
michael@0 1487 ptrdiff_t ptr;
michael@0 1488 inheritance inherit;
michael@0 1489 cacheDesc my;
michael@0 1490 #ifdef WINNT
michael@0 1491 sidCacheLock* newLocks;
michael@0 1492 int locks_initialized = 0;
michael@0 1493 int locks_to_initialize = 0;
michael@0 1494 #endif
michael@0 1495 SECStatus status = ssl_Init();
michael@0 1496
michael@0 1497 if (status != SECSuccess) {
michael@0 1498 return status;
michael@0 1499 }
michael@0 1500
michael@0 1501 myPid = SSL_GETPID();
michael@0 1502
michael@0 1503 /* If this child was created by fork(), and not by exec() on unix,
michael@0 1504 ** then isMultiProcess will already be set.
michael@0 1505 ** If not, we'll set it below.
michael@0 1506 */
michael@0 1507 if (isMultiProcess) {
michael@0 1508 if (cache && cache->sharedCache) {
michael@0 1509 cache->sharedCache->everInherited = PR_TRUE;
michael@0 1510 }
michael@0 1511 return SECSuccess; /* already done. */
michael@0 1512 }
michael@0 1513
michael@0 1514 ssl_InitSessionCacheLocks(PR_FALSE);
michael@0 1515
michael@0 1516 ssl_sid_lookup = ServerSessionIDLookup;
michael@0 1517 ssl_sid_cache = ServerSessionIDCache;
michael@0 1518 ssl_sid_uncache = ServerSessionIDUncache;
michael@0 1519
michael@0 1520 if (!envString) {
michael@0 1521 envString = getenv(envVarName);
michael@0 1522 if (!envString) {
michael@0 1523 SET_ERROR_CODE
michael@0 1524 return SECFailure;
michael@0 1525 }
michael@0 1526 }
michael@0 1527 myEnvString = PORT_Strdup(envString);
michael@0 1528 if (!myEnvString)
michael@0 1529 return SECFailure;
michael@0 1530 fmString = strchr(myEnvString, ',');
michael@0 1531 if (!fmString)
michael@0 1532 goto loser;
michael@0 1533 *fmString++ = 0;
michael@0 1534
michael@0 1535 decoString = ATOB_AsciiToData(myEnvString, &decoLen);
michael@0 1536 if (!decoString) {
michael@0 1537 SET_ERROR_CODE
michael@0 1538 goto loser;
michael@0 1539 }
michael@0 1540 if (decoLen != sizeof inherit) {
michael@0 1541 SET_ERROR_CODE
michael@0 1542 goto loser;
michael@0 1543 }
michael@0 1544
michael@0 1545 PORT_Memcpy(&inherit, decoString, sizeof inherit);
michael@0 1546
michael@0 1547 if (strlen(fmString) != inherit.fmStrLen ) {
michael@0 1548 goto loser;
michael@0 1549 }
michael@0 1550
michael@0 1551 memset(cache, 0, sizeof *cache);
michael@0 1552 cache->cacheMemSize = inherit.cacheMemSize;
michael@0 1553
michael@0 1554 /* Create cache */
michael@0 1555 cache->cacheMemMap = PR_ImportFileMapFromString(fmString);
michael@0 1556 if(! cache->cacheMemMap) {
michael@0 1557 goto loser;
michael@0 1558 }
michael@0 1559 cache->cacheMem = PR_MemMap(cache->cacheMemMap, 0, cache->cacheMemSize);
michael@0 1560 if (! cache->cacheMem) {
michael@0 1561 goto loser;
michael@0 1562 }
michael@0 1563 cache->sharedCache = (cacheDesc *)cache->cacheMem;
michael@0 1564
michael@0 1565 if (cache->sharedCache->cacheMemSize != cache->cacheMemSize) {
michael@0 1566 SET_ERROR_CODE
michael@0 1567 goto loser;
michael@0 1568 }
michael@0 1569
michael@0 1570 /* We're now going to overwrite the local cache instance with the
michael@0 1571 ** shared copy of the cache struct, then update several values in
michael@0 1572 ** the local cache using the values for cache->cacheMemMap and
michael@0 1573 ** cache->cacheMem computed just above. So, we copy cache into
michael@0 1574 ** the automatic variable "my", to preserve the variables while
michael@0 1575 ** cache is overwritten.
michael@0 1576 */
michael@0 1577 my = *cache; /* save values computed above. */
michael@0 1578 memcpy(cache, cache->sharedCache, sizeof *cache); /* overwrite */
michael@0 1579
michael@0 1580 /* Fix pointers in our private copy of cache descriptor to point to
michael@0 1581 ** spaces in shared memory, whose address is now in "my".
michael@0 1582 */
michael@0 1583 ptr = (ptrdiff_t)my.cacheMem;
michael@0 1584 *(ptrdiff_t *)(&cache->sidCacheLocks) += ptr;
michael@0 1585 *(ptrdiff_t *)(&cache->keyCacheLock ) += ptr;
michael@0 1586 *(ptrdiff_t *)(&cache->certCacheLock) += ptr;
michael@0 1587 *(ptrdiff_t *)(&cache->srvNameCacheLock) += ptr;
michael@0 1588 *(ptrdiff_t *)(&cache->sidCacheSets ) += ptr;
michael@0 1589 *(ptrdiff_t *)(&cache->sidCacheData ) += ptr;
michael@0 1590 *(ptrdiff_t *)(&cache->certCacheData) += ptr;
michael@0 1591 *(ptrdiff_t *)(&cache->keyCacheData ) += ptr;
michael@0 1592 *(ptrdiff_t *)(&cache->ticketKeyNameSuffix) += ptr;
michael@0 1593 *(ptrdiff_t *)(&cache->ticketEncKey ) += ptr;
michael@0 1594 *(ptrdiff_t *)(&cache->ticketMacKey ) += ptr;
michael@0 1595 *(ptrdiff_t *)(&cache->ticketKeysValid) += ptr;
michael@0 1596 *(ptrdiff_t *)(&cache->srvNameCacheData) += ptr;
michael@0 1597
michael@0 1598 cache->cacheMemMap = my.cacheMemMap;
michael@0 1599 cache->cacheMem = my.cacheMem;
michael@0 1600 cache->sharedCache = (cacheDesc *)cache->cacheMem;
michael@0 1601
michael@0 1602 #ifdef WINNT
michael@0 1603 /* On Windows NT we need to "fix" the sidCacheLocks here to support fibers
michael@0 1604 ** When NT fibers are used in a multi-process server, a second level of
michael@0 1605 ** locking is needed to prevent a deadlock, in case a fiber acquires the
michael@0 1606 ** cross-process mutex, yields, and another fiber is later scheduled on
michael@0 1607 ** the same native thread and tries to acquire the cross-process mutex.
michael@0 1608 ** We do this by using a PRLock in the sslMutex. However, it is stored in
michael@0 1609 ** shared memory as part of sidCacheLocks, and we don't want to overwrite
michael@0 1610 ** the PRLock of the parent process. So we need to make new, private
michael@0 1611 ** copies of sidCacheLocks before modifying the sslMutex with our own
michael@0 1612 ** PRLock
michael@0 1613 */
michael@0 1614
michael@0 1615 /* note from jpierre : this should be free'd in child processes when
michael@0 1616 ** a function is added to delete the SSL session cache in the future.
michael@0 1617 */
michael@0 1618 locks_to_initialize = cache->numSIDCacheLocks + 3;
michael@0 1619 newLocks = PORT_NewArray(sidCacheLock, locks_to_initialize);
michael@0 1620 if (!newLocks)
michael@0 1621 goto loser;
michael@0 1622 /* copy the old locks */
michael@0 1623 memcpy(newLocks, cache->sidCacheLocks,
michael@0 1624 locks_to_initialize * sizeof(sidCacheLock));
michael@0 1625 cache->sidCacheLocks = newLocks;
michael@0 1626 /* fix the locks */
michael@0 1627 for (; locks_initialized < locks_to_initialize; ++locks_initialized) {
michael@0 1628 /* now, make a local PRLock in this sslMutex for this child process */
michael@0 1629 SECStatus err;
michael@0 1630 err = sslMutex_2LevelInit(&newLocks[locks_initialized].mutex);
michael@0 1631 if (err != SECSuccess) {
michael@0 1632 cache->numSIDCacheLocksInitialized = locks_initialized;
michael@0 1633 goto loser;
michael@0 1634 }
michael@0 1635 }
michael@0 1636 cache->numSIDCacheLocksInitialized = locks_initialized;
michael@0 1637
michael@0 1638 /* also fix the key and cert cache which use the last 2 lock entries */
michael@0 1639 cache->keyCacheLock = cache->sidCacheLocks + cache->numSIDCacheLocks;
michael@0 1640 cache->certCacheLock = cache->keyCacheLock + 1;
michael@0 1641 cache->srvNameCacheLock = cache->certCacheLock + 1;
michael@0 1642 #endif
michael@0 1643
michael@0 1644 PORT_Free(myEnvString);
michael@0 1645 PORT_Free(decoString);
michael@0 1646
michael@0 1647 /* mark that we have inherited this. */
michael@0 1648 cache->sharedCache->everInherited = PR_TRUE;
michael@0 1649 isMultiProcess = PR_TRUE;
michael@0 1650
michael@0 1651 return SECSuccess;
michael@0 1652
michael@0 1653 loser:
michael@0 1654 PORT_Free(myEnvString);
michael@0 1655 if (decoString)
michael@0 1656 PORT_Free(decoString);
michael@0 1657 CloseCache(cache);
michael@0 1658 return SECFailure;
michael@0 1659 }
michael@0 1660
michael@0 1661 SECStatus
michael@0 1662 SSL_InheritMPServerSIDCache(const char * envString)
michael@0 1663 {
michael@0 1664 return SSL_InheritMPServerSIDCacheInstance(&globalCache, envString);
michael@0 1665 }
michael@0 1666
michael@0 1667 #if defined(XP_UNIX) || defined(XP_BEOS)
michael@0 1668
michael@0 1669 #define SID_LOCK_EXPIRATION_TIMEOUT 30 /* seconds */
michael@0 1670
michael@0 1671 static void
michael@0 1672 LockPoller(void * arg)
michael@0 1673 {
michael@0 1674 cacheDesc * cache = (cacheDesc *)arg;
michael@0 1675 cacheDesc * sharedCache = cache->sharedCache;
michael@0 1676 sidCacheLock * pLock;
michael@0 1677 PRIntervalTime timeout;
michael@0 1678 PRUint32 now;
michael@0 1679 PRUint32 then;
michael@0 1680 int locks_polled = 0;
michael@0 1681 int locks_to_poll = cache->numSIDCacheLocks + 2;
michael@0 1682 PRUint32 expiration = cache->mutexTimeout;
michael@0 1683
michael@0 1684 timeout = PR_SecondsToInterval(expiration);
michael@0 1685 while(!sharedCache->stopPolling) {
michael@0 1686 PR_Sleep(timeout);
michael@0 1687 if (sharedCache->stopPolling)
michael@0 1688 break;
michael@0 1689
michael@0 1690 now = ssl_Time();
michael@0 1691 then = now - expiration;
michael@0 1692 for (pLock = cache->sidCacheLocks, locks_polled = 0;
michael@0 1693 locks_to_poll > locks_polled && !sharedCache->stopPolling;
michael@0 1694 ++locks_polled, ++pLock ) {
michael@0 1695 pid_t pid;
michael@0 1696
michael@0 1697 if (pLock->timeStamp < then &&
michael@0 1698 pLock->timeStamp != 0 &&
michael@0 1699 (pid = pLock->pid) != 0) {
michael@0 1700
michael@0 1701 /* maybe we should try the lock? */
michael@0 1702 int result = kill(pid, 0);
michael@0 1703 if (result < 0 && errno == ESRCH) {
michael@0 1704 SECStatus rv;
michael@0 1705 /* No process exists by that pid any more.
michael@0 1706 ** Treat this mutex as abandoned.
michael@0 1707 */
michael@0 1708 pLock->timeStamp = now;
michael@0 1709 pLock->pid = 0;
michael@0 1710 rv = sslMutex_Unlock(&pLock->mutex);
michael@0 1711 if (rv != SECSuccess) {
michael@0 1712 /* Now what? */
michael@0 1713 }
michael@0 1714 }
michael@0 1715 }
michael@0 1716 } /* end of loop over locks */
michael@0 1717 } /* end of entire polling loop */
michael@0 1718 }
michael@0 1719
michael@0 1720 /* Launch thread to poll cache for expired locks */
michael@0 1721 static SECStatus
michael@0 1722 LaunchLockPoller(cacheDesc *cache)
michael@0 1723 {
michael@0 1724 const char * timeoutString;
michael@0 1725 PRThread * pollerThread;
michael@0 1726
michael@0 1727 cache->mutexTimeout = SID_LOCK_EXPIRATION_TIMEOUT;
michael@0 1728 timeoutString = getenv("NSS_SSL_SERVER_CACHE_MUTEX_TIMEOUT");
michael@0 1729 if (timeoutString) {
michael@0 1730 long newTime = strtol(timeoutString, 0, 0);
michael@0 1731 if (newTime == 0)
michael@0 1732 return SECSuccess; /* application doesn't want poller thread */
michael@0 1733 if (newTime > 0)
michael@0 1734 cache->mutexTimeout = (PRUint32)newTime;
michael@0 1735 /* if error (newTime < 0) ignore it and use default */
michael@0 1736 }
michael@0 1737
michael@0 1738 pollerThread =
michael@0 1739 PR_CreateThread(PR_USER_THREAD, LockPoller, cache, PR_PRIORITY_NORMAL,
michael@0 1740 PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
michael@0 1741 if (!pollerThread) {
michael@0 1742 return SECFailure;
michael@0 1743 }
michael@0 1744 cache->poller = pollerThread;
michael@0 1745 return SECSuccess;
michael@0 1746 }
michael@0 1747
michael@0 1748 /* Stop the thread that polls cache for expired locks */
michael@0 1749 static SECStatus
michael@0 1750 StopLockPoller(cacheDesc *cache)
michael@0 1751 {
michael@0 1752 if (!cache->poller) {
michael@0 1753 return SECSuccess;
michael@0 1754 }
michael@0 1755 cache->sharedCache->stopPolling = PR_TRUE;
michael@0 1756 if (PR_Interrupt(cache->poller) != PR_SUCCESS) {
michael@0 1757 return SECFailure;
michael@0 1758 }
michael@0 1759 if (PR_JoinThread(cache->poller) != PR_SUCCESS) {
michael@0 1760 return SECFailure;
michael@0 1761 }
michael@0 1762 cache->poller = NULL;
michael@0 1763 return SECSuccess;
michael@0 1764 }
michael@0 1765 #endif
michael@0 1766
michael@0 1767 /************************************************************************
michael@0 1768 * Code dealing with shared wrapped symmetric wrapping keys below *
michael@0 1769 ************************************************************************/
michael@0 1770
michael@0 1771 /* If now is zero, it implies that the lock is not held, and must be
michael@0 1772 ** aquired here.
michael@0 1773 */
michael@0 1774 static PRBool
michael@0 1775 getSvrWrappingKey(PRInt32 symWrapMechIndex,
michael@0 1776 SSL3KEAType exchKeyType,
michael@0 1777 SSLWrappedSymWrappingKey *wswk,
michael@0 1778 cacheDesc * cache,
michael@0 1779 PRUint32 lockTime)
michael@0 1780 {
michael@0 1781 PRUint32 ndx = (exchKeyType * SSL_NUM_WRAP_MECHS) + symWrapMechIndex;
michael@0 1782 SSLWrappedSymWrappingKey * pwswk = cache->keyCacheData + ndx;
michael@0 1783 PRUint32 now = 0;
michael@0 1784 PRBool rv = PR_FALSE;
michael@0 1785
michael@0 1786 if (!cache->cacheMem) { /* cache is uninitialized */
michael@0 1787 PORT_SetError(SSL_ERROR_SERVER_CACHE_NOT_CONFIGURED);
michael@0 1788 return rv;
michael@0 1789 }
michael@0 1790 if (!lockTime) {
michael@0 1791 lockTime = now = LockSidCacheLock(cache->keyCacheLock, now);
michael@0 1792 if (!lockTime) {
michael@0 1793 return rv;
michael@0 1794 }
michael@0 1795 }
michael@0 1796 if (pwswk->exchKeyType == exchKeyType &&
michael@0 1797 pwswk->symWrapMechIndex == symWrapMechIndex &&
michael@0 1798 pwswk->wrappedSymKeyLen != 0) {
michael@0 1799 *wswk = *pwswk;
michael@0 1800 rv = PR_TRUE;
michael@0 1801 }
michael@0 1802 if (now) {
michael@0 1803 UnlockSidCacheLock(cache->keyCacheLock);
michael@0 1804 }
michael@0 1805 return rv;
michael@0 1806 }
michael@0 1807
michael@0 1808 PRBool
michael@0 1809 ssl_GetWrappingKey( PRInt32 symWrapMechIndex,
michael@0 1810 SSL3KEAType exchKeyType,
michael@0 1811 SSLWrappedSymWrappingKey *wswk)
michael@0 1812 {
michael@0 1813 PRBool rv;
michael@0 1814
michael@0 1815 PORT_Assert( (unsigned)exchKeyType < kt_kea_size);
michael@0 1816 PORT_Assert( (unsigned)symWrapMechIndex < SSL_NUM_WRAP_MECHS);
michael@0 1817 if ((unsigned)exchKeyType < kt_kea_size &&
michael@0 1818 (unsigned)symWrapMechIndex < SSL_NUM_WRAP_MECHS) {
michael@0 1819 rv = getSvrWrappingKey(symWrapMechIndex, exchKeyType, wswk,
michael@0 1820 &globalCache, 0);
michael@0 1821 } else {
michael@0 1822 rv = PR_FALSE;
michael@0 1823 }
michael@0 1824
michael@0 1825 return rv;
michael@0 1826 }
michael@0 1827
michael@0 1828 /* Wrap and cache a session ticket key. */
michael@0 1829 static PRBool
michael@0 1830 WrapTicketKey(SECKEYPublicKey *svrPubKey, PK11SymKey *symKey,
michael@0 1831 const char *keyName, encKeyCacheEntry* cacheEntry)
michael@0 1832 {
michael@0 1833 SECItem wrappedKey = {siBuffer, NULL, 0};
michael@0 1834
michael@0 1835 wrappedKey.len = SECKEY_PublicKeyStrength(svrPubKey);
michael@0 1836 PORT_Assert(wrappedKey.len <= sizeof(cacheEntry->bytes));
michael@0 1837 if (wrappedKey.len > sizeof(cacheEntry->bytes))
michael@0 1838 return PR_FALSE;
michael@0 1839 wrappedKey.data = cacheEntry->bytes;
michael@0 1840
michael@0 1841 if (PK11_PubWrapSymKey(CKM_RSA_PKCS, svrPubKey, symKey, &wrappedKey)
michael@0 1842 != SECSuccess) {
michael@0 1843 SSL_DBG(("%d: SSL[%s]: Unable to wrap session ticket %s.",
michael@0 1844 SSL_GETPID(), "unknown", keyName));
michael@0 1845 return PR_FALSE;
michael@0 1846 }
michael@0 1847 cacheEntry->length = wrappedKey.len;
michael@0 1848 return PR_TRUE;
michael@0 1849 }
michael@0 1850
michael@0 1851 static PRBool
michael@0 1852 GenerateTicketKeys(void *pwArg, unsigned char *keyName, PK11SymKey **aesKey,
michael@0 1853 PK11SymKey **macKey)
michael@0 1854 {
michael@0 1855 PK11SlotInfo *slot;
michael@0 1856 CK_MECHANISM_TYPE mechanismArray[2];
michael@0 1857 PK11SymKey *aesKeyTmp = NULL;
michael@0 1858 PK11SymKey *macKeyTmp = NULL;
michael@0 1859 cacheDesc *cache = &globalCache;
michael@0 1860 PRUint8 ticketKeyNameSuffixLocal[SESS_TICKET_KEY_VAR_NAME_LEN];
michael@0 1861 PRUint8 *ticketKeyNameSuffix;
michael@0 1862
michael@0 1863 if (!cache->cacheMem) {
michael@0 1864 /* cache is not initalized. Use stack buffer */
michael@0 1865 ticketKeyNameSuffix = ticketKeyNameSuffixLocal;
michael@0 1866 } else {
michael@0 1867 ticketKeyNameSuffix = cache->ticketKeyNameSuffix;
michael@0 1868 }
michael@0 1869
michael@0 1870 if (PK11_GenerateRandom(ticketKeyNameSuffix,
michael@0 1871 SESS_TICKET_KEY_VAR_NAME_LEN) != SECSuccess) {
michael@0 1872 SSL_DBG(("%d: SSL[%s]: Unable to generate random key name bytes.",
michael@0 1873 SSL_GETPID(), "unknown"));
michael@0 1874 goto loser;
michael@0 1875 }
michael@0 1876
michael@0 1877 mechanismArray[0] = CKM_AES_CBC;
michael@0 1878 mechanismArray[1] = CKM_SHA256_HMAC;
michael@0 1879
michael@0 1880 slot = PK11_GetBestSlotMultiple(mechanismArray, 2, pwArg);
michael@0 1881 if (slot) {
michael@0 1882 aesKeyTmp = PK11_KeyGen(slot, mechanismArray[0], NULL,
michael@0 1883 AES_256_KEY_LENGTH, pwArg);
michael@0 1884 macKeyTmp = PK11_KeyGen(slot, mechanismArray[1], NULL,
michael@0 1885 SHA256_LENGTH, pwArg);
michael@0 1886 PK11_FreeSlot(slot);
michael@0 1887 }
michael@0 1888
michael@0 1889 if (aesKeyTmp == NULL || macKeyTmp == NULL) {
michael@0 1890 SSL_DBG(("%d: SSL[%s]: Unable to generate session ticket keys.",
michael@0 1891 SSL_GETPID(), "unknown"));
michael@0 1892 goto loser;
michael@0 1893 }
michael@0 1894 PORT_Memcpy(keyName, ticketKeyNameSuffix, SESS_TICKET_KEY_VAR_NAME_LEN);
michael@0 1895 *aesKey = aesKeyTmp;
michael@0 1896 *macKey = macKeyTmp;
michael@0 1897 return PR_TRUE;
michael@0 1898
michael@0 1899 loser:
michael@0 1900 if (aesKeyTmp)
michael@0 1901 PK11_FreeSymKey(aesKeyTmp);
michael@0 1902 if (macKeyTmp)
michael@0 1903 PK11_FreeSymKey(macKeyTmp);
michael@0 1904 return PR_FALSE;
michael@0 1905 }
michael@0 1906
michael@0 1907 static PRBool
michael@0 1908 GenerateAndWrapTicketKeys(SECKEYPublicKey *svrPubKey, void *pwArg,
michael@0 1909 unsigned char *keyName, PK11SymKey **aesKey,
michael@0 1910 PK11SymKey **macKey)
michael@0 1911 {
michael@0 1912 PK11SymKey *aesKeyTmp = NULL;
michael@0 1913 PK11SymKey *macKeyTmp = NULL;
michael@0 1914 cacheDesc *cache = &globalCache;
michael@0 1915
michael@0 1916 if (!GenerateTicketKeys(pwArg, keyName, &aesKeyTmp, &macKeyTmp)) {
michael@0 1917 goto loser;
michael@0 1918 }
michael@0 1919
michael@0 1920 if (cache->cacheMem) {
michael@0 1921 /* Export the keys to the shared cache in wrapped form. */
michael@0 1922 if (!WrapTicketKey(svrPubKey, aesKeyTmp, "enc key", cache->ticketEncKey))
michael@0 1923 goto loser;
michael@0 1924 if (!WrapTicketKey(svrPubKey, macKeyTmp, "mac key", cache->ticketMacKey))
michael@0 1925 goto loser;
michael@0 1926 }
michael@0 1927 *aesKey = aesKeyTmp;
michael@0 1928 *macKey = macKeyTmp;
michael@0 1929 return PR_TRUE;
michael@0 1930
michael@0 1931 loser:
michael@0 1932 if (aesKeyTmp)
michael@0 1933 PK11_FreeSymKey(aesKeyTmp);
michael@0 1934 if (macKeyTmp)
michael@0 1935 PK11_FreeSymKey(macKeyTmp);
michael@0 1936 return PR_FALSE;
michael@0 1937 }
michael@0 1938
michael@0 1939 static PRBool
michael@0 1940 UnwrapCachedTicketKeys(SECKEYPrivateKey *svrPrivKey, unsigned char *keyName,
michael@0 1941 PK11SymKey **aesKey, PK11SymKey **macKey)
michael@0 1942 {
michael@0 1943 SECItem wrappedKey = {siBuffer, NULL, 0};
michael@0 1944 PK11SymKey *aesKeyTmp = NULL;
michael@0 1945 PK11SymKey *macKeyTmp = NULL;
michael@0 1946 cacheDesc *cache = &globalCache;
michael@0 1947
michael@0 1948 wrappedKey.data = cache->ticketEncKey->bytes;
michael@0 1949 wrappedKey.len = cache->ticketEncKey->length;
michael@0 1950 PORT_Assert(wrappedKey.len <= sizeof(cache->ticketEncKey->bytes));
michael@0 1951 aesKeyTmp = PK11_PubUnwrapSymKey(svrPrivKey, &wrappedKey,
michael@0 1952 CKM_AES_CBC, CKA_DECRYPT, 0);
michael@0 1953
michael@0 1954 wrappedKey.data = cache->ticketMacKey->bytes;
michael@0 1955 wrappedKey.len = cache->ticketMacKey->length;
michael@0 1956 PORT_Assert(wrappedKey.len <= sizeof(cache->ticketMacKey->bytes));
michael@0 1957 macKeyTmp = PK11_PubUnwrapSymKey(svrPrivKey, &wrappedKey,
michael@0 1958 CKM_SHA256_HMAC, CKA_SIGN, 0);
michael@0 1959
michael@0 1960 if (aesKeyTmp == NULL || macKeyTmp == NULL) {
michael@0 1961 SSL_DBG(("%d: SSL[%s]: Unable to unwrap session ticket keys.",
michael@0 1962 SSL_GETPID(), "unknown"));
michael@0 1963 goto loser;
michael@0 1964 }
michael@0 1965 SSL_DBG(("%d: SSL[%s]: Successfully unwrapped session ticket keys.",
michael@0 1966 SSL_GETPID(), "unknown"));
michael@0 1967
michael@0 1968 PORT_Memcpy(keyName, cache->ticketKeyNameSuffix,
michael@0 1969 SESS_TICKET_KEY_VAR_NAME_LEN);
michael@0 1970 *aesKey = aesKeyTmp;
michael@0 1971 *macKey = macKeyTmp;
michael@0 1972 return PR_TRUE;
michael@0 1973
michael@0 1974 loser:
michael@0 1975 if (aesKeyTmp)
michael@0 1976 PK11_FreeSymKey(aesKeyTmp);
michael@0 1977 if (macKeyTmp)
michael@0 1978 PK11_FreeSymKey(macKeyTmp);
michael@0 1979 return PR_FALSE;
michael@0 1980 }
michael@0 1981
michael@0 1982 PRBool
michael@0 1983 ssl_GetSessionTicketKeysPKCS11(SECKEYPrivateKey *svrPrivKey,
michael@0 1984 SECKEYPublicKey *svrPubKey, void *pwArg,
michael@0 1985 unsigned char *keyName, PK11SymKey **aesKey,
michael@0 1986 PK11SymKey **macKey)
michael@0 1987 {
michael@0 1988 PRUint32 now = 0;
michael@0 1989 PRBool rv = PR_FALSE;
michael@0 1990 PRBool keysGenerated = PR_FALSE;
michael@0 1991 cacheDesc *cache = &globalCache;
michael@0 1992
michael@0 1993 if (!cache->cacheMem) {
michael@0 1994 /* cache is uninitialized. Generate keys and return them
michael@0 1995 * without caching. */
michael@0 1996 return GenerateTicketKeys(pwArg, keyName, aesKey, macKey);
michael@0 1997 }
michael@0 1998
michael@0 1999 now = LockSidCacheLock(cache->keyCacheLock, now);
michael@0 2000 if (!now)
michael@0 2001 return rv;
michael@0 2002
michael@0 2003 if (!*(cache->ticketKeysValid)) {
michael@0 2004 /* Keys do not exist, create them. */
michael@0 2005 if (!GenerateAndWrapTicketKeys(svrPubKey, pwArg, keyName,
michael@0 2006 aesKey, macKey))
michael@0 2007 goto loser;
michael@0 2008 keysGenerated = PR_TRUE;
michael@0 2009 *(cache->ticketKeysValid) = 1;
michael@0 2010 }
michael@0 2011
michael@0 2012 rv = PR_TRUE;
michael@0 2013
michael@0 2014 loser:
michael@0 2015 UnlockSidCacheLock(cache->keyCacheLock);
michael@0 2016 if (rv && !keysGenerated)
michael@0 2017 rv = UnwrapCachedTicketKeys(svrPrivKey, keyName, aesKey, macKey);
michael@0 2018 return rv;
michael@0 2019 }
michael@0 2020
michael@0 2021 PRBool
michael@0 2022 ssl_GetSessionTicketKeys(unsigned char *keyName, unsigned char *encKey,
michael@0 2023 unsigned char *macKey)
michael@0 2024 {
michael@0 2025 PRBool rv = PR_FALSE;
michael@0 2026 PRUint32 now = 0;
michael@0 2027 cacheDesc *cache = &globalCache;
michael@0 2028 PRUint8 ticketMacKey[SHA256_LENGTH], ticketEncKey[AES_256_KEY_LENGTH];
michael@0 2029 PRUint8 ticketKeyNameSuffixLocal[SESS_TICKET_KEY_VAR_NAME_LEN];
michael@0 2030 PRUint8 *ticketMacKeyPtr, *ticketEncKeyPtr, *ticketKeyNameSuffix;
michael@0 2031 PRBool cacheIsEnabled = PR_TRUE;
michael@0 2032
michael@0 2033 if (!cache->cacheMem) { /* cache is uninitialized */
michael@0 2034 cacheIsEnabled = PR_FALSE;
michael@0 2035 ticketKeyNameSuffix = ticketKeyNameSuffixLocal;
michael@0 2036 ticketEncKeyPtr = ticketEncKey;
michael@0 2037 ticketMacKeyPtr = ticketMacKey;
michael@0 2038 } else {
michael@0 2039 /* these values have constant memory locations in the cache.
michael@0 2040 * Ok to reference them without holding the lock. */
michael@0 2041 ticketKeyNameSuffix = cache->ticketKeyNameSuffix;
michael@0 2042 ticketEncKeyPtr = cache->ticketEncKey->bytes;
michael@0 2043 ticketMacKeyPtr = cache->ticketMacKey->bytes;
michael@0 2044 }
michael@0 2045
michael@0 2046 if (cacheIsEnabled) {
michael@0 2047 /* Grab lock if initialized. */
michael@0 2048 now = LockSidCacheLock(cache->keyCacheLock, now);
michael@0 2049 if (!now)
michael@0 2050 return rv;
michael@0 2051 }
michael@0 2052 /* Going to regenerate keys on every call if cache was not
michael@0 2053 * initialized. */
michael@0 2054 if (!cacheIsEnabled || !*(cache->ticketKeysValid)) {
michael@0 2055 if (PK11_GenerateRandom(ticketKeyNameSuffix,
michael@0 2056 SESS_TICKET_KEY_VAR_NAME_LEN) != SECSuccess)
michael@0 2057 goto loser;
michael@0 2058 if (PK11_GenerateRandom(ticketEncKeyPtr,
michael@0 2059 AES_256_KEY_LENGTH) != SECSuccess)
michael@0 2060 goto loser;
michael@0 2061 if (PK11_GenerateRandom(ticketMacKeyPtr,
michael@0 2062 SHA256_LENGTH) != SECSuccess)
michael@0 2063 goto loser;
michael@0 2064 if (cacheIsEnabled) {
michael@0 2065 *(cache->ticketKeysValid) = 1;
michael@0 2066 }
michael@0 2067 }
michael@0 2068
michael@0 2069 rv = PR_TRUE;
michael@0 2070
michael@0 2071 loser:
michael@0 2072 if (cacheIsEnabled) {
michael@0 2073 UnlockSidCacheLock(cache->keyCacheLock);
michael@0 2074 }
michael@0 2075 if (rv) {
michael@0 2076 PORT_Memcpy(keyName, ticketKeyNameSuffix,
michael@0 2077 SESS_TICKET_KEY_VAR_NAME_LEN);
michael@0 2078 PORT_Memcpy(encKey, ticketEncKeyPtr, AES_256_KEY_LENGTH);
michael@0 2079 PORT_Memcpy(macKey, ticketMacKeyPtr, SHA256_LENGTH);
michael@0 2080 }
michael@0 2081 return rv;
michael@0 2082 }
michael@0 2083
michael@0 2084 /* The caller passes in the new value it wants
michael@0 2085 * to set. This code tests the wrapped sym key entry in the shared memory.
michael@0 2086 * If it is uninitialized, this function writes the caller's value into
michael@0 2087 * the disk entry, and returns false.
michael@0 2088 * Otherwise, it overwrites the caller's wswk with the value obtained from
michael@0 2089 * the disk, and returns PR_TRUE.
michael@0 2090 * This is all done while holding the locks/mutexes necessary to make
michael@0 2091 * the operation atomic.
michael@0 2092 */
michael@0 2093 PRBool
michael@0 2094 ssl_SetWrappingKey(SSLWrappedSymWrappingKey *wswk)
michael@0 2095 {
michael@0 2096 cacheDesc * cache = &globalCache;
michael@0 2097 PRBool rv = PR_FALSE;
michael@0 2098 SSL3KEAType exchKeyType = wswk->exchKeyType;
michael@0 2099 /* type of keys used to wrap SymWrapKey*/
michael@0 2100 PRInt32 symWrapMechIndex = wswk->symWrapMechIndex;
michael@0 2101 PRUint32 ndx;
michael@0 2102 PRUint32 now = 0;
michael@0 2103 SSLWrappedSymWrappingKey myWswk;
michael@0 2104
michael@0 2105 if (!cache->cacheMem) { /* cache is uninitialized */
michael@0 2106 PORT_SetError(SSL_ERROR_SERVER_CACHE_NOT_CONFIGURED);
michael@0 2107 return 0;
michael@0 2108 }
michael@0 2109
michael@0 2110 PORT_Assert( (unsigned)exchKeyType < kt_kea_size);
michael@0 2111 if ((unsigned)exchKeyType >= kt_kea_size)
michael@0 2112 return 0;
michael@0 2113
michael@0 2114 PORT_Assert( (unsigned)symWrapMechIndex < SSL_NUM_WRAP_MECHS);
michael@0 2115 if ((unsigned)symWrapMechIndex >= SSL_NUM_WRAP_MECHS)
michael@0 2116 return 0;
michael@0 2117
michael@0 2118 ndx = (exchKeyType * SSL_NUM_WRAP_MECHS) + symWrapMechIndex;
michael@0 2119 PORT_Memset(&myWswk, 0, sizeof myWswk); /* eliminate UMRs. */
michael@0 2120
michael@0 2121 now = LockSidCacheLock(cache->keyCacheLock, now);
michael@0 2122 if (now) {
michael@0 2123 rv = getSvrWrappingKey(wswk->symWrapMechIndex, wswk->exchKeyType,
michael@0 2124 &myWswk, cache, now);
michael@0 2125 if (rv) {
michael@0 2126 /* we found it on disk, copy it out to the caller. */
michael@0 2127 PORT_Memcpy(wswk, &myWswk, sizeof *wswk);
michael@0 2128 } else {
michael@0 2129 /* Wasn't on disk, and we're still holding the lock, so write it. */
michael@0 2130 cache->keyCacheData[ndx] = *wswk;
michael@0 2131 }
michael@0 2132 UnlockSidCacheLock(cache->keyCacheLock);
michael@0 2133 }
michael@0 2134 return rv;
michael@0 2135 }
michael@0 2136
michael@0 2137 #else /* MAC version or other platform */
michael@0 2138
michael@0 2139 #include "seccomon.h"
michael@0 2140 #include "cert.h"
michael@0 2141 #include "ssl.h"
michael@0 2142 #include "sslimpl.h"
michael@0 2143
michael@0 2144 SECStatus
michael@0 2145 SSL_ConfigServerSessionIDCache( int maxCacheEntries,
michael@0 2146 PRUint32 ssl2_timeout,
michael@0 2147 PRUint32 ssl3_timeout,
michael@0 2148 const char * directory)
michael@0 2149 {
michael@0 2150 PR_ASSERT(!"SSL servers are not supported on this platform. (SSL_ConfigServerSessionIDCache)");
michael@0 2151 return SECFailure;
michael@0 2152 }
michael@0 2153
michael@0 2154 SECStatus
michael@0 2155 SSL_ConfigMPServerSIDCache( int maxCacheEntries,
michael@0 2156 PRUint32 ssl2_timeout,
michael@0 2157 PRUint32 ssl3_timeout,
michael@0 2158 const char * directory)
michael@0 2159 {
michael@0 2160 PR_ASSERT(!"SSL servers are not supported on this platform. (SSL_ConfigMPServerSIDCache)");
michael@0 2161 return SECFailure;
michael@0 2162 }
michael@0 2163
michael@0 2164 SECStatus
michael@0 2165 SSL_InheritMPServerSIDCache(const char * envString)
michael@0 2166 {
michael@0 2167 PR_ASSERT(!"SSL servers are not supported on this platform. (SSL_InheritMPServerSIDCache)");
michael@0 2168 return SECFailure;
michael@0 2169 }
michael@0 2170
michael@0 2171 PRBool
michael@0 2172 ssl_GetWrappingKey( PRInt32 symWrapMechIndex,
michael@0 2173 SSL3KEAType exchKeyType,
michael@0 2174 SSLWrappedSymWrappingKey *wswk)
michael@0 2175 {
michael@0 2176 PRBool rv = PR_FALSE;
michael@0 2177 PR_ASSERT(!"SSL servers are not supported on this platform. (ssl_GetWrappingKey)");
michael@0 2178 return rv;
michael@0 2179 }
michael@0 2180
michael@0 2181 /* This is a kind of test-and-set. The caller passes in the new value it wants
michael@0 2182 * to set. This code tests the wrapped sym key entry in the shared memory.
michael@0 2183 * If it is uninitialized, this function writes the caller's value into
michael@0 2184 * the disk entry, and returns false.
michael@0 2185 * Otherwise, it overwrites the caller's wswk with the value obtained from
michael@0 2186 * the disk, and returns PR_TRUE.
michael@0 2187 * This is all done while holding the locks/mutexes necessary to make
michael@0 2188 * the operation atomic.
michael@0 2189 */
michael@0 2190 PRBool
michael@0 2191 ssl_SetWrappingKey(SSLWrappedSymWrappingKey *wswk)
michael@0 2192 {
michael@0 2193 PRBool rv = PR_FALSE;
michael@0 2194 PR_ASSERT(!"SSL servers are not supported on this platform. (ssl_SetWrappingKey)");
michael@0 2195 return rv;
michael@0 2196 }
michael@0 2197
michael@0 2198 PRUint32
michael@0 2199 SSL_GetMaxServerCacheLocks(void)
michael@0 2200 {
michael@0 2201 PR_ASSERT(!"SSL servers are not supported on this platform. (SSL_GetMaxServerCacheLocks)");
michael@0 2202 return -1;
michael@0 2203 }
michael@0 2204
michael@0 2205 SECStatus
michael@0 2206 SSL_SetMaxServerCacheLocks(PRUint32 maxLocks)
michael@0 2207 {
michael@0 2208 PR_ASSERT(!"SSL servers are not supported on this platform. (SSL_SetMaxServerCacheLocks)");
michael@0 2209 return SECFailure;
michael@0 2210 }
michael@0 2211
michael@0 2212 #endif /* XP_UNIX || XP_WIN32 */

mercurial