1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 /*
6  * Moved from secpkcs7.c
7  */
8 
9 #include "cert.h"
10 #include "certi.h"
11 #include "secder.h"
12 #include "secasn1.h"
13 #include "secoid.h"
14 #include "certdb.h"
15 #include "certxutl.h"
16 #include "prtime.h"
17 #include "secerr.h"
18 #include "pk11func.h"
19 #include "dev.h"
20 #include "dev3hack.h"
21 #include "nssbase.h"
22 #if defined(DPC_RWLOCK) || defined(GLOBAL_RWLOCK)
23 #include "nssrwlk.h"
24 #endif
25 #include "pk11priv.h"
26 
27 const SEC_ASN1Template SEC_CERTExtensionTemplate[] = {
28     { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCertExtension) },
29     { SEC_ASN1_OBJECT_ID, offsetof(CERTCertExtension, id) },
30     { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN, /* XXX DER_DEFAULT */
31       offsetof(CERTCertExtension, critical) },
32     { SEC_ASN1_OCTET_STRING, offsetof(CERTCertExtension, value) },
33     { 0 }
34 };
35 
36 static const SEC_ASN1Template SEC_CERTExtensionsTemplate[] = {
37     { SEC_ASN1_SEQUENCE_OF, 0, SEC_CERTExtensionTemplate }
38 };
39 
40 /*
41  * XXX Also, these templates need to be tested; Lisa did the obvious
42  * translation but they still should be verified.
43  */
44 
45 const SEC_ASN1Template CERT_IssuerAndSNTemplate[] = {
46     { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTIssuerAndSN) },
47     { SEC_ASN1_SAVE, offsetof(CERTIssuerAndSN, derIssuer) },
48     { SEC_ASN1_INLINE, offsetof(CERTIssuerAndSN, issuer), CERT_NameTemplate },
49     { SEC_ASN1_INTEGER, offsetof(CERTIssuerAndSN, serialNumber) },
50     { 0 }
51 };
52 
53 SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
54 SEC_ASN1_MKSUB(CERT_TimeChoiceTemplate)
55 
56 static const SEC_ASN1Template cert_CrlKeyTemplate[] = {
57     { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCrlKey) },
58     { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof(CERTCrlKey, dummy) },
59     { SEC_ASN1_SKIP },
60     { SEC_ASN1_ANY, offsetof(CERTCrlKey, derName) },
61     { SEC_ASN1_SKIP_REST },
62     { 0 }
63 };
64 
65 static const SEC_ASN1Template cert_CrlEntryTemplate[] = {
66     { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCrlEntry) },
67     { SEC_ASN1_INTEGER, offsetof(CERTCrlEntry, serialNumber) },
68     { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTCrlEntry, revocationDate),
69       SEC_ASN1_SUB(CERT_TimeChoiceTemplate) },
70     { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE_OF,
71       offsetof(CERTCrlEntry, extensions), SEC_CERTExtensionTemplate },
72     { 0 }
73 };
74 
75 const SEC_ASN1Template CERT_CrlTemplate[] = {
76     { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCrl) },
77     { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof(CERTCrl, version) },
78     { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTCrl, signatureAlg),
79       SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
80     { SEC_ASN1_SAVE, offsetof(CERTCrl, derName) },
81     { SEC_ASN1_INLINE, offsetof(CERTCrl, name), CERT_NameTemplate },
82     { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTCrl, lastUpdate),
83       SEC_ASN1_SUB(CERT_TimeChoiceTemplate) },
84     { SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL | SEC_ASN1_XTRN,
85       offsetof(CERTCrl, nextUpdate), SEC_ASN1_SUB(CERT_TimeChoiceTemplate) },
86     { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE_OF, offsetof(CERTCrl, entries),
87       cert_CrlEntryTemplate },
88     { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
89           SEC_ASN1_EXPLICIT | 0,
90       offsetof(CERTCrl, extensions), SEC_CERTExtensionsTemplate },
91     { 0 }
92 };
93 
94 const SEC_ASN1Template CERT_CrlTemplateNoEntries[] = {
95     { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCrl) },
96     { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof(CERTCrl, version) },
97     { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTCrl, signatureAlg),
98       SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
99     { SEC_ASN1_SAVE, offsetof(CERTCrl, derName) },
100     { SEC_ASN1_INLINE, offsetof(CERTCrl, name), CERT_NameTemplate },
101     { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTCrl, lastUpdate),
102       SEC_ASN1_SUB(CERT_TimeChoiceTemplate) },
103     { SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL | SEC_ASN1_XTRN,
104       offsetof(CERTCrl, nextUpdate), SEC_ASN1_SUB(CERT_TimeChoiceTemplate) },
105     { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE_OF |
106       SEC_ASN1_SKIP }, /* skip entries */
107     { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
108           SEC_ASN1_EXPLICIT | 0,
109       offsetof(CERTCrl, extensions), SEC_CERTExtensionsTemplate },
110     { 0 }
111 };
112 
113 const SEC_ASN1Template CERT_CrlTemplateEntriesOnly[] = {
114     { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCrl) },
115     { SEC_ASN1_SKIP | SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL },
116     { SEC_ASN1_SKIP },
117     { SEC_ASN1_SKIP },
118     { SEC_ASN1_SKIP | SEC_ASN1_INLINE | SEC_ASN1_XTRN,
119       offsetof(CERTCrl, lastUpdate), SEC_ASN1_SUB(CERT_TimeChoiceTemplate) },
120     { SEC_ASN1_SKIP | SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL | SEC_ASN1_XTRN,
121       offsetof(CERTCrl, nextUpdate), SEC_ASN1_SUB(CERT_TimeChoiceTemplate) },
122     { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE_OF, offsetof(CERTCrl, entries),
123       cert_CrlEntryTemplate }, /* decode entries */
124     { SEC_ASN1_SKIP_REST },
125     { 0 }
126 };
127 
128 const SEC_ASN1Template CERT_SignedCrlTemplate[] = {
129     { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTSignedCrl) },
130     { SEC_ASN1_SAVE, offsetof(CERTSignedCrl, signatureWrap.data) },
131     { SEC_ASN1_INLINE, offsetof(CERTSignedCrl, crl), CERT_CrlTemplate },
132     { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
133       offsetof(CERTSignedCrl, signatureWrap.signatureAlgorithm),
134       SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
135     { SEC_ASN1_BIT_STRING, offsetof(CERTSignedCrl, signatureWrap.signature) },
136     { 0 }
137 };
138 
139 static const SEC_ASN1Template cert_SignedCrlTemplateNoEntries[] = {
140     { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTSignedCrl) },
141     { SEC_ASN1_SAVE, offsetof(CERTSignedCrl, signatureWrap.data) },
142     { SEC_ASN1_INLINE, offsetof(CERTSignedCrl, crl),
143       CERT_CrlTemplateNoEntries },
144     { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
145       offsetof(CERTSignedCrl, signatureWrap.signatureAlgorithm),
146       SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
147     { SEC_ASN1_BIT_STRING, offsetof(CERTSignedCrl, signatureWrap.signature) },
148     { 0 }
149 };
150 
151 const SEC_ASN1Template CERT_SetOfSignedCrlTemplate[] = {
152     { SEC_ASN1_SET_OF, 0, CERT_SignedCrlTemplate },
153 };
154 
155 /* get CRL version */
156 int
cert_get_crl_version(CERTCrl * crl)157 cert_get_crl_version(CERTCrl* crl)
158 {
159     /* CRL version is defaulted to v1 */
160     int version = SEC_CRL_VERSION_1;
161     if (crl && crl->version.data != 0) {
162         version = (int)DER_GetUInteger(&crl->version);
163     }
164     return version;
165 }
166 
167 /* check the entries in the CRL */
168 SECStatus
cert_check_crl_entries(CERTCrl * crl)169 cert_check_crl_entries(CERTCrl* crl)
170 {
171     CERTCrlEntry** entries;
172     CERTCrlEntry* entry;
173     PRBool hasCriticalExten = PR_FALSE;
174     SECStatus rv = SECSuccess;
175 
176     if (!crl) {
177         return SECFailure;
178     }
179 
180     if (crl->entries == NULL) {
181         /* CRLs with no entries are valid */
182         return (SECSuccess);
183     }
184 
185     /* Look in the crl entry extensions.  If there is a critical extension,
186        then the crl version must be v2; otherwise, it should be v1.
187      */
188     entries = crl->entries;
189     while (*entries) {
190         entry = *entries;
191         if (entry->extensions) {
192             /* If there is a critical extension in the entries, then the
193                CRL must be of version 2.  If we already saw a critical
194                extension,
195                there is no need to check the version again.
196             */
197             if (hasCriticalExten == PR_FALSE) {
198                 hasCriticalExten = cert_HasCriticalExtension(entry->extensions);
199                 if (hasCriticalExten) {
200                     if (cert_get_crl_version(crl) != SEC_CRL_VERSION_2) {
201                         /* only CRL v2 critical extensions are supported */
202                         PORT_SetError(SEC_ERROR_CRL_V1_CRITICAL_EXTENSION);
203                         rv = SECFailure;
204                         break;
205                     }
206                 }
207             }
208 
209             /* For each entry, make sure that it does not contain an unknown
210                critical extension.  If it does, we must reject the CRL since
211                we don't know how to process the extension.
212             */
213             if (cert_HasUnknownCriticalExten(entry->extensions) == PR_TRUE) {
214                 PORT_SetError(SEC_ERROR_CRL_UNKNOWN_CRITICAL_EXTENSION);
215                 rv = SECFailure;
216                 break;
217             }
218         }
219         ++entries;
220     }
221     return (rv);
222 }
223 
224 /* Check the version of the CRL.  If there is a critical extension in the crl
225    or crl entry, then the version must be v2. Otherwise, it should be v1. If
226    the crl contains critical extension(s), then we must recognized the
227    extension's OID.
228    */
229 SECStatus
cert_check_crl_version(CERTCrl * crl)230 cert_check_crl_version(CERTCrl* crl)
231 {
232     PRBool hasCriticalExten = PR_FALSE;
233     int version = cert_get_crl_version(crl);
234 
235     if (version > SEC_CRL_VERSION_2) {
236         PORT_SetError(SEC_ERROR_CRL_INVALID_VERSION);
237         return (SECFailure);
238     }
239 
240     /* Check the crl extensions for a critial extension.  If one is found,
241        and the version is not v2, then we are done.
242      */
243     if (crl->extensions) {
244         hasCriticalExten = cert_HasCriticalExtension(crl->extensions);
245         if (hasCriticalExten) {
246             if (version != SEC_CRL_VERSION_2) {
247                 /* only CRL v2 critical extensions are supported */
248                 PORT_SetError(SEC_ERROR_CRL_V1_CRITICAL_EXTENSION);
249                 return (SECFailure);
250             }
251             /* make sure that there is no unknown critical extension */
252             if (cert_HasUnknownCriticalExten(crl->extensions) == PR_TRUE) {
253                 PORT_SetError(SEC_ERROR_CRL_UNKNOWN_CRITICAL_EXTENSION);
254                 return (SECFailure);
255             }
256         }
257     }
258 
259     return (SECSuccess);
260 }
261 
262 /*
263  * Generate a database key, based on the issuer name from a
264  * DER crl.
265  */
266 SECStatus
CERT_KeyFromDERCrl(PLArenaPool * arena,SECItem * derCrl,SECItem * key)267 CERT_KeyFromDERCrl(PLArenaPool* arena, SECItem* derCrl, SECItem* key)
268 {
269     SECStatus rv;
270     CERTSignedData sd;
271     CERTCrlKey crlkey;
272     PLArenaPool* myArena;
273 
274     if (!arena) {
275         /* arena needed for QuickDER */
276         myArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
277     } else {
278         myArena = arena;
279     }
280     PORT_Memset(&sd, 0, sizeof(sd));
281     rv = SEC_QuickDERDecodeItem(myArena, &sd, CERT_SignedDataTemplate, derCrl);
282     if (SECSuccess == rv) {
283         PORT_Memset(&crlkey, 0, sizeof(crlkey));
284         rv = SEC_QuickDERDecodeItem(myArena, &crlkey, cert_CrlKeyTemplate,
285                                     &sd.data);
286     }
287 
288     /* make a copy so the data doesn't point to memory inside derCrl, which
289        may be temporary */
290     if (SECSuccess == rv) {
291         rv = SECITEM_CopyItem(arena, key, &crlkey.derName);
292     }
293 
294     if (myArena != arena) {
295         PORT_FreeArena(myArena, PR_FALSE);
296     }
297 
298     return rv;
299 }
300 
301 #define GetOpaqueCRLFields(x) ((OpaqueCRLFields*)x->opaque)
302 
303 SECStatus
CERT_CompleteCRLDecodeEntries(CERTSignedCrl * crl)304 CERT_CompleteCRLDecodeEntries(CERTSignedCrl* crl)
305 {
306     SECStatus rv = SECSuccess;
307     SECItem* crldata = NULL;
308     OpaqueCRLFields* extended = NULL;
309 
310     if ((!crl) || (!(extended = (OpaqueCRLFields*)crl->opaque)) ||
311         (PR_TRUE == extended->decodingError)) {
312         rv = SECFailure;
313     } else {
314         if (PR_FALSE == extended->partial) {
315             /* the CRL has already been fully decoded */
316             return SECSuccess;
317         }
318         if (PR_TRUE == extended->badEntries) {
319             /* the entries decoding already failed */
320             return SECFailure;
321         }
322         crldata = &crl->signatureWrap.data;
323         if (!crldata) {
324             rv = SECFailure;
325         }
326     }
327 
328     if (SECSuccess == rv) {
329         rv = SEC_QuickDERDecodeItem(crl->arena, &crl->crl,
330                                     CERT_CrlTemplateEntriesOnly, crldata);
331         if (SECSuccess == rv) {
332             extended->partial = PR_FALSE; /* successful decode, avoid
333                 decoding again */
334         } else {
335             extended->decodingError = PR_TRUE;
336             extended->badEntries = PR_TRUE;
337             /* cache the decoding failure. If it fails the first time,
338                it will fail again, which will grow the arena and leak
339                memory, so we want to avoid it */
340         }
341         rv = cert_check_crl_entries(&crl->crl);
342         if (rv != SECSuccess) {
343             extended->badExtensions = PR_TRUE;
344         }
345     }
346     return rv;
347 }
348 
349 /*
350  * take a DER CRL and decode it into a CRL structure
351  * allow reusing the input DER without making a copy
352  */
353 CERTSignedCrl*
CERT_DecodeDERCrlWithFlags(PLArenaPool * narena,SECItem * derSignedCrl,int type,PRInt32 options)354 CERT_DecodeDERCrlWithFlags(PLArenaPool* narena, SECItem* derSignedCrl, int type,
355                            PRInt32 options)
356 {
357     PLArenaPool* arena;
358     CERTSignedCrl* crl;
359     SECStatus rv;
360     OpaqueCRLFields* extended = NULL;
361     const SEC_ASN1Template* crlTemplate = CERT_SignedCrlTemplate;
362     PRInt32 testOptions = options;
363 
364     PORT_Assert(derSignedCrl);
365     if (!derSignedCrl) {
366         PORT_SetError(SEC_ERROR_INVALID_ARGS);
367         return NULL;
368     }
369 
370     /* Adopting DER requires not copying it.  Code that sets ADOPT flag
371      * but doesn't set DONT_COPY probably doesn't know What it is doing.
372      * That condition is a programming error in the caller.
373      */
374     testOptions &= (CRL_DECODE_ADOPT_HEAP_DER | CRL_DECODE_DONT_COPY_DER);
375     PORT_Assert(testOptions != CRL_DECODE_ADOPT_HEAP_DER);
376     if (testOptions == CRL_DECODE_ADOPT_HEAP_DER) {
377         PORT_SetError(SEC_ERROR_INVALID_ARGS);
378         return NULL;
379     }
380 
381     /* make a new arena if needed */
382     if (narena == NULL) {
383         arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
384         if (!arena) {
385             return NULL;
386         }
387     } else {
388         arena = narena;
389     }
390 
391     /* allocate the CRL structure */
392     crl = (CERTSignedCrl*)PORT_ArenaZAlloc(arena, sizeof(CERTSignedCrl));
393     if (!crl) {
394         PORT_SetError(SEC_ERROR_NO_MEMORY);
395         goto loser;
396     }
397 
398     crl->arena = arena;
399 
400     /* allocate opaque fields */
401     crl->opaque = (void*)PORT_ArenaZAlloc(arena, sizeof(OpaqueCRLFields));
402     if (!crl->opaque) {
403         goto loser;
404     }
405     extended = (OpaqueCRLFields*)crl->opaque;
406     if (options & CRL_DECODE_ADOPT_HEAP_DER) {
407         extended->heapDER = PR_TRUE;
408     }
409     if (options & CRL_DECODE_DONT_COPY_DER) {
410         crl->derCrl = derSignedCrl; /* DER is not copied . The application
411                                        must keep derSignedCrl until it
412                                        destroys the CRL */
413     } else {
414         crl->derCrl = (SECItem*)PORT_ArenaZAlloc(arena, sizeof(SECItem));
415         if (crl->derCrl == NULL) {
416             goto loser;
417         }
418         rv = SECITEM_CopyItem(arena, crl->derCrl, derSignedCrl);
419         if (rv != SECSuccess) {
420             goto loser;
421         }
422     }
423 
424     /* Save the arena in the inner crl for CRL extensions support */
425     crl->crl.arena = arena;
426     if (options & CRL_DECODE_SKIP_ENTRIES) {
427         crlTemplate = cert_SignedCrlTemplateNoEntries;
428         extended->partial = PR_TRUE;
429     }
430 
431     /* decode the CRL info */
432     switch (type) {
433         case SEC_CRL_TYPE:
434             rv = SEC_QuickDERDecodeItem(arena, crl, crlTemplate, crl->derCrl);
435             if (rv != SECSuccess) {
436                 extended->badDER = PR_TRUE;
437                 break;
438             }
439             /* check for critical extensions */
440             rv = cert_check_crl_version(&crl->crl);
441             if (rv != SECSuccess) {
442                 extended->badExtensions = PR_TRUE;
443                 break;
444             }
445 
446             if (PR_TRUE == extended->partial) {
447                 /* partial decoding, don't verify entries */
448                 break;
449             }
450 
451             rv = cert_check_crl_entries(&crl->crl);
452             if (rv != SECSuccess) {
453                 extended->badExtensions = PR_TRUE;
454             }
455 
456             break;
457 
458         default:
459             PORT_SetError(SEC_ERROR_INVALID_ARGS);
460             rv = SECFailure;
461             break;
462     }
463 
464     if (rv != SECSuccess) {
465         goto loser;
466     }
467 
468     crl->referenceCount = 1;
469 
470     return (crl);
471 
472 loser:
473     if (options & CRL_DECODE_KEEP_BAD_CRL) {
474         if (extended) {
475             extended->decodingError = PR_TRUE;
476         }
477         if (crl) {
478             crl->referenceCount = 1;
479             return (crl);
480         }
481     }
482 
483     if ((narena == NULL) && arena) {
484         PORT_FreeArena(arena, PR_FALSE);
485     }
486 
487     return (0);
488 }
489 
490 /*
491  * take a DER CRL and decode it into a CRL structure
492  */
493 CERTSignedCrl*
CERT_DecodeDERCrl(PLArenaPool * narena,SECItem * derSignedCrl,int type)494 CERT_DecodeDERCrl(PLArenaPool* narena, SECItem* derSignedCrl, int type)
495 {
496     return CERT_DecodeDERCrlWithFlags(narena, derSignedCrl, type,
497                                       CRL_DECODE_DEFAULT_OPTIONS);
498 }
499 
500 /*
501  * Lookup a CRL in the databases. We mirror the same fast caching data base
502  *  caching stuff used by certificates....?
503  * return values :
504  *
505  * SECSuccess means we got a valid decodable DER CRL, or no CRL at all.
506  * Caller may distinguish those cases by the value returned in "decoded".
507  * When DER CRL is not found, error code will be SEC_ERROR_CRL_NOT_FOUND.
508  *
509  * SECFailure means we got a fatal error - most likely, we found a CRL,
510  * and it failed decoding, or there was an out of memory error. Do NOT ignore
511  * it and specifically do NOT treat it the same as having no CRL, as this
512  * can compromise security !!! Ideally, you should treat this case as if you
513  * received a "catch-all" CRL where all certs you were looking up are
514  * considered to be revoked
515  */
516 static SECStatus
SEC_FindCrlByKeyOnSlot(PK11SlotInfo * slot,SECItem * crlKey,int type,CERTSignedCrl ** decoded,PRInt32 decodeoptions)517 SEC_FindCrlByKeyOnSlot(PK11SlotInfo* slot, SECItem* crlKey, int type,
518                        CERTSignedCrl** decoded, PRInt32 decodeoptions)
519 {
520     SECStatus rv = SECSuccess;
521     CERTSignedCrl* crl = NULL;
522     SECItem* derCrl = NULL;
523     CK_OBJECT_HANDLE crlHandle = 0;
524     char* url = NULL;
525 
526     PORT_Assert(decoded);
527     if (!decoded) {
528         PORT_SetError(SEC_ERROR_INVALID_ARGS);
529         return SECFailure;
530     }
531 
532     derCrl = PK11_FindCrlByName(&slot, &crlHandle, crlKey, type, &url);
533     if (derCrl == NULL) {
534         /* if we had a problem other than the CRL just didn't exist, return
535          * a failure to the upper level */
536         int nsserror = PORT_GetError();
537         if (nsserror != SEC_ERROR_CRL_NOT_FOUND) {
538             rv = SECFailure;
539         }
540         goto loser;
541     }
542     PORT_Assert(crlHandle != CK_INVALID_HANDLE);
543     /* PK11_FindCrlByName obtained a slot reference. */
544 
545     /* derCRL is a fresh HEAP copy made for us by PK11_FindCrlByName.
546        Force adoption of the DER CRL from the heap - this will cause it
547        to be automatically freed when SEC_DestroyCrl is invoked */
548     decodeoptions |= (CRL_DECODE_ADOPT_HEAP_DER | CRL_DECODE_DONT_COPY_DER);
549 
550     crl = CERT_DecodeDERCrlWithFlags(NULL, derCrl, type, decodeoptions);
551     if (crl) {
552         crl->slot = slot;
553         slot = NULL;   /* adopt it */
554         derCrl = NULL; /* adopted by the crl struct */
555         crl->pkcs11ID = crlHandle;
556         if (url) {
557             crl->url = PORT_ArenaStrdup(crl->arena, url);
558         }
559     } else {
560         rv = SECFailure;
561     }
562 
563     if (url) {
564         PORT_Free(url);
565     }
566 
567     if (slot) {
568         PK11_FreeSlot(slot);
569     }
570 
571 loser:
572     if (derCrl) {
573         SECITEM_FreeItem(derCrl, PR_TRUE);
574     }
575 
576     *decoded = crl;
577 
578     return rv;
579 }
580 
581 CERTSignedCrl*
crl_storeCRL(PK11SlotInfo * slot,char * url,CERTSignedCrl * newCrl,SECItem * derCrl,int type)582 crl_storeCRL(PK11SlotInfo* slot, char* url, CERTSignedCrl* newCrl,
583              SECItem* derCrl, int type)
584 {
585     CERTSignedCrl *oldCrl = NULL, *crl = NULL;
586     PRBool deleteOldCrl = PR_FALSE;
587     CK_OBJECT_HANDLE crlHandle = CK_INVALID_HANDLE;
588 
589     PORT_Assert(newCrl);
590     PORT_Assert(derCrl);
591     PORT_Assert(type == SEC_CRL_TYPE);
592 
593     if (type != SEC_CRL_TYPE) {
594         PORT_SetError(SEC_ERROR_INVALID_ARGS);
595         return NULL;
596     }
597 
598     /* we can't use the cache here because we must look in the same
599        token */
600     (void)SEC_FindCrlByKeyOnSlot(slot, &newCrl->crl.derName, type, &oldCrl,
601                                  CRL_DECODE_SKIP_ENTRIES);
602     /* if there is an old crl on the token, make sure the one we are
603        installing is newer. If not, exit out, otherwise delete the
604        old crl.
605      */
606     if (oldCrl != NULL) {
607         /* if it's already there, quietly continue */
608         if (SECITEM_CompareItem(newCrl->derCrl, oldCrl->derCrl) == SECEqual) {
609             crl = newCrl;
610             crl->slot = PK11_ReferenceSlot(slot);
611             crl->pkcs11ID = oldCrl->pkcs11ID;
612             if (oldCrl->url && !url)
613                 url = oldCrl->url;
614             if (url)
615                 crl->url = PORT_ArenaStrdup(crl->arena, url);
616             goto done;
617         }
618         if (!SEC_CrlIsNewer(&newCrl->crl, &oldCrl->crl)) {
619             PORT_SetError(SEC_ERROR_OLD_CRL);
620             goto done;
621         }
622 
623         /* if we have a url in the database, use that one */
624         if (oldCrl->url && !url) {
625             url = oldCrl->url;
626         }
627 
628         /* really destroy this crl */
629         /* first drum it out of the permanment Data base */
630         deleteOldCrl = PR_TRUE;
631     }
632 
633     /* invalidate CRL cache for this issuer */
634     CERT_CRLCacheRefreshIssuer(NULL, &newCrl->crl.derName);
635     /* Write the new entry into the data base */
636     crlHandle = PK11_PutCrl(slot, derCrl, &newCrl->crl.derName, url, type);
637     if (crlHandle != CK_INVALID_HANDLE) {
638         crl = newCrl;
639         crl->slot = PK11_ReferenceSlot(slot);
640         crl->pkcs11ID = crlHandle;
641         if (url) {
642             crl->url = PORT_ArenaStrdup(crl->arena, url);
643         }
644     }
645 
646 done:
647     if (oldCrl) {
648         if (deleteOldCrl && crlHandle != CK_INVALID_HANDLE) {
649             SEC_DeletePermCRL(oldCrl);
650         }
651         SEC_DestroyCrl(oldCrl);
652     }
653 
654     return crl;
655 }
656 
657 /*
658  *
659  * create a new CRL from DER material.
660  *
661  * The signature on this CRL must be checked before you
662  * load it. ???
663  */
664 CERTSignedCrl*
SEC_NewCrl(CERTCertDBHandle * handle,char * url,SECItem * derCrl,int type)665 SEC_NewCrl(CERTCertDBHandle* handle, char* url, SECItem* derCrl, int type)
666 {
667     CERTSignedCrl* retCrl = NULL;
668     PK11SlotInfo* slot = PK11_GetInternalKeySlot();
669     retCrl =
670         PK11_ImportCRL(slot, derCrl, url, type, NULL, CRL_IMPORT_BYPASS_CHECKS,
671                        NULL, CRL_DECODE_DEFAULT_OPTIONS);
672     PK11_FreeSlot(slot);
673 
674     return retCrl;
675 }
676 
677 CERTSignedCrl*
SEC_FindCrlByDERCert(CERTCertDBHandle * handle,SECItem * derCrl,int type)678 SEC_FindCrlByDERCert(CERTCertDBHandle* handle, SECItem* derCrl, int type)
679 {
680     PLArenaPool* arena;
681     SECItem crlKey;
682     SECStatus rv;
683     CERTSignedCrl* crl = NULL;
684 
685     /* create a scratch arena */
686     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
687     if (arena == NULL) {
688         return (NULL);
689     }
690 
691     /* extract the database key from the cert */
692     rv = CERT_KeyFromDERCrl(arena, derCrl, &crlKey);
693     if (rv != SECSuccess) {
694         goto loser;
695     }
696 
697     /* find the crl */
698     crl = SEC_FindCrlByName(handle, &crlKey, type);
699 
700 loser:
701     PORT_FreeArena(arena, PR_FALSE);
702     return (crl);
703 }
704 
705 CERTSignedCrl*
SEC_DupCrl(CERTSignedCrl * acrl)706 SEC_DupCrl(CERTSignedCrl* acrl)
707 {
708     if (acrl) {
709         PR_ATOMIC_INCREMENT(&acrl->referenceCount);
710         return acrl;
711     }
712     return NULL;
713 }
714 
715 SECStatus
SEC_DestroyCrl(CERTSignedCrl * crl)716 SEC_DestroyCrl(CERTSignedCrl* crl)
717 {
718     if (crl) {
719         if (PR_ATOMIC_DECREMENT(&crl->referenceCount) < 1) {
720             if (crl->slot) {
721                 PK11_FreeSlot(crl->slot);
722             }
723             if (GetOpaqueCRLFields(crl) &&
724                 PR_TRUE == GetOpaqueCRLFields(crl)->heapDER) {
725                 SECITEM_FreeItem(crl->derCrl, PR_TRUE);
726             }
727             if (crl->arena) {
728                 PORT_FreeArena(crl->arena, PR_FALSE);
729             }
730         }
731         return SECSuccess;
732     } else {
733         return SECFailure;
734     }
735 }
736 
737 SECStatus
SEC_LookupCrls(CERTCertDBHandle * handle,CERTCrlHeadNode ** nodes,int type)738 SEC_LookupCrls(CERTCertDBHandle* handle, CERTCrlHeadNode** nodes, int type)
739 {
740     CERTCrlHeadNode* head;
741     PLArenaPool* arena = NULL;
742     SECStatus rv;
743 
744     *nodes = NULL;
745 
746     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
747     if (arena == NULL) {
748         return SECFailure;
749     }
750 
751     /* build a head structure */
752     head = (CERTCrlHeadNode*)PORT_ArenaAlloc(arena, sizeof(CERTCrlHeadNode));
753     head->arena = arena;
754     head->first = NULL;
755     head->last = NULL;
756     head->dbhandle = handle;
757 
758     /* Look up the proper crl types */
759     *nodes = head;
760 
761     rv = PK11_LookupCrls(head, type, NULL);
762 
763     if (rv != SECSuccess) {
764         if (arena) {
765             PORT_FreeArena(arena, PR_FALSE);
766             *nodes = NULL;
767         }
768     }
769 
770     return rv;
771 }
772 
773 /* These functions simply return the address of the above-declared templates.
774 ** This is necessary for Windows DLLs.  Sigh.
775 */
776 SEC_ASN1_CHOOSER_IMPLEMENT(CERT_IssuerAndSNTemplate)
777 SEC_ASN1_CHOOSER_IMPLEMENT(CERT_CrlTemplate)
778 SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SignedCrlTemplate)
779 SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SetOfSignedCrlTemplate)
780 
781 /* CRL cache code starts here */
782 
783 /* constructor */
784 static SECStatus CachedCrl_Create(CachedCrl** returned, CERTSignedCrl* crl,
785                                   CRLOrigin origin);
786 /* destructor */
787 static SECStatus CachedCrl_Destroy(CachedCrl* crl);
788 
789 /* create hash table of CRL entries */
790 static SECStatus CachedCrl_Populate(CachedCrl* crlobject);
791 
792 /* empty the cache content */
793 static SECStatus CachedCrl_Depopulate(CachedCrl* crl);
794 
795 /* are these CRLs the same, as far as the cache is concerned ?
796    Or are they the same token object, but with different DER ? */
797 
798 static SECStatus CachedCrl_Compare(CachedCrl* a, CachedCrl* b, PRBool* isDupe,
799                                    PRBool* isUpdated);
800 
801 /* create a DPCache object */
802 static SECStatus DPCache_Create(CRLDPCache** returned, CERTCertificate* issuer,
803                                 const SECItem* subject, SECItem* dp);
804 
805 /* destructor for CRL DPCache object */
806 static SECStatus DPCache_Destroy(CRLDPCache* cache);
807 
808 /* add a new CRL object to the dynamic array of CRLs of the DPCache, and
809    returns the cached CRL object . Needs write access to DPCache. */
810 static SECStatus DPCache_AddCRL(CRLDPCache* cache, CachedCrl* crl,
811                                 PRBool* added);
812 
813 /* fetch the CRL for this DP from the PKCS#11 tokens */
814 static SECStatus DPCache_FetchFromTokens(CRLDPCache* cache, PRTime vfdate,
815                                          void* wincx);
816 
817 /* update the content of the CRL cache, including fetching of CRLs, and
818    reprocessing with specified issuer and date */
819 static SECStatus DPCache_GetUpToDate(CRLDPCache* cache, CERTCertificate* issuer,
820                                      PRBool readlocked, PRTime vfdate,
821                                      void* wincx);
822 
823 /* returns true if there are CRLs from PKCS#11 slots */
824 static PRBool DPCache_HasTokenCRLs(CRLDPCache* cache);
825 
826 /* remove CRL at offset specified */
827 static SECStatus DPCache_RemoveCRL(CRLDPCache* cache, PRUint32 offset);
828 
829 /* Pick best CRL to use . needs write access */
830 static SECStatus DPCache_SelectCRL(CRLDPCache* cache);
831 
832 /* create an issuer cache object (per CA subject ) */
833 static SECStatus IssuerCache_Create(CRLIssuerCache** returned,
834                                     CERTCertificate* issuer,
835                                     const SECItem* subject, const SECItem* dp);
836 
837 /* destructor for CRL IssuerCache object */
838 SECStatus IssuerCache_Destroy(CRLIssuerCache* cache);
839 
840 /* add a DPCache to the issuer cache */
841 static SECStatus IssuerCache_AddDP(CRLIssuerCache* cache,
842                                    CERTCertificate* issuer,
843                                    const SECItem* subject, const SECItem* dp,
844                                    CRLDPCache** newdpc);
845 
846 /* get a particular DPCache object from an IssuerCache */
847 static CRLDPCache* IssuerCache_GetDPCache(CRLIssuerCache* cache,
848                                           const SECItem* dp);
849 
850 /*
851 ** Pre-allocator hash allocator ops.
852 */
853 
854 /* allocate memory for hash table */
855 static void* PR_CALLBACK
PreAllocTable(void * pool,PRSize size)856 PreAllocTable(void* pool, PRSize size)
857 {
858     PreAllocator* alloc = (PreAllocator*)pool;
859     PORT_Assert(alloc);
860     if (!alloc) {
861         /* no allocator, or buffer full */
862         return NULL;
863     }
864     if (size > (alloc->len - alloc->used)) {
865         /* initial buffer full, let's use the arena */
866         alloc->extra += size;
867         return PORT_ArenaAlloc(alloc->arena, size);
868     }
869     /* use the initial buffer */
870     alloc->used += size;
871     return (char*)alloc->data + alloc->used - size;
872 }
873 
874 /* free hash table memory.
875    Individual PreAllocator elements cannot be freed, so this is a no-op. */
876 static void PR_CALLBACK
PreFreeTable(void * pool,void * item)877 PreFreeTable(void* pool, void* item)
878 {
879 }
880 
881 /* allocate memory for hash table */
882 static PLHashEntry* PR_CALLBACK
PreAllocEntry(void * pool,const void * key)883 PreAllocEntry(void* pool, const void* key)
884 {
885     return PreAllocTable(pool, sizeof(PLHashEntry));
886 }
887 
888 /* free hash table entry.
889    Individual PreAllocator elements cannot be freed, so this is a no-op. */
890 static void PR_CALLBACK
PreFreeEntry(void * pool,PLHashEntry * he,PRUintn flag)891 PreFreeEntry(void* pool, PLHashEntry* he, PRUintn flag)
892 {
893 }
894 
895 /* methods required for PL hash table functions */
896 static PLHashAllocOps preAllocOps = { PreAllocTable, PreFreeTable,
897                                       PreAllocEntry, PreFreeEntry };
898 
899 /* destructor for PreAllocator object */
900 void
PreAllocator_Destroy(PreAllocator * allocator)901 PreAllocator_Destroy(PreAllocator* allocator)
902 {
903     if (!allocator) {
904         return;
905     }
906     if (allocator->arena) {
907         PORT_FreeArena(allocator->arena, PR_TRUE);
908     }
909 }
910 
911 /* constructor for PreAllocator object */
912 PreAllocator*
PreAllocator_Create(PRSize size)913 PreAllocator_Create(PRSize size)
914 {
915     PLArenaPool* arena = NULL;
916     PreAllocator* prebuffer = NULL;
917     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
918     if (!arena) {
919         return NULL;
920     }
921     prebuffer = (PreAllocator*)PORT_ArenaZAlloc(arena, sizeof(PreAllocator));
922     if (!prebuffer) {
923         PORT_FreeArena(arena, PR_TRUE);
924         return NULL;
925     }
926     prebuffer->arena = arena;
927 
928     if (size) {
929         prebuffer->len = size;
930         prebuffer->data = PORT_ArenaAlloc(arena, size);
931         if (!prebuffer->data) {
932             PORT_FreeArena(arena, PR_TRUE);
933             return NULL;
934         }
935     }
936     return prebuffer;
937 }
938 
939 /* global Named CRL cache object */
940 static NamedCRLCache namedCRLCache = { NULL, NULL };
941 
942 /* global CRL cache object */
943 static CRLCache crlcache = { NULL, NULL };
944 
945 /* initial state is off */
946 static PRBool crlcache_initialized = PR_FALSE;
947 
948 PRTime CRLCache_Empty_TokenFetch_Interval = 60 * 1000000; /* how often
949     to query the tokens for CRL objects, in order to discover new objects, if
950     the cache does not contain any token CRLs . In microseconds */
951 
952 PRTime CRLCache_TokenRefetch_Interval = 600 * 1000000; /* how often
953    to query the tokens for CRL objects, in order to discover new objects, if
954    the cache already contains token CRLs In microseconds */
955 
956 PRTime CRLCache_ExistenceCheck_Interval = 60 * 1000000; /* how often to check
957     if a token CRL object still exists. In microseconds */
958 
959 /* this function is called at NSS initialization time */
960 SECStatus
InitCRLCache(void)961 InitCRLCache(void)
962 {
963     if (PR_FALSE == crlcache_initialized) {
964         PORT_Assert(NULL == crlcache.lock);
965         PORT_Assert(NULL == crlcache.issuers);
966         PORT_Assert(NULL == namedCRLCache.lock);
967         PORT_Assert(NULL == namedCRLCache.entries);
968         if (crlcache.lock || crlcache.issuers || namedCRLCache.lock ||
969             namedCRLCache.entries) {
970             /* CRL cache already partially initialized */
971             PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
972             return SECFailure;
973         }
974 #ifdef GLOBAL_RWLOCK
975         crlcache.lock = NSSRWLock_New(NSS_RWLOCK_RANK_NONE, NULL);
976 #else
977         crlcache.lock = PR_NewLock();
978 #endif
979         namedCRLCache.lock = PR_NewLock();
980         crlcache.issuers = PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare,
981                                            PL_CompareValues, NULL, NULL);
982         namedCRLCache.entries = PL_NewHashTable(
983             0, SECITEM_Hash, SECITEM_HashCompare, PL_CompareValues, NULL, NULL);
984         if (!crlcache.lock || !namedCRLCache.lock || !crlcache.issuers ||
985             !namedCRLCache.entries) {
986             if (crlcache.lock) {
987 #ifdef GLOBAL_RWLOCK
988                 NSSRWLock_Destroy(crlcache.lock);
989 #else
990                 PR_DestroyLock(crlcache.lock);
991 #endif
992                 crlcache.lock = NULL;
993             }
994             if (namedCRLCache.lock) {
995                 PR_DestroyLock(namedCRLCache.lock);
996                 namedCRLCache.lock = NULL;
997             }
998             if (crlcache.issuers) {
999                 PL_HashTableDestroy(crlcache.issuers);
1000                 crlcache.issuers = NULL;
1001             }
1002             if (namedCRLCache.entries) {
1003                 PL_HashTableDestroy(namedCRLCache.entries);
1004                 namedCRLCache.entries = NULL;
1005             }
1006 
1007             return SECFailure;
1008         }
1009         crlcache_initialized = PR_TRUE;
1010         return SECSuccess;
1011     } else {
1012         PORT_Assert(crlcache.lock);
1013         PORT_Assert(crlcache.issuers);
1014         if ((NULL == crlcache.lock) || (NULL == crlcache.issuers)) {
1015             /* CRL cache not fully initialized */
1016             return SECFailure;
1017         } else {
1018             /* CRL cache already initialized */
1019             return SECSuccess;
1020         }
1021     }
1022 }
1023 
1024 /* destructor for CRL DPCache object */
1025 static SECStatus
DPCache_Destroy(CRLDPCache * cache)1026 DPCache_Destroy(CRLDPCache* cache)
1027 {
1028     PRUint32 i = 0;
1029     PORT_Assert(cache);
1030     if (!cache) {
1031         PORT_Assert(0);
1032         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1033         return SECFailure;
1034     }
1035     if (cache->lock) {
1036 #ifdef DPC_RWLOCK
1037         NSSRWLock_Destroy(cache->lock);
1038 #else
1039         PR_DestroyLock(cache->lock);
1040 #endif
1041     } else {
1042         PORT_Assert(0);
1043         return SECFailure;
1044     }
1045     /* destroy all our CRL objects */
1046     for (i = 0; i < cache->ncrls; i++) {
1047         if (!cache->crls || !cache->crls[i] ||
1048             SECSuccess != CachedCrl_Destroy(cache->crls[i])) {
1049             return SECFailure;
1050         }
1051     }
1052     /* free the array of CRLs */
1053     if (cache->crls) {
1054         PORT_Free(cache->crls);
1055     }
1056     /* destroy the cert */
1057     if (cache->issuerDERCert) {
1058         SECITEM_FreeItem(cache->issuerDERCert, PR_TRUE);
1059     }
1060     /* free the subject */
1061     if (cache->subject) {
1062         SECITEM_FreeItem(cache->subject, PR_TRUE);
1063     }
1064     /* free the distribution points */
1065     if (cache->distributionPoint) {
1066         SECITEM_FreeItem(cache->distributionPoint, PR_TRUE);
1067     }
1068     PORT_Free(cache);
1069     return SECSuccess;
1070 }
1071 
1072 /* destructor for CRL IssuerCache object */
1073 SECStatus
IssuerCache_Destroy(CRLIssuerCache * cache)1074 IssuerCache_Destroy(CRLIssuerCache* cache)
1075 {
1076     PORT_Assert(cache);
1077     if (!cache) {
1078         PORT_Assert(0);
1079         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1080         return SECFailure;
1081     }
1082 #ifdef XCRL
1083     if (cache->lock) {
1084         NSSRWLock_Destroy(cache->lock);
1085     } else {
1086         PORT_Assert(0);
1087         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1088         return SECFailure;
1089     }
1090     if (cache->issuer) {
1091         CERT_DestroyCertificate(cache->issuer);
1092     }
1093 #endif
1094     /* free the subject */
1095     if (cache->subject) {
1096         SECITEM_FreeItem(cache->subject, PR_TRUE);
1097     }
1098     if (SECSuccess != DPCache_Destroy(cache->dpp)) {
1099         PORT_Assert(0);
1100         return SECFailure;
1101     }
1102     PORT_Free(cache);
1103     return SECSuccess;
1104 }
1105 
1106 /* create a named CRL entry object */
1107 static SECStatus
NamedCRLCacheEntry_Create(NamedCRLCacheEntry ** returned)1108 NamedCRLCacheEntry_Create(NamedCRLCacheEntry** returned)
1109 {
1110     NamedCRLCacheEntry* entry = NULL;
1111     if (!returned) {
1112         PORT_Assert(0);
1113         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1114         return SECFailure;
1115     }
1116     *returned = NULL;
1117     entry = (NamedCRLCacheEntry*)PORT_ZAlloc(sizeof(NamedCRLCacheEntry));
1118     if (!entry) {
1119         return SECFailure;
1120     }
1121     *returned = entry;
1122     return SECSuccess;
1123 }
1124 
1125 /* destroy a named CRL entry object */
1126 static SECStatus
NamedCRLCacheEntry_Destroy(NamedCRLCacheEntry * entry)1127 NamedCRLCacheEntry_Destroy(NamedCRLCacheEntry* entry)
1128 {
1129     if (!entry) {
1130         PORT_Assert(0);
1131         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1132         return SECFailure;
1133     }
1134     if (entry->crl) {
1135         /* named CRL cache owns DER memory */
1136         SECITEM_ZfreeItem(entry->crl, PR_TRUE);
1137     }
1138     if (entry->canonicalizedName) {
1139         SECITEM_FreeItem(entry->canonicalizedName, PR_TRUE);
1140     }
1141     PORT_Free(entry);
1142     return SECSuccess;
1143 }
1144 
1145 /* callback function used in hash table destructor */
1146 static PRIntn PR_CALLBACK
FreeIssuer(PLHashEntry * he,PRIntn i,void * arg)1147 FreeIssuer(PLHashEntry* he, PRIntn i, void* arg)
1148 {
1149     CRLIssuerCache* issuer = NULL;
1150     SECStatus* rv = (SECStatus*)arg;
1151 
1152     PORT_Assert(he);
1153     if (!he) {
1154         return HT_ENUMERATE_NEXT;
1155     }
1156     issuer = (CRLIssuerCache*)he->value;
1157     PORT_Assert(issuer);
1158     if (issuer) {
1159         if (SECSuccess != IssuerCache_Destroy(issuer)) {
1160             PORT_Assert(rv);
1161             if (rv) {
1162                 *rv = SECFailure;
1163             }
1164             return HT_ENUMERATE_NEXT;
1165         }
1166     }
1167     return HT_ENUMERATE_NEXT;
1168 }
1169 
1170 /* callback function used in hash table destructor */
1171 static PRIntn PR_CALLBACK
FreeNamedEntries(PLHashEntry * he,PRIntn i,void * arg)1172 FreeNamedEntries(PLHashEntry* he, PRIntn i, void* arg)
1173 {
1174     NamedCRLCacheEntry* entry = NULL;
1175     SECStatus* rv = (SECStatus*)arg;
1176 
1177     PORT_Assert(he);
1178     if (!he) {
1179         return HT_ENUMERATE_NEXT;
1180     }
1181     entry = (NamedCRLCacheEntry*)he->value;
1182     PORT_Assert(entry);
1183     if (entry) {
1184         if (SECSuccess != NamedCRLCacheEntry_Destroy(entry)) {
1185             PORT_Assert(rv);
1186             if (rv) {
1187                 *rv = SECFailure;
1188             }
1189             return HT_ENUMERATE_NEXT;
1190         }
1191     }
1192     return HT_ENUMERATE_NEXT;
1193 }
1194 
1195 /* needs to be called at NSS shutdown time
1196    This will destroy the global CRL cache, including
1197    - the hash table of issuer cache objects
1198    - the issuer cache objects
1199    - DPCache objects in issuer cache objects */
1200 SECStatus
ShutdownCRLCache(void)1201 ShutdownCRLCache(void)
1202 {
1203     SECStatus rv = SECSuccess;
1204     if (PR_FALSE == crlcache_initialized && !crlcache.lock &&
1205         !crlcache.issuers) {
1206         /* CRL cache has already been shut down */
1207         return SECSuccess;
1208     }
1209     if (PR_TRUE == crlcache_initialized &&
1210         (!crlcache.lock || !crlcache.issuers || !namedCRLCache.lock ||
1211          !namedCRLCache.entries)) {
1212         /* CRL cache has partially been shut down */
1213         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1214         return SECFailure;
1215     }
1216     /* empty the CRL cache */
1217     /* free the issuers */
1218     PL_HashTableEnumerateEntries(crlcache.issuers, &FreeIssuer, &rv);
1219     /* free the hash table of issuers */
1220     PL_HashTableDestroy(crlcache.issuers);
1221     crlcache.issuers = NULL;
1222 /* free the global lock */
1223 #ifdef GLOBAL_RWLOCK
1224     NSSRWLock_Destroy(crlcache.lock);
1225 #else
1226     PR_DestroyLock(crlcache.lock);
1227 #endif
1228     crlcache.lock = NULL;
1229 
1230     /* empty the named CRL cache. This must be done after freeing the CRL
1231      * cache, since some CRLs in this cache are in the memory for the other  */
1232     /* free the entries */
1233     PL_HashTableEnumerateEntries(namedCRLCache.entries, &FreeNamedEntries, &rv);
1234     /* free the hash table of issuers */
1235     PL_HashTableDestroy(namedCRLCache.entries);
1236     namedCRLCache.entries = NULL;
1237     /* free the global lock */
1238     PR_DestroyLock(namedCRLCache.lock);
1239     namedCRLCache.lock = NULL;
1240 
1241     crlcache_initialized = PR_FALSE;
1242     return rv;
1243 }
1244 
1245 /* add a new CRL object to the dynamic array of CRLs of the DPCache, and
1246    returns the cached CRL object . Needs write access to DPCache. */
1247 static SECStatus
DPCache_AddCRL(CRLDPCache * cache,CachedCrl * newcrl,PRBool * added)1248 DPCache_AddCRL(CRLDPCache* cache, CachedCrl* newcrl, PRBool* added)
1249 {
1250     CachedCrl** newcrls = NULL;
1251     PRUint32 i = 0;
1252     PORT_Assert(cache);
1253     PORT_Assert(newcrl);
1254     PORT_Assert(added);
1255     if (!cache || !newcrl || !added) {
1256         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1257         return SECFailure;
1258     }
1259 
1260     *added = PR_FALSE;
1261     /* before adding a new CRL, check if it is a duplicate */
1262     for (i = 0; i < cache->ncrls; i++) {
1263         CachedCrl* existing = NULL;
1264         SECStatus rv = SECSuccess;
1265         PRBool dupe = PR_FALSE, updated = PR_FALSE;
1266         if (!cache->crls) {
1267             PORT_Assert(0);
1268             return SECFailure;
1269         }
1270         existing = cache->crls[i];
1271         if (!existing) {
1272             PORT_Assert(0);
1273             return SECFailure;
1274         }
1275         rv = CachedCrl_Compare(existing, newcrl, &dupe, &updated);
1276         if (SECSuccess != rv) {
1277             PORT_Assert(0);
1278             PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1279             return SECFailure;
1280         }
1281         if (PR_TRUE == dupe) {
1282             /* dupe */
1283             PORT_SetError(SEC_ERROR_CRL_ALREADY_EXISTS);
1284             return SECSuccess;
1285         }
1286         if (PR_TRUE == updated) {
1287             /* this token CRL is in the same slot and has the same object ID,
1288                but different content. We need to remove the old object */
1289             if (SECSuccess != DPCache_RemoveCRL(cache, i)) {
1290                 PORT_Assert(0);
1291                 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1292                 return PR_FALSE;
1293             }
1294         }
1295     }
1296 
1297     newcrls = (CachedCrl**)PORT_Realloc(cache->crls, (cache->ncrls + 1) * sizeof(CachedCrl*));
1298     if (!newcrls) {
1299         return SECFailure;
1300     }
1301     cache->crls = newcrls;
1302     cache->ncrls++;
1303     cache->crls[cache->ncrls - 1] = newcrl;
1304     *added = PR_TRUE;
1305     return SECSuccess;
1306 }
1307 
1308 /* remove CRL at offset specified */
1309 static SECStatus
DPCache_RemoveCRL(CRLDPCache * cache,PRUint32 offset)1310 DPCache_RemoveCRL(CRLDPCache* cache, PRUint32 offset)
1311 {
1312     CachedCrl* acrl = NULL;
1313     PORT_Assert(cache);
1314     if (!cache || (!cache->crls) || (!(offset < cache->ncrls))) {
1315         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1316         return SECFailure;
1317     }
1318     acrl = cache->crls[offset];
1319     PORT_Assert(acrl);
1320     if (!acrl) {
1321         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1322         return SECFailure;
1323     }
1324     cache->crls[offset] = cache->crls[cache->ncrls - 1];
1325     cache->crls[cache->ncrls - 1] = NULL;
1326     cache->ncrls--;
1327     if (cache->selected == acrl) {
1328         cache->selected = NULL;
1329     }
1330     if (SECSuccess != CachedCrl_Destroy(acrl)) {
1331         PORT_Assert(0);
1332         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1333         return SECFailure;
1334     }
1335     return SECSuccess;
1336 }
1337 
1338 /* check whether a CRL object stored in a PKCS#11 token still exists in
1339    that token . This has to be efficient (the entire CRL value cannot be
1340    transferred accross the token boundaries), so this is accomplished by
1341    simply fetching the subject attribute and making sure it hasn't changed .
1342    Note that technically, the CRL object could have been replaced with a new
1343    PKCS#11 object of the same ID and subject (which actually happens in
1344    softoken), but this function has no way of knowing that the object
1345    value changed, since CKA_VALUE isn't checked. */
1346 static PRBool
TokenCRLStillExists(CERTSignedCrl * crl)1347 TokenCRLStillExists(CERTSignedCrl* crl)
1348 {
1349     NSSItem newsubject;
1350     SECItem subject;
1351     CK_ULONG crl_class;
1352     PRStatus status;
1353     PK11SlotInfo* slot = NULL;
1354     nssCryptokiObject instance;
1355     NSSArena* arena;
1356     PRBool xstatus = PR_TRUE;
1357     SECItem* oldSubject = NULL;
1358 
1359     PORT_Assert(crl);
1360     if (!crl) {
1361         return PR_FALSE;
1362     }
1363     slot = crl->slot;
1364     PORT_Assert(crl->slot);
1365     if (!slot) {
1366         return PR_FALSE;
1367     }
1368     oldSubject = &crl->crl.derName;
1369     PORT_Assert(oldSubject);
1370     if (!oldSubject) {
1371         return PR_FALSE;
1372     }
1373 
1374     /* query subject and type attributes in order to determine if the
1375        object has been deleted */
1376 
1377     /* first, make an nssCryptokiObject */
1378     instance.handle = crl->pkcs11ID;
1379     PORT_Assert(instance.handle);
1380     if (!instance.handle) {
1381         return PR_FALSE;
1382     }
1383     instance.token = PK11Slot_GetNSSToken(slot);
1384     PORT_Assert(instance.token);
1385     if (!instance.token) {
1386         return PR_FALSE;
1387     }
1388     instance.isTokenObject = PR_TRUE;
1389     instance.label = NULL;
1390 
1391     arena = NSSArena_Create();
1392     PORT_Assert(arena);
1393     if (!arena) {
1394         (void)nssToken_Destroy(instance.token);
1395         return PR_FALSE;
1396     }
1397 
1398     status =
1399         nssCryptokiCRL_GetAttributes(&instance, NULL,          /* XXX sessionOpt */
1400                                      arena, NULL, &newsubject, /* subject */
1401                                      &crl_class,               /* class */
1402                                      NULL, NULL);
1403     if (PR_SUCCESS == status) {
1404         subject.data = newsubject.data;
1405         subject.len = newsubject.size;
1406         if (SECITEM_CompareItem(oldSubject, &subject) != SECEqual) {
1407             xstatus = PR_FALSE;
1408         }
1409         if (CKO_NSS_CRL != crl_class) {
1410             xstatus = PR_FALSE;
1411         }
1412     } else {
1413         xstatus = PR_FALSE;
1414     }
1415     NSSArena_Destroy(arena);
1416     (void)nssToken_Destroy(instance.token);
1417     return xstatus;
1418 }
1419 
1420 /* verify the signature of a CRL against its issuer at a given date */
1421 static SECStatus
CERT_VerifyCRL(CERTSignedCrl * crlobject,CERTCertificate * issuer,PRTime vfdate,void * wincx)1422 CERT_VerifyCRL(CERTSignedCrl* crlobject, CERTCertificate* issuer, PRTime vfdate,
1423                void* wincx)
1424 {
1425     return CERT_VerifySignedData(&crlobject->signatureWrap, issuer, vfdate,
1426                                  wincx);
1427 }
1428 
1429 /* verify a CRL and update cache state */
1430 static SECStatus
CachedCrl_Verify(CRLDPCache * cache,CachedCrl * crlobject,PRTime vfdate,void * wincx)1431 CachedCrl_Verify(CRLDPCache* cache, CachedCrl* crlobject, PRTime vfdate,
1432                  void* wincx)
1433 {
1434     /*  Check if it is an invalid CRL
1435         if we got a bad CRL, we want to cache it in order to avoid
1436         subsequent fetches of this same identical bad CRL. We set
1437         the cache to the invalid state to ensure that all certs on this
1438         DP are considered to have unknown status from now on. The cache
1439         object will remain in this state until the bad CRL object
1440         is removed from the token it was fetched from. If the cause
1441         of the failure is that we didn't have the issuer cert to
1442         verify the signature, this state can be cleared when
1443         the issuer certificate becomes available if that causes the
1444         signature to verify */
1445 
1446     if (!cache || !crlobject) {
1447         PORT_Assert(0);
1448         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1449         return SECFailure;
1450     }
1451     if (PR_TRUE == GetOpaqueCRLFields(crlobject->crl)->decodingError) {
1452         crlobject->sigChecked = PR_TRUE; /* we can never verify a CRL
1453             with bogus DER. Mark it checked so we won't try again */
1454         PORT_SetError(SEC_ERROR_BAD_DER);
1455         return SECSuccess;
1456     } else {
1457         SECStatus signstatus = SECFailure;
1458         if (cache->issuerDERCert) {
1459             CERTCertificate* issuer = CERT_NewTempCertificate(
1460                 cache->dbHandle, cache->issuerDERCert, NULL, PR_FALSE, PR_TRUE);
1461 
1462             if (issuer) {
1463                 signstatus =
1464                     CERT_VerifyCRL(crlobject->crl, issuer, vfdate, wincx);
1465                 CERT_DestroyCertificate(issuer);
1466             }
1467         }
1468         if (SECSuccess != signstatus) {
1469             if (!cache->issuerDERCert) {
1470                 /* we tried to verify without an issuer cert . This is
1471                    because this CRL came through a call to SEC_FindCrlByName.
1472                    So, we don't cache this verification failure. We'll try
1473                    to verify the CRL again when a certificate from that issuer
1474                    becomes available */
1475             } else {
1476                 crlobject->sigChecked = PR_TRUE;
1477             }
1478             PORT_SetError(SEC_ERROR_CRL_BAD_SIGNATURE);
1479             return SECSuccess;
1480         } else {
1481             crlobject->sigChecked = PR_TRUE;
1482             crlobject->sigValid = PR_TRUE;
1483         }
1484     }
1485 
1486     return SECSuccess;
1487 }
1488 
1489 /* fetch the CRLs for this DP from the PKCS#11 tokens */
1490 static SECStatus
DPCache_FetchFromTokens(CRLDPCache * cache,PRTime vfdate,void * wincx)1491 DPCache_FetchFromTokens(CRLDPCache* cache, PRTime vfdate, void* wincx)
1492 {
1493     SECStatus rv = SECSuccess;
1494     CERTCrlHeadNode head;
1495     if (!cache) {
1496         PORT_Assert(0);
1497         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1498         return SECFailure;
1499     }
1500     /* first, initialize list */
1501     memset(&head, 0, sizeof(head));
1502     head.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1503     rv = pk11_RetrieveCrls(&head, cache->subject, wincx);
1504 
1505     /* if this function fails, something very wrong happened, such as an out
1506        of memory error during CRL decoding. We don't want to proceed and must
1507        mark the cache object invalid */
1508     if (SECFailure == rv) {
1509         /* fetch failed, add error bit */
1510         cache->invalid |= CRL_CACHE_LAST_FETCH_FAILED;
1511     } else {
1512         /* fetch was successful, clear this error bit */
1513         cache->invalid &= (~CRL_CACHE_LAST_FETCH_FAILED);
1514     }
1515 
1516     /* add any CRLs found to our array */
1517     if (SECSuccess == rv) {
1518         CERTCrlNode* crlNode = NULL;
1519 
1520         for (crlNode = head.first; crlNode; crlNode = crlNode->next) {
1521             CachedCrl* returned = NULL;
1522             CERTSignedCrl* crlobject = crlNode->crl;
1523             if (!crlobject) {
1524                 PORT_Assert(0);
1525                 continue;
1526             }
1527             rv = CachedCrl_Create(&returned, crlobject, CRL_OriginToken);
1528             if (SECSuccess == rv) {
1529                 PRBool added = PR_FALSE;
1530                 rv = DPCache_AddCRL(cache, returned, &added);
1531                 if (PR_TRUE != added) {
1532                     rv = CachedCrl_Destroy(returned);
1533                     returned = NULL;
1534                 } else if (vfdate) {
1535                     rv = CachedCrl_Verify(cache, returned, vfdate, wincx);
1536                 }
1537             } else {
1538                 /* not enough memory to add the CRL to the cache. mark it
1539                    invalid so we will try again . */
1540                 cache->invalid |= CRL_CACHE_LAST_FETCH_FAILED;
1541             }
1542             if (SECFailure == rv) {
1543                 break;
1544             }
1545         }
1546     }
1547 
1548     if (head.arena) {
1549         CERTCrlNode* crlNode = NULL;
1550         /* clean up the CRL list in case we got a partial one
1551            during a failed fetch */
1552         for (crlNode = head.first; crlNode; crlNode = crlNode->next) {
1553             if (crlNode->crl) {
1554                 SEC_DestroyCrl(crlNode->crl); /* free the CRL. Either it got
1555                    added to the cache and the refcount got bumped, or not, and
1556                    thus we need to free its RAM */
1557             }
1558         }
1559         PORT_FreeArena(head.arena, PR_FALSE); /* destroy CRL list */
1560     }
1561 
1562     return rv;
1563 }
1564 
1565 static SECStatus
CachedCrl_GetEntry(CachedCrl * crl,const SECItem * sn,CERTCrlEntry ** returned)1566 CachedCrl_GetEntry(CachedCrl* crl, const SECItem* sn, CERTCrlEntry** returned)
1567 {
1568     CERTCrlEntry* acrlEntry;
1569 
1570     PORT_Assert(crl);
1571     PORT_Assert(crl->entries);
1572     PORT_Assert(sn);
1573     PORT_Assert(returned);
1574     if (!crl || !sn || !returned || !crl->entries) {
1575         PORT_SetError(SEC_ERROR_INVALID_ARGS);
1576         return SECFailure;
1577     }
1578     acrlEntry = PL_HashTableLookup(crl->entries, (void*)sn);
1579     if (acrlEntry) {
1580         *returned = acrlEntry;
1581     } else {
1582         *returned = NULL;
1583     }
1584     return SECSuccess;
1585 }
1586 
1587 /* check if a particular SN is in the CRL cache and return its entry */
1588 dpcacheStatus
DPCache_Lookup(CRLDPCache * cache,const SECItem * sn,CERTCrlEntry ** returned)1589 DPCache_Lookup(CRLDPCache* cache, const SECItem* sn, CERTCrlEntry** returned)
1590 {
1591     SECStatus rv;
1592     if (!cache || !sn || !returned) {
1593         PORT_SetError(SEC_ERROR_INVALID_ARGS);
1594         /* no cache or SN to look up, or no way to return entry */
1595         return dpcacheCallerError;
1596     }
1597     *returned = NULL;
1598     if (0 != cache->invalid) {
1599         /* the cache contains a bad CRL, or there was a CRL fetching error. */
1600         PORT_SetError(SEC_ERROR_CRL_INVALID);
1601         return dpcacheInvalidCacheError;
1602     }
1603     if (!cache->selected) {
1604         /* no CRL means no entry to return. This is OK, except for
1605          * NIST policy */
1606         return dpcacheEmpty;
1607     }
1608     rv = CachedCrl_GetEntry(cache->selected, sn, returned);
1609     if (SECSuccess != rv) {
1610         return dpcacheLookupError;
1611     } else {
1612         if (*returned) {
1613             return dpcacheFoundEntry;
1614         } else {
1615             return dpcacheNoEntry;
1616         }
1617     }
1618 }
1619 
1620 #if defined(DPC_RWLOCK)
1621 
1622 #define DPCache_LockWrite()                    \
1623     {                                          \
1624         if (readlocked) {                      \
1625             NSSRWLock_UnlockRead(cache->lock); \
1626         }                                      \
1627         NSSRWLock_LockWrite(cache->lock);      \
1628     }
1629 
1630 #define DPCache_UnlockWrite()                \
1631     {                                        \
1632         if (readlocked) {                    \
1633             NSSRWLock_LockRead(cache->lock); \
1634         }                                    \
1635         NSSRWLock_UnlockWrite(cache->lock);  \
1636     }
1637 
1638 #else
1639 
1640 /* with a global lock, we are always locked for read before we need write
1641    access, so do nothing */
1642 
1643 #define DPCache_LockWrite() \
1644     {                       \
1645     }
1646 
1647 #define DPCache_UnlockWrite() \
1648     {                         \
1649     }
1650 
1651 #endif
1652 
1653 /* update the content of the CRL cache, including fetching of CRLs, and
1654    reprocessing with specified issuer and date . We are always holding
1655    either the read or write lock on DPCache upon entry. */
1656 static SECStatus
DPCache_GetUpToDate(CRLDPCache * cache,CERTCertificate * issuer,PRBool readlocked,PRTime vfdate,void * wincx)1657 DPCache_GetUpToDate(CRLDPCache* cache, CERTCertificate* issuer,
1658                     PRBool readlocked, PRTime vfdate, void* wincx)
1659 {
1660     /* Update the CRLDPCache now. We don't cache token CRL lookup misses
1661        yet, as we have no way of getting notified of new PKCS#11 object
1662        creation that happens in a token  */
1663     SECStatus rv = SECSuccess;
1664     PRUint32 i = 0;
1665     PRBool forcedrefresh = PR_FALSE;
1666     PRBool dirty = PR_FALSE; /* whether something was changed in the
1667                                 cache state during this update cycle */
1668     PRBool hastokenCRLs = PR_FALSE;
1669     PRTime now = 0;
1670     PRTime lastfetch = 0;
1671     PRBool mustunlock = PR_FALSE;
1672 
1673     if (!cache) {
1674         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1675         return SECFailure;
1676     }
1677 
1678     /* first, make sure we have obtained all the CRLs we need.
1679        We do an expensive token fetch in the following cases :
1680        1) cache is empty because no fetch was ever performed yet
1681        2) cache is explicitly set to refresh state
1682        3) cache is in invalid state because last fetch failed
1683        4) cache contains no token CRLs, and it's been more than one minute
1684           since the last fetch
1685        5) cache contains token CRLs, and it's been more than 10 minutes since
1686           the last fetch
1687     */
1688     forcedrefresh = cache->refresh;
1689     lastfetch = cache->lastfetch;
1690     if (PR_TRUE != forcedrefresh &&
1691         (!(cache->invalid & CRL_CACHE_LAST_FETCH_FAILED))) {
1692         now = PR_Now();
1693         hastokenCRLs = DPCache_HasTokenCRLs(cache);
1694     }
1695     if ((0 == lastfetch) ||
1696 
1697         (PR_TRUE == forcedrefresh) ||
1698 
1699         (cache->invalid & CRL_CACHE_LAST_FETCH_FAILED) ||
1700 
1701         ((PR_FALSE == hastokenCRLs) &&
1702          ((now - cache->lastfetch > CRLCache_Empty_TokenFetch_Interval) ||
1703           (now < cache->lastfetch))) ||
1704 
1705         ((PR_TRUE == hastokenCRLs) &&
1706          ((now - cache->lastfetch > CRLCache_TokenRefetch_Interval) ||
1707           (now < cache->lastfetch)))) {
1708         /* the cache needs to be refreshed, and/or we had zero CRL for this
1709            DP. Try to get one from PKCS#11 tokens */
1710         DPCache_LockWrite();
1711         /* check if another thread updated before us, and skip update if so */
1712         if (lastfetch == cache->lastfetch) {
1713             /* we are the first */
1714             rv = DPCache_FetchFromTokens(cache, vfdate, wincx);
1715             if (PR_TRUE == cache->refresh) {
1716                 cache->refresh = PR_FALSE; /* clear refresh state */
1717             }
1718             dirty = PR_TRUE;
1719             cache->lastfetch = PR_Now();
1720         }
1721         DPCache_UnlockWrite();
1722     }
1723 
1724     /* now, make sure we have no extraneous CRLs (deleted token objects)
1725        we'll do this inexpensive existence check either
1726        1) if there was a token object fetch
1727        2) every minute */
1728     if ((PR_TRUE != dirty) && (!now)) {
1729         now = PR_Now();
1730     }
1731     if ((PR_TRUE == dirty) ||
1732         ((now - cache->lastcheck > CRLCache_ExistenceCheck_Interval) ||
1733          (now < cache->lastcheck))) {
1734         PRTime lastcheck = cache->lastcheck;
1735         mustunlock = PR_FALSE;
1736         /* check if all CRLs still exist */
1737         for (i = 0; (i < cache->ncrls); i++) {
1738             CachedCrl* savcrl = cache->crls[i];
1739             if ((!savcrl) || (savcrl && CRL_OriginToken != savcrl->origin)) {
1740                 /* we only want to check token CRLs */
1741                 continue;
1742             }
1743             if ((PR_TRUE != TokenCRLStillExists(savcrl->crl))) {
1744 
1745                 /* this CRL is gone */
1746                 if (PR_TRUE != mustunlock) {
1747                     DPCache_LockWrite();
1748                     mustunlock = PR_TRUE;
1749                 }
1750                 /* first, we need to check if another thread did an update
1751                    before we did */
1752                 if (lastcheck == cache->lastcheck) {
1753                     /* the CRL is gone. And we are the one to do the update */
1754                     DPCache_RemoveCRL(cache, i);
1755                     dirty = PR_TRUE;
1756                 }
1757                 /* stay locked here intentionally so we do all the other
1758                    updates in this thread for the remaining CRLs */
1759             }
1760         }
1761         if (PR_TRUE == mustunlock) {
1762             cache->lastcheck = PR_Now();
1763             DPCache_UnlockWrite();
1764             mustunlock = PR_FALSE;
1765         }
1766     }
1767 
1768     /* add issuer certificate if it was previously unavailable */
1769     if (issuer && (NULL == cache->issuerDERCert) &&
1770         (SECSuccess == CERT_CheckCertUsage(issuer, KU_CRL_SIGN))) {
1771         /* if we didn't have a valid issuer cert yet, but we do now. add it */
1772         DPCache_LockWrite();
1773         if (!cache->issuerDERCert) {
1774             dirty = PR_TRUE;
1775             cache->dbHandle = issuer->dbhandle;
1776             cache->issuerDERCert = SECITEM_DupItem(&issuer->derCert);
1777         }
1778         DPCache_UnlockWrite();
1779     }
1780 
1781     /* verify CRLs that couldn't be checked when inserted into the cache
1782        because the issuer cert or a verification date was unavailable.
1783        These are CRLs that were inserted into the cache through
1784        SEC_FindCrlByName, or through manual insertion, rather than through a
1785        certificate verification (CERT_CheckCRL) */
1786 
1787     if (cache->issuerDERCert && vfdate) {
1788         mustunlock = PR_FALSE;
1789         /* re-process all unverified CRLs */
1790         for (i = 0; i < cache->ncrls; i++) {
1791             CachedCrl* savcrl = cache->crls[i];
1792             if (!savcrl) {
1793                 continue;
1794             }
1795             if (PR_TRUE != savcrl->sigChecked) {
1796                 if (!mustunlock) {
1797                     DPCache_LockWrite();
1798                     mustunlock = PR_TRUE;
1799                 }
1800                 /* first, we need to check if another thread updated
1801                    it before we did, and abort if it has been modified since
1802                    we acquired the lock. Make sure first that the CRL is still
1803                    in the array at the same position */
1804                 if ((i < cache->ncrls) && (savcrl == cache->crls[i]) &&
1805                     (PR_TRUE != savcrl->sigChecked)) {
1806                     /* the CRL is still there, unverified. Do it */
1807                     CachedCrl_Verify(cache, savcrl, vfdate, wincx);
1808                     dirty = PR_TRUE;
1809                 }
1810                 /* stay locked here intentionally so we do all the other
1811                    updates in this thread for the remaining CRLs */
1812             }
1813             if (mustunlock && !dirty) {
1814                 DPCache_UnlockWrite();
1815                 mustunlock = PR_FALSE;
1816             }
1817         }
1818     }
1819 
1820     if (dirty || cache->mustchoose) {
1821         /* changes to the content of the CRL cache necessitate examining all
1822            CRLs for selection of the most appropriate one to cache */
1823         if (!mustunlock) {
1824             DPCache_LockWrite();
1825             mustunlock = PR_TRUE;
1826         }
1827         DPCache_SelectCRL(cache);
1828         cache->mustchoose = PR_FALSE;
1829     }
1830     if (mustunlock)
1831         DPCache_UnlockWrite();
1832 
1833     return rv;
1834 }
1835 
1836 /* callback for qsort to sort by thisUpdate */
1837 static int
SortCRLsByThisUpdate(const void * arg1,const void * arg2)1838 SortCRLsByThisUpdate(const void* arg1, const void* arg2)
1839 {
1840     PRTime timea, timeb;
1841     SECStatus rv = SECSuccess;
1842     CachedCrl *a, *b;
1843 
1844     a = *(CachedCrl**)arg1;
1845     b = *(CachedCrl**)arg2;
1846 
1847     if (!a || !b) {
1848         PORT_Assert(0);
1849         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1850         rv = SECFailure;
1851     }
1852 
1853     if (SECSuccess == rv) {
1854         rv = DER_DecodeTimeChoice(&timea, &a->crl->crl.lastUpdate);
1855     }
1856     if (SECSuccess == rv) {
1857         rv = DER_DecodeTimeChoice(&timeb, &b->crl->crl.lastUpdate);
1858     }
1859     if (SECSuccess == rv) {
1860         if (timea > timeb) {
1861             return 1; /* a is better than b */
1862         }
1863         if (timea < timeb) {
1864             return -1; /* a is not as good as b */
1865         }
1866     }
1867 
1868     /* if they are equal, or if all else fails, use pointer differences */
1869     PORT_Assert(a != b); /* they should never be equal */
1870     return a > b ? 1 : -1;
1871 }
1872 
1873 /* callback for qsort to sort a set of disparate CRLs, some of which are
1874    invalid DER or failed signature check.
1875 
1876    Validated CRLs are differentiated by thisUpdate .
1877    Validated CRLs are preferred over non-validated CRLs .
1878    Proper DER CRLs are preferred over non-DER data .
1879 */
1880 static int
SortImperfectCRLs(const void * arg1,const void * arg2)1881 SortImperfectCRLs(const void* arg1, const void* arg2)
1882 {
1883     CachedCrl *a, *b;
1884 
1885     a = *(CachedCrl**)arg1;
1886     b = *(CachedCrl**)arg2;
1887 
1888     if (!a || !b) {
1889         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1890         PORT_Assert(0);
1891     } else {
1892         PRBool aDecoded = PR_FALSE, bDecoded = PR_FALSE;
1893         if ((PR_TRUE == a->sigValid) && (PR_TRUE == b->sigValid)) {
1894             /* both CRLs have been validated, choose the latest one */
1895             return SortCRLsByThisUpdate(arg1, arg2);
1896         }
1897         if (PR_TRUE == a->sigValid) {
1898             return 1; /* a is greater than b */
1899         }
1900         if (PR_TRUE == b->sigValid) {
1901             return -1; /* a is not as good as b */
1902         }
1903         aDecoded = GetOpaqueCRLFields(a->crl)->decodingError;
1904         bDecoded = GetOpaqueCRLFields(b->crl)->decodingError;
1905         /* neither CRL had its signature check pass */
1906         if ((PR_FALSE == aDecoded) && (PR_FALSE == bDecoded)) {
1907             /* both CRLs are proper DER, choose the latest one */
1908             return SortCRLsByThisUpdate(arg1, arg2);
1909         }
1910         if (PR_FALSE == aDecoded) {
1911             return 1; /* a is better than b */
1912         }
1913         if (PR_FALSE == bDecoded) {
1914             return -1; /* a is not as good as b */
1915         }
1916         /* both are invalid DER. sigh. */
1917     }
1918     /* if they are equal, or if all else fails, use pointer differences */
1919     PORT_Assert(a != b); /* they should never be equal */
1920     return a > b ? 1 : -1;
1921 }
1922 
1923 /* Pick best CRL to use . needs write access */
1924 static SECStatus
DPCache_SelectCRL(CRLDPCache * cache)1925 DPCache_SelectCRL(CRLDPCache* cache)
1926 {
1927     PRUint32 i;
1928     PRBool valid = PR_TRUE;
1929     CachedCrl* selected = NULL;
1930 
1931     PORT_Assert(cache);
1932     if (!cache) {
1933         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1934         return SECFailure;
1935     }
1936     /* if any invalid CRL is present, then the CRL cache is
1937        considered invalid, for security reasons */
1938     for (i = 0; i < cache->ncrls; i++) {
1939         if (!cache->crls[i] || !cache->crls[i]->sigChecked ||
1940             !cache->crls[i]->sigValid) {
1941             valid = PR_FALSE;
1942             break;
1943         }
1944     }
1945     if (PR_TRUE == valid) {
1946         /* all CRLs are valid, clear this error */
1947         cache->invalid &= (~CRL_CACHE_INVALID_CRLS);
1948     } else {
1949         /* some CRLs are invalid, set this error */
1950         cache->invalid |= CRL_CACHE_INVALID_CRLS;
1951     }
1952 
1953     if (cache->invalid) {
1954         /* cache is in an invalid state, so reset it */
1955         if (cache->selected) {
1956             cache->selected = NULL;
1957         }
1958         /* also sort the CRLs imperfectly */
1959         qsort(cache->crls, cache->ncrls, sizeof(CachedCrl*), SortImperfectCRLs);
1960         return SECSuccess;
1961     }
1962 
1963     if (cache->ncrls) {
1964         /* all CRLs are good, sort them by thisUpdate */
1965         qsort(cache->crls, cache->ncrls, sizeof(CachedCrl*), SortCRLsByThisUpdate);
1966 
1967         /* pick the newest CRL */
1968         selected = cache->crls[cache->ncrls - 1];
1969 
1970         /* and populate the cache */
1971         if (SECSuccess != CachedCrl_Populate(selected)) {
1972             return SECFailure;
1973         }
1974     }
1975 
1976     cache->selected = selected;
1977 
1978     return SECSuccess;
1979 }
1980 
1981 /* initialize a DPCache object */
1982 static SECStatus
DPCache_Create(CRLDPCache ** returned,CERTCertificate * issuer,const SECItem * subject,SECItem * dp)1983 DPCache_Create(CRLDPCache** returned, CERTCertificate* issuer,
1984                const SECItem* subject, SECItem* dp)
1985 {
1986     CRLDPCache* cache = NULL;
1987     PORT_Assert(returned);
1988     /* issuer and dp are allowed to be NULL */
1989     if (!returned || !subject) {
1990         PORT_Assert(0);
1991         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1992         return SECFailure;
1993     }
1994     *returned = NULL;
1995     cache = PORT_ZAlloc(sizeof(CRLDPCache));
1996     if (!cache) {
1997         return SECFailure;
1998     }
1999 #ifdef DPC_RWLOCK
2000     cache->lock = NSSRWLock_New(NSS_RWLOCK_RANK_NONE, NULL);
2001 #else
2002     cache->lock = PR_NewLock();
2003 #endif
2004     if (!cache->lock) {
2005         PORT_Free(cache);
2006         return SECFailure;
2007     }
2008     if (issuer) {
2009         cache->dbHandle = issuer->dbhandle;
2010         cache->issuerDERCert = SECITEM_DupItem(&issuer->derCert);
2011     }
2012     cache->distributionPoint = SECITEM_DupItem(dp);
2013     cache->subject = SECITEM_DupItem(subject);
2014     cache->lastfetch = 0;
2015     cache->lastcheck = 0;
2016     *returned = cache;
2017     return SECSuccess;
2018 }
2019 
2020 /* create an issuer cache object (per CA subject ) */
2021 static SECStatus
IssuerCache_Create(CRLIssuerCache ** returned,CERTCertificate * issuer,const SECItem * subject,const SECItem * dp)2022 IssuerCache_Create(CRLIssuerCache** returned, CERTCertificate* issuer,
2023                    const SECItem* subject, const SECItem* dp)
2024 {
2025     SECStatus rv = SECSuccess;
2026     CRLIssuerCache* cache = NULL;
2027     PORT_Assert(returned);
2028     PORT_Assert(subject);
2029     /* issuer and dp are allowed to be NULL */
2030     if (!returned || !subject) {
2031         PORT_Assert(0);
2032         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
2033         return SECFailure;
2034     }
2035     *returned = NULL;
2036     cache = (CRLIssuerCache*)PORT_ZAlloc(sizeof(CRLIssuerCache));
2037     if (!cache) {
2038         return SECFailure;
2039     }
2040     cache->subject = SECITEM_DupItem(subject);
2041 #ifdef XCRL
2042     cache->lock = NSSRWLock_New(NSS_RWLOCK_RANK_NONE, NULL);
2043     if (!cache->lock) {
2044         rv = SECFailure;
2045     }
2046     if (SECSuccess == rv && issuer) {
2047         cache->issuer = CERT_DupCertificate(issuer);
2048         if (!cache->issuer) {
2049             rv = SECFailure;
2050         }
2051     }
2052 #endif
2053     if (SECSuccess != rv) {
2054         PORT_Assert(SECSuccess == IssuerCache_Destroy(cache));
2055         return SECFailure;
2056     }
2057     *returned = cache;
2058     return SECSuccess;
2059 }
2060 
2061 /* add a DPCache to the issuer cache */
2062 static SECStatus
IssuerCache_AddDP(CRLIssuerCache * cache,CERTCertificate * issuer,const SECItem * subject,const SECItem * dp,CRLDPCache ** newdpc)2063 IssuerCache_AddDP(CRLIssuerCache* cache, CERTCertificate* issuer,
2064                   const SECItem* subject, const SECItem* dp,
2065                   CRLDPCache** newdpc)
2066 {
2067     /* now create the required DP cache object */
2068     if (!cache || !subject || !newdpc) {
2069         PORT_Assert(0);
2070         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
2071         return SECFailure;
2072     }
2073     if (!dp) {
2074         /* default distribution point */
2075         SECStatus rv = DPCache_Create(&cache->dpp, issuer, subject, NULL);
2076         if (SECSuccess == rv) {
2077             *newdpc = cache->dpp;
2078             return SECSuccess;
2079         }
2080     } else {
2081         /* we should never hit this until we support multiple DPs */
2082         PORT_Assert(dp);
2083         /* XCRL allocate a new distribution point cache object, initialize it,
2084            and add it to the hash table of DPs */
2085     }
2086     return SECFailure;
2087 }
2088 
2089 /* add an IssuerCache to the global hash table of issuers */
2090 static SECStatus
CRLCache_AddIssuer(CRLIssuerCache * issuer)2091 CRLCache_AddIssuer(CRLIssuerCache* issuer)
2092 {
2093     PORT_Assert(issuer);
2094     PORT_Assert(crlcache.issuers);
2095     if (!issuer || !crlcache.issuers) {
2096         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
2097         return SECFailure;
2098     }
2099     if (NULL == PL_HashTableAdd(crlcache.issuers, (void*)issuer->subject,
2100                                 (void*)issuer)) {
2101         return SECFailure;
2102     }
2103     return SECSuccess;
2104 }
2105 
2106 /* retrieve the issuer cache object for a given issuer subject */
2107 static SECStatus
CRLCache_GetIssuerCache(CRLCache * cache,const SECItem * subject,CRLIssuerCache ** returned)2108 CRLCache_GetIssuerCache(CRLCache* cache, const SECItem* subject,
2109                         CRLIssuerCache** returned)
2110 {
2111     /* we need to look up the issuer in the hash table */
2112     SECStatus rv = SECSuccess;
2113     PORT_Assert(cache);
2114     PORT_Assert(subject);
2115     PORT_Assert(returned);
2116     PORT_Assert(crlcache.issuers);
2117     if (!cache || !subject || !returned || !crlcache.issuers) {
2118         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
2119         rv = SECFailure;
2120     }
2121 
2122     if (SECSuccess == rv) {
2123         *returned = (CRLIssuerCache*)PL_HashTableLookup(crlcache.issuers,
2124                                                         (void*)subject);
2125     }
2126 
2127     return rv;
2128 }
2129 
2130 /* retrieve the full CRL object that best matches the content of a DPCache */
2131 static CERTSignedCrl*
GetBestCRL(CRLDPCache * cache,PRBool entries)2132 GetBestCRL(CRLDPCache* cache, PRBool entries)
2133 {
2134     CachedCrl* acrl = NULL;
2135 
2136     PORT_Assert(cache);
2137     if (!cache) {
2138         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
2139         return NULL;
2140     }
2141 
2142     if (0 == cache->ncrls) {
2143         /* empty cache*/
2144         PORT_SetError(SEC_ERROR_CRL_NOT_FOUND);
2145         return NULL;
2146     }
2147 
2148     /* if we have a valid full CRL selected, return it */
2149     if (cache->selected) {
2150         return SEC_DupCrl(cache->selected->crl);
2151     }
2152 
2153     /* otherwise, use latest valid DER CRL */
2154     acrl = cache->crls[cache->ncrls - 1];
2155 
2156     if (acrl && (PR_FALSE == GetOpaqueCRLFields(acrl->crl)->decodingError)) {
2157         SECStatus rv = SECSuccess;
2158         if (PR_TRUE == entries) {
2159             rv = CERT_CompleteCRLDecodeEntries(acrl->crl);
2160         }
2161         if (SECSuccess == rv) {
2162             return SEC_DupCrl(acrl->crl);
2163         }
2164     }
2165 
2166     PORT_SetError(SEC_ERROR_CRL_NOT_FOUND);
2167     return NULL;
2168 }
2169 
2170 /* get a particular DPCache object from an IssuerCache */
2171 static CRLDPCache*
IssuerCache_GetDPCache(CRLIssuerCache * cache,const SECItem * dp)2172 IssuerCache_GetDPCache(CRLIssuerCache* cache, const SECItem* dp)
2173 {
2174     CRLDPCache* dpp = NULL;
2175     PORT_Assert(cache);
2176     /* XCRL for now we only support the "default" DP, ie. the
2177        full CRL. So we can return the global one without locking. In
2178        the future we will have a lock */
2179     PORT_Assert(NULL == dp);
2180     if (!cache || dp) {
2181         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
2182         return NULL;
2183     }
2184 #ifdef XCRL
2185     NSSRWLock_LockRead(cache->lock);
2186 #endif
2187     dpp = cache->dpp;
2188 #ifdef XCRL
2189     NSSRWLock_UnlockRead(cache->lock);
2190 #endif
2191     return dpp;
2192 }
2193 
2194 /* get a DPCache object for the given issuer subject and dp
2195    Automatically creates the cache object if it doesn't exist yet.
2196    */
2197 SECStatus
AcquireDPCache(CERTCertificate * issuer,const SECItem * subject,const SECItem * dp,PRTime t,void * wincx,CRLDPCache ** dpcache,PRBool * writeLocked)2198 AcquireDPCache(CERTCertificate* issuer, const SECItem* subject,
2199                const SECItem* dp, PRTime t, void* wincx, CRLDPCache** dpcache,
2200                PRBool* writeLocked)
2201 {
2202     SECStatus rv = SECSuccess;
2203     CRLIssuerCache* issuercache = NULL;
2204 #ifdef GLOBAL_RWLOCK
2205     PRBool globalwrite = PR_FALSE;
2206 #endif
2207     PORT_Assert(crlcache.lock);
2208     if (!crlcache.lock) {
2209         /* CRL cache is not initialized */
2210         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
2211         return SECFailure;
2212     }
2213 #ifdef GLOBAL_RWLOCK
2214     NSSRWLock_LockRead(crlcache.lock);
2215 #else
2216     PR_Lock(crlcache.lock);
2217 #endif
2218     rv = CRLCache_GetIssuerCache(&crlcache, subject, &issuercache);
2219     if (SECSuccess != rv) {
2220 #ifdef GLOBAL_RWLOCK
2221         NSSRWLock_UnlockRead(crlcache.lock);
2222 #else
2223         PR_Unlock(crlcache.lock);
2224 #endif
2225         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
2226         return SECFailure;
2227     }
2228     if (!issuercache) {
2229         /* there is no cache for this issuer yet. This means this is the
2230            first time we look up a cert from that issuer, and we need to
2231            create the cache. */
2232 
2233         rv = IssuerCache_Create(&issuercache, issuer, subject, dp);
2234         if (SECSuccess == rv && !issuercache) {
2235             PORT_Assert(issuercache);
2236             rv = SECFailure;
2237         }
2238 
2239         if (SECSuccess == rv) {
2240             /* This is the first time we look up a cert of this issuer.
2241                Create the DPCache for this DP . */
2242             rv = IssuerCache_AddDP(issuercache, issuer, subject, dp, dpcache);
2243         }
2244 
2245         if (SECSuccess == rv) {
2246             /* lock the DPCache for write to ensure the update happens in this
2247                thread */
2248             *writeLocked = PR_TRUE;
2249 #ifdef DPC_RWLOCK
2250             NSSRWLock_LockWrite((*dpcache)->lock);
2251 #else
2252             PR_Lock((*dpcache)->lock);
2253 #endif
2254         }
2255 
2256         if (SECSuccess == rv) {
2257 /* now add the new issuer cache to the global hash table of
2258    issuers */
2259 #ifdef GLOBAL_RWLOCK
2260             CRLIssuerCache* existing = NULL;
2261             NSSRWLock_UnlockRead(crlcache.lock);
2262             /* when using a r/w lock for the global cache, check if the issuer
2263                already exists before adding to the hash table */
2264             NSSRWLock_LockWrite(crlcache.lock);
2265             globalwrite = PR_TRUE;
2266             rv = CRLCache_GetIssuerCache(&crlcache, subject, &existing);
2267             if (!existing) {
2268 #endif
2269                 rv = CRLCache_AddIssuer(issuercache);
2270                 if (SECSuccess != rv) {
2271                     /* failure */
2272                     rv = SECFailure;
2273                 }
2274 #ifdef GLOBAL_RWLOCK
2275             } else {
2276                 /* somebody else updated before we did */
2277                 IssuerCache_Destroy(issuercache); /* destroy the new object */
2278                 issuercache = existing;           /* use the existing one */
2279                 *dpcache = IssuerCache_GetDPCache(issuercache, dp);
2280             }
2281 #endif
2282         }
2283 
2284 /* now unlock the global cache. We only want to lock the issuer hash
2285    table addition. Holding it longer would hurt scalability */
2286 #ifdef GLOBAL_RWLOCK
2287         if (PR_TRUE == globalwrite) {
2288             NSSRWLock_UnlockWrite(crlcache.lock);
2289             globalwrite = PR_FALSE;
2290         } else {
2291             NSSRWLock_UnlockRead(crlcache.lock);
2292         }
2293 #else
2294         PR_Unlock(crlcache.lock);
2295 #endif
2296 
2297         /* if there was a failure adding an issuer cache object, destroy it */
2298         if (SECSuccess != rv && issuercache) {
2299             if (PR_TRUE == *writeLocked) {
2300 #ifdef DPC_RWLOCK
2301                 NSSRWLock_UnlockWrite((*dpcache)->lock);
2302 #else
2303                 PR_Unlock((*dpcache)->lock);
2304 #endif
2305             }
2306             IssuerCache_Destroy(issuercache);
2307             issuercache = NULL;
2308         }
2309 
2310         if (SECSuccess != rv) {
2311             return SECFailure;
2312         }
2313     } else {
2314 #ifdef GLOBAL_RWLOCK
2315         NSSRWLock_UnlockRead(crlcache.lock);
2316 #else
2317         PR_Unlock(crlcache.lock);
2318 #endif
2319         *dpcache = IssuerCache_GetDPCache(issuercache, dp);
2320     }
2321     /* we now have a DPCache that we can use for lookups */
2322     /* lock it for read, unless we already locked for write */
2323     if (PR_FALSE == *writeLocked) {
2324 #ifdef DPC_RWLOCK
2325         NSSRWLock_LockRead((*dpcache)->lock);
2326 #else
2327         PR_Lock((*dpcache)->lock);
2328 #endif
2329     }
2330 
2331     if (SECSuccess == rv) {
2332         /* currently there is always one and only one DPCache per issuer */
2333         PORT_Assert(*dpcache);
2334         if (*dpcache) {
2335             /* make sure the DP cache is up to date before using it */
2336             rv = DPCache_GetUpToDate(*dpcache, issuer, PR_FALSE == *writeLocked,
2337                                      t, wincx);
2338         } else {
2339             rv = SECFailure;
2340         }
2341     }
2342     return rv;
2343 }
2344 
2345 /* unlock access to the DPCache */
2346 void
ReleaseDPCache(CRLDPCache * dpcache,PRBool writeLocked)2347 ReleaseDPCache(CRLDPCache* dpcache, PRBool writeLocked)
2348 {
2349     if (!dpcache) {
2350         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
2351         return;
2352     }
2353 #ifdef DPC_RWLOCK
2354     if (PR_TRUE == writeLocked) {
2355         NSSRWLock_UnlockWrite(dpcache->lock);
2356     } else {
2357         NSSRWLock_UnlockRead(dpcache->lock);
2358     }
2359 #else
2360     PR_Unlock(dpcache->lock);
2361 #endif
2362 }
2363 
2364 SECStatus
cert_CheckCertRevocationStatus(CERTCertificate * cert,CERTCertificate * issuer,const SECItem * dp,PRTime t,void * wincx,CERTRevocationStatus * revStatus,CERTCRLEntryReasonCode * revReason)2365 cert_CheckCertRevocationStatus(CERTCertificate* cert, CERTCertificate* issuer,
2366                                const SECItem* dp, PRTime t, void* wincx,
2367                                CERTRevocationStatus* revStatus,
2368                                CERTCRLEntryReasonCode* revReason)
2369 {
2370     PRBool lockedwrite = PR_FALSE;
2371     SECStatus rv = SECSuccess;
2372     CRLDPCache* dpcache = NULL;
2373     CERTRevocationStatus status = certRevocationStatusRevoked;
2374     CERTCRLEntryReasonCode reason = crlEntryReasonUnspecified;
2375     CERTCrlEntry* entry = NULL;
2376     dpcacheStatus ds;
2377 
2378     if (!cert || !issuer) {
2379         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
2380         return SECFailure;
2381     }
2382 
2383     if (revStatus) {
2384         *revStatus = status;
2385     }
2386     if (revReason) {
2387         *revReason = reason;
2388     }
2389 
2390     if (t &&
2391         secCertTimeValid != CERT_CheckCertValidTimes(issuer, t, PR_FALSE)) {
2392         /* we won't be able to check the CRL's signature if the issuer cert
2393            is expired as of the time we are verifying. This may cause a valid
2394            CRL to be cached as bad. short-circuit to avoid this case. */
2395         PORT_SetError(SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE);
2396         return SECFailure;
2397     }
2398 
2399     rv = AcquireDPCache(issuer, &issuer->derSubject, dp, t, wincx, &dpcache,
2400                         &lockedwrite);
2401     PORT_Assert(SECSuccess == rv);
2402     if (SECSuccess != rv) {
2403         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
2404         return SECFailure;
2405     }
2406     /* now look up the certificate SN in the DP cache's CRL */
2407     ds = DPCache_Lookup(dpcache, &cert->serialNumber, &entry);
2408     switch (ds) {
2409         case dpcacheFoundEntry:
2410             PORT_Assert(entry);
2411             /* check the time if we have one */
2412             if (entry->revocationDate.data && entry->revocationDate.len) {
2413                 PRTime revocationDate = 0;
2414                 if (SECSuccess ==
2415                     DER_DecodeTimeChoice(&revocationDate,
2416                                          &entry->revocationDate)) {
2417                     /* we got a good revocation date, only consider the
2418                        certificate revoked if the time we are inquiring about
2419                        is past the revocation date */
2420                     if (t >= revocationDate) {
2421                         rv = SECFailure;
2422                     } else {
2423                         status = certRevocationStatusValid;
2424                     }
2425                 } else {
2426                     /* invalid revocation date, consider the certificate
2427                        permanently revoked */
2428                     rv = SECFailure;
2429                 }
2430             } else {
2431                 /* no revocation date, certificate is permanently revoked */
2432                 rv = SECFailure;
2433             }
2434             if (SECFailure == rv) {
2435                 (void)CERT_FindCRLEntryReasonExten(entry, &reason);
2436                 PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE);
2437             }
2438             break;
2439 
2440         case dpcacheEmpty:
2441             /* useful for NIST policy */
2442             status = certRevocationStatusUnknown;
2443             break;
2444 
2445         case dpcacheNoEntry:
2446             status = certRevocationStatusValid;
2447             break;
2448 
2449         case dpcacheInvalidCacheError:
2450             /* treat it as unknown and let the caller decide based on
2451                the policy */
2452             status = certRevocationStatusUnknown;
2453             break;
2454 
2455         default:
2456             /* leave status as revoked */
2457             break;
2458     }
2459 
2460     ReleaseDPCache(dpcache, lockedwrite);
2461     if (revStatus) {
2462         *revStatus = status;
2463     }
2464     if (revReason) {
2465         *revReason = reason;
2466     }
2467     return rv;
2468 }
2469 
2470 /* check CRL revocation status of given certificate and issuer */
2471 SECStatus
CERT_CheckCRL(CERTCertificate * cert,CERTCertificate * issuer,const SECItem * dp,PRTime t,void * wincx)2472 CERT_CheckCRL(CERTCertificate* cert, CERTCertificate* issuer, const SECItem* dp,
2473               PRTime t, void* wincx)
2474 {
2475     return cert_CheckCertRevocationStatus(cert, issuer, dp, t, wincx, NULL,
2476                                           NULL);
2477 }
2478 
2479 /* retrieve full CRL object that best matches the cache status */
2480 CERTSignedCrl*
SEC_FindCrlByName(CERTCertDBHandle * handle,SECItem * crlKey,int type)2481 SEC_FindCrlByName(CERTCertDBHandle* handle, SECItem* crlKey, int type)
2482 {
2483     CERTSignedCrl* acrl = NULL;
2484     CRLDPCache* dpcache = NULL;
2485     SECStatus rv = SECSuccess;
2486     PRBool writeLocked = PR_FALSE;
2487 
2488     if (!crlKey) {
2489         PORT_SetError(SEC_ERROR_INVALID_ARGS);
2490         return NULL;
2491     }
2492 
2493     rv = AcquireDPCache(NULL, crlKey, NULL, 0, NULL, &dpcache, &writeLocked);
2494     if (SECSuccess == rv) {
2495         acrl = GetBestCRL(dpcache, PR_TRUE); /* decode entries, because
2496         SEC_FindCrlByName always returned fully decoded CRLs in the past */
2497         ReleaseDPCache(dpcache, writeLocked);
2498     }
2499     return acrl;
2500 }
2501 
2502 /* invalidate the CRL cache for a given issuer, which forces a refetch of
2503    CRL objects from PKCS#11 tokens */
2504 void
CERT_CRLCacheRefreshIssuer(CERTCertDBHandle * dbhandle,SECItem * crlKey)2505 CERT_CRLCacheRefreshIssuer(CERTCertDBHandle* dbhandle, SECItem* crlKey)
2506 {
2507     CRLDPCache* cache = NULL;
2508     SECStatus rv = SECSuccess;
2509     PRBool writeLocked = PR_FALSE;
2510     PRBool readlocked;
2511 
2512     (void)dbhandle; /* silence compiler warnings */
2513 
2514     /* XCRL we will need to refresh all the DPs of the issuer in the future,
2515             not just the default one */
2516     rv = AcquireDPCache(NULL, crlKey, NULL, 0, NULL, &cache, &writeLocked);
2517     if (SECSuccess != rv) {
2518         return;
2519     }
2520     /* we need to invalidate the DPCache here */
2521     readlocked = (writeLocked == PR_TRUE ? PR_FALSE : PR_TRUE);
2522     DPCache_LockWrite();
2523     cache->refresh = PR_TRUE;
2524     DPCache_UnlockWrite();
2525     ReleaseDPCache(cache, writeLocked);
2526     return;
2527 }
2528 
2529 /* add the specified RAM CRL object to the cache */
2530 SECStatus
CERT_CacheCRL(CERTCertDBHandle * dbhandle,SECItem * newdercrl)2531 CERT_CacheCRL(CERTCertDBHandle* dbhandle, SECItem* newdercrl)
2532 {
2533     CRLDPCache* cache = NULL;
2534     SECStatus rv = SECSuccess;
2535     PRBool writeLocked = PR_FALSE;
2536     PRBool readlocked;
2537     CachedCrl* returned = NULL;
2538     PRBool added = PR_FALSE;
2539     CERTSignedCrl* newcrl = NULL;
2540     int realerror = 0;
2541 
2542     if (!dbhandle || !newdercrl) {
2543         PORT_SetError(SEC_ERROR_INVALID_ARGS);
2544         return SECFailure;
2545     }
2546 
2547     /* first decode the DER CRL to make sure it's OK */
2548     newcrl = CERT_DecodeDERCrlWithFlags(NULL, newdercrl, SEC_CRL_TYPE,
2549                                         CRL_DECODE_DONT_COPY_DER |
2550                                             CRL_DECODE_SKIP_ENTRIES);
2551 
2552     if (!newcrl) {
2553         return SECFailure;
2554     }
2555 
2556     /* XXX check if it has IDP extension. If so, do not proceed and set error */
2557 
2558     rv = AcquireDPCache(NULL, &newcrl->crl.derName, NULL, 0, NULL, &cache,
2559                         &writeLocked);
2560     if (SECSuccess == rv) {
2561         readlocked = (writeLocked == PR_TRUE ? PR_FALSE : PR_TRUE);
2562 
2563         rv = CachedCrl_Create(&returned, newcrl, CRL_OriginExplicit);
2564         if (SECSuccess == rv && returned) {
2565             DPCache_LockWrite();
2566             rv = DPCache_AddCRL(cache, returned, &added);
2567             if (PR_TRUE != added) {
2568                 realerror = PORT_GetError();
2569                 CachedCrl_Destroy(returned);
2570                 returned = NULL;
2571             }
2572             DPCache_UnlockWrite();
2573         }
2574 
2575         ReleaseDPCache(cache, writeLocked);
2576 
2577         if (!added) {
2578             rv = SECFailure;
2579         }
2580     }
2581     SEC_DestroyCrl(newcrl); /* free the CRL. Either it got added to the cache
2582         and the refcount got bumped, or not, and thus we need to free its
2583         RAM */
2584     if (realerror) {
2585         PORT_SetError(realerror);
2586     }
2587     return rv;
2588 }
2589 
2590 /* remove the specified RAM CRL object from the cache */
2591 SECStatus
CERT_UncacheCRL(CERTCertDBHandle * dbhandle,SECItem * olddercrl)2592 CERT_UncacheCRL(CERTCertDBHandle* dbhandle, SECItem* olddercrl)
2593 {
2594     CRLDPCache* cache = NULL;
2595     SECStatus rv = SECSuccess;
2596     PRBool writeLocked = PR_FALSE;
2597     PRBool readlocked;
2598     PRBool removed = PR_FALSE;
2599     PRUint32 i;
2600     CERTSignedCrl* oldcrl = NULL;
2601 
2602     if (!dbhandle || !olddercrl) {
2603         PORT_SetError(SEC_ERROR_INVALID_ARGS);
2604         return SECFailure;
2605     }
2606 
2607     /* first decode the DER CRL to make sure it's OK */
2608     oldcrl = CERT_DecodeDERCrlWithFlags(NULL, olddercrl, SEC_CRL_TYPE,
2609                                         CRL_DECODE_DONT_COPY_DER |
2610                                             CRL_DECODE_SKIP_ENTRIES);
2611 
2612     if (!oldcrl) {
2613         /* if this DER CRL can't decode, it can't be in the cache */
2614         return SECFailure;
2615     }
2616 
2617     rv = AcquireDPCache(NULL, &oldcrl->crl.derName, NULL, 0, NULL, &cache,
2618                         &writeLocked);
2619     if (SECSuccess == rv) {
2620         CachedCrl* returned = NULL;
2621 
2622         readlocked = (writeLocked == PR_TRUE ? PR_FALSE : PR_TRUE);
2623 
2624         rv = CachedCrl_Create(&returned, oldcrl, CRL_OriginExplicit);
2625         if (SECSuccess == rv && returned) {
2626             DPCache_LockWrite();
2627             for (i = 0; i < cache->ncrls; i++) {
2628                 PRBool dupe = PR_FALSE, updated = PR_FALSE;
2629                 rv = CachedCrl_Compare(returned, cache->crls[i], &dupe,
2630                                        &updated);
2631                 if (SECSuccess != rv) {
2632                     PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
2633                     break;
2634                 }
2635                 if (PR_TRUE == dupe) {
2636                     rv = DPCache_RemoveCRL(cache, i); /* got a match */
2637                     if (SECSuccess == rv) {
2638                         cache->mustchoose = PR_TRUE;
2639                         removed = PR_TRUE;
2640                     }
2641                     break;
2642                 }
2643             }
2644 
2645             DPCache_UnlockWrite();
2646 
2647             if (SECSuccess != CachedCrl_Destroy(returned)) {
2648                 rv = SECFailure;
2649             }
2650         }
2651 
2652         ReleaseDPCache(cache, writeLocked);
2653     }
2654     if (SECSuccess != SEC_DestroyCrl(oldcrl)) {
2655         /* need to do this because object is refcounted */
2656         rv = SECFailure;
2657     }
2658     if (SECSuccess == rv && PR_TRUE != removed) {
2659         PORT_SetError(SEC_ERROR_CRL_NOT_FOUND);
2660     }
2661     return rv;
2662 }
2663 
2664 SECStatus
cert_AcquireNamedCRLCache(NamedCRLCache ** returned)2665 cert_AcquireNamedCRLCache(NamedCRLCache** returned)
2666 {
2667     PORT_Assert(returned);
2668     if (!namedCRLCache.lock) {
2669         PORT_Assert(0);
2670         return SECFailure;
2671     }
2672     PR_Lock(namedCRLCache.lock);
2673     *returned = &namedCRLCache;
2674     return SECSuccess;
2675 }
2676 
2677 /* This must be called only while cache is acquired, and the entry is only
2678  * valid until cache is released.
2679  */
2680 SECStatus
cert_FindCRLByGeneralName(NamedCRLCache * ncc,const SECItem * canonicalizedName,NamedCRLCacheEntry ** retEntry)2681 cert_FindCRLByGeneralName(NamedCRLCache* ncc, const SECItem* canonicalizedName,
2682                           NamedCRLCacheEntry** retEntry)
2683 {
2684     if (!ncc || !canonicalizedName || !retEntry) {
2685         PORT_SetError(SEC_ERROR_INVALID_ARGS);
2686         return SECFailure;
2687     }
2688     *retEntry = (NamedCRLCacheEntry*)PL_HashTableLookup(
2689         namedCRLCache.entries, (void*)canonicalizedName);
2690     return SECSuccess;
2691 }
2692 
2693 SECStatus
cert_ReleaseNamedCRLCache(NamedCRLCache * ncc)2694 cert_ReleaseNamedCRLCache(NamedCRLCache* ncc)
2695 {
2696     if (!ncc) {
2697         return SECFailure;
2698     }
2699     if (!ncc->lock) {
2700         PORT_Assert(0);
2701         return SECFailure;
2702     }
2703     PR_Unlock(namedCRLCache.lock);
2704     return SECSuccess;
2705 }
2706 
2707 /* creates new named cache entry from CRL, and tries to add it to CRL cache */
2708 static SECStatus
addCRLToCache(CERTCertDBHandle * dbhandle,SECItem * crl,const SECItem * canonicalizedName,NamedCRLCacheEntry ** newEntry)2709 addCRLToCache(CERTCertDBHandle* dbhandle, SECItem* crl,
2710               const SECItem* canonicalizedName, NamedCRLCacheEntry** newEntry)
2711 {
2712     SECStatus rv = SECSuccess;
2713     NamedCRLCacheEntry* entry = NULL;
2714 
2715     /* create new named entry */
2716     if (SECSuccess != NamedCRLCacheEntry_Create(newEntry) || !*newEntry) {
2717         /* no need to keep unused CRL around */
2718         SECITEM_ZfreeItem(crl, PR_TRUE);
2719         return SECFailure;
2720     }
2721     entry = *newEntry;
2722     entry->crl = crl; /* named CRL cache owns DER */
2723     entry->lastAttemptTime = PR_Now();
2724     entry->canonicalizedName = SECITEM_DupItem(canonicalizedName);
2725     if (!entry->canonicalizedName) {
2726         rv = NamedCRLCacheEntry_Destroy(entry); /* destroys CRL too */
2727         PORT_Assert(SECSuccess == rv);
2728         return SECFailure;
2729     }
2730     /* now, attempt to insert CRL into CRL cache */
2731     if (SECSuccess == CERT_CacheCRL(dbhandle, entry->crl)) {
2732         entry->inCRLCache = PR_TRUE;
2733         entry->successfulInsertionTime = entry->lastAttemptTime;
2734     } else {
2735         switch (PR_GetError()) {
2736             case SEC_ERROR_CRL_ALREADY_EXISTS:
2737                 entry->dupe = PR_TRUE;
2738                 break;
2739 
2740             case SEC_ERROR_BAD_DER:
2741                 entry->badDER = PR_TRUE;
2742                 break;
2743 
2744             /* all other reasons */
2745             default:
2746                 entry->unsupported = PR_TRUE;
2747                 break;
2748         }
2749         rv = SECFailure;
2750         /* no need to keep unused CRL around */
2751         SECITEM_ZfreeItem(entry->crl, PR_TRUE);
2752         entry->crl = NULL;
2753     }
2754     return rv;
2755 }
2756 
2757 /* take ownership of CRL, and insert it into the named CRL cache
2758  * and indexed CRL cache
2759  */
2760 SECStatus
cert_CacheCRLByGeneralName(CERTCertDBHandle * dbhandle,SECItem * crl,const SECItem * canonicalizedName)2761 cert_CacheCRLByGeneralName(CERTCertDBHandle* dbhandle, SECItem* crl,
2762                            const SECItem* canonicalizedName)
2763 {
2764     NamedCRLCacheEntry *oldEntry, *newEntry = NULL;
2765     NamedCRLCache* ncc = NULL;
2766     SECStatus rv = SECSuccess;
2767 
2768     PORT_Assert(namedCRLCache.lock);
2769     PORT_Assert(namedCRLCache.entries);
2770 
2771     if (!crl || !canonicalizedName) {
2772         PORT_Assert(0);
2773         PORT_SetError(SEC_ERROR_INVALID_ARGS);
2774         return SECFailure;
2775     }
2776 
2777     rv = cert_AcquireNamedCRLCache(&ncc);
2778     PORT_Assert(SECSuccess == rv);
2779     if (SECSuccess != rv) {
2780         SECITEM_ZfreeItem(crl, PR_TRUE);
2781         return SECFailure;
2782     }
2783     rv = cert_FindCRLByGeneralName(ncc, canonicalizedName, &oldEntry);
2784     PORT_Assert(SECSuccess == rv);
2785     if (SECSuccess != rv) {
2786         (void)cert_ReleaseNamedCRLCache(ncc);
2787         SECITEM_ZfreeItem(crl, PR_TRUE);
2788         return SECFailure;
2789     }
2790     if (SECSuccess ==
2791         addCRLToCache(dbhandle, crl, canonicalizedName, &newEntry)) {
2792         if (!oldEntry) {
2793             /* add new good entry to the hash table */
2794             if (NULL == PL_HashTableAdd(namedCRLCache.entries,
2795                                         (void*)newEntry->canonicalizedName,
2796                                         (void*)newEntry)) {
2797                 PORT_Assert(0);
2798                 NamedCRLCacheEntry_Destroy(newEntry);
2799                 rv = SECFailure;
2800             }
2801         } else {
2802             PRBool removed;
2803             /* remove the old CRL from the cache if needed */
2804             if (oldEntry->inCRLCache) {
2805                 rv = CERT_UncacheCRL(dbhandle, oldEntry->crl);
2806                 PORT_Assert(SECSuccess == rv);
2807             }
2808             removed = PL_HashTableRemove(namedCRLCache.entries,
2809                                          (void*)oldEntry->canonicalizedName);
2810             PORT_Assert(removed);
2811             if (!removed) {
2812                 rv = SECFailure;
2813                 /* leak old entry since we couldn't remove it from the hash
2814                  * table */
2815             } else {
2816                 PORT_CheckSuccess(NamedCRLCacheEntry_Destroy(oldEntry));
2817             }
2818             if (NULL == PL_HashTableAdd(namedCRLCache.entries,
2819                                         (void*)newEntry->canonicalizedName,
2820                                         (void*)newEntry)) {
2821                 PORT_Assert(0);
2822                 rv = SECFailure;
2823             }
2824         }
2825     } else {
2826         /* error adding new CRL to cache */
2827         if (!oldEntry) {
2828             /* no old cache entry, use the new one even though it's bad */
2829             if (NULL == PL_HashTableAdd(namedCRLCache.entries,
2830                                         (void*)newEntry->canonicalizedName,
2831                                         (void*)newEntry)) {
2832                 PORT_Assert(0);
2833                 rv = SECFailure;
2834             }
2835         } else {
2836             if (oldEntry->inCRLCache) {
2837                 /* previous cache entry was good, keep it and update time */
2838                 oldEntry->lastAttemptTime = newEntry->lastAttemptTime;
2839                 /* throw away new bad entry */
2840                 rv = NamedCRLCacheEntry_Destroy(newEntry);
2841                 PORT_Assert(SECSuccess == rv);
2842             } else {
2843                 /* previous cache entry was bad, just replace it */
2844                 PRBool removed = PL_HashTableRemove(
2845                     namedCRLCache.entries, (void*)oldEntry->canonicalizedName);
2846                 PORT_Assert(removed);
2847                 if (!removed) {
2848                     /* leak old entry since we couldn't remove it from the hash
2849                      * table */
2850                     rv = SECFailure;
2851                 } else {
2852                     PORT_CheckSuccess(NamedCRLCacheEntry_Destroy(oldEntry));
2853                 }
2854                 if (NULL == PL_HashTableAdd(namedCRLCache.entries,
2855                                             (void*)newEntry->canonicalizedName,
2856                                             (void*)newEntry)) {
2857                     PORT_Assert(0);
2858                     rv = SECFailure;
2859                 }
2860             }
2861         }
2862     }
2863     PORT_CheckSuccess(cert_ReleaseNamedCRLCache(ncc));
2864 
2865     return rv;
2866 }
2867 
2868 static SECStatus
CachedCrl_Create(CachedCrl ** returned,CERTSignedCrl * crl,CRLOrigin origin)2869 CachedCrl_Create(CachedCrl** returned, CERTSignedCrl* crl, CRLOrigin origin)
2870 {
2871     CachedCrl* newcrl = NULL;
2872     if (!returned) {
2873         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
2874         return SECFailure;
2875     }
2876     newcrl = PORT_ZAlloc(sizeof(CachedCrl));
2877     if (!newcrl) {
2878         return SECFailure;
2879     }
2880     newcrl->crl = SEC_DupCrl(crl);
2881     newcrl->origin = origin;
2882     *returned = newcrl;
2883     return SECSuccess;
2884 }
2885 
2886 /* empty the cache content */
2887 static SECStatus
CachedCrl_Depopulate(CachedCrl * crl)2888 CachedCrl_Depopulate(CachedCrl* crl)
2889 {
2890     if (!crl) {
2891         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
2892         return SECFailure;
2893     }
2894     /* destroy the hash table */
2895     if (crl->entries) {
2896         PL_HashTableDestroy(crl->entries);
2897         crl->entries = NULL;
2898     }
2899 
2900     /* free the pre buffer */
2901     if (crl->prebuffer) {
2902         PreAllocator_Destroy(crl->prebuffer);
2903         crl->prebuffer = NULL;
2904     }
2905     return SECSuccess;
2906 }
2907 
2908 static SECStatus
CachedCrl_Destroy(CachedCrl * crl)2909 CachedCrl_Destroy(CachedCrl* crl)
2910 {
2911     if (!crl) {
2912         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
2913         return SECFailure;
2914     }
2915     CachedCrl_Depopulate(crl);
2916     SEC_DestroyCrl(crl->crl);
2917     PORT_Free(crl);
2918     return SECSuccess;
2919 }
2920 
2921 /* create hash table of CRL entries */
2922 static SECStatus
CachedCrl_Populate(CachedCrl * crlobject)2923 CachedCrl_Populate(CachedCrl* crlobject)
2924 {
2925     SECStatus rv = SECFailure;
2926     CERTCrlEntry** crlEntry = NULL;
2927     PRUint32 numEntries = 0;
2928 
2929     if (!crlobject) {
2930         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
2931         return SECFailure;
2932     }
2933     /* complete the entry decoding . XXX thread-safety of CRL object */
2934     rv = CERT_CompleteCRLDecodeEntries(crlobject->crl);
2935     if (SECSuccess != rv) {
2936         crlobject->unbuildable = PR_TRUE; /* don't try to build this again */
2937         return SECFailure;
2938     }
2939 
2940     if (crlobject->entries && crlobject->prebuffer) {
2941         /* cache is already built */
2942         return SECSuccess;
2943     }
2944 
2945     /* build the hash table from the full CRL */
2946     /* count CRL entries so we can pre-allocate space for hash table entries */
2947     for (crlEntry = crlobject->crl->crl.entries; crlEntry && *crlEntry;
2948          crlEntry++) {
2949         numEntries++;
2950     }
2951     crlobject->prebuffer =
2952         PreAllocator_Create(numEntries * sizeof(PLHashEntry));
2953     PORT_Assert(crlobject->prebuffer);
2954     if (!crlobject->prebuffer) {
2955         return SECFailure;
2956     }
2957     /* create a new hash table */
2958     crlobject->entries =
2959         PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare, PL_CompareValues,
2960                         &preAllocOps, crlobject->prebuffer);
2961     PORT_Assert(crlobject->entries);
2962     if (!crlobject->entries) {
2963         return SECFailure;
2964     }
2965     /* add all serial numbers to the hash table */
2966     for (crlEntry = crlobject->crl->crl.entries; crlEntry && *crlEntry;
2967          crlEntry++) {
2968         PL_HashTableAdd(crlobject->entries, &(*crlEntry)->serialNumber,
2969                         *crlEntry);
2970     }
2971 
2972     return SECSuccess;
2973 }
2974 
2975 /* returns true if there are CRLs from PKCS#11 slots */
2976 static PRBool
DPCache_HasTokenCRLs(CRLDPCache * cache)2977 DPCache_HasTokenCRLs(CRLDPCache* cache)
2978 {
2979     PRBool answer = PR_FALSE;
2980     PRUint32 i;
2981     for (i = 0; i < cache->ncrls; i++) {
2982         if (cache->crls[i] && (CRL_OriginToken == cache->crls[i]->origin)) {
2983             answer = PR_TRUE;
2984             break;
2985         }
2986     }
2987     return answer;
2988 }
2989 
2990 /* are these CRLs the same, as far as the cache is concerned ? */
2991 /* are these CRLs the same token object but with different DER ?
2992    This can happen if the DER CRL got updated in the token, but the PKCS#11
2993    object ID did not change. NSS softoken has the unfortunate property to
2994    never change the object ID for CRL objects. */
2995 static SECStatus
CachedCrl_Compare(CachedCrl * a,CachedCrl * b,PRBool * isDupe,PRBool * isUpdated)2996 CachedCrl_Compare(CachedCrl* a, CachedCrl* b, PRBool* isDupe, PRBool* isUpdated)
2997 {
2998     PORT_Assert(a);
2999     PORT_Assert(b);
3000     PORT_Assert(isDupe);
3001     PORT_Assert(isUpdated);
3002     if (!a || !b || !isDupe || !isUpdated || !a->crl || !b->crl) {
3003         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
3004         return SECFailure;
3005     }
3006 
3007     *isDupe = *isUpdated = PR_FALSE;
3008 
3009     if (a == b) {
3010         /* dupe */
3011         *isDupe = PR_TRUE;
3012         *isUpdated = PR_FALSE;
3013         return SECSuccess;
3014     }
3015     if (b->origin != a->origin) {
3016         /* CRLs of different origins are not considered dupes,
3017            and can't be updated either */
3018         return SECSuccess;
3019     }
3020     if (CRL_OriginToken == b->origin) {
3021         /* for token CRLs, slot and PKCS#11 object handle must match for CRL
3022            to truly be a dupe */
3023         if ((b->crl->slot == a->crl->slot) &&
3024             (b->crl->pkcs11ID == a->crl->pkcs11ID)) {
3025             /* ASN.1 DER needs to match for dupe check */
3026             /* could optimize by just checking a few fields like thisUpdate */
3027             if (SECEqual ==
3028                 SECITEM_CompareItem(b->crl->derCrl, a->crl->derCrl)) {
3029                 *isDupe = PR_TRUE;
3030             } else {
3031                 *isUpdated = PR_TRUE;
3032             }
3033         }
3034         return SECSuccess;
3035     }
3036     if (CRL_OriginExplicit == b->origin) {
3037         /* We need to make sure this is the same object that the user provided
3038            to CERT_CacheCRL previously. That API takes a SECItem*, thus, we
3039            just do a pointer comparison here.
3040         */
3041         if (b->crl->derCrl == a->crl->derCrl) {
3042             *isDupe = PR_TRUE;
3043         }
3044     }
3045     return SECSuccess;
3046 }
3047