Implement the CRL cache . Bug 149854
authorjpierre%netscape.com
Fri, 30 Aug 2002 22:57:03 +0000
changeset 3521 e97372ffc4f7d542b45e4f356fd160c7d3e5e05b
parent 3520 558922d2d6848c39ccf3aa3c5850e741c411f1c7
child 3522 0ed4d96eafbf4ebae38fdf1bcb6cde5e4c3812bb
push idunknown
push userunknown
push dateunknown
bugs149854
Implement the CRL cache . Bug 149854
security/nss/lib/certdb/cert.h
security/nss/lib/certdb/certi.h
security/nss/lib/certdb/crl.c
security/nss/lib/certhigh/certvfy.c
security/nss/lib/dev/ckhelper.c
security/nss/lib/dev/ckhelper.h
security/nss/lib/dev/dev.h
security/nss/lib/nss/nssinit.c
security/nss/lib/pki/certificate.c
security/nss/lib/pki/pkibase.c
--- a/security/nss/lib/certdb/cert.h
+++ b/security/nss/lib/certdb/cert.h
@@ -403,16 +403,17 @@ CERT_DecodeDERCrlEx(PRArenaPool *narena,
 
 /* when CRL_DECODE_DONT_COPY_DER is set, the DER is not copied . The
    application must then keep derSignedCrl until it destroys the
    CRL . Ideally, it should allocate derSignedCrl in an arena
    and pass that arena in as the first argument to CERT_DecodeDERCrlEx */
 
 #define CRL_DECODE_DONT_COPY_DER            0x00000001
 #define CRL_DECODE_SKIP_ENTRIES             0x00000002
+#define CRL_DECODE_KEEP_BAD_CRL             0x00000004
 
 /* complete the decoding of a partially decoded CRL, ie. decode the
    entries. Note that entries is an optional field in a CRL, so the
    "entries" pointer in CERTCrlStr may still be NULL even after
    function returns SECSuccess */
 
 extern SECStatus CERT_CompleteCRLDecodeEntries(CERTSignedCrl* crl);
 
@@ -1424,11 +1425,19 @@ extern SECItem *
 CERT_SPKDigestValueForCert(PRArenaPool *arena, CERTCertificate *cert,
 			   SECOidTag digestAlg, SECItem *fill);
 
 /*
  * fill in nsCertType field of the cert based on the cert extension
  */
 extern SECStatus CERT_GetCertType(CERTCertificate *cert);
 
+
+SECStatus InitCRLCache(void);
+SECStatus ShutdownCRLCache(void);
+
+SECStatus CERT_CheckCRL(CERTCertificate* cert, CERTCertificate* issuer,
+                        SECItem* dp, int64 t, void* wincx);
+
+
 SEC_END_PROTOS
 
 #endif /* _CERT_H_ */
--- a/security/nss/lib/certdb/certi.h
+++ b/security/nss/lib/certdb/certi.h
@@ -26,25 +26,152 @@
  * allow others to use your version of this file under the MPL,
  * indicate your decision by deleting the provisions above and
  * replace them with the notice and other provisions required by
  * the GPL.  If you do not delete the provisions above, a recipient
  * may use your version of this file under either the MPL or the
  * GPL.
  */
 /*
- * certt.h - public data structures for the certificate library
+ * certi.h - private data structures for the certificate library
  *
  * $Id$
  */
 #ifndef _CERTI_H_
 #define _CERTI_H_
 
 #include "certt.h"
+#include "nssrwlkt.h"
 
-typedef struct OpaqueCRLFieldsStr                OpaqueCRLFields;
+#define USE_RWLOCK 1
+
+/* all definitions in this file are subject to change */
+
+typedef struct OpaqueCRLFieldsStr OpaqueCRLFields;
+typedef struct CRLEntryCacheStr CRLEntryCache;
+typedef struct CRLDPCacheStr CRLDPCache;
+typedef struct CRLIssuerCacheStr CRLIssuerCache;
+typedef struct CRLCacheStr CRLCache;
 
 struct OpaqueCRLFieldsStr {
-    /* these fields are subject to change */
     PRBool partial;
+    PRBool bad;
+    PRBool badDER;
+    PRBool badExtensions;
+    PRBool deleted;
+    PRBool heapDER;
+};
+
+typedef struct PreAllocatorStr PreAllocator;
+
+struct PreAllocatorStr
+{
+    PRSize len;
+    void* data;
+    PRSize used;
+    PRArenaPool* arena;
+    PRSize extra;
+};
+
+/*  CRL entry cache.
+    This is the same as an entry plus the next/prev pointers for the hash table
+*/
+
+struct CRLEntryCacheStr {
+    CERTCrlEntry entry;
+    CRLEntryCache *prev, *next;
+};
+
+#define CRL_CACHE_INVALID_CRLS              0x0001 /* this state will be set
+        if we have CRL objects with an invalid DER or signature. Can be
+        cleared if the invalid objects are deleted from the token */
+#define CRL_CACHE_LAST_FETCH_FAILED         0x0002 /* this state will be set
+        if the last CRL fetch encountered an error. Can be cleared if a
+        new fetch succeeds */
+
+/*  CRL distribution point cache object
+    This is a cache of CRL entries for a given distribution point of an issuer
+    It is built from a collection of one full and 0 or more delta CRLs.
+*/
+
+struct CRLDPCacheStr {
+#ifdef USE_RWLOCK
+    NSSRWLock* lock;
+#else
+    PRLock* lock;
+#endif
+    CERTCertificate* issuer;    /* DER of cert issuer */
+    SECItem* distributionPoint; /* DER of distribution point. This may be
+                                   NULL when distribution points aren't
+                                   in use (ie. the CA has a single CRL) */
+
+    /* hash table of entries. We use a PLHashTable and pre-allocate the
+       required amount of memory in one shot, so that our allocator can
+       simply pass offsets into it when hashing.
+
+       This won't work anymore when we support delta CRLs and iCRLs, because
+       the size of the hash table will vary over time. At that point, the best
+       solution will be to allocate large CRLEntry structures by modifying
+       the DER decoding template. The extra space would be for next/prev
+       pointers. This would allow entries from different CRLs to be mixed in
+       the same hash table.
+    */
+    PLHashTable* entries;
+    PreAllocator* prebuffer; /* big pre-allocated buffer mentioned above */
+
+    /* array of CRLs matching this distribution point */
+    PRUint32 ncrls;              /* total number of CRLs in crls */
+    CERTSignedCrl** crls;       /* array of all matching DER CRLs
+                                   from all tokens */
+    /* XCRL With iCRLs and multiple DPs, the CRL can be shared accross several
+       issuers. In the future, we'll need to globally recycle the CRL in a
+       separate list in order to avoid extra lookups, decodes, and copies */
+
+    /* pointers to good decoded CRLs used to build the cache */
+    CERTSignedCrl* full;    /* full CRL used for the cache */
+#if 0
+    /* for future use */
+    PRInt32 numdeltas;      /* number of delta CRLs used for the cache */
+    CERTSignedCrl** deltas; /* delta CRLs used for the cache */
+#endif
+    /* invalidity bitflag */
+    PRUint16 invalid;       /* this state will be set if either
+             CRL_CACHE_INVALID_CRLS or CRL_CACHE_LAST_FETCH_FAILED is set.
+             In those cases, all certs are considered revoked as a
+             security precaution. The invalid state can only be cleared
+             during an update if all error states are cleared */
+};
+
+/*  CRL issuer cache object
+    This object tracks all the distribution point caches for a given issuer.
+    XCRL once we support multiple issuing distribution points, this object
+    will be a hash table. For now, it just holds the single CRL distribution
+    point cache structure.
+*/
+
+struct CRLIssuerCacheStr {
+    PRUint32 refcount;
+    CERTCertificate* issuer;
+    CRLDPCache dp;
+    CRLDPCache* dpp;
+#if 0
+    /* XCRL for future use.
+       We don't need to lock at the moment because we only have one DP,
+       which gets created at the same time as this object */
+    NSSRWLock* lock;
+    CRLDPCache** dps;
+    PLHashTable* distributionpoints;
+#endif
+};
+
+/*  CRL revocation cache object
+    This object tracks all the issuer caches
+*/
+
+struct CRLCacheStr {
+    PRLock* lock;
+    /* hash table of issuer to CRLIssuerCacheStr,
+       indexed by issuer DER subject */
+    PLHashTable* issuers;
 };
 
 #endif /* _CERTI_H_ */
+
--- a/security/nss/lib/certdb/crl.c
+++ b/security/nss/lib/certdb/crl.c
@@ -31,27 +31,33 @@
  * GPL.
  */
 
 /*
  * Moved from secpkcs7.c
  *
  * $Id$
  */
-
+ 
 #include "cert.h"
 #include "certi.h"
 #include "secder.h"
 #include "secasn1.h"
 #include "secoid.h"
 #include "certdb.h"
 #include "certxutl.h"
 #include "prtime.h"
 #include "secerr.h"
 #include "pk11func.h"
+#include "dev.h"
+#include "../pk11wrap/secmodti.h"
+#include "../base/nssbase.h"
+#ifdef USE_RWLOCK
+#include "nssrwlk.h"
+#endif
 
 const SEC_ASN1Template SEC_CERTExtensionTemplate[] = {
     { SEC_ASN1_SEQUENCE,
 	  0, NULL, sizeof(CERTCertExtension) },
     { SEC_ASN1_OBJECT_ID,
 	  offsetof(CERTCertExtension,id) },
     { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN,		/* XXX DER_DEFAULT */
 	  offsetof(CERTCertExtension,critical), },
@@ -359,16 +365,30 @@ CERT_KeyFromDERCrl(PRArenaPool *arena, S
     }
 
     key->len =  crlkey.derName.len;
     key->data = crlkey.derName.data;
 
     return(SECSuccess);
 }
 
+#define GetOpaqueCRLFields(x) ((OpaqueCRLFields*)x->opaque)
+
+/*
+PRBool CERT_CRLIsInvalid(CERTSignedCrl* crl)
+{
+    OpaqueCRLFields* extended = NULL;
+
+    if (crl && (extended = (OpaqueCRLFields*) crl->opaque)) {
+        return extended->bad;
+    }
+    return PR_TRUE;
+}
+*/
+
 SECStatus CERT_CompleteCRLDecodeEntries(CERTSignedCrl* crl)
 {
     SECStatus rv = SECSuccess;
     SECItem* crldata = NULL;
     OpaqueCRLFields* extended = NULL;
 
     if ( (!crl) ||
         (!(extended = (OpaqueCRLFields*) crl->opaque))  ) {
@@ -417,16 +437,17 @@ CERT_DecodeDERCrlEx(PRArenaPool *narena,
 	}
     } else {
 	arena = narena;
     }
 
     /* allocate the CRL structure */
     crl = (CERTSignedCrl *)PORT_ArenaZAlloc(arena, sizeof(CERTSignedCrl));
     if ( !crl ) {
+        PORT_SetError(SEC_ERROR_NO_MEMORY);
 	goto loser;
     }
 
     crl->arena = arena;
 
     /* allocate opaque fields */
     crl->opaque = (void*)PORT_ArenaZAlloc(arena, sizeof(OpaqueCRLFields));
     if ( !crl->opaque ) {
@@ -455,20 +476,25 @@ CERT_DecodeDERCrlEx(PRArenaPool *narena,
         crlTemplate = cert_SignedCrlTemplateNoEntries;
         extended->partial = PR_TRUE;
     }
 
     /* decode the CRL info */
     switch (type) {
     case SEC_CRL_TYPE:
         rv = SEC_QuickDERDecodeItem(arena, crl, crlTemplate, crl->derCrl);
-	if (rv != SECSuccess)
-	    break;
+	if (rv != SECSuccess) {
+            extended->badDER = PR_TRUE;
+            break;
+        }
         /* check for critical extentions */
 	rv =  cert_check_crl_version (&crl->crl);
+	if (rv != SECSuccess) {
+            extended->badExtensions = PR_TRUE;
+        }
 	break;
 
     case SEC_KRL_TYPE:
 	rv = SEC_QuickDERDecodeItem
 	     (arena, crl, cert_SignedKrlTemplate, derSignedCrl);
 	break;
     default:
 	rv = SECFailure;
@@ -479,16 +505,21 @@ CERT_DecodeDERCrlEx(PRArenaPool *narena,
 	goto loser;
     }
 
     crl->referenceCount = 1;
     
     return(crl);
     
 loser:
+    if (options & CRL_DECODE_KEEP_BAD_CRL) {
+        extended->bad = PR_TRUE;
+        crl->referenceCount = 1;
+        return(crl);
+    }
 
     if ((narena == NULL) && arena ) {
 	PORT_FreeArena(arena, PR_FALSE);
     }
     
     return(0);
 }
 
@@ -499,68 +530,107 @@ CERTSignedCrl *
 CERT_DecodeDERCrl(PRArenaPool *narena, SECItem *derSignedCrl, int type)
 {
     return CERT_DecodeDERCrlEx(narena, derSignedCrl, type, CRL_DECODE_DEFAULT_OPTIONS);
 }
 
 /*
  * Lookup a CRL in the databases. We mirror the same fast caching data base
  *  caching stuff used by certificates....?
+ * return values :
+ *
+ * SECSuccess means we got a valid DER CRL (passed in "decoded"), or no CRL at all
+ *
+ * SECFailure means we got a fatal error - most likely, we found a CRL,
+ * and it failed decoding, or there was an out of memory error. Do NOT ignore
+ * it and specifically do NOT treat it the same as having no CRL, as this
+ * can compromise security !!! Ideally, you should treat this case as if you
+ * received a "catch-all" CRL where all certs you were looking up are
+ * considered to be revoked
  */
-CERTSignedCrl *
-SEC_FindCrlByKeyOnSlot(PK11SlotInfo *slot, SECItem *crlKey, int type)
+static SECStatus
+SEC_FindCrlByKeyOnSlot(PK11SlotInfo *slot, SECItem *crlKey, int type,
+                       CERTSignedCrl** decoded, PRInt32 decodeoptions)
 {
+    SECStatus rv = SECSuccess;
     CERTSignedCrl *crl = NULL;
-    SECItem *derCrl;
-    CK_OBJECT_HANDLE crlHandle;
+    SECItem *derCrl = NULL;
+    CK_OBJECT_HANDLE crlHandle = 0;
     char *url = NULL;
 
+    PORT_Assert(decoded);
+    if (!decoded) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+
     if (slot) {
 	PK11_ReferenceSlot(slot);
     }
-     
+
+    /* XXX it would be really useful to be able to fetch the CRL directly into an
+       arena. This would avoid a copy later on in the decode step */
     derCrl = PK11_FindCrlByName(&slot, &crlHandle, crlKey, type, &url);
     if (derCrl == NULL) {
 	goto loser;
     }
     PORT_Assert(crlHandle != CK_INVALID_HANDLE);
     
-    crl = CERT_DecodeDERCrl(NULL, derCrl, type);
+    crl = CERT_DecodeDERCrlEx(NULL, derCrl, type, decodeoptions);
     if (crl) {
-	crl->slot = slot;
-	slot = NULL; /* adopt it */
-	crl->pkcs11ID = crlHandle;
-	if (url) {
-	    crl->url = PORT_ArenaStrdup(crl->arena,url);
-	}
+        crl->slot = slot;
+        slot = NULL; /* adopt it */
+        crl->pkcs11ID = crlHandle;
+        if (url) {
+            crl->url = PORT_ArenaStrdup(crl->arena,url);
+        }
+    } else {
+        rv = SECFailure;
     }
+    
     if (url) {
 	PORT_Free(url);
     }
 
 loser:
     if (slot) {
 	PK11_FreeSlot(slot);
     }
+
     if (derCrl) {
-	SECITEM_FreeItem(derCrl,PR_TRUE);
+        /* destroy the DER, unless a decoded CRL was returned with DER
+           allocated on the heap. This is solely for cache purposes */
+        if (crl && (decodeoptions & CRL_DECODE_DONT_COPY_DER)) {
+            /* mark the DER as having come from the heap instead of the
+               arena, so it can be destroyed */
+            GetOpaqueCRLFields(crl)->heapDER = PR_TRUE;
+        } else {
+            SECITEM_FreeItem(derCrl, PR_TRUE);
+        }
     }
-    return(crl);
+
+    *decoded = crl;
+
+    return rv;
 }
 
 SECStatus SEC_DestroyCrl(CERTSignedCrl *crl);
 
 CERTSignedCrl *
 crl_storeCRL (PK11SlotInfo *slot,char *url,
                   CERTSignedCrl *newCrl, SECItem *derCrl, int type)
 {
     CERTSignedCrl *oldCrl = NULL, *crl = NULL;
     CK_OBJECT_HANDLE crlHandle;
 
-    oldCrl = SEC_FindCrlByKeyOnSlot(slot, &newCrl->crl.derName, type);
+    PORT_Assert(newCrl);
+    PORT_Assert(derCrl);
+
+    SEC_FindCrlByKeyOnSlot(slot, &newCrl->crl.derName, type,
+                                &oldCrl, CRL_DECODE_SKIP_ENTRIES);
 
     /* if there is an old crl, make sure the one we are installing
      * is newer. If not, exit out, otherwise delete the old crl.
      */
     if (oldCrl != NULL) {
 	/* if it's already there, quietly continue */
 	if (SECITEM_CompareItem(newCrl->derCrl, oldCrl->derCrl) 
 						== SECEqual) {
@@ -614,17 +684,25 @@ done:
     if (oldCrl) SEC_DestroyCrl(oldCrl);
 
     return crl;
 }
 
 CERTSignedCrl *
 SEC_FindCrlByName(CERTCertDBHandle *handle, SECItem *crlKey, int type)
 {
-	return SEC_FindCrlByKeyOnSlot(NULL,crlKey,type);
+    SECStatus rv = SECSuccess;    
+    CERTSignedCrl* crl = NULL;
+    /* XXX we should check the return value and fail, unfortunately, we can't,
+       because of this legacy exported prototype */
+    SEC_FindCrlByKeyOnSlot(NULL,crlKey,type, &crl, CRL_DECODE_DEFAULT_OPTIONS);
+    if (!crl) {
+        return NULL;
+    }
+    return crl;
 }
 
 /*
  *
  * create a new CRL from DER material.
  *
  * The signature on this CRL must be checked before you
  * load it. ???
@@ -673,16 +751,19 @@ loser:
 SECStatus
 SEC_DestroyCrl(CERTSignedCrl *crl)
 {
     if (crl) {
 	if (crl->referenceCount-- <= 1) {
 	    if (crl->slot) {
 		PK11_FreeSlot(crl->slot);
 	    }
+            if (PR_TRUE == GetOpaqueCRLFields(crl)->heapDER) {
+                SECITEM_FreeItem(crl->derCrl, PR_TRUE);
+            }
 	    PORT_FreeArena(crl->arena, PR_FALSE);
 	}
     }
     return SECSuccess;
 }
 
 SECStatus
 SEC_LookupCrls(CERTCertDBHandle *handle, CERTCrlHeadNode **nodes, int type)
@@ -722,8 +803,846 @@ SEC_LookupCrls(CERTCertDBHandle *handle,
 
 /* These functions simply return the address of the above-declared templates.
 ** This is necessary for Windows DLLs.  Sigh.
 */
 SEC_ASN1_CHOOSER_IMPLEMENT(CERT_IssuerAndSNTemplate)
 SEC_ASN1_CHOOSER_IMPLEMENT(CERT_CrlTemplate)
 SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SetOfSignedCrlTemplate)
 
+static CRLCache crlcache = { NULL, NULL };
+
+/* this needs to be called at NSS initialization time */
+
+SECStatus InitCRLCache(void)
+{
+    if (!crlcache.lock)
+    {
+        crlcache.lock = PR_NewLock();
+        if (!crlcache.lock)
+        {
+            return SECFailure;
+        }
+        crlcache.issuers = PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare,
+                                  PL_CompareValues, NULL, NULL);
+        if (!crlcache.issuers)
+        {
+            PR_DestroyLock(crlcache.lock);
+            crlcache.lock = PR_FALSE;
+            return SECFailure;
+        }
+    }
+    return SECSuccess;
+}
+
+SECStatus IssuerCache_Destroy(CRLIssuerCache* cache);
+
+PRIntn PR_CALLBACK FreeIssuer(PLHashEntry *he, PRIntn i, void *arg)
+{
+    CRLIssuerCache* issuer = NULL;
+    PR_ASSERT(he);
+    if (!he) {
+        return HT_ENUMERATE_NEXT;
+    }
+    issuer = (CRLIssuerCache*) he->value;
+    PR_ASSERT(issuer);
+    if (issuer) {
+        IssuerCache_Destroy(issuer);
+    }
+    return HT_ENUMERATE_NEXT;
+}
+
+SECStatus ShutdownCRLCache(void)
+{
+    PR_ASSERT(crlcache.lock);
+    PR_ASSERT(crlcache.issuers);
+    if (!crlcache.lock || !crlcache.issuers)
+    {
+        return SECFailure;
+    }
+    /* empty the cache */
+    PL_HashTableEnumerateEntries(crlcache.issuers, &FreeIssuer, NULL);
+    PL_HashTableDestroy(crlcache.issuers);
+    PR_DestroyLock(crlcache.lock);
+    return SECSuccess;
+}
+
+SECStatus DP_AddCRL(CRLDPCache* cache, CERTSignedCrl* crl)
+{
+    CERTSignedCrl** newcrls = NULL;
+    PORT_Assert(cache);
+    PORT_Assert(crl);
+    if (!cache || !crl) {
+        return SECFailure;
+    }
+
+    newcrls = (CERTSignedCrl**)PORT_Realloc(cache->crls,
+        (cache->ncrls+1)*sizeof(CERTSignedCrl*));
+    if (!newcrls) {
+        return SECFailure;
+    }
+    cache->crls = newcrls;
+    cache->ncrls++;
+    cache->crls[cache->ncrls-1] = crl;
+    return SECSuccess;
+}
+
+PRBool CRLStillExists(CERTSignedCrl* crl)
+{
+    NSSItem newsubject;
+    SECItem subject;
+    CK_ULONG crl_class;
+    PRStatus status;
+    PK11SlotInfo* slot = NULL;
+    NSSToken* token = NULL;
+    nssCryptokiObject instance;
+    NSSArena* arena;
+    PRBool xstatus = PR_TRUE;
+    SECItem* oldSubject = NULL;
+
+    PORT_Assert(crl);
+    if (!crl) {
+        return PR_FALSE;
+    }
+    slot = crl->slot;
+    PORT_Assert(slot);
+    if (!slot) {
+        return PR_FALSE;
+    }
+    oldSubject = &crl->crl.derName;
+    PR_ASSERT(oldSubject);
+    if (!oldSubject) {
+        return PR_FALSE;
+    }
+
+    /* XXX query subject and type attributes in order to determine if the
+       object has been deleted */
+
+    /* first, make an nssCryptokiObject */
+    instance.handle = crl->pkcs11ID;
+    PORT_Assert(instance.handle);
+    if (!instance.handle) {
+        return PR_FALSE;
+    }
+    instance.token = slot->nssToken;
+    PORT_Assert(instance.token);
+    if (!instance.token) {
+        return PR_FALSE;
+    }
+    instance.isTokenObject = PR_TRUE;
+    instance.label = NULL;
+
+    arena = NSSArena_Create();
+    PORT_Assert(arena);
+    if (!arena) {
+        return PR_FALSE;
+    }
+
+    status = nssCryptokiCRL_GetAttributes(&instance,
+                                          NULL,  /* XXX sessionOpt */
+                                          arena,
+                                          NULL,
+                                          &newsubject,  /* subject */
+                                          &crl_class,   /* class */
+                                          NULL,
+                                          NULL);
+    if (PR_SUCCESS == status) {
+        subject.data = newsubject.data;
+        subject.len = newsubject.size;
+        if (SECITEM_CompareItem(oldSubject, &subject) != SECEqual) {
+            xstatus = PR_FALSE;
+        }
+        if (CKO_NETSCAPE_CRL != crl_class) {
+            xstatus = PR_FALSE;
+        }
+    } else {
+        xstatus = PR_FALSE;
+    }
+    NSSArena_Destroy(arena);
+    return xstatus;
+}
+
+/*
+** Pre-allocator hash allocator ops.
+*/
+static void * PR_CALLBACK
+PreAllocTable(void *pool, PRSize size)
+{
+    PreAllocator* alloc = (PreAllocator*)pool;
+    PR_ASSERT(alloc);
+    if (!alloc)
+    {
+        /* no allocator, or buffer full */
+        return NULL;
+    }
+    if (size > (alloc->len - alloc->used))
+    {
+        alloc->extra += size;
+        return PORT_ArenaAlloc(alloc->arena, size);
+    }
+    alloc->used += size;
+    return (char*) alloc->data + alloc->used - size;
+}
+
+static void PR_CALLBACK
+PreFreeTable(void *pool, void *item)
+{
+}
+
+static PLHashEntry * PR_CALLBACK
+PreAllocEntry(void *pool, const void *key)
+{
+    return PreAllocTable(pool, sizeof(PLHashEntry));
+}
+
+static void PR_CALLBACK
+PreFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
+{
+}
+
+static PLHashAllocOps preAllocOps = {
+    PreAllocTable, PreFreeTable,
+    PreAllocEntry, PreFreeEntry
+};
+
+void PreAllocator_Destroy(PreAllocator* PreAllocator)
+{
+    if (!PreAllocator)
+    {
+        return;
+    }
+    if (PreAllocator->arena)
+    {
+        PORT_FreeArena(PreAllocator->arena, PR_TRUE);
+    }
+    if (PreAllocator->data)
+    {
+        PORT_Free(PreAllocator->data);
+    }
+    PORT_Free(PreAllocator);
+}
+
+PreAllocator* PreAllocator_Create(PRSize size)
+{
+    PreAllocator prebuffer;
+    PreAllocator* prepointer = NULL;
+    memset(&prebuffer, 0, sizeof(PreAllocator));
+    prebuffer.len = size;
+    prebuffer.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    PR_ASSERT(prebuffer.arena);
+    if (!prebuffer.arena) {
+        PreAllocator_Destroy(&prebuffer);
+        return NULL;
+    }
+    if (prebuffer.len) {
+        prebuffer.data = PR_Malloc(prebuffer.len);
+        if (!prebuffer.data) {
+            PreAllocator_Destroy(&prebuffer);
+            return NULL;
+        }
+    } else {
+        prebuffer.data = NULL;
+    }
+    prepointer = (PreAllocator*)PR_Malloc(sizeof(PreAllocator));
+    if (!prepointer) {
+        PreAllocator_Destroy(&prebuffer);
+        return NULL;
+    }
+    *prepointer = prebuffer;
+    return prepointer;
+}
+
+SECStatus DPCache_Fetch(CRLDPCache* cache, int64 t, void* wincx)
+{
+    SECStatus rv = SECSuccess;
+    SECStatus signstatus = SECSuccess;
+    CERTSignedCrl* crlobject = NULL;
+    PRUint32 i=0;
+    /* XCRL For now, we can only get one full CRL. In the future, we'll be able to
+       find more than one object, because of multiple tokens and deltas */
+    rv = SEC_FindCrlByKeyOnSlot(NULL, &cache->issuer->derSubject, SEC_CRL_TYPE,
+                                &crlobject, CRL_DECODE_DONT_COPY_DER |
+                                            CRL_DECODE_SKIP_ENTRIES  |
+                                            CRL_DECODE_KEEP_BAD_CRL);
+    /* if this function fails, something very wrong happened, such as an out
+       of memory error during CRL decoding. We don't want to proceed and must
+       mark the cache object invalid */
+    if (SECFailure == rv) {
+        cache->invalid |= CRL_CACHE_LAST_FETCH_FAILED;
+    } else {
+        cache->invalid &= (~CRL_CACHE_LAST_FETCH_FAILED);
+    }
+
+    if ((SECSuccess == rv) && (!crlobject)) {
+        /* no CRL was found. This is OK */
+        return SECSuccess;
+    }
+
+    /* now check if we already have a binary equivalent DER CRL */
+    for (i=0;i<cache->ncrls;i++) {
+        CERTSignedCrl* existing = cache->crls[i];
+        if (existing && (SECEqual == SECITEM_CompareItem(existing->derCrl, crlobject->derCrl))) {
+            /* yes. Has the matching CRL been marked deleted ? */
+            if (PR_TRUE == GetOpaqueCRLFields(crlobject)->deleted) {
+                /* Yes. Just replace the CK object ID and slot in the existing object.
+                   This avoids an unnecessary signature verification & entry decode */
+                /* XCRL we'll need to lock the CRL here in the future for iCRLs that are
+                   shared between multiple CAs */
+                existing->pkcs11ID = crlobject->pkcs11ID;
+                PK11_FreeSlot(existing->slot); /* release reference to old
+                                                  CRL slot */
+                existing->slot = crlobject->slot; /* adopt new CRL slot */
+                crlobject->slot = NULL; /* clear slot to avoid double-freeing it
+                                           during CRL destroy */
+                rv = SEC_DestroyCrl(crlobject);
+                PORT_Assert(SECSuccess == rv);
+                return rv;
+            } else {
+                /* We got an identical CRL from a different token.
+                   Throw it away. */
+                return SEC_DestroyCrl(crlobject);
+            }
+        }
+    }
+
+    /* add the CRL to our array */
+    if (SECSuccess == rv) {
+        rv = DP_AddCRL(cache, crlobject);
+    }
+    /* check if it is an invalid CRL */
+    if ( (SECSuccess == rv) &&
+         ( (PR_TRUE == GetOpaqueCRLFields(crlobject)->bad) ||
+           (SECSuccess != (signstatus = CERT_VerifySignedData(
+                                                &crlobject->signatureWrap,
+                                                cache->issuer, t, wincx)) ) )
+       ) {
+            if (SECSuccess != signstatus) {
+                PORT_SetError(SEC_ERROR_CRL_BAD_SIGNATURE);
+            }
+        /*  we got a bad CRL. We want to cache it in order to avoid
+            subsequent fetches of this same identical bad CRL. We set
+            the cache to the invalid state to ensure that all certs
+            on this DP are considered revoked from now on. The cache
+            object will remain in this state until the bad CRL object
+            is removed from the token it was fetched from */
+        cache->invalid = PR_TRUE;
+        return SECSuccess;
+    }
+    
+    /* XXX complete the entry decoding */
+    rv = CERT_CompleteCRLDecodeEntries(crlobject);
+    if (SECSuccess == rv) {
+        /* XCRL : if this is a delta, add it to the hash table */
+        /* for now, always build the hash table from the full CRL */
+        CERTCrlEntry** crlEntry = NULL;
+        PRUint32 numEntries = 0;
+        if (cache->entries) {
+            /* we already have a hash table, destroy it */
+            PL_HashTableDestroy(cache->entries);
+            cache->entries = NULL;
+            /* also destroy the PreAllocator */
+            PreAllocator_Destroy(cache->prebuffer);
+            cache->prebuffer = NULL;
+        }
+        /* count CRL entries so we can pre-allocate space for hash table entries */
+        for (crlEntry = crlobject->crl.entries; crlEntry && *crlEntry; crlEntry++) {
+            numEntries++;
+        }
+        cache->prebuffer = PreAllocator_Create(numEntries*sizeof(PLHashEntry));
+        PR_ASSERT(cache->prebuffer);
+        if (cache->prebuffer) {
+            /* create a new hash table */
+            cache->entries = PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare,
+                                      PL_CompareValues, &preAllocOps, cache->prebuffer);
+        }
+        PR_ASSERT(cache->entries);
+        if (!cache->entries) {
+            rv = SECFailure;
+        }
+        if (SECSuccess == rv){
+            /* add all serial numbers to the hash table */
+            for (crlEntry = crlobject->crl.entries; crlEntry && *crlEntry; crlEntry++) {
+                PL_HashTableAdd(cache->entries, &(*crlEntry)->serialNumber, *crlEntry);
+            }
+            cache->full = crlobject;
+        } else {
+            cache->invalid = PR_TRUE;
+        }
+    } else {
+        cache->invalid = PR_TRUE;
+    }
+    return rv;
+}
+
+SECStatus DPCache_Lookup(CRLDPCache* cache, SECItem* sn, CERTCrlEntry** returned)
+{
+    CERTSignedCrl* crl = NULL;
+    CERTCrlEntry* acrlEntry = NULL;
+    if (!cache || !sn) {
+        /* no cache or SN to look up, this is bad */
+        return SECFailure;
+    }
+    if (!cache->full) {
+        /* no CRL means no entry to return, but this is OK */
+        *returned = NULL;
+        return SECSuccess;
+    }
+    crl = cache->full;
+    PR_ASSERT(cache->entries);
+    if (!cache->entries)
+    {
+        return SECFailure;
+    }
+    acrlEntry = PL_HashTableLookup(cache->entries, (void*)sn);
+    if (acrlEntry)
+    {
+        *returned = acrlEntry;
+    }
+    return SECSuccess;
+}
+
+SECStatus DPCache_Update(CRLDPCache* cache, int64 t, void* wincx, PRBool readlocked)
+{
+    /* Update the CRLDPCache now. We don't cache token CRL lookup misses
+       yet, as we have no way of getting notified of new PKCS#11 object
+       creation that happens in a token  */
+    SECStatus rv = SECSuccess;
+    if (!cache) {
+        return SECFailure;
+    }
+
+    if (cache->full) {
+        /* check if the full CRL still exists */
+        if (PR_TRUE != CRLStillExists(cache->full)) {
+            /* the CRL is gone. But first, we need to check if we are
+               the first to do the update. We can't acquire the write lock
+               now or it could create a deadlock */
+#ifdef USE_RWLOCK
+            if (readlocked){
+                NSSRWLock_UnlockRead(cache->lock);
+            }
+            NSSRWLock_LockWrite(cache->lock);
+#else
+            /* no extra locking here, we already hold the PRLock, and it's
+               not re-entrant */
+#endif
+            /* first, we need to check if another thread updated
+               it before we did, and abort if it has been created since
+               we created the lock */
+            if (PR_TRUE != CRLStillExists(cache->full)) {
+                /* the CRL is gone. And we are the one to do the update */
+                /* Mark the CRL deleted, and try to fetch a new one */
+                GetOpaqueCRLFields(cache->full)->deleted = PR_TRUE;
+                rv = DPCache_Fetch(cache, t, wincx);
+            }
+#ifdef USE_RWLOCK
+            if (readlocked){
+                NSSRWLock_LockRead(cache->lock);
+            }
+            NSSRWLock_UnlockWrite(cache->lock);
+#endif
+        }
+    } else {
+        /* this is the first time we fetch a CRL for this DP, or we had
+           an invalid CRL */
+#ifdef USE_RWLOCK
+        if (readlocked) {
+            NSSRWLock_UnlockRead(cache->lock);
+        }
+        NSSRWLock_LockWrite(cache->lock);
+#endif
+        /* check if another thread updated before us, and skip update if so */
+        if (!cache->full)
+        {
+            /* we are the first */
+            rv = DPCache_Fetch(cache, t, wincx);
+        }
+#ifdef USE_RWLOCK
+        if (readlocked) {
+            NSSRWLock_LockRead(cache->lock);
+        }
+        NSSRWLock_UnlockWrite(cache->lock);
+#endif
+    }
+
+    return rv;
+}
+
+SECStatus DPCache_Initialize(CRLDPCache* cache,
+                         CERTCertificate* issuer, SECItem* dp)
+{
+    CK_OBJECT_HANDLE crloid = 0;
+    SECItem* crlder = NULL;
+
+    PORT_Assert(cache);
+    PORT_Assert(issuer);
+    if (!cache || !issuer) {
+        return SECFailure;
+    }
+    memset(cache, 0, sizeof(CRLDPCache));
+#ifdef USE_RWLOCK
+    cache->lock = NSSRWLock_New(NSS_RWLOCK_RANK_NONE, NULL);
+#else
+    cache->lock = PR_NewLock();
+#endif
+    if (!cache->lock)
+    {
+        return SECFailure;
+    }
+    cache->issuer = CERT_DupCertificate(issuer);
+    cache->distributionPoint = SECITEM_DupItem(dp);
+    return SECSuccess;
+}
+
+SECStatus DPCache_Destroy(CRLDPCache* cache)
+{
+    PRUint32 i = 0;
+    PR_ASSERT(cache);
+    if (!cache) {
+        return SECFailure;
+    }
+    if (cache->lock)
+    {
+#ifdef USE_RWLOCK
+        NSSRWLock_Destroy(cache->lock);
+#else
+        PR_DestroyLock(cache->lock);
+#endif
+    }
+    /* destroy all our CRL objects */
+    for (i=0;i<cache->ncrls;i++)
+    {
+        SEC_DestroyCrl(cache->crls[i]);
+    }
+    /* destroy the hash table */
+    if (cache->entries)
+    {
+        PL_HashTableDestroy(cache->entries);
+    }
+    /* free the pre buffer */
+    if (cache->prebuffer)
+    {
+        PreAllocator_Destroy(cache->prebuffer);
+    }
+    /* destroy the cert */
+    if (cache->issuer)
+    {
+        CERT_DestroyCertificate(cache->issuer);
+    }
+    return SECSuccess;
+}
+
+SECStatus IssuerCache_Destroy(CRLIssuerCache* cache)
+{
+    PORT_Assert(cache);
+    if (!cache)
+    {
+        return SECFailure;
+    }
+    if (!--cache->refcount)
+    {
+#if 0
+        /* XCRL */
+        if (cache->lock)
+        {
+            NSSRWLock_Destroy(cache->lock);
+        }
+#endif
+        if (cache->issuer)
+        {
+            CERT_DestroyCertificate(cache->issuer);
+        }
+        DPCache_Destroy(&cache->dp);
+        PR_Free(cache);
+    }
+    return SECSuccess;
+}
+
+SECStatus IssuerCache_Create(CRLIssuerCache** returned,
+                             CERTCertificate* issuer, SECItem* dp)
+{
+    SECStatus rv = SECSuccess;
+    CRLIssuerCache* cache = NULL;
+    PORT_Assert(returned);
+    if (!returned)
+    {
+        return SECFailure;
+    }
+    cache = (CRLIssuerCache*) PR_Malloc(sizeof(CRLIssuerCache));
+    if (!cache)
+    {
+        return SECFailure;
+    }
+    memset(cache, 0, sizeof(CRLIssuerCache));
+#if 0
+    /* XCRL */
+    cache->lock = NSSRWLock_New(NSS_RWLOCK_RANK_NONE, NULL);
+    if (!cache->lock)
+    {
+        rv = SECFailure;
+    }
+#endif
+    cache->refcount = 0;
+    if ((SECSuccess == rv) && (!(cache->issuer = CERT_DupCertificate(issuer))))
+    {
+        rv = SECFailure;
+    }
+
+    if (SECSuccess != rv)
+    {
+        return IssuerCache_Destroy(cache);
+    }
+    *returned = cache;
+    return SECSuccess;
+}
+
+SECStatus IssuerCache_AddDP(CRLIssuerCache* cache,
+                            SECItem* dp, CRLDPCache** newdpc)
+{
+    SECStatus rv = SECSuccess;
+    /* now create the required DP cache object */
+    if (!dp) {
+        /* default distribution point */
+        rv = DPCache_Initialize(&cache->dp, cache->issuer, NULL);
+        if (SECSuccess == rv) {
+            cache->dpp = &cache->dp;
+            if (newdpc) {
+                *newdpc = cache->dpp;
+            }
+        }
+    } else {
+        /* we should never hit this until we support multiple DPs */
+        PORT_Assert(dp);
+        rv = SECFailure;
+        /* XCRL allocate a new distribution point cache object, initialize it,
+           and add it to the hash table of DPs */
+    }
+    return rv;
+}
+
+SECStatus CRLCache_AddIssuer(SECItem* issuerDER, CRLIssuerCache* issuer)
+{    
+    PORT_Assert(issuerDER);
+    PORT_Assert(issuer);
+    PORT_Assert(crlcache.issuers);
+    if (!issuerDER || !issuer || !crlcache.issuers) {
+        return SECFailure;
+    }
+    if (NULL == PL_HashTableAdd(crlcache.issuers, (void*) issuerDER,
+                                (void*) issuer)) {
+        return SECFailure;
+    }
+    return SECSuccess;
+}
+
+SECStatus GetIssuerCache(CRLCache* cache, SECItem* subject, CRLIssuerCache** returned)
+{
+    /* we need to look up the issuer in the hash table */
+    SECStatus rv = SECSuccess;
+    PORT_Assert(cache);
+    PORT_Assert(subject);
+    PORT_Assert(returned);
+    PORT_Assert(crlcache.issuers);
+    if (!cache || !subject || !returned || !crlcache.issuers) {
+        rv = SECFailure;
+    }
+
+    if (SECSuccess == rv){
+        *returned = (CRLIssuerCache*) PL_HashTableLookup(crlcache.issuers,
+                                                         (void*) subject);
+    }
+
+    return rv;
+}
+
+CRLDPCache* GetDPCache(CRLIssuerCache* cache, SECItem* dp)
+{
+    CRLDPCache* dpp = NULL;
+    PORT_Assert(cache);
+    /* XCRL for now we only support the "default" DP, ie. the
+       full CRL. So we can return the global one without locking. In
+       the future we will have a lock */
+    PORT_Assert(NULL == dp);
+    if (!cache || dp) {
+        return NULL;
+    }
+#if 0
+    /* XCRL */
+    NSSRWLock_LockRead(cache->lock);
+#endif
+    dpp = cache->dpp;
+#if 0
+    /* XCRL */
+    NSSRWLock_UnlockRead(cache->lock);
+#endif
+    return dpp;
+}
+
+SECStatus
+CERT_CheckCRL(CERTCertificate* cert, CERTCertificate* issuer, SECItem* dp,
+              int64 t, void* wincx)
+{
+    PRBool lockedwrite = PR_FALSE;
+    SECStatus rv = SECSuccess;
+    SECCertTimeValidity validity;
+    CRLIssuerCache* issuercache = NULL;
+    CRLDPCache* dpcache = NULL;
+    if (!cert || !issuer) {
+        return SECFailure;
+    }
+    /* we must check the cert issuer (or more appropriately, the CRL
+       signer)'s validity time first. If it's expired, then don't go to the
+       cache.
+       If we do and the cache is empty, a CRL will be fetched, but it won't
+       verify because of the expired issuer, causing us to put the cache in
+       the invalid state.
+       If we do and the cache is already populated, we will lookup the cert
+       in the CRL for no good reason. */
+    validity = CERT_CheckCertValidTimes(issuer, t, PR_FALSE);
+    if ( validity != secCertTimeValid ) {
+	return SECFailure;
+    }
+    PORT_Assert(crlcache.lock);
+    if (!crlcache.lock) {
+        /* CRL cache is not initialized */
+        return SECFailure;
+    }
+    PR_Lock(crlcache.lock);
+    rv = GetIssuerCache(&crlcache, &issuer->derSubject, &issuercache);
+    if (SECSuccess != rv) {
+        PR_Unlock(crlcache.lock);
+        return SECFailure;
+    }
+    if (!issuercache) {
+        /* there is no cache for this issuer yet. This means this is the
+           first time we look up a cert from that issuer, and we need to
+           create the cache. Do it within the global cache lock to ensure
+           no two threads will simultaneously try to create the same issuer
+           cache. XXX this could be optimized with a r/w lock at this level
+           too. But the code would have to check if it already exists when
+           adding to the hash table */
+        
+        rv = IssuerCache_Create(&issuercache, issuer, dp);
+        if (SECSuccess == rv && !issuercache) {
+            PORT_Assert(issuercache);
+            rv = SECFailure;
+        }
+
+        if (SECSuccess == rv) {
+            /* This is the first time we look up a cert of this issuer.
+               Create the DPCache for this DP . */
+            rv = IssuerCache_AddDP(issuercache, dp, &dpcache);
+        }
+
+        if (SECSuccess == rv) {
+            /* lock the DPCache for write to ensure the update happens in this thread */
+            lockedwrite = PR_TRUE;
+#ifdef USE_RWLOCK
+            NSSRWLock_LockWrite(dpcache->lock);
+#else
+            PR_Lock(dpcache->lock);
+#endif
+        }
+        
+        if (SECSuccess == rv) {
+            /* now add the new issuer cache to the global hash table of issuers */
+            rv = CRLCache_AddIssuer(&issuercache->issuer->derSubject, issuercache);
+            if (SECSuccess != rv) {
+                /* failure */
+                rv = SECFailure;
+            }
+        }
+
+        /* now unlock the global cache. We only want to lock the hash table
+           addition. Holding it longer would hurt scalability */
+        PR_Unlock(crlcache.lock);
+
+        if (SECSuccess != rv && issuercache) {
+            if (PR_TRUE == lockedwrite) {
+#ifdef USE_RWLOCK
+                NSSRWLock_UnlockWrite(dpcache->lock);
+#else
+                PR_Unlock(dpcache->lock);
+#endif
+            }
+            IssuerCache_Destroy(issuercache);
+            issuercache = NULL;
+        }
+
+        if (SECSuccess != rv) {
+            return SECFailure;
+        }
+    } else {
+        PR_Unlock(crlcache.lock);
+        dpcache = GetDPCache(issuercache, dp);
+    }
+    /* we now have a DPCache that we can use for lookups */
+    /* lock it for read, unless we already locked for write */
+    if (PR_FALSE == lockedwrite)
+    {
+#ifdef USE_RWLOCK
+        NSSRWLock_LockRead(dpcache->lock);
+#else
+        PR_Lock(dpcache->lock);
+#endif
+    }
+    
+    if (SECSuccess == rv) {
+        /* currently there is always one and only one DPCache */
+        PORT_Assert(dpcache);
+        if (dpcache)
+        {
+            /* make sure the DP cache is up to date before using it */
+            rv = DPCache_Update(dpcache, t, wincx, PR_FALSE == lockedwrite);
+        }
+        else
+        {
+            rv = SECFailure;
+        }
+    }
+    if (SECSuccess == rv) {
+        /* now look up the certificate SN in the DP cache's CRL */
+        CERTCrlEntry* entry = NULL;
+        rv = DPCache_Lookup(dpcache, &cert->serialNumber, &entry);
+        if (SECSuccess == rv && entry) {
+            /* check the time if we have one */
+            if (entry->revocationDate.data && entry->revocationDate.len) {
+                int64 revocationDate = 0;
+                if (SECSuccess == DER_UTCTimeToTime(&revocationDate,
+                                                    &entry->revocationDate)) {
+                    /* we got a good revocation date, only consider the
+                       certificate revoked if the time we are inquiring about
+                       is past the revocation date */
+                    if (t>=revocationDate) {
+                        rv = SECFailure;
+                    }
+                } else {
+                    /* invalid revocation date, consider the certificate
+                       permanently revoked */
+                    rv = SECFailure;
+                }
+            } else {
+                /* no revocation date, certificate is permanently revoked */
+                rv = SECFailure;
+            }
+            if (SECFailure == rv) {
+                PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE);
+            }
+        }
+    }
+    if (PR_TRUE == lockedwrite) {
+#ifdef USE_RWLOCK
+        NSSRWLock_UnlockWrite(dpcache->lock);
+#else
+        PR_Unlock(dpcache->lock);
+#endif
+    } else {
+#ifdef USE_RWLOCK
+        NSSRWLock_UnlockRead(dpcache->lock);
+#else
+        PR_Unlock(dpcache->lock);
+#endif
+    }
+
+    PORT_Assert(issuercache);
+    return rv;
+}
+
--- a/security/nss/lib/certhigh/certvfy.c
+++ b/security/nss/lib/certhigh/certvfy.c
@@ -108,17 +108,17 @@ CERT_VerifySignedData(CERTSignedData *sd
 	return(SECFailure);
     }
 
     /* get cert's public key */
     pubKey = CERT_ExtractPublicKey(cert);
     if ( !pubKey ) {
 	return(SECFailure);
     }
-    
+
     /* check the signature */
     sig = sd->signature;
     DER_ConvertBitString(&sig);
 
     algid = SECOID_GetAlgorithmTag(&sd->signatureAlgorithm);
     rv = VFY_VerifyData(sd->data.data, sd->data.len, pubKey, &sig,
 			algid, wincx);
 
@@ -262,47 +262,17 @@ done:
     if (crl) SEC_DestroyCrl(crl);
     return rv;
 }
 
 SECStatus
 SEC_CheckCRL(CERTCertDBHandle *handle,CERTCertificate *cert,
 	     CERTCertificate *caCert, int64 t, void * wincx)
 {
-    CERTSignedCrl *crl = NULL;
-    SECStatus rv = SECSuccess;
-    CERTCrlEntry **crlEntry;
-
-    /* first look up the CRL */
-    crl = SEC_FindCrlByName(handle,&caCert->derSubject, SEC_CRL_TYPE);
-    if (crl == NULL) {
-	/* XXX for now no CRL is ok */
-	goto done;
-    }
-
-    /* now verify the CRL signature */
-    rv = CERT_VerifySignedData(&crl->signatureWrap, caCert, t, wincx);
-    if (rv != SECSuccess) {
-	PORT_SetError(SEC_ERROR_CRL_BAD_SIGNATURE);
-        rv = SECWouldBlock; /* Soft error, ask the user */
-    	goto done;
-    }
-
-    /* now make sure the key is not on the revocation list */
-    for (crlEntry = crl->crl.entries; crlEntry && *crlEntry; crlEntry++) {
-	if (SECITEM_CompareItem(&(*crlEntry)->serialNumber,&cert->serialNumber) == SECEqual) {
-	    PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE);
-	    rv = SECFailure; /* cert is revoked */
-	    goto done;
-	}
-    }
-
-done:
-    if (crl) SEC_DestroyCrl(crl);
-    return rv;
+    return CERT_CheckCRL(cert, caCert, NULL, t, wincx);
 }
 
 /*
  * Find the issuer of a cert.  Use the authorityKeyID if it exists.
  */
 CERTCertificate *
 CERT_FindCertIssuer(CERTCertificate *cert, int64 validTime, SECCertUsage usage)
 {
--- a/security/nss/lib/dev/ckhelper.c
+++ b/security/nss/lib/dev/ckhelper.c
@@ -606,38 +606,46 @@ nssCryptokiTrust_GetAttributes
 
 NSS_IMPLEMENT PRStatus
 nssCryptokiCRL_GetAttributes
 (
   nssCryptokiObject *crlObject,
   nssSession *sessionOpt,
   NSSArena *arenaOpt,
   NSSItem *encodingOpt,
+  NSSItem *subjectOpt,
+  CK_ULONG* crl_class,
   NSSUTF8 **urlOpt,
   PRBool *isKRLOpt
 )
 {
     PRStatus status;
     NSSSlot *slot;
     nssSession *session;
     CK_ATTRIBUTE_PTR attr;
-    CK_ATTRIBUTE crl_template[5];
+    CK_ATTRIBUTE crl_template[7];
     CK_ULONG crl_size;
     PRUint32 i;
 
     NSS_CK_TEMPLATE_START(crl_template, attr, crl_size);
+    if (crl_class) {
+        NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_CLASS);
+    }
     if (encodingOpt) {
 	NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_VALUE);
     }
     if (urlOpt) {
 	NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_NETSCAPE_URL);
     }
     if (isKRLOpt) {
 	NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_NETSCAPE_KRL);
     }
+    if (subjectOpt) {
+	NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_SUBJECT);
+    }
     NSS_CK_TEMPLATE_FINISH(crl_template, attr, crl_size);
 
     status = nssToken_GetCachedObjectAttributes(crlObject->token, NULL,
                                                 crlObject, 
                                                 CKO_NETSCAPE_CRL,
                                                 crl_template, crl_size);
     if (status != PR_SUCCESS) {
 	session = sessionOpt ? 
@@ -650,25 +658,31 @@ nssCryptokiCRL_GetAttributes
 	                                   arenaOpt, session, slot);
 	nssSlot_Destroy(slot);
 	if (status != PR_SUCCESS) {
 	    return status;
 	}
     }
 
     i=0;
+    if (crl_class) {
+        NSS_CK_ATTRIBUTE_TO_ULONG(&crl_template[i], *crl_class); i++;
+    }
     if (encodingOpt) {
 	NSS_CK_ATTRIBUTE_TO_ITEM(&crl_template[i], encodingOpt); i++;
     }
     if (urlOpt) {
 	NSS_CK_ATTRIBUTE_TO_UTF8(&crl_template[i], *urlOpt); i++;
     }
     if (isKRLOpt) {
 	NSS_CK_ATTRIBUTE_TO_BOOL(&crl_template[i], *isKRLOpt); i++;
     }
+    if (subjectOpt) {
+	NSS_CK_ATTRIBUTE_TO_ITEM(&crl_template[i], subjectOpt); i++;
+    }
     return PR_SUCCESS;
 }
 
 NSS_IMPLEMENT PRStatus
 nssCryptokiPrivateKey_SetCertificate
 (
   nssCryptokiObject *keyObject,
   nssSession *sessionOpt,
--- a/security/nss/lib/dev/ckhelper.h
+++ b/security/nss/lib/dev/ckhelper.h
@@ -113,16 +113,21 @@ NSS_EXTERN_DATA const NSSItem g_ck_class
     if ((attrib)->ulValueLen > 0) {                      \
 	if (*((CK_BBOOL*)(attrib)->pValue) == CK_TRUE) { \
 	    boolvar = PR_TRUE;                           \
 	} else {                                         \
 	    boolvar = PR_FALSE;                          \
 	}                                                \
     }
 
+#define NSS_CK_ATTRIBUTE_TO_ULONG(attrib, ulongvar)      \
+    if ((attrib)->ulValueLen > 0) {                      \
+	ulongvar = *((CK_ULONG*)(attrib)->pValue);       \
+    }
+
 /* NSS_CK_ATTRIBUTE_TO_UTF8(attrib, str)
  *
  * Convert a CK_ATTRIBUTE to a string.
  */
 #define NSS_CK_ATTRIBUTE_TO_UTF8(attrib, str)      \
     str = (NSSUTF8 *)((attrib)->pValue);
 
 /* NSS_CK_ITEM_TO_ATTRIBUTE(item, attrib)
--- a/security/nss/lib/dev/dev.h
+++ b/security/nss/lib/dev/dev.h
@@ -750,16 +750,18 @@ nssCryptokiTrust_GetAttributes
 
 NSS_EXTERN PRStatus
 nssCryptokiCRL_GetAttributes
 (
   nssCryptokiObject *crlObject,
   nssSession *sessionOpt,
   NSSArena *arenaOpt,
   NSSItem *encodingOpt,
+  NSSItem * subjectOpt,
+  CK_ULONG * crl_class,
   NSSUTF8 **urlOpt,
   PRBool *isKRLOpt
 );
 
 /* I'm including this to handle import of certificates in NSS 3.5.  This
  * function will set the cert-related attributes of a key, in order to
  * associate it with a cert.  Does it stay like this for 4.0?
  */
--- a/security/nss/lib/nss/nssinit.c
+++ b/security/nss/lib/nss/nssinit.c
@@ -406,16 +406,20 @@ nss_Init(const char *configdir, const ch
     char *lcertPrefix = NULL;
     char *lkeyPrefix = NULL;
     char *lsecmodName = NULL;
 
     if (nss_IsInitted) {
 	return SECSuccess;
     }
 
+    if (SECSuccess != InitCRLCache()) {
+        return SECFailure;
+    }
+
     flags = nss_makeFlags(readOnly,noCertDB,noModDB,forceOpen,
 					pk11_password_required, optimizeSpace);
     if (flags == NULL) return rv;
 
     /*
      * configdir is double nested, and Windows uses the same character
      * for file seps as we use for escapes! (sigh).
      */
@@ -531,16 +535,17 @@ NSS_NoDB_Init(const char * configdir)
 			PR_TRUE,PR_TRUE,PR_TRUE,PR_TRUE,PR_TRUE,PR_TRUE);
 }
 
 SECStatus
 NSS_Shutdown(void)
 {
     SECStatus rv;
 
+    ShutdownCRLCache();
     SECOID_Shutdown();
     STAN_Shutdown();
     rv = SECMOD_Shutdown();
     nss_IsInitted = PR_FALSE;
     return rv;
 }
 
 
--- a/security/nss/lib/pki/certificate.c
+++ b/security/nss/lib/pki/certificate.c
@@ -1093,16 +1093,18 @@ nssCRL_Create
 	return (NSSCRL *)NULL;
     }
     rvCRL->object = *object;
     /* XXX should choose instance based on some criteria */
     status = nssCryptokiCRL_GetAttributes(object->instances[0],
                                           NULL,  /* XXX sessionOpt */
                                           arena,
                                           &rvCRL->encoding,
+                                          NULL, /* subject */
+                                          NULL, /* class */
                                           &rvCRL->url,
                                           &rvCRL->isKRL);
     if (status != PR_SUCCESS) {
 	return (NSSCRL *)NULL;
     }
     return rvCRL;
 }
 
--- a/security/nss/lib/pki/pkibase.c
+++ b/security/nss/lib/pki/pkibase.c
@@ -1152,16 +1152,18 @@ crl_getUIDFromObject(nssPKIObject *o, NS
 static PRStatus
 crl_getUIDFromInstance(nssCryptokiObject *instance, NSSItem *uid, 
                        NSSArena *arena)
 {
     return nssCryptokiCRL_GetAttributes(instance,
                                         NULL,    /* XXX sessionOpt */
                                         arena,   /* arena    */
                                         &uid[0], /* encoding */
+                                        NULL,    /* subject  */
+                                        NULL,    /* class    */
                                         NULL,    /* url      */
                                         NULL);   /* isKRL    */
 }
 
 static nssPKIObject *
 crl_createObject(nssPKIObject *o)
 {
     return (nssPKIObject *)nssCRL_Create(o);