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