Windows should animate when they are about to get docked at screen edges.
[chromium-blink-merge.git] / net / third_party / nss / ssl / sslnonce.c
blob5d8a95407aa09711660be45711464b26769f7207
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 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 SECStatus
39 ssl_InitClientSessionCacheLock(void)
41 cacheLock = PZ_NewLock(nssILockCache);
42 return cacheLock ? SECSuccess : SECFailure;
45 static SECStatus
46 ssl_FreeClientSessionCacheLock(void)
48 if (cacheLock) {
49 PZ_DestroyLock(cacheLock);
50 cacheLock = NULL;
51 return SECSuccess;
53 PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
54 return SECFailure;
57 static PRBool LocksInitializedEarly = PR_FALSE;
59 static SECStatus
60 FreeSessionCacheLocks()
62 SECStatus rv1, rv2;
63 rv1 = ssl_FreeSymWrapKeysLock();
64 rv2 = ssl_FreeClientSessionCacheLock();
65 if ( (SECSuccess == rv1) && (SECSuccess == rv2) ) {
66 return SECSuccess;
68 return SECFailure;
71 static SECStatus
72 InitSessionCacheLocks(void)
74 SECStatus rv1, rv2;
75 PRErrorCode rc;
76 rv1 = ssl_InitSymWrapKeysLock();
77 rv2 = ssl_InitClientSessionCacheLock();
78 if ( (SECSuccess == rv1) && (SECSuccess == rv2) ) {
79 return SECSuccess;
81 rc = PORT_GetError();
82 FreeSessionCacheLocks();
83 PORT_SetError(rc);
84 return SECFailure;
87 /* free the session cache locks if they were initialized early */
88 SECStatus
89 ssl_FreeSessionCacheLocks()
91 PORT_Assert(PR_TRUE == LocksInitializedEarly);
92 if (!LocksInitializedEarly) {
93 PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
94 return SECFailure;
96 FreeSessionCacheLocks();
97 LocksInitializedEarly = PR_FALSE;
98 return SECSuccess;
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);
109 return SECFailure;
111 FreeSessionCacheLocks();
112 memset(&lockOnce, 0, sizeof(lockOnce));
113 return SECSuccess;
116 static PRStatus initSessionCacheLocksLazily(void)
118 SECStatus rv = InitSessionCacheLocks();
119 if (SECSuccess != rv) {
120 return PR_FAILURE;
122 rv = NSS_RegisterShutdown(ssl_ShutdownLocks, NULL);
123 PORT_Assert(SECSuccess == rv);
124 if (SECSuccess != rv) {
125 return PR_FAILURE;
127 return PR_SUCCESS;
130 /* lazyInit means that the call is not happening during a 1-time
131 * initialization function, but rather during dynamic, lazy initialization
133 SECStatus
134 ssl_InitSessionCacheLocks(PRBool lazyInit)
136 if (LocksInitializedEarly) {
137 return SECSuccess;
140 if (lazyInit) {
141 return (PR_SUCCESS ==
142 PR_CallOnce(&lockOnce, initSessionCacheLocksLazily)) ?
143 SECSuccess : SECFailure;
146 if (SECSuccess == InitSessionCacheLocks()) {
147 LocksInitializedEarly = PR_TRUE;
148 return SECSuccess;
151 return SECFailure;
154 static void
155 lock_cache(void)
157 ssl_InitSessionCacheLocks(PR_TRUE);
158 PZ_Lock(cacheLock);
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.
164 static void
165 ssl_DestroySID(sslSessionID *sid)
167 int i;
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.
214 static void
215 ssl_FreeLockedSID(sslSessionID *sid)
217 PORT_Assert(sid->references >= 1);
218 if (--sid->references == 0) {
219 ssl_DestroySID(sid);
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.
229 void
230 ssl_FreeSID(sslSessionID *sid)
232 LOCK_CACHE;
233 ssl_FreeLockedSID(sid);
234 UNLOCK_CACHE;
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.
245 sslSessionID *
246 ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, const char *peerID,
247 const char * urlSvrName)
249 sslSessionID **sidp;
250 sslSessionID * sid;
251 PRUint32 now;
253 if (!urlSvrName)
254 return NULL;
255 now = ssl_Time();
256 LOCK_CACHE;
257 sidp = &cache;
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)
275 ssl_DestroySID(sid);
276 else
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)) &&
285 /* is cacheable */
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))) )
294 /* Hit */
295 sid->lastAccessTime = now;
296 sid->references++;
297 break;
298 } else {
299 sidp = &sid->next;
302 UNLOCK_CACHE;
303 return sid;
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().
310 static void
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 "
315 "time=%x cached=%d",
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,
319 sid->cached));
321 if (sid->cached == in_client_cache)
322 return;
324 if (!sid->urlSvrName) {
325 /* don't cache this SID because it can never be matched */
326 return;
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));
338 } else {
339 if (sid->u.ssl3.sessionIDLength == 0 &&
340 sid->u.ssl3.sessionTicket.ticket.data == NULL)
341 return;
342 /* Client generates the SessionID if this was a stateless resume. */
343 if (sid->u.ssl3.sessionIDLength == 0) {
344 SECStatus rv;
345 rv = PK11_GenerateRandom(sid->u.ssl3.sessionID,
346 SSL3_SESSIONID_BYTES);
347 if (rv != SECSuccess)
348 return;
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
364 * reference.
366 LOCK_CACHE;
367 sid->references++;
368 sid->cached = in_client_cache;
369 sid->next = cache;
370 cache = sid;
371 UNLOCK_CACHE;
375 * If sid "zap" is in the cache,
376 * removes sid from cache, and decrements reference count.
377 * Caller must hold cache lock.
379 static void
380 UncacheSID(sslSessionID *zap)
382 sslSessionID **sidp = &cache;
383 sslSessionID *sid;
385 if (zap->cached != in_client_cache) {
386 return;
389 SSL_TRC(8,("SSL: Uncache: zap=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
390 "time=%x cipher=%d",
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) {
406 if (sid == zap) {
408 ** Bingo. Reduce reference count by one so that when
409 ** everyone is done with the sid we can free it up.
411 *sidp = zap->next;
412 zap->cached = invalid_cache;
413 ssl_FreeLockedSID(zap);
414 return;
416 sidp = &sid->next;
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
423 * ss->sec.uncache().
425 static void
426 LockAndUncacheSID(sslSessionID *zap)
428 LOCK_CACHE;
429 UncacheSID(zap);
430 UNLOCK_CACHE;
434 /* choose client or server cache functions for this sslsocket. */
435 void
436 ssl_ChooseSessionIDProcs(sslSecurityInfo *sec)
438 if (sec->isServer) {
439 sec->cache = ssl_sid_cache;
440 sec->uncache = ssl_sid_uncache;
441 } else {
442 sec->cache = CacheSID;
443 sec->uncache = LockAndUncacheSID;
447 /* wipe out the entire client session cache. */
448 void
449 SSL_ClearSessionCache(void)
451 LOCK_CACHE;
452 while(cache != NULL)
453 UncacheSID(cache);
454 UNLOCK_CACHE;
457 /* returns an unsigned int containing the number of seconds in PR_Now() */
458 PRUint32
459 ssl_Time(void)
461 PRUint32 myTime;
462 #if defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)
463 myTime = time(NULL); /* accurate until the year 2038. */
464 #else
465 /* portable, but possibly slower */
466 PRTime now;
467 PRInt64 ll;
469 now = PR_Now();
470 LL_I2L(ll, 1000000L);
471 LL_DIV(now, now, ll);
472 LL_L2UI(myTime, now);
473 #endif
474 return myTime;
477 SECStatus
478 ssl3_SetSIDSessionTicket(sslSessionID *sid, NewSessionTicket *session_ticket)
480 SECStatus rv;
482 /* We need to lock the cache, as this sid might already be in the cache. */
483 LOCK_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) {
494 UNLOCK_CACHE;
495 return rv;
497 } else {
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;
506 UNLOCK_CACHE;
507 return SECSuccess;