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