- add sources.
[platform/framework/web/crosswalk.git] / src / net / third_party / nss / ssl / sslnonce.c
1 /* 
2  * This file implements the CLIENT Session ID cache.  
3  *
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
8 #include "cert.h"
9 #include "pk11pub.h"
10 #include "secitem.h"
11 #include "ssl.h"
12 #include "nss.h"
13
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
20
21 PRUint32 ssl_sid_timeout = 100;
22 PRUint32 ssl3_sid_timeout = 86400L; /* 24 hours */
23
24 static sslSessionID *cache = NULL;
25 static PZLock *      cacheLock = NULL;
26
27 /* sids can be in one of 4 states:
28  *
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. 
33  */
34
35 #define LOCK_CACHE      lock_cache()
36 #define UNLOCK_CACHE    PZ_Unlock(cacheLock)
37
38 static PRCallOnceType lockOnce;
39
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)
45 {
46     static const PRCallOnceType pristineCallOnce;
47     SECStatus rv;
48
49     if (!cacheLock) {
50         PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
51         return SECFailure;
52     }
53
54     PZ_DestroyLock(cacheLock);
55     cacheLock = NULL;
56
57     rv = ssl_FreeSymWrapKeysLock();
58     if (rv != SECSuccess) {
59         return rv;
60     }
61
62     lockOnce = pristineCallOnce;
63     return SECSuccess;
64 }
65
66 /* InitSessionCacheLocks is called, protected by lockOnce, to create the
67  * session cache locks. */
68 static PRStatus
69 InitSessionCacheLocks(void)
70 {
71     SECStatus rv;
72
73     cacheLock = PZ_NewLock(nssILockCache);
74     if (cacheLock == NULL) {
75         return PR_FAILURE;
76     }
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;
84     }
85
86     rv = NSS_RegisterShutdown(FreeSessionCacheLocks, NULL);
87     PORT_Assert(SECSuccess == rv);
88     if (SECSuccess != rv) {
89         return PR_FAILURE;
90     }
91     return PR_SUCCESS;
92 }
93
94 SECStatus
95 ssl_InitSessionCacheLocks(void)
96 {
97     return (PR_SUCCESS ==
98             PR_CallOnce(&lockOnce, InitSessionCacheLocks)) ?
99            SECSuccess : SECFailure;
100 }
101
102 static void
103 lock_cache(void)
104 {
105     ssl_InitSessionCacheLocks();
106     PZ_Lock(cacheLock);
107 }
108
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.
111  */
112 static void
113 ssl_DestroySID(sslSessionID *sid)
114 {
115     int i;
116     SSL_TRC(8, ("SSL: destroy sid: sid=0x%x cached=%d", sid, sid->cached));
117     PORT_Assert((sid->references == 0));
118
119     if (sid->cached == in_client_cache)
120         return; /* it will get taken care of next time cache is traversed. */
121
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     }
126     if (sid->peerID != NULL)
127         PORT_Free((void *)sid->peerID);         /* CONST */
128
129     if (sid->urlSvrName != NULL)
130         PORT_Free((void *)sid->urlSvrName);     /* CONST */
131
132     if ( sid->peerCert ) {
133         CERT_DestroyCertificate(sid->peerCert);
134     }
135     for (i = 0; i < MAX_PEER_CERT_CHAIN_SIZE && sid->peerCertChain[i]; i++) {
136         CERT_DestroyCertificate(sid->peerCertChain[i]);
137     }
138     if (sid->peerCertStatus.items) {
139         SECITEM_FreeArray(&sid->peerCertStatus, PR_FALSE);
140     }
141
142     if ( sid->localCert ) {
143         CERT_DestroyCertificate(sid->localCert);
144     }
145     if (sid->u.ssl3.sessionTicket.ticket.data) {
146         SECITEM_FreeItem(&sid->u.ssl3.sessionTicket.ticket, PR_FALSE);
147     }
148     if (sid->u.ssl3.srvName.data) {
149         SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
150     }
151     
152     PORT_ZFree(sid, sizeof(sslSessionID));
153 }
154
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.
161  */
162 static void 
163 ssl_FreeLockedSID(sslSessionID *sid)
164 {
165     PORT_Assert(sid->references >= 1);
166     if (--sid->references == 0) {
167         ssl_DestroySID(sid);
168     }
169 }
170
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.
176  */
177 void
178 ssl_FreeSID(sslSessionID *sid)
179 {
180     LOCK_CACHE;
181     ssl_FreeLockedSID(sid);
182     UNLOCK_CACHE;
183 }
184
185 /************************************************************************/
186
187 /*
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.
191 */
192
193 sslSessionID *
194 ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, const char *peerID, 
195               const char * urlSvrName)
196 {
197     sslSessionID **sidp;
198     sslSessionID * sid;
199     PRUint32       now;
200
201     if (!urlSvrName)
202         return NULL;
203     now = ssl_Time();
204     LOCK_CACHE;
205     sidp = &cache;
206     while ((sid = *sidp) != 0) {
207         PORT_Assert(sid->cached == in_client_cache);
208         PORT_Assert(sid->references >= 1);
209
210         SSL_TRC(8, ("SSL: Lookup1: sid=0x%x", sid));
211
212         if (sid->expirationTime < now || !sid->references) {
213             /*
214             ** This session-id timed out, or was orphaned.
215             ** Don't even care who it belongs to, blow it out of our cache.
216             */
217             SSL_TRC(7, ("SSL: lookup1, throwing sid out, age=%d refs=%d",
218                         now - sid->creationTime, sid->references));
219
220             *sidp = sid->next;                  /* delink it from the list. */
221             sid->cached = invalid_cache;        /* mark not on list. */
222             if (!sid->references)
223                 ssl_DestroySID(sid);
224             else
225                 ssl_FreeLockedSID(sid);         /* drop ref count, free. */
226
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)) &&
233                    /* is cacheable */
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))) )
241                   ) {
242             /* Hit */
243             sid->lastAccessTime = now;
244             sid->references++;
245             break;
246         } else {
247             sidp = &sid->next;
248         }
249     }
250     UNLOCK_CACHE;
251     return sid;
252 }
253
254 /*
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().
257 */
258 static void 
259 CacheSID(sslSessionID *sid)
260 {
261     PRUint32  expirationPeriod;
262     SSL_TRC(8, ("SSL: Cache: sid=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
263                 "time=%x cached=%d",
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,
267                 sid->cached));
268
269     if (sid->cached == in_client_cache)
270         return;
271
272     if (!sid->urlSvrName) {
273         /* don't cache this SID because it can never be matched */
274         return;
275     }
276
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));
286     } else {
287         if (sid->u.ssl3.sessionIDLength == 0 &&
288             sid->u.ssl3.sessionTicket.ticket.data == NULL)
289             return;
290         /* Client generates the SessionID if this was a stateless resume. */
291         if (sid->u.ssl3.sessionIDLength == 0) {
292             SECStatus rv;
293             rv = PK11_GenerateRandom(sid->u.ssl3.sessionID,
294                 SSL3_SESSIONID_BYTES);
295             if (rv != SECSuccess)
296                 return;
297             sid->u.ssl3.sessionIDLength = SSL3_SESSIONID_BYTES;
298         }
299         expirationPeriod = ssl3_sid_timeout;
300         PRINT_BUF(8, (0, "sessionID:",
301                       sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength));
302     }
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;
308
309     /*
310      * Put sid into the cache.  Bump reference count to indicate that
311      * cache is holding a reference. Uncache will reduce the cache
312      * reference.
313      */
314     LOCK_CACHE;
315     sid->references++;
316     sid->cached = in_client_cache;
317     sid->next   = cache;
318     cache       = sid;
319     UNLOCK_CACHE;
320 }
321
322 /* 
323  * If sid "zap" is in the cache,
324  *    removes sid from cache, and decrements reference count.
325  * Caller must hold cache lock.
326  */
327 static void
328 UncacheSID(sslSessionID *zap)
329 {
330     sslSessionID **sidp = &cache;
331     sslSessionID *sid;
332
333     if (zap->cached != in_client_cache) {
334         return;
335     }
336
337     SSL_TRC(8,("SSL: Uncache: zap=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
338                "time=%x cipher=%d",
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));
350     }
351
352     /* See if it's in the cache, if so nuke it */
353     while ((sid = *sidp) != 0) {
354         if (sid == zap) {
355             /*
356             ** Bingo. Reduce reference count by one so that when
357             ** everyone is done with the sid we can free it up.
358             */
359             *sidp = zap->next;
360             zap->cached = invalid_cache;
361             ssl_FreeLockedSID(zap);
362             return;
363         }
364         sidp = &sid->next;
365     }
366 }
367
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 
371  *    ss->sec.uncache().
372  */
373 static void
374 LockAndUncacheSID(sslSessionID *zap)
375 {
376     LOCK_CACHE;
377     UncacheSID(zap);
378     UNLOCK_CACHE;
379
380 }
381
382 /* choose client or server cache functions for this sslsocket. */
383 void 
384 ssl_ChooseSessionIDProcs(sslSecurityInfo *sec)
385 {
386     if (sec->isServer) {
387         sec->cache   = ssl_sid_cache;
388         sec->uncache = ssl_sid_uncache;
389     } else {
390         sec->cache   = CacheSID;
391         sec->uncache = LockAndUncacheSID;
392     }
393 }
394
395 /* wipe out the entire client session cache. */
396 void
397 SSL_ClearSessionCache(void)
398 {
399     LOCK_CACHE;
400     while(cache != NULL)
401         UncacheSID(cache);
402     UNLOCK_CACHE;
403 }
404
405 /* returns an unsigned int containing the number of seconds in PR_Now() */
406 PRUint32
407 ssl_Time(void)
408 {
409     PRUint32 myTime;
410 #if defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)
411     myTime = time(NULL);        /* accurate until the year 2038. */
412 #else
413     /* portable, but possibly slower */
414     PRTime now;
415     PRInt64 ll;
416
417     now = PR_Now();
418     LL_I2L(ll, 1000000L);
419     LL_DIV(now, now, ll);
420     LL_L2UI(myTime, now);
421 #endif
422     return myTime;
423 }
424
425 SECStatus
426 ssl3_SetSIDSessionTicket(sslSessionID *sid, NewSessionTicket *session_ticket)
427 {
428     SECStatus rv;
429
430     /* We need to lock the cache, as this sid might already be in the cache. */
431     LOCK_CACHE;
432
433     /* A server might have sent us an empty ticket, which has the
434      * effect of clearing the previously known ticket.
435      */
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) {
442             UNLOCK_CACHE;
443             return rv;
444         }
445     } else {
446         sid->u.ssl3.sessionTicket.ticket.data = NULL;
447         sid->u.ssl3.sessionTicket.ticket.len = 0;
448     }
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;
453
454     UNLOCK_CACHE;
455     return SECSuccess;
456 }