Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(198)

Side by Side Diff: mozilla/security/nss/lib/certdb/crl.c

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

Powered by Google App Engine
This is Rietveld 408576698