Home | History | Annotate | Download | only in ssl
      1 /*
      2  * This file implements the CLIENT Session ID cache.
      3  *
      4  * ***** BEGIN LICENSE BLOCK *****
      5  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
      6  *
      7  * The contents of this file are subject to the Mozilla Public License Version
      8  * 1.1 (the "License"); you may not use this file except in compliance with
      9  * the License. You may obtain a copy of the License at
     10  * http://www.mozilla.org/MPL/
     11  *
     12  * Software distributed under the License is distributed on an "AS IS" basis,
     13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
     14  * for the specific language governing rights and limitations under the
     15  * License.
     16  *
     17  * The Original Code is the Netscape security libraries.
     18  *
     19  * The Initial Developer of the Original Code is
     20  * Netscape Communications Corporation.
     21  * Portions created by the Initial Developer are Copyright (C) 1994-2000
     22  * the Initial Developer. All Rights Reserved.
     23  *
     24  * Contributor(s):
     25  *
     26  * Alternatively, the contents of this file may be used under the terms of
     27  * either the GNU General Public License Version 2 or later (the "GPL"), or
     28  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
     29  * in which case the provisions of the GPL or the LGPL are applicable instead
     30  * of those above. If you wish to allow use of your version of this file only
     31  * under the terms of either the GPL or the LGPL, and not to allow others to
     32  * use your version of this file under the terms of the MPL, indicate your
     33  * decision by deleting the provisions above and replace them with the notice
     34  * and other provisions required by the GPL or the LGPL. If you do not delete
     35  * the provisions above, a recipient may use your version of this file under
     36  * the terms of any one of the MPL, the GPL or the LGPL.
     37  *
     38  * ***** END LICENSE BLOCK ***** */
     39 /* $Id: sslnonce.c,v 1.25 2008/03/10 00:01:28 wtc%google.com Exp $ */
     40 
     41 #include "cert.h"
     42 #include "pk11pub.h"
     43 #include "secitem.h"
     44 #include "ssl.h"
     45 #include "nss.h"
     46 
     47 #include "sslimpl.h"
     48 #include "sslproto.h"
     49 #include "nssilock.h"
     50 #if (defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)) && !defined(_WIN32_WCE)
     51 #include <time.h>
     52 #endif
     53 
     54 PRUint32 ssl_sid_timeout = 100;
     55 PRUint32 ssl3_sid_timeout = 86400L; /* 24 hours */
     56 
     57 static sslSessionID *cache = NULL;
     58 static PZLock *      cacheLock = NULL;
     59 
     60 /* sids can be in one of 4 states:
     61  *
     62  * never_cached, 	created, but not yet put into cache.
     63  * in_client_cache, 	in the client cache's linked list.
     64  * in_server_cache, 	entry came from the server's cache file.
     65  * invalid_cache	has been removed from the cache.
     66  */
     67 
     68 #define LOCK_CACHE 	lock_cache()
     69 #define UNLOCK_CACHE	PZ_Unlock(cacheLock)
     70 
     71 static SECStatus
     72 ssl_InitClientSessionCacheLock(void)
     73 {
     74     cacheLock = PZ_NewLock(nssILockCache);
     75     return cacheLock ? SECSuccess : SECFailure;
     76 }
     77 
     78 static SECStatus
     79 ssl_FreeClientSessionCacheLock(void)
     80 {
     81     if (cacheLock) {
     82         PZ_DestroyLock(cacheLock);
     83         cacheLock = NULL;
     84         return SECSuccess;
     85     }
     86     PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
     87     return SECFailure;
     88 }
     89 
     90 static PRBool LocksInitializedEarly = PR_FALSE;
     91 
     92 static SECStatus
     93 FreeSessionCacheLocks()
     94 {
     95     SECStatus rv1, rv2;
     96     rv1 = ssl_FreeSymWrapKeysLock();
     97     rv2 = ssl_FreeClientSessionCacheLock();
     98     if ( (SECSuccess == rv1) && (SECSuccess == rv2) ) {
     99         return SECSuccess;
    100     }
    101     return SECFailure;
    102 }
    103 
    104 static SECStatus
    105 InitSessionCacheLocks(void)
    106 {
    107     SECStatus rv1, rv2;
    108     PRErrorCode rc;
    109     rv1 = ssl_InitSymWrapKeysLock();
    110     rv2 = ssl_InitClientSessionCacheLock();
    111     if ( (SECSuccess == rv1) && (SECSuccess == rv2) ) {
    112         return SECSuccess;
    113     }
    114     rc = PORT_GetError();
    115     FreeSessionCacheLocks();
    116     PORT_SetError(rc);
    117     return SECFailure;
    118 }
    119 
    120 /* free the session cache locks if they were initialized early */
    121 SECStatus
    122 ssl_FreeSessionCacheLocks()
    123 {
    124     PORT_Assert(PR_TRUE == LocksInitializedEarly);
    125     if (!LocksInitializedEarly) {
    126         PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
    127         return SECFailure;
    128     }
    129     FreeSessionCacheLocks();
    130     LocksInitializedEarly = PR_FALSE;
    131     return SECSuccess;
    132 }
    133 
    134 static PRCallOnceType lockOnce;
    135 
    136 /* free the session cache locks if they were initialized lazily */
    137 static SECStatus ssl_ShutdownLocks(void* appData, void* nssData)
    138 {
    139     PORT_Assert(PR_FALSE == LocksInitializedEarly);
    140     if (LocksInitializedEarly) {
    141         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
    142         return SECFailure;
    143     }
    144     FreeSessionCacheLocks();
    145     memset(&lockOnce, 0, sizeof(lockOnce));
    146     return SECSuccess;
    147 }
    148 
    149 static PRStatus initSessionCacheLocksLazily(void)
    150 {
    151     SECStatus rv = InitSessionCacheLocks();
    152     if (SECSuccess != rv) {
    153         return PR_FAILURE;
    154     }
    155     rv = NSS_RegisterShutdown(ssl_ShutdownLocks, NULL);
    156     PORT_Assert(SECSuccess == rv);
    157     if (SECSuccess != rv) {
    158         return PR_FAILURE;
    159     }
    160     return PR_SUCCESS;
    161 }
    162 
    163 /* lazyInit means that the call is not happening during a 1-time
    164  * initialization function, but rather during dynamic, lazy initialization
    165  */
    166 SECStatus
    167 ssl_InitSessionCacheLocks(PRBool lazyInit)
    168 {
    169     if (LocksInitializedEarly) {
    170         return SECSuccess;
    171     }
    172 
    173     if (lazyInit) {
    174         return (PR_SUCCESS ==
    175                 PR_CallOnce(&lockOnce, initSessionCacheLocksLazily)) ?
    176                SECSuccess : SECFailure;
    177     }
    178 
    179     if (SECSuccess == InitSessionCacheLocks()) {
    180         LocksInitializedEarly = PR_TRUE;
    181         return SECSuccess;
    182     }
    183 
    184     return SECFailure;
    185 }
    186 
    187 static void
    188 lock_cache(void)
    189 {
    190     ssl_InitSessionCacheLocks(PR_TRUE);
    191     PZ_Lock(cacheLock);
    192 }
    193 
    194 /* BEWARE: This function gets called for both client and server SIDs !!
    195  * If the unreferenced sid is not in the cache, Free sid and its contents.
    196  */
    197 static void
    198 ssl_DestroySID(sslSessionID *sid)
    199 {
    200     SSL_TRC(8, ("SSL: destroy sid: sid=0x%x cached=%d", sid, sid->cached));
    201     PORT_Assert((sid->references == 0));
    202 
    203     if (sid->cached == in_client_cache)
    204     	return;	/* it will get taken care of next time cache is traversed. */
    205 
    206     if (sid->version < SSL_LIBRARY_VERSION_3_0) {
    207 	SECITEM_ZfreeItem(&sid->u.ssl2.masterKey, PR_FALSE);
    208 	SECITEM_ZfreeItem(&sid->u.ssl2.cipherArg, PR_FALSE);
    209     }
    210     if (sid->peerID != NULL)
    211 	PORT_Free((void *)sid->peerID);		/* CONST */
    212 
    213     if (sid->urlSvrName != NULL)
    214 	PORT_Free((void *)sid->urlSvrName);	/* CONST */
    215 
    216     if ( sid->peerCert ) {
    217 	CERT_DestroyCertificate(sid->peerCert);
    218     }
    219     if ( sid->localCert ) {
    220 	CERT_DestroyCertificate(sid->localCert);
    221     }
    222     if (sid->u.ssl3.sessionTicket.ticket.data) {
    223 	SECITEM_FreeItem(&sid->u.ssl3.sessionTicket.ticket, PR_FALSE);
    224     }
    225 
    226     PORT_ZFree(sid, sizeof(sslSessionID));
    227 }
    228 
    229 /* BEWARE: This function gets called for both client and server SIDs !!
    230  * Decrement reference count, and
    231  *    free sid if ref count is zero, and sid is not in the cache.
    232  * Does NOT remove from the cache first.
    233  * If the sid is still in the cache, it is left there until next time
    234  * the cache list is traversed.
    235  */
    236 static void
    237 ssl_FreeLockedSID(sslSessionID *sid)
    238 {
    239     PORT_Assert(sid->references >= 1);
    240     if (--sid->references == 0) {
    241 	ssl_DestroySID(sid);
    242     }
    243 }
    244 
    245 /* BEWARE: This function gets called for both client and server SIDs !!
    246  * Decrement reference count, and
    247  *    free sid if ref count is zero, and sid is not in the cache.
    248  * Does NOT remove from the cache first.
    249  * These locks are necessary because the sid _might_ be in the cache list.
    250  */
    251 void
    252 ssl_FreeSID(sslSessionID *sid)
    253 {
    254     LOCK_CACHE;
    255     ssl_FreeLockedSID(sid);
    256     UNLOCK_CACHE;
    257 }
    258 
    259 /************************************************************************/
    260 
    261 /*
    262 **  Lookup sid entry in cache by Address, port, and peerID string.
    263 **  If found, Increment reference count, and return pointer to caller.
    264 **  If it has timed out or ref count is zero, remove from list and free it.
    265 */
    266 
    267 sslSessionID *
    268 ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, const char *peerID,
    269               const char * urlSvrName)
    270 {
    271     sslSessionID **sidp;
    272     sslSessionID * sid;
    273     PRUint32       now;
    274 
    275     if (!urlSvrName)
    276     	return NULL;
    277     now = ssl_Time();
    278     LOCK_CACHE;
    279     sidp = &cache;
    280     while ((sid = *sidp) != 0) {
    281 	PORT_Assert(sid->cached == in_client_cache);
    282 	PORT_Assert(sid->references >= 1);
    283 
    284 	SSL_TRC(8, ("SSL: Lookup1: sid=0x%x", sid));
    285 
    286 	if (sid->expirationTime < now || !sid->references) {
    287 	    /*
    288 	    ** This session-id timed out, or was orphaned.
    289 	    ** Don't even care who it belongs to, blow it out of our cache.
    290 	    */
    291 	    SSL_TRC(7, ("SSL: lookup1, throwing sid out, age=%d refs=%d",
    292 			now - sid->creationTime, sid->references));
    293 
    294 	    *sidp = sid->next; 			/* delink it from the list. */
    295 	    sid->cached = invalid_cache;	/* mark not on list. */
    296 	    if (!sid->references)
    297 	    	ssl_DestroySID(sid);
    298 	    else
    299 		ssl_FreeLockedSID(sid);		/* drop ref count, free. */
    300 
    301 	} else if (!memcmp(&sid->addr, addr, sizeof(PRIPv6Addr)) && /* server IP addr matches */
    302 	           (sid->port == port) && /* server port matches */
    303 		   /* proxy (peerID) matches */
    304 		   (((peerID == NULL) && (sid->peerID == NULL)) ||
    305 		    ((peerID != NULL) && (sid->peerID != NULL) &&
    306 		     PORT_Strcmp(sid->peerID, peerID) == 0)) &&
    307 		   /* is cacheable */
    308 		   (sid->version < SSL_LIBRARY_VERSION_3_0 ||
    309 		    sid->u.ssl3.keys.resumable) &&
    310 		   /* server hostname matches. */
    311 	           (sid->urlSvrName != NULL) &&
    312 		   ((0 == PORT_Strcmp(urlSvrName, sid->urlSvrName)) ||
    313 		    ((sid->peerCert != NULL) && (SECSuccess ==
    314 		      CERT_VerifyCertName(sid->peerCert, urlSvrName))) )
    315 		  ) {
    316 	    /* Hit */
    317 	    sid->lastAccessTime = now;
    318 	    sid->references++;
    319 	    break;
    320 	} else {
    321 	    sidp = &sid->next;
    322 	}
    323     }
    324     UNLOCK_CACHE;
    325     return sid;
    326 }
    327 
    328 /*
    329 ** Add an sid to the cache or return a previously cached entry to the cache.
    330 ** Although this is static, it is called via ss->sec.cache().
    331 */
    332 static void
    333 CacheSID(sslSessionID *sid)
    334 {
    335     PRUint32  expirationPeriod;
    336     SSL_TRC(8, ("SSL: Cache: sid=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
    337 		"time=%x cached=%d",
    338 		sid, sid->cached, sid->addr.pr_s6_addr32[0],
    339 		sid->addr.pr_s6_addr32[1], sid->addr.pr_s6_addr32[2],
    340 		sid->addr.pr_s6_addr32[3],  sid->port, sid->creationTime,
    341 		sid->cached));
    342 
    343     if (sid->cached == in_client_cache)
    344 	return;
    345 
    346     if (!sid->urlSvrName) {
    347         /* don't cache this SID because it can never be matched */
    348         return;
    349     }
    350 
    351     /* XXX should be different trace for version 2 vs. version 3 */
    352     if (sid->version < SSL_LIBRARY_VERSION_3_0) {
    353 	expirationPeriod = ssl_sid_timeout;
    354 	PRINT_BUF(8, (0, "sessionID:",
    355 		  sid->u.ssl2.sessionID, sizeof(sid->u.ssl2.sessionID)));
    356 	PRINT_BUF(8, (0, "masterKey:",
    357 		  sid->u.ssl2.masterKey.data, sid->u.ssl2.masterKey.len));
    358 	PRINT_BUF(8, (0, "cipherArg:",
    359 		  sid->u.ssl2.cipherArg.data, sid->u.ssl2.cipherArg.len));
    360     } else {
    361 	if (sid->u.ssl3.sessionIDLength == 0 &&
    362 	    sid->u.ssl3.sessionTicket.ticket.data == NULL)
    363 	    return;
    364 	/* Client generates the SessionID if this was a stateless resume. */
    365 	if (sid->u.ssl3.sessionIDLength == 0) {
    366 	    SECStatus rv;
    367 	    rv = PK11_GenerateRandom(sid->u.ssl3.sessionID,
    368 		SSL3_SESSIONID_BYTES);
    369 	    if (rv != SECSuccess)
    370 		return;
    371 	    sid->u.ssl3.sessionIDLength = SSL3_SESSIONID_BYTES;
    372 	}
    373 	expirationPeriod = ssl3_sid_timeout;
    374 	PRINT_BUF(8, (0, "sessionID:",
    375 		      sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength));
    376     }
    377     PORT_Assert(sid->creationTime != 0 && sid->expirationTime != 0);
    378     if (!sid->creationTime)
    379 	sid->lastAccessTime = sid->creationTime = ssl_Time();
    380     if (!sid->expirationTime)
    381 	sid->expirationTime = sid->creationTime + expirationPeriod;
    382 
    383     /*
    384      * Put sid into the cache.  Bump reference count to indicate that
    385      * cache is holding a reference. Uncache will reduce the cache
    386      * reference.
    387      */
    388     LOCK_CACHE;
    389     sid->references++;
    390     sid->cached = in_client_cache;
    391     sid->next   = cache;
    392     cache       = sid;
    393     UNLOCK_CACHE;
    394 }
    395 
    396 /*
    397  * If sid "zap" is in the cache,
    398  *    removes sid from cache, and decrements reference count.
    399  * Caller must hold cache lock.
    400  */
    401 static void
    402 UncacheSID(sslSessionID *zap)
    403 {
    404     sslSessionID **sidp = &cache;
    405     sslSessionID *sid;
    406 
    407     if (zap->cached != in_client_cache) {
    408 	return;
    409     }
    410 
    411     SSL_TRC(8,("SSL: Uncache: zap=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
    412 	       "time=%x cipher=%d",
    413 	       zap, zap->cached, zap->addr.pr_s6_addr32[0],
    414 	       zap->addr.pr_s6_addr32[1], zap->addr.pr_s6_addr32[2],
    415 	       zap->addr.pr_s6_addr32[3], zap->port, zap->creationTime,
    416 	       zap->u.ssl2.cipherType));
    417     if (zap->version < SSL_LIBRARY_VERSION_3_0) {
    418 	PRINT_BUF(8, (0, "sessionID:",
    419 		      zap->u.ssl2.sessionID, sizeof(zap->u.ssl2.sessionID)));
    420 	PRINT_BUF(8, (0, "masterKey:",
    421 		      zap->u.ssl2.masterKey.data, zap->u.ssl2.masterKey.len));
    422 	PRINT_BUF(8, (0, "cipherArg:",
    423 		      zap->u.ssl2.cipherArg.data, zap->u.ssl2.cipherArg.len));
    424     }
    425 
    426     /* See if it's in the cache, if so nuke it */
    427     while ((sid = *sidp) != 0) {
    428 	if (sid == zap) {
    429 	    /*
    430 	    ** Bingo. Reduce reference count by one so that when
    431 	    ** everyone is done with the sid we can free it up.
    432 	    */
    433 	    *sidp = zap->next;
    434 	    zap->cached = invalid_cache;
    435 	    ssl_FreeLockedSID(zap);
    436 	    return;
    437 	}
    438 	sidp = &sid->next;
    439     }
    440 }
    441 
    442 /* If sid "zap" is in the cache,
    443  *    removes sid from cache, and decrements reference count.
    444  * Although this function is static, it is called externally via
    445  *    ss->sec.uncache().
    446  */
    447 static void
    448 LockAndUncacheSID(sslSessionID *zap)
    449 {
    450     LOCK_CACHE;
    451     UncacheSID(zap);
    452     UNLOCK_CACHE;
    453 
    454 }
    455 
    456 /* choose client or server cache functions for this sslsocket. */
    457 void
    458 ssl_ChooseSessionIDProcs(sslSecurityInfo *sec)
    459 {
    460     if (sec->isServer) {
    461 	sec->cache   = ssl_sid_cache;
    462 	sec->uncache = ssl_sid_uncache;
    463     } else {
    464 	sec->cache   = CacheSID;
    465 	sec->uncache = LockAndUncacheSID;
    466     }
    467 }
    468 
    469 /* wipe out the entire client session cache. */
    470 void
    471 SSL_ClearSessionCache(void)
    472 {
    473     LOCK_CACHE;
    474     while(cache != NULL)
    475 	UncacheSID(cache);
    476     UNLOCK_CACHE;
    477 }
    478 
    479 /* returns an unsigned int containing the number of seconds in PR_Now() */
    480 PRUint32
    481 ssl_Time(void)
    482 {
    483     PRUint32 myTime;
    484 #if (defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)) && !defined(_WIN32_WCE)
    485     myTime = time(NULL);	/* accurate until the year 2038. */
    486 #else
    487     /* portable, but possibly slower */
    488     PRTime now;
    489     PRInt64 ll;
    490 
    491     now = PR_Now();
    492     LL_I2L(ll, 1000000L);
    493     LL_DIV(now, now, ll);
    494     LL_L2UI(myTime, now);
    495 #endif
    496     return myTime;
    497 }
    498 
    499 SECStatus
    500 ssl3_SetSIDSessionTicket(sslSessionID *sid, NewSessionTicket *session_ticket)
    501 {
    502     SECStatus rv;
    503 
    504     /* We need to lock the cache, as this sid might already be in the cache. */
    505     LOCK_CACHE;
    506 
    507     /* A server might have sent us an empty ticket, which has the
    508      * effect of clearing the previously known ticket.
    509      */
    510     if (sid->u.ssl3.sessionTicket.ticket.data)
    511 	SECITEM_FreeItem(&sid->u.ssl3.sessionTicket.ticket, PR_FALSE);
    512     if (session_ticket->ticket.len > 0) {
    513 	rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.sessionTicket.ticket,
    514 	    &session_ticket->ticket);
    515 	if (rv != SECSuccess) {
    516 	    UNLOCK_CACHE;
    517 	    return rv;
    518 	}
    519     } else {
    520 	sid->u.ssl3.sessionTicket.ticket.data = NULL;
    521 	sid->u.ssl3.sessionTicket.ticket.len = 0;
    522     }
    523     sid->u.ssl3.sessionTicket.received_timestamp =
    524 	session_ticket->received_timestamp;
    525     sid->u.ssl3.sessionTicket.ticket_lifetime_hint =
    526 	session_ticket->ticket_lifetime_hint;
    527 
    528     UNLOCK_CACHE;
    529     return SECSuccess;
    530 }
    531