2 * This file implements the CLIENT Session ID cache.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
17 #if defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)
21 PRUint32 ssl_sid_timeout
= 100;
22 PRUint32 ssl3_sid_timeout
= 86400L; /* 24 hours */
24 static sslSessionID
*cache
= NULL
;
25 static PZLock
* cacheLock
= NULL
;
27 /* sids can be in one of 4 states:
29 * never_cached, created, but not yet put into cache.
30 * in_client_cache, in the client cache's linked list.
31 * in_server_cache, entry came from the server's cache file.
32 * invalid_cache has been removed from the cache.
35 #define LOCK_CACHE lock_cache()
36 #define UNLOCK_CACHE PZ_Unlock(cacheLock)
38 static PRCallOnceType lockOnce
;
40 /* FreeSessionCacheLocks is a callback from NSS_RegisterShutdown which destroys
41 * the session cache locks on shutdown and resets them to their initial
44 FreeSessionCacheLocks(void* appData
, void* nssData
)
46 static const PRCallOnceType pristineCallOnce
;
50 PORT_SetError(SEC_ERROR_NOT_INITIALIZED
);
54 PZ_DestroyLock(cacheLock
);
57 rv
= ssl_FreeSymWrapKeysLock();
58 if (rv
!= SECSuccess
) {
62 lockOnce
= pristineCallOnce
;
66 /* InitSessionCacheLocks is called, protected by lockOnce, to create the
67 * session cache locks. */
69 InitSessionCacheLocks(void)
73 cacheLock
= PZ_NewLock(nssILockCache
);
74 if (cacheLock
== NULL
) {
77 rv
= ssl_InitSymWrapKeysLock();
78 if (rv
!= SECSuccess
) {
79 PRErrorCode error
= PORT_GetError();
80 PZ_DestroyLock(cacheLock
);
86 rv
= NSS_RegisterShutdown(FreeSessionCacheLocks
, NULL
);
87 PORT_Assert(SECSuccess
== rv
);
88 if (SECSuccess
!= rv
) {
95 ssl_InitSessionCacheLocks(void)
98 PR_CallOnce(&lockOnce
, InitSessionCacheLocks
)) ?
99 SECSuccess
: SECFailure
;
105 ssl_InitSessionCacheLocks();
109 /* BEWARE: This function gets called for both client and server SIDs !!
110 * If the unreferenced sid is not in the cache, Free sid and its contents.
113 ssl_DestroySID(sslSessionID
*sid
)
116 SSL_TRC(8, ("SSL: destroy sid: sid=0x%x cached=%d", sid
, sid
->cached
));
117 PORT_Assert(sid
->references
== 0);
118 PORT_Assert(sid
->cached
!= in_client_cache
);
120 if (sid
->version
< SSL_LIBRARY_VERSION_3_0
) {
121 SECITEM_ZfreeItem(&sid
->u
.ssl2
.masterKey
, PR_FALSE
);
122 SECITEM_ZfreeItem(&sid
->u
.ssl2
.cipherArg
, PR_FALSE
);
124 if (sid
->u
.ssl3
.locked
.sessionTicket
.ticket
.data
) {
125 SECITEM_FreeItem(&sid
->u
.ssl3
.locked
.sessionTicket
.ticket
,
128 if (sid
->u
.ssl3
.srvName
.data
) {
129 SECITEM_FreeItem(&sid
->u
.ssl3
.srvName
, PR_FALSE
);
131 if (sid
->u
.ssl3
.originalHandshakeHash
.data
) {
132 SECITEM_FreeItem(&sid
->u
.ssl3
.originalHandshakeHash
, PR_FALSE
);
134 if (sid
->u
.ssl3
.signedCertTimestamps
.data
) {
135 SECITEM_FreeItem(&sid
->u
.ssl3
.signedCertTimestamps
, PR_FALSE
);
138 if (sid
->u
.ssl3
.lock
) {
139 NSSRWLock_Destroy(sid
->u
.ssl3
.lock
);
143 if (sid
->peerID
!= NULL
)
144 PORT_Free((void *)sid
->peerID
); /* CONST */
146 if (sid
->urlSvrName
!= NULL
)
147 PORT_Free((void *)sid
->urlSvrName
); /* CONST */
149 if ( sid
->peerCert
) {
150 CERT_DestroyCertificate(sid
->peerCert
);
152 for (i
= 0; i
< MAX_PEER_CERT_CHAIN_SIZE
&& sid
->peerCertChain
[i
]; i
++) {
153 CERT_DestroyCertificate(sid
->peerCertChain
[i
]);
155 if (sid
->peerCertStatus
.items
) {
156 SECITEM_FreeArray(&sid
->peerCertStatus
, PR_FALSE
);
159 if ( sid
->localCert
) {
160 CERT_DestroyCertificate(sid
->localCert
);
163 PORT_ZFree(sid
, sizeof(sslSessionID
));
166 /* BEWARE: This function gets called for both client and server SIDs !!
167 * Decrement reference count, and
168 * free sid if ref count is zero, and sid is not in the cache.
169 * Does NOT remove from the cache first.
170 * If the sid is still in the cache, it is left there until next time
171 * the cache list is traversed.
174 ssl_FreeLockedSID(sslSessionID
*sid
)
176 PORT_Assert(sid
->references
>= 1);
177 if (--sid
->references
== 0) {
182 /* BEWARE: This function gets called for both client and server SIDs !!
183 * Decrement reference count, and
184 * free sid if ref count is zero, and sid is not in the cache.
185 * Does NOT remove from the cache first.
186 * These locks are necessary because the sid _might_ be in the cache list.
189 ssl_FreeSID(sslSessionID
*sid
)
192 ssl_FreeLockedSID(sid
);
196 /************************************************************************/
199 ** Lookup sid entry in cache by Address, port, and peerID string.
200 ** If found, Increment reference count, and return pointer to caller.
201 ** If it has timed out or ref count is zero, remove from list and free it.
205 ssl_LookupSID(const PRIPv6Addr
*addr
, PRUint16 port
, const char *peerID
,
206 const char * urlSvrName
)
217 while ((sid
= *sidp
) != 0) {
218 PORT_Assert(sid
->cached
== in_client_cache
);
219 PORT_Assert(sid
->references
>= 1);
221 SSL_TRC(8, ("SSL: Lookup1: sid=0x%x", sid
));
223 if (sid
->expirationTime
< now
) {
225 ** This session-id timed out.
226 ** Don't even care who it belongs to, blow it out of our cache.
228 SSL_TRC(7, ("SSL: lookup1, throwing sid out, age=%d refs=%d",
229 now
- sid
->creationTime
, sid
->references
));
231 *sidp
= sid
->next
; /* delink it from the list. */
232 sid
->cached
= invalid_cache
; /* mark not on list. */
233 ssl_FreeLockedSID(sid
); /* drop ref count, free. */
234 } else if (!memcmp(&sid
->addr
, addr
, sizeof(PRIPv6Addr
)) && /* server IP addr matches */
235 (sid
->port
== port
) && /* server port matches */
236 /* proxy (peerID) matches */
237 (((peerID
== NULL
) && (sid
->peerID
== NULL
)) ||
238 ((peerID
!= NULL
) && (sid
->peerID
!= NULL
) &&
239 PORT_Strcmp(sid
->peerID
, peerID
) == 0)) &&
241 (sid
->version
< SSL_LIBRARY_VERSION_3_0
||
242 sid
->u
.ssl3
.keys
.resumable
) &&
243 /* server hostname matches. */
244 (sid
->urlSvrName
!= NULL
) &&
245 ((0 == PORT_Strcmp(urlSvrName
, sid
->urlSvrName
)) ||
246 ((sid
->peerCert
!= NULL
) && (SECSuccess
==
247 CERT_VerifyCertName(sid
->peerCert
, urlSvrName
))) )
250 sid
->lastAccessTime
= now
;
262 ** Add an sid to the cache or return a previously cached entry to the cache.
263 ** Although this is static, it is called via ss->sec.cache().
266 CacheSID(sslSessionID
*sid
)
268 PRUint32 expirationPeriod
;
270 PORT_Assert(sid
->cached
== never_cached
);
272 SSL_TRC(8, ("SSL: Cache: sid=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
274 sid
, sid
->cached
, sid
->addr
.pr_s6_addr32
[0],
275 sid
->addr
.pr_s6_addr32
[1], sid
->addr
.pr_s6_addr32
[2],
276 sid
->addr
.pr_s6_addr32
[3], sid
->port
, sid
->creationTime
,
279 if (!sid
->urlSvrName
) {
280 /* don't cache this SID because it can never be matched */
284 /* XXX should be different trace for version 2 vs. version 3 */
285 if (sid
->version
< SSL_LIBRARY_VERSION_3_0
) {
286 expirationPeriod
= ssl_sid_timeout
;
287 PRINT_BUF(8, (0, "sessionID:",
288 sid
->u
.ssl2
.sessionID
, sizeof(sid
->u
.ssl2
.sessionID
)));
289 PRINT_BUF(8, (0, "masterKey:",
290 sid
->u
.ssl2
.masterKey
.data
, sid
->u
.ssl2
.masterKey
.len
));
291 PRINT_BUF(8, (0, "cipherArg:",
292 sid
->u
.ssl2
.cipherArg
.data
, sid
->u
.ssl2
.cipherArg
.len
));
294 if (sid
->u
.ssl3
.sessionIDLength
== 0 &&
295 sid
->u
.ssl3
.locked
.sessionTicket
.ticket
.data
== NULL
)
298 /* Client generates the SessionID if this was a stateless resume. */
299 if (sid
->u
.ssl3
.sessionIDLength
== 0) {
301 rv
= PK11_GenerateRandom(sid
->u
.ssl3
.sessionID
,
302 SSL3_SESSIONID_BYTES
);
303 if (rv
!= SECSuccess
)
305 sid
->u
.ssl3
.sessionIDLength
= SSL3_SESSIONID_BYTES
;
307 expirationPeriod
= ssl3_sid_timeout
;
308 PRINT_BUF(8, (0, "sessionID:",
309 sid
->u
.ssl3
.sessionID
, sid
->u
.ssl3
.sessionIDLength
));
311 sid
->u
.ssl3
.lock
= NSSRWLock_New(NSS_RWLOCK_RANK_NONE
, NULL
);
312 if (!sid
->u
.ssl3
.lock
) {
316 PORT_Assert(sid
->creationTime
!= 0 && sid
->expirationTime
!= 0);
317 if (!sid
->creationTime
)
318 sid
->lastAccessTime
= sid
->creationTime
= ssl_Time();
319 if (!sid
->expirationTime
)
320 sid
->expirationTime
= sid
->creationTime
+ expirationPeriod
;
323 * Put sid into the cache. Bump reference count to indicate that
324 * cache is holding a reference. Uncache will reduce the cache
329 sid
->cached
= in_client_cache
;
336 * If sid "zap" is in the cache,
337 * removes sid from cache, and decrements reference count.
338 * Caller must hold cache lock.
341 UncacheSID(sslSessionID
*zap
)
343 sslSessionID
**sidp
= &cache
;
346 if (zap
->cached
!= in_client_cache
) {
350 SSL_TRC(8,("SSL: Uncache: zap=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
352 zap
, zap
->cached
, zap
->addr
.pr_s6_addr32
[0],
353 zap
->addr
.pr_s6_addr32
[1], zap
->addr
.pr_s6_addr32
[2],
354 zap
->addr
.pr_s6_addr32
[3], zap
->port
, zap
->creationTime
,
355 zap
->u
.ssl2
.cipherType
));
356 if (zap
->version
< SSL_LIBRARY_VERSION_3_0
) {
357 PRINT_BUF(8, (0, "sessionID:",
358 zap
->u
.ssl2
.sessionID
, sizeof(zap
->u
.ssl2
.sessionID
)));
359 PRINT_BUF(8, (0, "masterKey:",
360 zap
->u
.ssl2
.masterKey
.data
, zap
->u
.ssl2
.masterKey
.len
));
361 PRINT_BUF(8, (0, "cipherArg:",
362 zap
->u
.ssl2
.cipherArg
.data
, zap
->u
.ssl2
.cipherArg
.len
));
365 /* See if it's in the cache, if so nuke it */
366 while ((sid
= *sidp
) != 0) {
369 ** Bingo. Reduce reference count by one so that when
370 ** everyone is done with the sid we can free it up.
373 zap
->cached
= invalid_cache
;
374 ssl_FreeLockedSID(zap
);
381 /* If sid "zap" is in the cache,
382 * removes sid from cache, and decrements reference count.
383 * Although this function is static, it is called externally via
387 LockAndUncacheSID(sslSessionID
*zap
)
395 /* choose client or server cache functions for this sslsocket. */
397 ssl_ChooseSessionIDProcs(sslSecurityInfo
*sec
)
400 sec
->cache
= ssl_sid_cache
;
401 sec
->uncache
= ssl_sid_uncache
;
403 sec
->cache
= CacheSID
;
404 sec
->uncache
= LockAndUncacheSID
;
408 /* wipe out the entire client session cache. */
410 SSL_ClearSessionCache(void)
418 /* returns an unsigned int containing the number of seconds in PR_Now() */
423 #if defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)
424 myTime
= time(NULL
); /* accurate until the year 2038. */
426 /* portable, but possibly slower */
431 LL_I2L(ll
, 1000000L);
432 LL_DIV(now
, now
, ll
);
433 LL_L2UI(myTime
, now
);
439 ssl3_SetSIDSessionTicket(sslSessionID
*sid
,
440 /*in/out*/ NewSessionTicket
*newSessionTicket
)
443 PORT_Assert(newSessionTicket
);
445 /* if sid->u.ssl3.lock, we are updating an existing entry that is already
446 * cached or was once cached, so we need to acquire and release the write
447 * lock. Otherwise, this is a new session that isn't shared with anything
448 * yet, so no locking is needed.
450 if (sid
->u
.ssl3
.lock
) {
451 NSSRWLock_LockWrite(sid
->u
.ssl3
.lock
);
453 /* A server might have sent us an empty ticket, which has the
454 * effect of clearing the previously known ticket.
456 if (sid
->u
.ssl3
.locked
.sessionTicket
.ticket
.data
) {
457 SECITEM_FreeItem(&sid
->u
.ssl3
.locked
.sessionTicket
.ticket
,
462 PORT_Assert(!sid
->u
.ssl3
.locked
.sessionTicket
.ticket
.data
);
464 /* Do a shallow copy, moving the ticket data. */
465 sid
->u
.ssl3
.locked
.sessionTicket
= *newSessionTicket
;
466 newSessionTicket
->ticket
.data
= NULL
;
467 newSessionTicket
->ticket
.len
= 0;
469 if (sid
->u
.ssl3
.lock
) {
470 NSSRWLock_UnlockWrite(sid
->u
.ssl3
.lock
);