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