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)
39 ssl_InitClientSessionCacheLock(void)
41 cacheLock
= PZ_NewLock(nssILockCache
);
42 return cacheLock
? SECSuccess
: SECFailure
;
46 ssl_FreeClientSessionCacheLock(void)
49 PZ_DestroyLock(cacheLock
);
53 PORT_SetError(SEC_ERROR_NOT_INITIALIZED
);
57 static PRBool LocksInitializedEarly
= PR_FALSE
;
60 FreeSessionCacheLocks()
63 rv1
= ssl_FreeSymWrapKeysLock();
64 rv2
= ssl_FreeClientSessionCacheLock();
65 if ( (SECSuccess
== rv1
) && (SECSuccess
== rv2
) ) {
72 InitSessionCacheLocks(void)
76 rv1
= ssl_InitSymWrapKeysLock();
77 rv2
= ssl_InitClientSessionCacheLock();
78 if ( (SECSuccess
== rv1
) && (SECSuccess
== rv2
) ) {
82 FreeSessionCacheLocks();
87 /* free the session cache locks if they were initialized early */
89 ssl_FreeSessionCacheLocks()
91 PORT_Assert(PR_TRUE
== LocksInitializedEarly
);
92 if (!LocksInitializedEarly
) {
93 PORT_SetError(SEC_ERROR_NOT_INITIALIZED
);
96 FreeSessionCacheLocks();
97 LocksInitializedEarly
= PR_FALSE
;
101 static PRCallOnceType lockOnce
;
103 /* free the session cache locks if they were initialized lazily */
104 static SECStatus
ssl_ShutdownLocks(void* appData
, void* nssData
)
106 PORT_Assert(PR_FALSE
== LocksInitializedEarly
);
107 if (LocksInitializedEarly
) {
108 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE
);
111 FreeSessionCacheLocks();
112 memset(&lockOnce
, 0, sizeof(lockOnce
));
116 static PRStatus
initSessionCacheLocksLazily(void)
118 SECStatus rv
= InitSessionCacheLocks();
119 if (SECSuccess
!= rv
) {
122 rv
= NSS_RegisterShutdown(ssl_ShutdownLocks
, NULL
);
123 PORT_Assert(SECSuccess
== rv
);
124 if (SECSuccess
!= rv
) {
130 /* lazyInit means that the call is not happening during a 1-time
131 * initialization function, but rather during dynamic, lazy initialization
134 ssl_InitSessionCacheLocks(PRBool lazyInit
)
136 if (LocksInitializedEarly
) {
141 return (PR_SUCCESS
==
142 PR_CallOnce(&lockOnce
, initSessionCacheLocksLazily
)) ?
143 SECSuccess
: SECFailure
;
146 if (SECSuccess
== InitSessionCacheLocks()) {
147 LocksInitializedEarly
= PR_TRUE
;
157 ssl_InitSessionCacheLocks(PR_TRUE
);
161 /* BEWARE: This function gets called for both client and server SIDs !!
162 * If the unreferenced sid is not in the cache, Free sid and its contents.
165 ssl_DestroySID(sslSessionID
*sid
)
168 SSL_TRC(8, ("SSL: destroy sid: sid=0x%x cached=%d", sid
, sid
->cached
));
169 PORT_Assert((sid
->references
== 0));
171 if (sid
->cached
== in_client_cache
)
172 return; /* it will get taken care of next time cache is traversed. */
174 if (sid
->version
< SSL_LIBRARY_VERSION_3_0
) {
175 SECITEM_ZfreeItem(&sid
->u
.ssl2
.masterKey
, PR_FALSE
);
176 SECITEM_ZfreeItem(&sid
->u
.ssl2
.cipherArg
, PR_FALSE
);
178 if (sid
->peerID
!= NULL
)
179 PORT_Free((void *)sid
->peerID
); /* CONST */
181 if (sid
->urlSvrName
!= NULL
)
182 PORT_Free((void *)sid
->urlSvrName
); /* CONST */
184 if ( sid
->peerCert
) {
185 CERT_DestroyCertificate(sid
->peerCert
);
187 for (i
= 0; i
< MAX_PEER_CERT_CHAIN_SIZE
&& sid
->peerCertChain
[i
]; i
++) {
188 CERT_DestroyCertificate(sid
->peerCertChain
[i
]);
190 if (sid
->peerCertStatus
.items
) {
191 SECITEM_FreeArray(&sid
->peerCertStatus
, PR_FALSE
);
194 if ( sid
->localCert
) {
195 CERT_DestroyCertificate(sid
->localCert
);
197 if (sid
->u
.ssl3
.sessionTicket
.ticket
.data
) {
198 SECITEM_FreeItem(&sid
->u
.ssl3
.sessionTicket
.ticket
, PR_FALSE
);
200 if (sid
->u
.ssl3
.srvName
.data
) {
201 SECITEM_FreeItem(&sid
->u
.ssl3
.srvName
, PR_FALSE
);
204 PORT_ZFree(sid
, sizeof(sslSessionID
));
207 /* BEWARE: This function gets called for both client and server SIDs !!
208 * Decrement reference count, and
209 * free sid if ref count is zero, and sid is not in the cache.
210 * Does NOT remove from the cache first.
211 * If the sid is still in the cache, it is left there until next time
212 * the cache list is traversed.
215 ssl_FreeLockedSID(sslSessionID
*sid
)
217 PORT_Assert(sid
->references
>= 1);
218 if (--sid
->references
== 0) {
223 /* BEWARE: This function gets called for both client and server SIDs !!
224 * Decrement reference count, and
225 * free sid if ref count is zero, and sid is not in the cache.
226 * Does NOT remove from the cache first.
227 * These locks are necessary because the sid _might_ be in the cache list.
230 ssl_FreeSID(sslSessionID
*sid
)
233 ssl_FreeLockedSID(sid
);
237 /************************************************************************/
240 ** Lookup sid entry in cache by Address, port, and peerID string.
241 ** If found, Increment reference count, and return pointer to caller.
242 ** If it has timed out or ref count is zero, remove from list and free it.
246 ssl_LookupSID(const PRIPv6Addr
*addr
, PRUint16 port
, const char *peerID
,
247 const char * urlSvrName
)
258 while ((sid
= *sidp
) != 0) {
259 PORT_Assert(sid
->cached
== in_client_cache
);
260 PORT_Assert(sid
->references
>= 1);
262 SSL_TRC(8, ("SSL: Lookup1: sid=0x%x", sid
));
264 if (sid
->expirationTime
< now
|| !sid
->references
) {
266 ** This session-id timed out, or was orphaned.
267 ** Don't even care who it belongs to, blow it out of our cache.
269 SSL_TRC(7, ("SSL: lookup1, throwing sid out, age=%d refs=%d",
270 now
- sid
->creationTime
, sid
->references
));
272 *sidp
= sid
->next
; /* delink it from the list. */
273 sid
->cached
= invalid_cache
; /* mark not on list. */
274 if (!sid
->references
)
277 ssl_FreeLockedSID(sid
); /* drop ref count, free. */
279 } else if (!memcmp(&sid
->addr
, addr
, sizeof(PRIPv6Addr
)) && /* server IP addr matches */
280 (sid
->port
== port
) && /* server port matches */
281 /* proxy (peerID) matches */
282 (((peerID
== NULL
) && (sid
->peerID
== NULL
)) ||
283 ((peerID
!= NULL
) && (sid
->peerID
!= NULL
) &&
284 PORT_Strcmp(sid
->peerID
, peerID
) == 0)) &&
286 (sid
->version
< SSL_LIBRARY_VERSION_3_0
||
287 sid
->u
.ssl3
.keys
.resumable
) &&
288 /* server hostname matches. */
289 (sid
->urlSvrName
!= NULL
) &&
290 ((0 == PORT_Strcmp(urlSvrName
, sid
->urlSvrName
)) ||
291 ((sid
->peerCert
!= NULL
) && (SECSuccess
==
292 CERT_VerifyCertName(sid
->peerCert
, urlSvrName
))) )
295 sid
->lastAccessTime
= now
;
307 ** Add an sid to the cache or return a previously cached entry to the cache.
308 ** Although this is static, it is called via ss->sec.cache().
311 CacheSID(sslSessionID
*sid
)
313 PRUint32 expirationPeriod
;
314 SSL_TRC(8, ("SSL: Cache: sid=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
316 sid
, sid
->cached
, sid
->addr
.pr_s6_addr32
[0],
317 sid
->addr
.pr_s6_addr32
[1], sid
->addr
.pr_s6_addr32
[2],
318 sid
->addr
.pr_s6_addr32
[3], sid
->port
, sid
->creationTime
,
321 if (sid
->cached
== in_client_cache
)
324 if (!sid
->urlSvrName
) {
325 /* don't cache this SID because it can never be matched */
329 /* XXX should be different trace for version 2 vs. version 3 */
330 if (sid
->version
< SSL_LIBRARY_VERSION_3_0
) {
331 expirationPeriod
= ssl_sid_timeout
;
332 PRINT_BUF(8, (0, "sessionID:",
333 sid
->u
.ssl2
.sessionID
, sizeof(sid
->u
.ssl2
.sessionID
)));
334 PRINT_BUF(8, (0, "masterKey:",
335 sid
->u
.ssl2
.masterKey
.data
, sid
->u
.ssl2
.masterKey
.len
));
336 PRINT_BUF(8, (0, "cipherArg:",
337 sid
->u
.ssl2
.cipherArg
.data
, sid
->u
.ssl2
.cipherArg
.len
));
339 if (sid
->u
.ssl3
.sessionIDLength
== 0 &&
340 sid
->u
.ssl3
.sessionTicket
.ticket
.data
== NULL
)
342 /* Client generates the SessionID if this was a stateless resume. */
343 if (sid
->u
.ssl3
.sessionIDLength
== 0) {
345 rv
= PK11_GenerateRandom(sid
->u
.ssl3
.sessionID
,
346 SSL3_SESSIONID_BYTES
);
347 if (rv
!= SECSuccess
)
349 sid
->u
.ssl3
.sessionIDLength
= SSL3_SESSIONID_BYTES
;
351 expirationPeriod
= ssl3_sid_timeout
;
352 PRINT_BUF(8, (0, "sessionID:",
353 sid
->u
.ssl3
.sessionID
, sid
->u
.ssl3
.sessionIDLength
));
355 PORT_Assert(sid
->creationTime
!= 0 && sid
->expirationTime
!= 0);
356 if (!sid
->creationTime
)
357 sid
->lastAccessTime
= sid
->creationTime
= ssl_Time();
358 if (!sid
->expirationTime
)
359 sid
->expirationTime
= sid
->creationTime
+ expirationPeriod
;
362 * Put sid into the cache. Bump reference count to indicate that
363 * cache is holding a reference. Uncache will reduce the cache
368 sid
->cached
= in_client_cache
;
375 * If sid "zap" is in the cache,
376 * removes sid from cache, and decrements reference count.
377 * Caller must hold cache lock.
380 UncacheSID(sslSessionID
*zap
)
382 sslSessionID
**sidp
= &cache
;
385 if (zap
->cached
!= in_client_cache
) {
389 SSL_TRC(8,("SSL: Uncache: zap=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
391 zap
, zap
->cached
, zap
->addr
.pr_s6_addr32
[0],
392 zap
->addr
.pr_s6_addr32
[1], zap
->addr
.pr_s6_addr32
[2],
393 zap
->addr
.pr_s6_addr32
[3], zap
->port
, zap
->creationTime
,
394 zap
->u
.ssl2
.cipherType
));
395 if (zap
->version
< SSL_LIBRARY_VERSION_3_0
) {
396 PRINT_BUF(8, (0, "sessionID:",
397 zap
->u
.ssl2
.sessionID
, sizeof(zap
->u
.ssl2
.sessionID
)));
398 PRINT_BUF(8, (0, "masterKey:",
399 zap
->u
.ssl2
.masterKey
.data
, zap
->u
.ssl2
.masterKey
.len
));
400 PRINT_BUF(8, (0, "cipherArg:",
401 zap
->u
.ssl2
.cipherArg
.data
, zap
->u
.ssl2
.cipherArg
.len
));
404 /* See if it's in the cache, if so nuke it */
405 while ((sid
= *sidp
) != 0) {
408 ** Bingo. Reduce reference count by one so that when
409 ** everyone is done with the sid we can free it up.
412 zap
->cached
= invalid_cache
;
413 ssl_FreeLockedSID(zap
);
420 /* If sid "zap" is in the cache,
421 * removes sid from cache, and decrements reference count.
422 * Although this function is static, it is called externally via
426 LockAndUncacheSID(sslSessionID
*zap
)
434 /* choose client or server cache functions for this sslsocket. */
436 ssl_ChooseSessionIDProcs(sslSecurityInfo
*sec
)
439 sec
->cache
= ssl_sid_cache
;
440 sec
->uncache
= ssl_sid_uncache
;
442 sec
->cache
= CacheSID
;
443 sec
->uncache
= LockAndUncacheSID
;
447 /* wipe out the entire client session cache. */
449 SSL_ClearSessionCache(void)
457 /* returns an unsigned int containing the number of seconds in PR_Now() */
462 #if defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)
463 myTime
= time(NULL
); /* accurate until the year 2038. */
465 /* portable, but possibly slower */
470 LL_I2L(ll
, 1000000L);
471 LL_DIV(now
, now
, ll
);
472 LL_L2UI(myTime
, now
);
478 ssl3_SetSIDSessionTicket(sslSessionID
*sid
, NewSessionTicket
*session_ticket
)
482 /* We need to lock the cache, as this sid might already be in the cache. */
485 /* A server might have sent us an empty ticket, which has the
486 * effect of clearing the previously known ticket.
488 if (sid
->u
.ssl3
.sessionTicket
.ticket
.data
)
489 SECITEM_FreeItem(&sid
->u
.ssl3
.sessionTicket
.ticket
, PR_FALSE
);
490 if (session_ticket
->ticket
.len
> 0) {
491 rv
= SECITEM_CopyItem(NULL
, &sid
->u
.ssl3
.sessionTicket
.ticket
,
492 &session_ticket
->ticket
);
493 if (rv
!= SECSuccess
) {
498 sid
->u
.ssl3
.sessionTicket
.ticket
.data
= NULL
;
499 sid
->u
.ssl3
.sessionTicket
.ticket
.len
= 0;
501 sid
->u
.ssl3
.sessionTicket
.received_timestamp
=
502 session_ticket
->received_timestamp
;
503 sid
->u
.ssl3
.sessionTicket
.ticket_lifetime_hint
=
504 session_ticket
->ticket_lifetime_hint
;