Revert "Merged all Chromoting Host code into remoting_core.dll (Windows)."
[chromium-blink-merge.git] / net / third_party / nss / ssl / sslnonce.c
blob16af5f4316fbc2f057870fc004845b48661a496b
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/. */
7 /* $Id: sslnonce.c,v 1.27 2012/04/25 14:50:12 gerv%gerv.net Exp $ */
9 #include "cert.h"
10 #include "pk11pub.h"
11 #include "secitem.h"
12 #include "ssl.h"
13 #include "nss.h"
15 #include "sslimpl.h"
16 #include "sslproto.h"
17 #include "nssilock.h"
18 #if (defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)) && !defined(_WIN32_WCE)
19 #include <time.h>
20 #endif
22 PRUint32 ssl_sid_timeout = 100;
23 PRUint32 ssl3_sid_timeout = 86400L; /* 24 hours */
25 static sslSessionID *cache = NULL;
26 static PZLock * cacheLock = NULL;
28 /* sids can be in one of 4 states:
30 * never_cached, created, but not yet put into cache.
31 * in_client_cache, in the client cache's linked list.
32 * in_server_cache, entry came from the server's cache file.
33 * invalid_cache has been removed from the cache.
36 #define LOCK_CACHE lock_cache()
37 #define UNLOCK_CACHE PZ_Unlock(cacheLock)
39 static SECStatus
40 ssl_InitClientSessionCacheLock(void)
42 cacheLock = PZ_NewLock(nssILockCache);
43 return cacheLock ? SECSuccess : SECFailure;
46 static SECStatus
47 ssl_FreeClientSessionCacheLock(void)
49 if (cacheLock) {
50 PZ_DestroyLock(cacheLock);
51 cacheLock = NULL;
52 return SECSuccess;
54 PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
55 return SECFailure;
58 static PRBool LocksInitializedEarly = PR_FALSE;
60 static SECStatus
61 FreeSessionCacheLocks()
63 SECStatus rv1, rv2;
64 rv1 = ssl_FreeSymWrapKeysLock();
65 rv2 = ssl_FreeClientSessionCacheLock();
66 if ( (SECSuccess == rv1) && (SECSuccess == rv2) ) {
67 return SECSuccess;
69 return SECFailure;
72 static SECStatus
73 InitSessionCacheLocks(void)
75 SECStatus rv1, rv2;
76 PRErrorCode rc;
77 rv1 = ssl_InitSymWrapKeysLock();
78 rv2 = ssl_InitClientSessionCacheLock();
79 if ( (SECSuccess == rv1) && (SECSuccess == rv2) ) {
80 return SECSuccess;
82 rc = PORT_GetError();
83 FreeSessionCacheLocks();
84 PORT_SetError(rc);
85 return SECFailure;
88 /* free the session cache locks if they were initialized early */
89 SECStatus
90 ssl_FreeSessionCacheLocks()
92 PORT_Assert(PR_TRUE == LocksInitializedEarly);
93 if (!LocksInitializedEarly) {
94 PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
95 return SECFailure;
97 FreeSessionCacheLocks();
98 LocksInitializedEarly = PR_FALSE;
99 return SECSuccess;
102 static PRCallOnceType lockOnce;
104 /* free the session cache locks if they were initialized lazily */
105 static SECStatus ssl_ShutdownLocks(void* appData, void* nssData)
107 PORT_Assert(PR_FALSE == LocksInitializedEarly);
108 if (LocksInitializedEarly) {
109 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
110 return SECFailure;
112 FreeSessionCacheLocks();
113 memset(&lockOnce, 0, sizeof(lockOnce));
114 return SECSuccess;
117 static PRStatus initSessionCacheLocksLazily(void)
119 SECStatus rv = InitSessionCacheLocks();
120 if (SECSuccess != rv) {
121 return PR_FAILURE;
123 rv = NSS_RegisterShutdown(ssl_ShutdownLocks, NULL);
124 PORT_Assert(SECSuccess == rv);
125 if (SECSuccess != rv) {
126 return PR_FAILURE;
128 return PR_SUCCESS;
131 /* lazyInit means that the call is not happening during a 1-time
132 * initialization function, but rather during dynamic, lazy initialization
134 SECStatus
135 ssl_InitSessionCacheLocks(PRBool lazyInit)
137 if (LocksInitializedEarly) {
138 return SECSuccess;
141 if (lazyInit) {
142 return (PR_SUCCESS ==
143 PR_CallOnce(&lockOnce, initSessionCacheLocksLazily)) ?
144 SECSuccess : SECFailure;
147 if (SECSuccess == InitSessionCacheLocks()) {
148 LocksInitializedEarly = PR_TRUE;
149 return SECSuccess;
152 return SECFailure;
155 static void
156 lock_cache(void)
158 ssl_InitSessionCacheLocks(PR_TRUE);
159 PZ_Lock(cacheLock);
162 /* BEWARE: This function gets called for both client and server SIDs !!
163 * If the unreferenced sid is not in the cache, Free sid and its contents.
165 static void
166 ssl_DestroySID(sslSessionID *sid)
168 int i;
169 SSL_TRC(8, ("SSL: destroy sid: sid=0x%x cached=%d", sid, sid->cached));
170 PORT_Assert((sid->references == 0));
172 if (sid->cached == in_client_cache)
173 return; /* it will get taken care of next time cache is traversed. */
175 if (sid->version < SSL_LIBRARY_VERSION_3_0) {
176 SECITEM_ZfreeItem(&sid->u.ssl2.masterKey, PR_FALSE);
177 SECITEM_ZfreeItem(&sid->u.ssl2.cipherArg, PR_FALSE);
179 if (sid->peerID != NULL)
180 PORT_Free((void *)sid->peerID); /* CONST */
182 if (sid->urlSvrName != NULL)
183 PORT_Free((void *)sid->urlSvrName); /* CONST */
185 if ( sid->peerCert ) {
186 CERT_DestroyCertificate(sid->peerCert);
188 for (i = 0; i < MAX_PEER_CERT_CHAIN_SIZE && sid->peerCertChain[i]; i++) {
189 CERT_DestroyCertificate(sid->peerCertChain[i]);
191 if ( sid->localCert ) {
192 CERT_DestroyCertificate(sid->localCert);
194 if (sid->u.ssl3.sessionTicket.ticket.data) {
195 SECITEM_FreeItem(&sid->u.ssl3.sessionTicket.ticket, PR_FALSE);
197 if (sid->u.ssl3.srvName.data) {
198 SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
201 PORT_ZFree(sid, sizeof(sslSessionID));
204 /* BEWARE: This function gets called for both client and server SIDs !!
205 * Decrement reference count, and
206 * free sid if ref count is zero, and sid is not in the cache.
207 * Does NOT remove from the cache first.
208 * If the sid is still in the cache, it is left there until next time
209 * the cache list is traversed.
211 static void
212 ssl_FreeLockedSID(sslSessionID *sid)
214 PORT_Assert(sid->references >= 1);
215 if (--sid->references == 0) {
216 ssl_DestroySID(sid);
220 /* BEWARE: This function gets called for both client and server SIDs !!
221 * Decrement reference count, and
222 * free sid if ref count is zero, and sid is not in the cache.
223 * Does NOT remove from the cache first.
224 * These locks are necessary because the sid _might_ be in the cache list.
226 void
227 ssl_FreeSID(sslSessionID *sid)
229 LOCK_CACHE;
230 ssl_FreeLockedSID(sid);
231 UNLOCK_CACHE;
234 /************************************************************************/
237 ** Lookup sid entry in cache by Address, port, and peerID string.
238 ** If found, Increment reference count, and return pointer to caller.
239 ** If it has timed out or ref count is zero, remove from list and free it.
242 sslSessionID *
243 ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, const char *peerID,
244 const char * urlSvrName)
246 sslSessionID **sidp;
247 sslSessionID * sid;
248 PRUint32 now;
250 if (!urlSvrName)
251 return NULL;
252 now = ssl_Time();
253 LOCK_CACHE;
254 sidp = &cache;
255 while ((sid = *sidp) != 0) {
256 PORT_Assert(sid->cached == in_client_cache);
257 PORT_Assert(sid->references >= 1);
259 SSL_TRC(8, ("SSL: Lookup1: sid=0x%x", sid));
261 if (sid->expirationTime < now || !sid->references) {
263 ** This session-id timed out, or was orphaned.
264 ** Don't even care who it belongs to, blow it out of our cache.
266 SSL_TRC(7, ("SSL: lookup1, throwing sid out, age=%d refs=%d",
267 now - sid->creationTime, sid->references));
269 *sidp = sid->next; /* delink it from the list. */
270 sid->cached = invalid_cache; /* mark not on list. */
271 if (!sid->references)
272 ssl_DestroySID(sid);
273 else
274 ssl_FreeLockedSID(sid); /* drop ref count, free. */
276 } else if (!memcmp(&sid->addr, addr, sizeof(PRIPv6Addr)) && /* server IP addr matches */
277 (sid->port == port) && /* server port matches */
278 /* proxy (peerID) matches */
279 (((peerID == NULL) && (sid->peerID == NULL)) ||
280 ((peerID != NULL) && (sid->peerID != NULL) &&
281 PORT_Strcmp(sid->peerID, peerID) == 0)) &&
282 /* is cacheable */
283 (sid->version < SSL_LIBRARY_VERSION_3_0 ||
284 sid->u.ssl3.keys.resumable) &&
285 /* server hostname matches. */
286 (sid->urlSvrName != NULL) &&
287 ((0 == PORT_Strcmp(urlSvrName, sid->urlSvrName)) ||
288 ((sid->peerCert != NULL) && (SECSuccess ==
289 CERT_VerifyCertName(sid->peerCert, urlSvrName))) )
291 /* Hit */
292 sid->lastAccessTime = now;
293 sid->references++;
294 break;
295 } else {
296 sidp = &sid->next;
299 UNLOCK_CACHE;
300 return sid;
304 ** Add an sid to the cache or return a previously cached entry to the cache.
305 ** Although this is static, it is called via ss->sec.cache().
307 static void
308 CacheSID(sslSessionID *sid)
310 PRUint32 expirationPeriod;
311 SSL_TRC(8, ("SSL: Cache: sid=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
312 "time=%x cached=%d",
313 sid, sid->cached, sid->addr.pr_s6_addr32[0],
314 sid->addr.pr_s6_addr32[1], sid->addr.pr_s6_addr32[2],
315 sid->addr.pr_s6_addr32[3], sid->port, sid->creationTime,
316 sid->cached));
318 if (sid->cached == in_client_cache)
319 return;
321 if (!sid->urlSvrName) {
322 /* don't cache this SID because it can never be matched */
323 return;
326 /* XXX should be different trace for version 2 vs. version 3 */
327 if (sid->version < SSL_LIBRARY_VERSION_3_0) {
328 expirationPeriod = ssl_sid_timeout;
329 PRINT_BUF(8, (0, "sessionID:",
330 sid->u.ssl2.sessionID, sizeof(sid->u.ssl2.sessionID)));
331 PRINT_BUF(8, (0, "masterKey:",
332 sid->u.ssl2.masterKey.data, sid->u.ssl2.masterKey.len));
333 PRINT_BUF(8, (0, "cipherArg:",
334 sid->u.ssl2.cipherArg.data, sid->u.ssl2.cipherArg.len));
335 } else {
336 if (sid->u.ssl3.sessionIDLength == 0 &&
337 sid->u.ssl3.sessionTicket.ticket.data == NULL)
338 return;
339 /* Client generates the SessionID if this was a stateless resume. */
340 if (sid->u.ssl3.sessionIDLength == 0) {
341 SECStatus rv;
342 rv = PK11_GenerateRandom(sid->u.ssl3.sessionID,
343 SSL3_SESSIONID_BYTES);
344 if (rv != SECSuccess)
345 return;
346 sid->u.ssl3.sessionIDLength = SSL3_SESSIONID_BYTES;
348 expirationPeriod = ssl3_sid_timeout;
349 PRINT_BUF(8, (0, "sessionID:",
350 sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength));
352 PORT_Assert(sid->creationTime != 0 && sid->expirationTime != 0);
353 if (!sid->creationTime)
354 sid->lastAccessTime = sid->creationTime = ssl_Time();
355 if (!sid->expirationTime)
356 sid->expirationTime = sid->creationTime + expirationPeriod;
359 * Put sid into the cache. Bump reference count to indicate that
360 * cache is holding a reference. Uncache will reduce the cache
361 * reference.
363 LOCK_CACHE;
364 sid->references++;
365 sid->cached = in_client_cache;
366 sid->next = cache;
367 cache = sid;
368 UNLOCK_CACHE;
372 * If sid "zap" is in the cache,
373 * removes sid from cache, and decrements reference count.
374 * Caller must hold cache lock.
376 static void
377 UncacheSID(sslSessionID *zap)
379 sslSessionID **sidp = &cache;
380 sslSessionID *sid;
382 if (zap->cached != in_client_cache) {
383 return;
386 SSL_TRC(8,("SSL: Uncache: zap=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
387 "time=%x cipher=%d",
388 zap, zap->cached, zap->addr.pr_s6_addr32[0],
389 zap->addr.pr_s6_addr32[1], zap->addr.pr_s6_addr32[2],
390 zap->addr.pr_s6_addr32[3], zap->port, zap->creationTime,
391 zap->u.ssl2.cipherType));
392 if (zap->version < SSL_LIBRARY_VERSION_3_0) {
393 PRINT_BUF(8, (0, "sessionID:",
394 zap->u.ssl2.sessionID, sizeof(zap->u.ssl2.sessionID)));
395 PRINT_BUF(8, (0, "masterKey:",
396 zap->u.ssl2.masterKey.data, zap->u.ssl2.masterKey.len));
397 PRINT_BUF(8, (0, "cipherArg:",
398 zap->u.ssl2.cipherArg.data, zap->u.ssl2.cipherArg.len));
401 /* See if it's in the cache, if so nuke it */
402 while ((sid = *sidp) != 0) {
403 if (sid == zap) {
405 ** Bingo. Reduce reference count by one so that when
406 ** everyone is done with the sid we can free it up.
408 *sidp = zap->next;
409 zap->cached = invalid_cache;
410 ssl_FreeLockedSID(zap);
411 return;
413 sidp = &sid->next;
417 /* If sid "zap" is in the cache,
418 * removes sid from cache, and decrements reference count.
419 * Although this function is static, it is called externally via
420 * ss->sec.uncache().
422 static void
423 LockAndUncacheSID(sslSessionID *zap)
425 LOCK_CACHE;
426 UncacheSID(zap);
427 UNLOCK_CACHE;
431 /* choose client or server cache functions for this sslsocket. */
432 void
433 ssl_ChooseSessionIDProcs(sslSecurityInfo *sec)
435 if (sec->isServer) {
436 sec->cache = ssl_sid_cache;
437 sec->uncache = ssl_sid_uncache;
438 } else {
439 sec->cache = CacheSID;
440 sec->uncache = LockAndUncacheSID;
444 /* wipe out the entire client session cache. */
445 void
446 SSL_ClearSessionCache(void)
448 LOCK_CACHE;
449 while(cache != NULL)
450 UncacheSID(cache);
451 UNLOCK_CACHE;
454 /* returns an unsigned int containing the number of seconds in PR_Now() */
455 PRUint32
456 ssl_Time(void)
458 PRUint32 myTime;
459 #if (defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)) && !defined(_WIN32_WCE)
460 myTime = time(NULL); /* accurate until the year 2038. */
461 #else
462 /* portable, but possibly slower */
463 PRTime now;
464 PRInt64 ll;
466 now = PR_Now();
467 LL_I2L(ll, 1000000L);
468 LL_DIV(now, now, ll);
469 LL_L2UI(myTime, now);
470 #endif
471 return myTime;
474 SECStatus
475 ssl3_SetSIDSessionTicket(sslSessionID *sid, NewSessionTicket *session_ticket)
477 SECStatus rv;
479 /* We need to lock the cache, as this sid might already be in the cache. */
480 LOCK_CACHE;
482 /* A server might have sent us an empty ticket, which has the
483 * effect of clearing the previously known ticket.
485 if (sid->u.ssl3.sessionTicket.ticket.data)
486 SECITEM_FreeItem(&sid->u.ssl3.sessionTicket.ticket, PR_FALSE);
487 if (session_ticket->ticket.len > 0) {
488 rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.sessionTicket.ticket,
489 &session_ticket->ticket);
490 if (rv != SECSuccess) {
491 UNLOCK_CACHE;
492 return rv;
494 } else {
495 sid->u.ssl3.sessionTicket.ticket.data = NULL;
496 sid->u.ssl3.sessionTicket.ticket.len = 0;
498 sid->u.ssl3.sessionTicket.received_timestamp =
499 session_ticket->received_timestamp;
500 sid->u.ssl3.sessionTicket.ticket_lifetime_hint =
501 session_ticket->ticket_lifetime_hint;
503 UNLOCK_CACHE;
504 return SECSuccess;