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));
119 if (sid->cached == in_client_cache)
120 return; /* it will get taken care of next time cache is traversed. */
122 if (sid->version < SSL_LIBRARY_VERSION_3_0) {
123 SECITEM_ZfreeItem(&sid->u.ssl2.masterKey, PR_FALSE);
124 SECITEM_ZfreeItem(&sid->u.ssl2.cipherArg, PR_FALSE);
126 if (sid->peerID != NULL)
127 PORT_Free((void *)sid->peerID); /* CONST */
129 if (sid->urlSvrName != NULL)
130 PORT_Free((void *)sid->urlSvrName); /* CONST */
132 if ( sid->peerCert ) {
133 CERT_DestroyCertificate(sid->peerCert);
135 for (i = 0; i < MAX_PEER_CERT_CHAIN_SIZE && sid->peerCertChain[i]; i++) {
136 CERT_DestroyCertificate(sid->peerCertChain[i]);
138 if (sid->peerCertStatus.items) {
139 SECITEM_FreeArray(&sid->peerCertStatus, PR_FALSE);
142 if ( sid->localCert ) {
143 CERT_DestroyCertificate(sid->localCert);
145 if (sid->u.ssl3.sessionTicket.ticket.data) {
146 SECITEM_FreeItem(&sid->u.ssl3.sessionTicket.ticket, PR_FALSE);
148 if (sid->u.ssl3.srvName.data) {
149 SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
152 PORT_ZFree(sid, sizeof(sslSessionID));
155 /* BEWARE: This function gets called for both client and server SIDs !!
156 * Decrement reference count, and
157 * free sid if ref count is zero, and sid is not in the cache.
158 * Does NOT remove from the cache first.
159 * If the sid is still in the cache, it is left there until next time
160 * the cache list is traversed.
163 ssl_FreeLockedSID(sslSessionID *sid)
165 PORT_Assert(sid->references >= 1);
166 if (--sid->references == 0) {
171 /* BEWARE: This function gets called for both client and server SIDs !!
172 * Decrement reference count, and
173 * free sid if ref count is zero, and sid is not in the cache.
174 * Does NOT remove from the cache first.
175 * These locks are necessary because the sid _might_ be in the cache list.
178 ssl_FreeSID(sslSessionID *sid)
181 ssl_FreeLockedSID(sid);
185 /************************************************************************/
188 ** Lookup sid entry in cache by Address, port, and peerID string.
189 ** If found, Increment reference count, and return pointer to caller.
190 ** If it has timed out or ref count is zero, remove from list and free it.
194 ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, const char *peerID,
195 const char * urlSvrName)
206 while ((sid = *sidp) != 0) {
207 PORT_Assert(sid->cached == in_client_cache);
208 PORT_Assert(sid->references >= 1);
210 SSL_TRC(8, ("SSL: Lookup1: sid=0x%x", sid));
212 if (sid->expirationTime < now || !sid->references) {
214 ** This session-id timed out, or was orphaned.
215 ** Don't even care who it belongs to, blow it out of our cache.
217 SSL_TRC(7, ("SSL: lookup1, throwing sid out, age=%d refs=%d",
218 now - sid->creationTime, sid->references));
220 *sidp = sid->next; /* delink it from the list. */
221 sid->cached = invalid_cache; /* mark not on list. */
222 if (!sid->references)
225 ssl_FreeLockedSID(sid); /* drop ref count, free. */
227 } else if (!memcmp(&sid->addr, addr, sizeof(PRIPv6Addr)) && /* server IP addr matches */
228 (sid->port == port) && /* server port matches */
229 /* proxy (peerID) matches */
230 (((peerID == NULL) && (sid->peerID == NULL)) ||
231 ((peerID != NULL) && (sid->peerID != NULL) &&
232 PORT_Strcmp(sid->peerID, peerID) == 0)) &&
234 (sid->version < SSL_LIBRARY_VERSION_3_0 ||
235 sid->u.ssl3.keys.resumable) &&
236 /* server hostname matches. */
237 (sid->urlSvrName != NULL) &&
238 ((0 == PORT_Strcmp(urlSvrName, sid->urlSvrName)) ||
239 ((sid->peerCert != NULL) && (SECSuccess ==
240 CERT_VerifyCertName(sid->peerCert, urlSvrName))) )
243 sid->lastAccessTime = now;
255 ** Add an sid to the cache or return a previously cached entry to the cache.
256 ** Although this is static, it is called via ss->sec.cache().
259 CacheSID(sslSessionID *sid)
261 PRUint32 expirationPeriod;
262 SSL_TRC(8, ("SSL: Cache: sid=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
264 sid, sid->cached, sid->addr.pr_s6_addr32[0],
265 sid->addr.pr_s6_addr32[1], sid->addr.pr_s6_addr32[2],
266 sid->addr.pr_s6_addr32[3], sid->port, sid->creationTime,
269 if (sid->cached == in_client_cache)
272 if (!sid->urlSvrName) {
273 /* don't cache this SID because it can never be matched */
277 /* XXX should be different trace for version 2 vs. version 3 */
278 if (sid->version < SSL_LIBRARY_VERSION_3_0) {
279 expirationPeriod = ssl_sid_timeout;
280 PRINT_BUF(8, (0, "sessionID:",
281 sid->u.ssl2.sessionID, sizeof(sid->u.ssl2.sessionID)));
282 PRINT_BUF(8, (0, "masterKey:",
283 sid->u.ssl2.masterKey.data, sid->u.ssl2.masterKey.len));
284 PRINT_BUF(8, (0, "cipherArg:",
285 sid->u.ssl2.cipherArg.data, sid->u.ssl2.cipherArg.len));
287 if (sid->u.ssl3.sessionIDLength == 0 &&
288 sid->u.ssl3.sessionTicket.ticket.data == NULL)
290 /* Client generates the SessionID if this was a stateless resume. */
291 if (sid->u.ssl3.sessionIDLength == 0) {
293 rv = PK11_GenerateRandom(sid->u.ssl3.sessionID,
294 SSL3_SESSIONID_BYTES);
295 if (rv != SECSuccess)
297 sid->u.ssl3.sessionIDLength = SSL3_SESSIONID_BYTES;
299 expirationPeriod = ssl3_sid_timeout;
300 PRINT_BUF(8, (0, "sessionID:",
301 sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength));
303 PORT_Assert(sid->creationTime != 0 && sid->expirationTime != 0);
304 if (!sid->creationTime)
305 sid->lastAccessTime = sid->creationTime = ssl_Time();
306 if (!sid->expirationTime)
307 sid->expirationTime = sid->creationTime + expirationPeriod;
310 * Put sid into the cache. Bump reference count to indicate that
311 * cache is holding a reference. Uncache will reduce the cache
316 sid->cached = in_client_cache;
323 * If sid "zap" is in the cache,
324 * removes sid from cache, and decrements reference count.
325 * Caller must hold cache lock.
328 UncacheSID(sslSessionID *zap)
330 sslSessionID **sidp = &cache;
333 if (zap->cached != in_client_cache) {
337 SSL_TRC(8,("SSL: Uncache: zap=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
339 zap, zap->cached, zap->addr.pr_s6_addr32[0],
340 zap->addr.pr_s6_addr32[1], zap->addr.pr_s6_addr32[2],
341 zap->addr.pr_s6_addr32[3], zap->port, zap->creationTime,
342 zap->u.ssl2.cipherType));
343 if (zap->version < SSL_LIBRARY_VERSION_3_0) {
344 PRINT_BUF(8, (0, "sessionID:",
345 zap->u.ssl2.sessionID, sizeof(zap->u.ssl2.sessionID)));
346 PRINT_BUF(8, (0, "masterKey:",
347 zap->u.ssl2.masterKey.data, zap->u.ssl2.masterKey.len));
348 PRINT_BUF(8, (0, "cipherArg:",
349 zap->u.ssl2.cipherArg.data, zap->u.ssl2.cipherArg.len));
352 /* See if it's in the cache, if so nuke it */
353 while ((sid = *sidp) != 0) {
356 ** Bingo. Reduce reference count by one so that when
357 ** everyone is done with the sid we can free it up.
360 zap->cached = invalid_cache;
361 ssl_FreeLockedSID(zap);
368 /* If sid "zap" is in the cache,
369 * removes sid from cache, and decrements reference count.
370 * Although this function is static, it is called externally via
374 LockAndUncacheSID(sslSessionID *zap)
382 /* choose client or server cache functions for this sslsocket. */
384 ssl_ChooseSessionIDProcs(sslSecurityInfo *sec)
387 sec->cache = ssl_sid_cache;
388 sec->uncache = ssl_sid_uncache;
390 sec->cache = CacheSID;
391 sec->uncache = LockAndUncacheSID;
395 /* wipe out the entire client session cache. */
397 SSL_ClearSessionCache(void)
405 /* returns an unsigned int containing the number of seconds in PR_Now() */
410 #if defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)
411 myTime = time(NULL); /* accurate until the year 2038. */
413 /* portable, but possibly slower */
418 LL_I2L(ll, 1000000L);
419 LL_DIV(now, now, ll);
420 LL_L2UI(myTime, now);
426 ssl3_SetSIDSessionTicket(sslSessionID *sid, NewSessionTicket *session_ticket)
430 /* We need to lock the cache, as this sid might already be in the cache. */
433 /* A server might have sent us an empty ticket, which has the
434 * effect of clearing the previously known ticket.
436 if (sid->u.ssl3.sessionTicket.ticket.data)
437 SECITEM_FreeItem(&sid->u.ssl3.sessionTicket.ticket, PR_FALSE);
438 if (session_ticket->ticket.len > 0) {
439 rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.sessionTicket.ticket,
440 &session_ticket->ticket);
441 if (rv != SECSuccess) {
446 sid->u.ssl3.sessionTicket.ticket.data = NULL;
447 sid->u.ssl3.sessionTicket.ticket.len = 0;
449 sid->u.ssl3.sessionTicket.received_timestamp =
450 session_ticket->received_timestamp;
451 sid->u.ssl3.sessionTicket.ticket_lifetime_hint =
452 session_ticket->ticket_lifetime_hint;