security/nss/lib/certdb/crl.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial