security/nss/lib/ssl/sslnonce.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/. */
     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:
    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  */
    35 #define LOCK_CACHE 	lock_cache()
    36 #define UNLOCK_CACHE	PZ_Unlock(cacheLock)
    38 static SECStatus
    39 ssl_InitClientSessionCacheLock(void)
    40 {
    41     cacheLock = PZ_NewLock(nssILockCache);
    42     return cacheLock ? SECSuccess : SECFailure;
    43 }
    45 static SECStatus
    46 ssl_FreeClientSessionCacheLock(void)
    47 {
    48     if (cacheLock) {
    49         PZ_DestroyLock(cacheLock);
    50         cacheLock = NULL;
    51         return SECSuccess;
    52     }
    53     PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
    54     return SECFailure;
    55 }
    57 static PRBool LocksInitializedEarly = PR_FALSE;
    59 static SECStatus
    60 FreeSessionCacheLocks()
    61 {
    62     SECStatus rv1, rv2;
    63     rv1 = ssl_FreeSymWrapKeysLock();
    64     rv2 = ssl_FreeClientSessionCacheLock();
    65     if ( (SECSuccess == rv1) && (SECSuccess == rv2) ) {
    66         return SECSuccess;
    67     }
    68     return SECFailure;
    69 }
    71 static SECStatus
    72 InitSessionCacheLocks(void)
    73 {
    74     SECStatus rv1, rv2;
    75     PRErrorCode rc;
    76     rv1 = ssl_InitSymWrapKeysLock();
    77     rv2 = ssl_InitClientSessionCacheLock();
    78     if ( (SECSuccess == rv1) && (SECSuccess == rv2) ) {
    79         return SECSuccess;
    80     }
    81     rc = PORT_GetError();
    82     FreeSessionCacheLocks();
    83     PORT_SetError(rc);
    84     return SECFailure;
    85 }
    87 /* free the session cache locks if they were initialized early */
    88 SECStatus
    89 ssl_FreeSessionCacheLocks()
    90 {
    91     PORT_Assert(PR_TRUE == LocksInitializedEarly);
    92     if (!LocksInitializedEarly) {
    93         PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
    94         return SECFailure;
    95     }
    96     FreeSessionCacheLocks();
    97     LocksInitializedEarly = PR_FALSE;
    98     return SECSuccess;
    99 }
   101 static PRCallOnceType lockOnce;
   103 /* free the session cache locks if they were initialized lazily */
   104 static SECStatus ssl_ShutdownLocks(void* appData, void* nssData)
   105 {
   106     PORT_Assert(PR_FALSE == LocksInitializedEarly);
   107     if (LocksInitializedEarly) {
   108         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
   109         return SECFailure;
   110     }
   111     FreeSessionCacheLocks();
   112     memset(&lockOnce, 0, sizeof(lockOnce));
   113     return SECSuccess;
   114 }
   116 static PRStatus initSessionCacheLocksLazily(void)
   117 {
   118     SECStatus rv = InitSessionCacheLocks();
   119     if (SECSuccess != rv) {
   120         return PR_FAILURE;
   121     }
   122     rv = NSS_RegisterShutdown(ssl_ShutdownLocks, NULL);
   123     PORT_Assert(SECSuccess == rv);
   124     if (SECSuccess != rv) {
   125         return PR_FAILURE;
   126     }
   127     return PR_SUCCESS;
   128 }
   130 /* lazyInit means that the call is not happening during a 1-time
   131  * initialization function, but rather during dynamic, lazy initialization
   132  */
   133 SECStatus
   134 ssl_InitSessionCacheLocks(PRBool lazyInit)
   135 {
   136     if (LocksInitializedEarly) {
   137         return SECSuccess;
   138     }
   140     if (lazyInit) {
   141         return (PR_SUCCESS ==
   142                 PR_CallOnce(&lockOnce, initSessionCacheLocksLazily)) ?
   143                SECSuccess : SECFailure;
   144     }
   146     if (SECSuccess == InitSessionCacheLocks()) {
   147         LocksInitializedEarly = PR_TRUE;
   148         return SECSuccess;
   149     }
   151     return SECFailure;
   152 }
   154 static void 
   155 lock_cache(void)
   156 {
   157     ssl_InitSessionCacheLocks(PR_TRUE);
   158     PZ_Lock(cacheLock);
   159 }
   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.
   163  */
   164 static void
   165 ssl_DestroySID(sslSessionID *sid)
   166 {
   167     SSL_TRC(8, ("SSL: destroy sid: sid=0x%x cached=%d", sid, sid->cached));
   168     PORT_Assert(sid->references == 0);
   169     PORT_Assert(sid->cached != in_client_cache);
   171     if (sid->version < SSL_LIBRARY_VERSION_3_0) {
   172 	SECITEM_ZfreeItem(&sid->u.ssl2.masterKey, PR_FALSE);
   173 	SECITEM_ZfreeItem(&sid->u.ssl2.cipherArg, PR_FALSE);
   174     } else {
   175         if (sid->u.ssl3.locked.sessionTicket.ticket.data) {
   176             SECITEM_FreeItem(&sid->u.ssl3.locked.sessionTicket.ticket,
   177                              PR_FALSE);
   178         }
   179         if (sid->u.ssl3.srvName.data) {
   180             SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
   181         }
   183         if (sid->u.ssl3.lock) {
   184             PR_DestroyRWLock(sid->u.ssl3.lock);
   185         }
   186     }
   188     if (sid->peerID != NULL)
   189 	PORT_Free((void *)sid->peerID);		/* CONST */
   191     if (sid->urlSvrName != NULL)
   192 	PORT_Free((void *)sid->urlSvrName);	/* CONST */
   194     if ( sid->peerCert ) {
   195 	CERT_DestroyCertificate(sid->peerCert);
   196     }
   197     if (sid->peerCertStatus.items) {
   198         SECITEM_FreeArray(&sid->peerCertStatus, PR_FALSE);
   199     }
   201     if ( sid->localCert ) {
   202 	CERT_DestroyCertificate(sid->localCert);
   203     }
   205     PORT_ZFree(sid, sizeof(sslSessionID));
   206 }
   208 /* BEWARE: This function gets called for both client and server SIDs !!
   209  * Decrement reference count, and 
   210  *    free sid if ref count is zero, and sid is not in the cache. 
   211  * Does NOT remove from the cache first.  
   212  * If the sid is still in the cache, it is left there until next time
   213  * the cache list is traversed.
   214  */
   215 static void 
   216 ssl_FreeLockedSID(sslSessionID *sid)
   217 {
   218     PORT_Assert(sid->references >= 1);
   219     if (--sid->references == 0) {
   220 	ssl_DestroySID(sid);
   221     }
   222 }
   224 /* BEWARE: This function gets called for both client and server SIDs !!
   225  * Decrement reference count, and 
   226  *    free sid if ref count is zero, and sid is not in the cache. 
   227  * Does NOT remove from the cache first.  
   228  * These locks are necessary because the sid _might_ be in the cache list.
   229  */
   230 void
   231 ssl_FreeSID(sslSessionID *sid)
   232 {
   233     LOCK_CACHE;
   234     ssl_FreeLockedSID(sid);
   235     UNLOCK_CACHE;
   236 }
   238 /************************************************************************/
   240 /*
   241 **  Lookup sid entry in cache by Address, port, and peerID string.
   242 **  If found, Increment reference count, and return pointer to caller.
   243 **  If it has timed out or ref count is zero, remove from list and free it.
   244 */
   246 sslSessionID *
   247 ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, const char *peerID, 
   248               const char * urlSvrName)
   249 {
   250     sslSessionID **sidp;
   251     sslSessionID * sid;
   252     PRUint32       now;
   254     if (!urlSvrName)
   255     	return NULL;
   256     now = ssl_Time();
   257     LOCK_CACHE;
   258     sidp = &cache;
   259     while ((sid = *sidp) != 0) {
   260 	PORT_Assert(sid->cached == in_client_cache);
   261 	PORT_Assert(sid->references >= 1);
   263 	SSL_TRC(8, ("SSL: Lookup1: sid=0x%x", sid));
   265 	if (sid->expirationTime < now) {
   266 	    /*
   267 	    ** This session-id timed out.
   268 	    ** Don't even care who it belongs to, blow it out of our cache.
   269 	    */
   270 	    SSL_TRC(7, ("SSL: lookup1, throwing sid out, age=%d refs=%d",
   271 			now - sid->creationTime, sid->references));
   273 	    *sidp = sid->next; 			/* delink it from the list. */
   274 	    sid->cached = invalid_cache;	/* mark not on list. */
   275 	    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))) )
   290 		  ) {
   291 	    /* Hit */
   292 	    sid->lastAccessTime = now;
   293 	    sid->references++;
   294 	    break;
   295 	} else {
   296 	    sidp = &sid->next;
   297 	}
   298     }
   299     UNLOCK_CACHE;
   300     return sid;
   301 }
   303 /*
   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().
   306 */
   307 static void 
   308 CacheSID(sslSessionID *sid)
   309 {
   310     PRUint32  expirationPeriod;
   312     PORT_Assert(sid->cached == never_cached);
   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->urlSvrName) {
   322         /* don't cache this SID because it can never be matched */
   323         return;
   324     }
   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.locked.sessionTicket.ticket.data == NULL)
   338 	    return;
   340 	/* Client generates the SessionID if this was a stateless resume. */
   341 	if (sid->u.ssl3.sessionIDLength == 0) {
   342 	    SECStatus rv;
   343 	    rv = PK11_GenerateRandom(sid->u.ssl3.sessionID,
   344 		SSL3_SESSIONID_BYTES);
   345 	    if (rv != SECSuccess)
   346 		return;
   347 	    sid->u.ssl3.sessionIDLength = SSL3_SESSIONID_BYTES;
   348 	}
   349 	expirationPeriod = ssl3_sid_timeout;
   350 	PRINT_BUF(8, (0, "sessionID:",
   351 		      sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength));
   353 	sid->u.ssl3.lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, NULL);
   354 	if (!sid->u.ssl3.lock) {
   355 	    return;
   356 	}
   357     }
   358     PORT_Assert(sid->creationTime != 0 && sid->expirationTime != 0);
   359     if (!sid->creationTime)
   360 	sid->lastAccessTime = sid->creationTime = ssl_Time();
   361     if (!sid->expirationTime)
   362 	sid->expirationTime = sid->creationTime + expirationPeriod;
   364     /*
   365      * Put sid into the cache.  Bump reference count to indicate that
   366      * cache is holding a reference. Uncache will reduce the cache
   367      * reference.
   368      */
   369     LOCK_CACHE;
   370     sid->references++;
   371     sid->cached = in_client_cache;
   372     sid->next   = cache;
   373     cache       = sid;
   374     UNLOCK_CACHE;
   375 }
   377 /* 
   378  * If sid "zap" is in the cache,
   379  *    removes sid from cache, and decrements reference count.
   380  * Caller must hold cache lock.
   381  */
   382 static void
   383 UncacheSID(sslSessionID *zap)
   384 {
   385     sslSessionID **sidp = &cache;
   386     sslSessionID *sid;
   388     if (zap->cached != in_client_cache) {
   389 	return;
   390     }
   392     SSL_TRC(8,("SSL: Uncache: zap=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
   393 	       "time=%x cipher=%d",
   394 	       zap, zap->cached, zap->addr.pr_s6_addr32[0],
   395 	       zap->addr.pr_s6_addr32[1], zap->addr.pr_s6_addr32[2],
   396 	       zap->addr.pr_s6_addr32[3], zap->port, zap->creationTime,
   397 	       zap->u.ssl2.cipherType));
   398     if (zap->version < SSL_LIBRARY_VERSION_3_0) {
   399 	PRINT_BUF(8, (0, "sessionID:",
   400 		      zap->u.ssl2.sessionID, sizeof(zap->u.ssl2.sessionID)));
   401 	PRINT_BUF(8, (0, "masterKey:",
   402 		      zap->u.ssl2.masterKey.data, zap->u.ssl2.masterKey.len));
   403 	PRINT_BUF(8, (0, "cipherArg:",
   404 		      zap->u.ssl2.cipherArg.data, zap->u.ssl2.cipherArg.len));
   405     }
   407     /* See if it's in the cache, if so nuke it */
   408     while ((sid = *sidp) != 0) {
   409 	if (sid == zap) {
   410 	    /*
   411 	    ** Bingo. Reduce reference count by one so that when
   412 	    ** everyone is done with the sid we can free it up.
   413 	    */
   414 	    *sidp = zap->next;
   415 	    zap->cached = invalid_cache;
   416 	    ssl_FreeLockedSID(zap);
   417 	    return;
   418 	}
   419 	sidp = &sid->next;
   420     }
   421 }
   423 /* If sid "zap" is in the cache,
   424  *    removes sid from cache, and decrements reference count.
   425  * Although this function is static, it is called externally via 
   426  *    ss->sec.uncache().
   427  */
   428 static void
   429 LockAndUncacheSID(sslSessionID *zap)
   430 {
   431     LOCK_CACHE;
   432     UncacheSID(zap);
   433     UNLOCK_CACHE;
   435 }
   437 /* choose client or server cache functions for this sslsocket. */
   438 void 
   439 ssl_ChooseSessionIDProcs(sslSecurityInfo *sec)
   440 {
   441     if (sec->isServer) {
   442 	sec->cache   = ssl_sid_cache;
   443 	sec->uncache = ssl_sid_uncache;
   444     } else {
   445 	sec->cache   = CacheSID;
   446 	sec->uncache = LockAndUncacheSID;
   447     }
   448 }
   450 /* wipe out the entire client session cache. */
   451 void
   452 SSL_ClearSessionCache(void)
   453 {
   454     LOCK_CACHE;
   455     while(cache != NULL)
   456 	UncacheSID(cache);
   457     UNLOCK_CACHE;
   458 }
   460 /* returns an unsigned int containing the number of seconds in PR_Now() */
   461 PRUint32
   462 ssl_Time(void)
   463 {
   464     PRUint32 myTime;
   465 #if defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)
   466     myTime = time(NULL);	/* accurate until the year 2038. */
   467 #else
   468     /* portable, but possibly slower */
   469     PRTime now;
   470     PRInt64 ll;
   472     now = PR_Now();
   473     LL_I2L(ll, 1000000L);
   474     LL_DIV(now, now, ll);
   475     LL_L2UI(myTime, now);
   476 #endif
   477     return myTime;
   478 }
   480 void
   481 ssl3_SetSIDSessionTicket(sslSessionID *sid,
   482                          /*in/out*/ NewSessionTicket *newSessionTicket)
   483 {
   484     PORT_Assert(sid);
   485     PORT_Assert(newSessionTicket);
   486     PORT_Assert(newSessionTicket->ticket.data);
   487     PORT_Assert(newSessionTicket->ticket.len != 0);
   489     /* if sid->u.ssl3.lock, we are updating an existing entry that is already
   490      * cached or was once cached, so we need to acquire and release the write
   491      * lock. Otherwise, this is a new session that isn't shared with anything
   492      * yet, so no locking is needed.
   493      */
   494     if (sid->u.ssl3.lock) {
   495 	PR_RWLock_Wlock(sid->u.ssl3.lock);
   496 	if (sid->u.ssl3.locked.sessionTicket.ticket.data) {
   497 	    SECITEM_FreeItem(&sid->u.ssl3.locked.sessionTicket.ticket,
   498 			     PR_FALSE);
   499 	}
   500     }
   502     PORT_Assert(!sid->u.ssl3.locked.sessionTicket.ticket.data);
   504     /* Do a shallow copy, moving the ticket data. */
   505     sid->u.ssl3.locked.sessionTicket = *newSessionTicket;
   506     newSessionTicket->ticket.data = NULL;
   507     newSessionTicket->ticket.len = 0;
   509     if (sid->u.ssl3.lock) {
   510 	PR_RWLock_Unlock(sid->u.ssl3.lock);
   511     }
   512 }

mercurial