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