Add explicit |forceOnlineSignin| to user pod status
[chromium-blink-merge.git] / net / third_party / nss / ssl / sslnonce.c
blob105ce9f1fc5b9f7979510449bd0017619b5d241f
1 /*
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/. */
8 #include "cert.h"
9 #include "pk11pub.h"
10 #include "secitem.h"
11 #include "ssl.h"
12 #include "nss.h"
14 #include "sslimpl.h"
15 #include "sslproto.h"
16 #include "nssilock.h"
17 #if defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)
18 #include <time.h>
19 #endif
21 PRUint32 ssl_sid_timeout = 100;
22 PRUint32 ssl3_sid_timeout = 86400L; /* 24 hours */
24 sslSessionID *cache = NULL;
25 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
42 * state. */
43 static SECStatus
44 FreeSessionCacheLocks(void* appData, void* nssData)
46 static const PRCallOnceType pristineCallOnce;
47 SECStatus rv;
49 if (!cacheLock) {
50 PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
51 return SECFailure;
54 PZ_DestroyLock(cacheLock);
55 cacheLock = NULL;
57 rv = ssl_FreeSymWrapKeysLock();
58 if (rv != SECSuccess) {
59 return rv;
62 lockOnce = pristineCallOnce;
63 return SECSuccess;
66 /* InitSessionCacheLocks is called, protected by lockOnce, to create the
67 * session cache locks. */
68 static PRStatus
69 InitSessionCacheLocks(void)
71 SECStatus rv;
73 cacheLock = PZ_NewLock(nssILockCache);
74 if (cacheLock == NULL) {
75 return PR_FAILURE;
77 rv = ssl_InitSymWrapKeysLock();
78 if (rv != SECSuccess) {
79 PRErrorCode error = PORT_GetError();
80 PZ_DestroyLock(cacheLock);
81 cacheLock = NULL;
82 PORT_SetError(error);
83 return PR_FAILURE;
86 rv = NSS_RegisterShutdown(FreeSessionCacheLocks, NULL);
87 PORT_Assert(SECSuccess == rv);
88 if (SECSuccess != rv) {
89 return PR_FAILURE;
91 return PR_SUCCESS;
94 SECStatus
95 ssl_InitSessionCacheLocks(void)
97 return (PR_SUCCESS ==
98 PR_CallOnce(&lockOnce, InitSessionCacheLocks)) ?
99 SECSuccess : SECFailure;
102 static void
103 lock_cache(void)
105 ssl_InitSessionCacheLocks();
106 PZ_Lock(cacheLock);
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.
112 static void
113 ssl_DestroySID(sslSessionID *sid)
115 int i;
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);
125 } else {
126 if (sid->u.ssl3.locked.sessionTicket.ticket.data) {
127 SECITEM_FreeItem(&sid->u.ssl3.locked.sessionTicket.ticket,
128 PR_FALSE);
130 if (sid->u.ssl3.srvName.data) {
131 SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
133 if (sid->u.ssl3.originalHandshakeHash.data) {
134 SECITEM_FreeItem(&sid->u.ssl3.originalHandshakeHash, PR_FALSE);
136 if (sid->u.ssl3.signedCertTimestamps.data) {
137 SECITEM_FreeItem(&sid->u.ssl3.signedCertTimestamps, PR_FALSE);
140 if (sid->u.ssl3.lock) {
141 NSSRWLock_Destroy(sid->u.ssl3.lock);
145 if (sid->peerID != NULL)
146 PORT_Free((void *)sid->peerID); /* CONST */
148 if (sid->urlSvrName != NULL)
149 PORT_Free((void *)sid->urlSvrName); /* CONST */
151 if ( sid->peerCert ) {
152 CERT_DestroyCertificate(sid->peerCert);
154 for (i = 0; i < MAX_PEER_CERT_CHAIN_SIZE && sid->peerCertChain[i]; i++) {
155 CERT_DestroyCertificate(sid->peerCertChain[i]);
157 if (sid->peerCertStatus.items) {
158 SECITEM_FreeArray(&sid->peerCertStatus, PR_FALSE);
161 if ( sid->localCert ) {
162 CERT_DestroyCertificate(sid->localCert);
165 PORT_ZFree(sid, sizeof(sslSessionID));
168 /* BEWARE: This function gets called for both client and server SIDs !!
169 * Decrement reference count, and
170 * free sid if ref count is zero, and sid is not in the cache.
171 * Does NOT remove from the cache first.
172 * If the sid is still in the cache, it is left there until next time
173 * the cache list is traversed.
175 static void
176 ssl_FreeLockedSID(sslSessionID *sid)
178 PORT_Assert(sid->references >= 1);
179 if (--sid->references == 0) {
180 ssl_DestroySID(sid);
184 /* BEWARE: This function gets called for both client and server SIDs !!
185 * Decrement reference count, and
186 * free sid if ref count is zero, and sid is not in the cache.
187 * Does NOT remove from the cache first.
188 * These locks are necessary because the sid _might_ be in the cache list.
190 void
191 ssl_FreeSID(sslSessionID *sid)
193 LOCK_CACHE;
194 ssl_FreeLockedSID(sid);
195 UNLOCK_CACHE;
198 /************************************************************************/
201 ** Lookup sid entry in cache by Address, port, and peerID string.
202 ** If found, Increment reference count, and return pointer to caller.
203 ** If it has timed out or ref count is zero, remove from list and free it.
206 sslSessionID *
207 ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, const char *peerID,
208 const char * urlSvrName)
210 sslSessionID **sidp;
211 sslSessionID * sid;
212 PRUint32 now;
214 if (!urlSvrName)
215 return NULL;
216 now = ssl_Time();
217 LOCK_CACHE;
218 sidp = &cache;
219 while ((sid = *sidp) != 0) {
220 PORT_Assert(sid->cached == in_client_cache);
221 PORT_Assert(sid->references >= 1);
223 SSL_TRC(8, ("SSL: Lookup1: sid=0x%x", sid));
225 if (sid->expirationTime < now || !sid->references) {
227 ** This session-id timed out, or was orphaned.
228 ** Don't even care who it belongs to, blow it out of our cache.
230 SSL_TRC(7, ("SSL: lookup1, throwing sid out, age=%d refs=%d",
231 now - sid->creationTime, sid->references));
233 *sidp = sid->next; /* delink it from the list. */
234 sid->cached = invalid_cache; /* mark not on list. */
235 if (!sid->references)
236 ssl_DestroySID(sid);
237 else
238 ssl_FreeLockedSID(sid); /* drop ref count, free. */
240 } else if (!memcmp(&sid->addr, addr, sizeof(PRIPv6Addr)) && /* server IP addr matches */
241 (sid->port == port) && /* server port matches */
242 /* proxy (peerID) matches */
243 (((peerID == NULL) && (sid->peerID == NULL)) ||
244 ((peerID != NULL) && (sid->peerID != NULL) &&
245 PORT_Strcmp(sid->peerID, peerID) == 0)) &&
246 /* is cacheable */
247 (sid->version < SSL_LIBRARY_VERSION_3_0 ||
248 sid->u.ssl3.keys.resumable) &&
249 /* server hostname matches. */
250 (sid->urlSvrName != NULL) &&
251 ((0 == PORT_Strcmp(urlSvrName, sid->urlSvrName)) ||
252 ((sid->peerCert != NULL) && (SECSuccess ==
253 CERT_VerifyCertName(sid->peerCert, urlSvrName))) )
255 /* Hit */
256 sid->lastAccessTime = now;
257 sid->references++;
258 break;
259 } else {
260 sidp = &sid->next;
263 UNLOCK_CACHE;
264 return sid;
268 ** Add an sid to the cache or return a previously cached entry to the cache.
269 ** Although this is static, it is called via ss->sec.cache().
271 static void
272 CacheSID(sslSessionID *sid)
274 PRUint32 expirationPeriod;
276 PORT_Assert(sid->cached == never_cached);
278 SSL_TRC(8, ("SSL: Cache: sid=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
279 "time=%x cached=%d",
280 sid, sid->cached, sid->addr.pr_s6_addr32[0],
281 sid->addr.pr_s6_addr32[1], sid->addr.pr_s6_addr32[2],
282 sid->addr.pr_s6_addr32[3], sid->port, sid->creationTime,
283 sid->cached));
285 if (!sid->urlSvrName) {
286 /* don't cache this SID because it can never be matched */
287 return;
290 /* XXX should be different trace for version 2 vs. version 3 */
291 if (sid->version < SSL_LIBRARY_VERSION_3_0) {
292 expirationPeriod = ssl_sid_timeout;
293 PRINT_BUF(8, (0, "sessionID:",
294 sid->u.ssl2.sessionID, sizeof(sid->u.ssl2.sessionID)));
295 PRINT_BUF(8, (0, "masterKey:",
296 sid->u.ssl2.masterKey.data, sid->u.ssl2.masterKey.len));
297 PRINT_BUF(8, (0, "cipherArg:",
298 sid->u.ssl2.cipherArg.data, sid->u.ssl2.cipherArg.len));
299 } else {
300 if (sid->u.ssl3.sessionIDLength == 0 &&
301 sid->u.ssl3.locked.sessionTicket.ticket.data == NULL)
302 return;
304 /* Client generates the SessionID if this was a stateless resume. */
305 if (sid->u.ssl3.sessionIDLength == 0) {
306 SECStatus rv;
307 rv = PK11_GenerateRandom(sid->u.ssl3.sessionID,
308 SSL3_SESSIONID_BYTES);
309 if (rv != SECSuccess)
310 return;
311 sid->u.ssl3.sessionIDLength = SSL3_SESSIONID_BYTES;
313 expirationPeriod = ssl3_sid_timeout;
314 PRINT_BUF(8, (0, "sessionID:",
315 sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength));
317 sid->u.ssl3.lock = NSSRWLock_New(NSS_RWLOCK_RANK_NONE, NULL);
318 if (!sid->u.ssl3.lock) {
319 return;
322 PORT_Assert(sid->creationTime != 0 && sid->expirationTime != 0);
323 if (!sid->creationTime)
324 sid->lastAccessTime = sid->creationTime = ssl_Time();
325 if (!sid->expirationTime)
326 sid->expirationTime = sid->creationTime + expirationPeriod;
329 * Put sid into the cache. Bump reference count to indicate that
330 * cache is holding a reference. Uncache will reduce the cache
331 * reference.
333 LOCK_CACHE;
334 sid->references++;
335 sid->cached = in_client_cache;
336 sid->next = cache;
337 cache = sid;
338 UNLOCK_CACHE;
342 * If sid "zap" is in the cache,
343 * removes sid from cache, and decrements reference count.
344 * Caller must hold cache lock.
346 static void
347 UncacheSID(sslSessionID *zap)
349 sslSessionID **sidp = &cache;
350 sslSessionID *sid;
352 if (zap->cached != in_client_cache) {
353 return;
356 SSL_TRC(8,("SSL: Uncache: zap=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
357 "time=%x cipher=%d",
358 zap, zap->cached, zap->addr.pr_s6_addr32[0],
359 zap->addr.pr_s6_addr32[1], zap->addr.pr_s6_addr32[2],
360 zap->addr.pr_s6_addr32[3], zap->port, zap->creationTime,
361 zap->u.ssl2.cipherType));
362 if (zap->version < SSL_LIBRARY_VERSION_3_0) {
363 PRINT_BUF(8, (0, "sessionID:",
364 zap->u.ssl2.sessionID, sizeof(zap->u.ssl2.sessionID)));
365 PRINT_BUF(8, (0, "masterKey:",
366 zap->u.ssl2.masterKey.data, zap->u.ssl2.masterKey.len));
367 PRINT_BUF(8, (0, "cipherArg:",
368 zap->u.ssl2.cipherArg.data, zap->u.ssl2.cipherArg.len));
371 /* See if it's in the cache, if so nuke it */
372 while ((sid = *sidp) != 0) {
373 if (sid == zap) {
375 ** Bingo. Reduce reference count by one so that when
376 ** everyone is done with the sid we can free it up.
378 *sidp = zap->next;
379 zap->cached = invalid_cache;
380 ssl_FreeLockedSID(zap);
381 return;
383 sidp = &sid->next;
387 /* If sid "zap" is in the cache,
388 * removes sid from cache, and decrements reference count.
389 * Although this function is static, it is called externally via
390 * ss->sec.uncache().
392 static void
393 LockAndUncacheSID(sslSessionID *zap)
395 LOCK_CACHE;
396 UncacheSID(zap);
397 UNLOCK_CACHE;
401 /* choose client or server cache functions for this sslsocket. */
402 void
403 ssl_ChooseSessionIDProcs(sslSecurityInfo *sec)
405 if (sec->isServer) {
406 sec->cache = ssl_sid_cache;
407 sec->uncache = ssl_sid_uncache;
408 } else {
409 sec->cache = CacheSID;
410 sec->uncache = LockAndUncacheSID;
414 /* wipe out the entire client session cache. */
415 void
416 SSL_ClearSessionCache(void)
418 LOCK_CACHE;
419 while(cache != NULL)
420 UncacheSID(cache);
421 UNLOCK_CACHE;
424 /* returns an unsigned int containing the number of seconds in PR_Now() */
425 PRUint32
426 ssl_Time(void)
428 PRUint32 myTime;
429 #if defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)
430 myTime = time(NULL); /* accurate until the year 2038. */
431 #else
432 /* portable, but possibly slower */
433 PRTime now;
434 PRInt64 ll;
436 now = PR_Now();
437 LL_I2L(ll, 1000000L);
438 LL_DIV(now, now, ll);
439 LL_L2UI(myTime, now);
440 #endif
441 return myTime;
444 void
445 ssl3_SetSIDSessionTicket(sslSessionID *sid,
446 /*in/out*/ NewSessionTicket *newSessionTicket)
448 PORT_Assert(sid);
449 PORT_Assert(newSessionTicket);
451 /* if sid->u.ssl3.lock, we are updating an existing entry that is already
452 * cached or was once cached, so we need to acquire and release the write
453 * lock. Otherwise, this is a new session that isn't shared with anything
454 * yet, so no locking is needed.
456 if (sid->u.ssl3.lock) {
457 NSSRWLock_LockWrite(sid->u.ssl3.lock);
459 /* A server might have sent us an empty ticket, which has the
460 * effect of clearing the previously known ticket.
462 if (sid->u.ssl3.locked.sessionTicket.ticket.data) {
463 SECITEM_FreeItem(&sid->u.ssl3.locked.sessionTicket.ticket,
464 PR_FALSE);
468 PORT_Assert(!sid->u.ssl3.locked.sessionTicket.ticket.data);
470 /* Do a shallow copy, moving the ticket data. */
471 sid->u.ssl3.locked.sessionTicket = *newSessionTicket;
472 newSessionTicket->ticket.data = NULL;
473 newSessionTicket->ticket.len = 0;
475 if (sid->u.ssl3.lock) {
476 NSSRWLock_UnlockWrite(sid->u.ssl3.lock);