OLD | NEW |
| (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 } | |
OLD | NEW |