Back to index

lightning-sunbird  0.9+nobinonly
sslnonce.c
Go to the documentation of this file.
00001 /* 
00002  * This file implements the CLIENT Session ID cache.  
00003  *
00004  * ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is the Netscape security libraries.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 /* $Id: sslnonce.c,v 1.17 2005/09/09 03:02:16 nelsonb%netscape.com Exp $ */
00040 
00041 #include "nssrenam.h"
00042 #include "cert.h"
00043 #include "secitem.h"
00044 #include "ssl.h"
00045 
00046 #include "sslimpl.h"
00047 #include "sslproto.h"
00048 #include "nssilock.h"
00049 #include "nsslocks.h"
00050 #if (defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)) && !defined(_WIN32_WCE)
00051 #include <time.h>
00052 #endif
00053 
00054 PRUint32 ssl_sid_timeout = 100;
00055 PRUint32 ssl3_sid_timeout = 86400L; /* 24 hours */
00056 
00057 static sslSessionID *cache = NULL;
00058 static PZLock *      cacheLock = NULL;
00059 
00060 /* sids can be in one of 4 states:
00061  *
00062  * never_cached,     created, but not yet put into cache. 
00063  * in_client_cache,  in the client cache's linked list.
00064  * in_server_cache,  entry came from the server's cache file.
00065  * invalid_cache     has been removed from the cache. 
00066  */
00067 
00068 #define LOCK_CACHE   lock_cache()
00069 #define UNLOCK_CACHE PZ_Unlock(cacheLock)
00070 
00071 void ssl_InitClientSessionCacheLock(void)
00072 {
00073     if (!cacheLock)
00074        nss_InitLock(&cacheLock, nssILockCache);
00075 }
00076 
00077 static void 
00078 lock_cache(void)
00079 {
00080     ssl_InitClientSessionCacheLock();
00081     PZ_Lock(cacheLock);
00082 }
00083 
00084 /* BEWARE: This function gets called for both client and server SIDs !!
00085  * If the unreferenced sid is not in the cache, Free sid and its contents.
00086  */
00087 static void
00088 ssl_DestroySID(sslSessionID *sid)
00089 {
00090     SSL_TRC(8, ("SSL: destroy sid: sid=0x%x cached=%d", sid, sid->cached));
00091     PORT_Assert((sid->references == 0));
00092 
00093     if (sid->cached == in_client_cache)
00094        return;       /* it will get taken care of next time cache is traversed. */
00095 
00096     if (sid->version < SSL_LIBRARY_VERSION_3_0) {
00097        SECITEM_ZfreeItem(&sid->u.ssl2.masterKey, PR_FALSE);
00098        SECITEM_ZfreeItem(&sid->u.ssl2.cipherArg, PR_FALSE);
00099     }
00100     if (sid->peerID != NULL)
00101        PORT_Free((void *)sid->peerID);           /* CONST */
00102 
00103     if (sid->urlSvrName != NULL)
00104        PORT_Free((void *)sid->urlSvrName);       /* CONST */
00105 
00106     if ( sid->peerCert ) {
00107        CERT_DestroyCertificate(sid->peerCert);
00108     }
00109     if ( sid->localCert ) {
00110        CERT_DestroyCertificate(sid->localCert);
00111     }
00112     
00113     PORT_ZFree(sid, sizeof(sslSessionID));
00114 }
00115 
00116 /* BEWARE: This function gets called for both client and server SIDs !!
00117  * Decrement reference count, and 
00118  *    free sid if ref count is zero, and sid is not in the cache. 
00119  * Does NOT remove from the cache first.  
00120  * If the sid is still in the cache, it is left there until next time
00121  * the cache list is traversed.
00122  */
00123 static void 
00124 ssl_FreeLockedSID(sslSessionID *sid)
00125 {
00126     PORT_Assert(sid->references >= 1);
00127     if (--sid->references == 0) {
00128        ssl_DestroySID(sid);
00129     }
00130 }
00131 
00132 /* BEWARE: This function gets called for both client and server SIDs !!
00133  * Decrement reference count, and 
00134  *    free sid if ref count is zero, and sid is not in the cache. 
00135  * Does NOT remove from the cache first.  
00136  * These locks are necessary because the sid _might_ be in the cache list.
00137  */
00138 void
00139 ssl_FreeSID(sslSessionID *sid)
00140 {
00141     LOCK_CACHE;
00142     ssl_FreeLockedSID(sid);
00143     UNLOCK_CACHE;
00144 }
00145 
00146 /************************************************************************/
00147 
00148 /*
00149 **  Lookup sid entry in cache by Address, port, and peerID string.
00150 **  If found, Increment reference count, and return pointer to caller.
00151 **  If it has timed out or ref count is zero, remove from list and free it.
00152 */
00153 
00154 sslSessionID *
00155 ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, const char *peerID, 
00156               const char * urlSvrName)
00157 {
00158     sslSessionID **sidp;
00159     sslSessionID * sid;
00160     PRUint32       now;
00161 
00162     if (!urlSvrName)
00163        return NULL;
00164     now = ssl_Time();
00165     LOCK_CACHE;
00166     sidp = &cache;
00167     while ((sid = *sidp) != 0) {
00168        PORT_Assert(sid->cached == in_client_cache);
00169        PORT_Assert(sid->references >= 1);
00170 
00171        SSL_TRC(8, ("SSL: Lookup1: sid=0x%x", sid));
00172 
00173        if (sid->expirationTime < now || !sid->references) {
00174            /*
00175            ** This session-id timed out, or was orphaned.
00176            ** Don't even care who it belongs to, blow it out of our cache.
00177            */
00178            SSL_TRC(7, ("SSL: lookup1, throwing sid out, age=%d refs=%d",
00179                      now - sid->creationTime, sid->references));
00180 
00181            *sidp = sid->next;                    /* delink it from the list. */
00182            sid->cached = invalid_cache;   /* mark not on list. */
00183            if (!sid->references)
00184               ssl_DestroySID(sid);
00185            else
00186               ssl_FreeLockedSID(sid);            /* drop ref count, free. */
00187 
00188        } else if (!memcmp(&sid->addr, addr, sizeof(PRIPv6Addr)) && /* server IP addr matches */
00189                   (sid->port == port) && /* server port matches */
00190                  /* proxy (peerID) matches */
00191                  (((peerID == NULL) && (sid->peerID == NULL)) ||
00192                   ((peerID != NULL) && (sid->peerID != NULL) &&
00193                    PORT_Strcmp(sid->peerID, peerID) == 0)) &&
00194                  /* is cacheable */
00195                  (sid->version < SSL_LIBRARY_VERSION_3_0 ||
00196                   sid->u.ssl3.keys.resumable) &&
00197                  /* server hostname matches. */
00198                   (sid->urlSvrName != NULL) &&
00199                  ((0 == PORT_Strcmp(urlSvrName, sid->urlSvrName)) ||
00200                   ((sid->peerCert != NULL) && (SECSuccess == 
00201                     CERT_VerifyCertName(sid->peerCert, urlSvrName))) )
00202                 ) {
00203            /* Hit */
00204            sid->lastAccessTime = now;
00205            sid->references++;
00206            break;
00207        } else {
00208            sidp = &sid->next;
00209        }
00210     }
00211     UNLOCK_CACHE;
00212     return sid;
00213 }
00214 
00215 /*
00216 ** Add an sid to the cache or return a previously cached entry to the cache.
00217 ** Although this is static, it is called via ss->sec.cache().
00218 */
00219 static void 
00220 CacheSID(sslSessionID *sid)
00221 {
00222     PRUint32  expirationPeriod;
00223     SSL_TRC(8, ("SSL: Cache: sid=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
00224               "time=%x cached=%d",
00225               sid, sid->cached, sid->addr.pr_s6_addr32[0], 
00226               sid->addr.pr_s6_addr32[1], sid->addr.pr_s6_addr32[2],
00227               sid->addr.pr_s6_addr32[3],  sid->port, sid->creationTime,
00228               sid->cached));
00229 
00230     if (sid->cached == in_client_cache)
00231        return;
00232 
00233     if (!sid->urlSvrName) {
00234         /* don't cache this SID because it can never be matched */
00235         return;
00236     }
00237 
00238     /* XXX should be different trace for version 2 vs. version 3 */
00239     if (sid->version < SSL_LIBRARY_VERSION_3_0) {
00240        expirationPeriod = ssl_sid_timeout;
00241        PRINT_BUF(8, (0, "sessionID:",
00242                 sid->u.ssl2.sessionID, sizeof(sid->u.ssl2.sessionID)));
00243        PRINT_BUF(8, (0, "masterKey:",
00244                 sid->u.ssl2.masterKey.data, sid->u.ssl2.masterKey.len));
00245        PRINT_BUF(8, (0, "cipherArg:",
00246                 sid->u.ssl2.cipherArg.data, sid->u.ssl2.cipherArg.len));
00247     } else {
00248        if (sid->u.ssl3.sessionIDLength == 0) 
00249            return;
00250        expirationPeriod = ssl3_sid_timeout;
00251        PRINT_BUF(8, (0, "sessionID:",
00252                     sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength));
00253     }
00254     PORT_Assert(sid->creationTime != 0 && sid->expirationTime != 0);
00255     if (!sid->creationTime)
00256        sid->lastAccessTime = sid->creationTime = ssl_Time();
00257     if (!sid->expirationTime)
00258        sid->expirationTime = sid->creationTime + expirationPeriod;
00259 
00260     /*
00261      * Put sid into the cache.  Bump reference count to indicate that
00262      * cache is holding a reference. Uncache will reduce the cache
00263      * reference.
00264      */
00265     LOCK_CACHE;
00266     sid->references++;
00267     sid->cached = in_client_cache;
00268     sid->next   = cache;
00269     cache       = sid;
00270     UNLOCK_CACHE;
00271 }
00272 
00273 /* 
00274  * If sid "zap" is in the cache,
00275  *    removes sid from cache, and decrements reference count.
00276  * Caller must hold cache lock.
00277  */
00278 static void
00279 UncacheSID(sslSessionID *zap)
00280 {
00281     sslSessionID **sidp = &cache;
00282     sslSessionID *sid;
00283 
00284     if (zap->cached != in_client_cache) {
00285        return;
00286     }
00287 
00288     SSL_TRC(8,("SSL: Uncache: zap=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
00289               "time=%x cipher=%d",
00290               zap, zap->cached, zap->addr.pr_s6_addr32[0],
00291               zap->addr.pr_s6_addr32[1], zap->addr.pr_s6_addr32[2],
00292               zap->addr.pr_s6_addr32[3], zap->port, zap->creationTime,
00293               zap->u.ssl2.cipherType));
00294     if (zap->version < SSL_LIBRARY_VERSION_3_0) {
00295        PRINT_BUF(8, (0, "sessionID:",
00296                     zap->u.ssl2.sessionID, sizeof(zap->u.ssl2.sessionID)));
00297        PRINT_BUF(8, (0, "masterKey:",
00298                     zap->u.ssl2.masterKey.data, zap->u.ssl2.masterKey.len));
00299        PRINT_BUF(8, (0, "cipherArg:",
00300                     zap->u.ssl2.cipherArg.data, zap->u.ssl2.cipherArg.len));
00301     }
00302 
00303     /* See if it's in the cache, if so nuke it */
00304     while ((sid = *sidp) != 0) {
00305        if (sid == zap) {
00306            /*
00307            ** Bingo. Reduce reference count by one so that when
00308            ** everyone is done with the sid we can free it up.
00309            */
00310            *sidp = zap->next;
00311            zap->cached = invalid_cache;
00312            ssl_FreeLockedSID(zap);
00313            return;
00314        }
00315        sidp = &sid->next;
00316     }
00317 }
00318 
00319 /* If sid "zap" is in the cache,
00320  *    removes sid from cache, and decrements reference count.
00321  * Although this function is static, it is called externally via 
00322  *    ss->sec.uncache().
00323  */
00324 static void
00325 LockAndUncacheSID(sslSessionID *zap)
00326 {
00327     LOCK_CACHE;
00328     UncacheSID(zap);
00329     UNLOCK_CACHE;
00330 
00331 }
00332 
00333 /* choose client or server cache functions for this sslsocket. */
00334 void 
00335 ssl_ChooseSessionIDProcs(sslSecurityInfo *sec)
00336 {
00337     if (sec->isServer) {
00338        sec->cache   = ssl_sid_cache;
00339        sec->uncache = ssl_sid_uncache;
00340     } else {
00341        sec->cache   = CacheSID;
00342        sec->uncache = LockAndUncacheSID;
00343     }
00344 }
00345 
00346 /* wipe out the entire client session cache. */
00347 void
00348 SSL_ClearSessionCache(void)
00349 {
00350     LOCK_CACHE;
00351     while(cache != NULL)
00352        UncacheSID(cache);
00353     UNLOCK_CACHE;
00354 }
00355 
00356 /* returns an unsigned int containing the number of seconds in PR_Now() */
00357 PRUint32
00358 ssl_Time(void)
00359 {
00360     PRUint32 myTime;
00361 #if (defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)) && !defined(_WIN32_WCE)
00362     myTime = time(NULL);    /* accurate until the year 2038. */
00363 #else
00364     /* portable, but possibly slower */
00365     PRTime now;
00366     PRInt64 ll;
00367 
00368     now = PR_Now();
00369     LL_I2L(ll, 1000000L);
00370     LL_DIV(now, now, ll);
00371     LL_L2UI(myTime, now);
00372 #endif
00373     return myTime;
00374 }
00375