tdcache.c (32103B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #ifndef PKIM_H 6 #include "pkim.h" 7 #endif /* PKIM_H */ 8 9 #ifndef PKIT_H 10 #include "pkit.h" 11 #endif /* PKIT_H */ 12 13 #ifndef NSSPKI_H 14 #include "nsspki.h" 15 #endif /* NSSPKI_H */ 16 17 #ifndef PKI_H 18 #include "pki.h" 19 #endif /* PKI_H */ 20 21 #ifndef NSSBASE_H 22 #include "nssbase.h" 23 #endif /* NSSBASE_H */ 24 25 #ifndef BASE_H 26 #include "base.h" 27 #endif /* BASE_H */ 28 29 #include "cert.h" 30 #include "dev.h" 31 #include "pki3hack.h" 32 33 #ifdef DEBUG_CACHE 34 static PRLogModuleInfo *s_log = NULL; 35 #endif 36 37 #ifdef DEBUG_CACHE 38 static void 39 log_item_dump(const char *msg, NSSItem *it) 40 { 41 char buf[33]; 42 int i, j; 43 for (i = 0; i < 10 && i < it->size; i++) { 44 snprintf(&buf[2 * i], sizeof(buf) - 2 * i, "%02X", ((PRUint8 *)it->data)[i]); 45 } 46 if (it->size > 10) { 47 snprintf(&buf[2 * i], sizeof(buf) - 2 * i, ".."); 48 i += 1; 49 for (j = it->size - 1; i <= 16 && j > 10; i++, j--) { 50 snprintf(&buf[2 * i], sizeof(buf) - 2 * i, "%02X", ((PRUint8 *)it->data)[j]); 51 } 52 } 53 PR_LOG(s_log, PR_LOG_DEBUG, ("%s: %s", msg, buf)); 54 } 55 #endif 56 57 #ifdef DEBUG_CACHE 58 static void 59 log_cert_ref(const char *msg, NSSCertificate *c) 60 { 61 PR_LOG(s_log, PR_LOG_DEBUG, ("%s: %s", msg, (c->nickname) ? c->nickname : c->email)); 62 log_item_dump("\tserial", &c->serial); 63 log_item_dump("\tsubject", &c->subject); 64 } 65 #endif 66 67 /* Certificate cache routines */ 68 69 /* XXX 70 * Locking is not handled well at all. A single, global lock with sub-locks 71 * in the collection types. Cleanup needed. 72 */ 73 74 /* should it live in its own arena? */ 75 struct nssTDCertificateCacheStr { 76 PZLock *lock; /* Must not be held when calling nssSlot_IsTokenPresent. See bug 1625791. */ 77 NSSArena *arena; 78 nssHash *issuerAndSN; 79 nssHash *subject; 80 nssHash *nickname; 81 nssHash *email; 82 }; 83 84 struct cache_entry_str { 85 union { 86 NSSCertificate *cert; 87 nssList *list; 88 void *value; 89 } entry; 90 PRUint32 hits; 91 PRTime lastHit; 92 NSSArena *arena; 93 NSSUTF8 *nickname; 94 }; 95 96 typedef struct cache_entry_str cache_entry; 97 98 static cache_entry * 99 new_cache_entry(NSSArena *arena, void *value, PRBool ownArena) 100 { 101 cache_entry *ce = nss_ZNEW(arena, cache_entry); 102 if (ce) { 103 ce->entry.value = value; 104 ce->hits = 1; 105 ce->lastHit = PR_Now(); 106 if (ownArena) { 107 ce->arena = arena; 108 } 109 ce->nickname = NULL; 110 } 111 return ce; 112 } 113 114 /* this should not be exposed in a header, but is here to keep the above 115 * types/functions static 116 */ 117 NSS_IMPLEMENT PRStatus 118 nssTrustDomain_InitializeCache( 119 NSSTrustDomain *td, 120 PRUint32 cacheSize) 121 { 122 NSSArena *arena; 123 nssTDCertificateCache *cache = td->cache; 124 #ifdef DEBUG_CACHE 125 s_log = PR_NewLogModule("nss_cache"); 126 PR_ASSERT(s_log); 127 #endif 128 PR_ASSERT(!cache); 129 arena = nssArena_Create(); 130 if (!arena) { 131 return PR_FAILURE; 132 } 133 cache = nss_ZNEW(arena, nssTDCertificateCache); 134 if (!cache) { 135 nssArena_Destroy(arena); 136 return PR_FAILURE; 137 } 138 cache->lock = PZ_NewLock(nssILockCache); 139 if (!cache->lock) { 140 nssArena_Destroy(arena); 141 return PR_FAILURE; 142 } 143 /* Create the issuer and serial DER --> certificate hash */ 144 cache->issuerAndSN = nssHash_CreateCertificate(arena, cacheSize); 145 if (!cache->issuerAndSN) { 146 goto loser; 147 } 148 /* Create the subject DER --> subject list hash */ 149 cache->subject = nssHash_CreateItem(arena, cacheSize); 150 if (!cache->subject) { 151 goto loser; 152 } 153 /* Create the nickname --> subject list hash */ 154 cache->nickname = nssHash_CreateString(arena, cacheSize); 155 if (!cache->nickname) { 156 goto loser; 157 } 158 /* Create the email --> list of subject lists hash */ 159 cache->email = nssHash_CreateString(arena, cacheSize); 160 if (!cache->email) { 161 goto loser; 162 } 163 cache->arena = arena; 164 td->cache = cache; 165 #ifdef DEBUG_CACHE 166 PR_LOG(s_log, PR_LOG_DEBUG, ("Cache initialized.")); 167 #endif 168 return PR_SUCCESS; 169 loser: 170 PZ_DestroyLock(cache->lock); 171 nssArena_Destroy(arena); 172 td->cache = NULL; 173 #ifdef DEBUG_CACHE 174 PR_LOG(s_log, PR_LOG_DEBUG, ("Cache initialization failed.")); 175 #endif 176 return PR_FAILURE; 177 } 178 179 /* The entries of the hashtable are currently dependent on the certificate(s) 180 * that produced them. That is, the entries will be freed when the cert is 181 * released from the cache. If there are certs in the cache at any time, 182 * including shutdown, the hash table entries will hold memory. In order for 183 * clean shutdown, it is necessary for there to be no certs in the cache. 184 */ 185 186 extern const NSSError NSS_ERROR_INTERNAL_ERROR; 187 extern const NSSError NSS_ERROR_BUSY; 188 189 NSS_IMPLEMENT PRStatus 190 nssTrustDomain_DestroyCache(NSSTrustDomain *td) 191 { 192 if (!td->cache) { 193 nss_SetError(NSS_ERROR_INTERNAL_ERROR); 194 return PR_FAILURE; 195 } 196 if (nssHash_Count(td->cache->issuerAndSN) > 0) { 197 nss_SetError(NSS_ERROR_BUSY); 198 return PR_FAILURE; 199 } 200 PZ_DestroyLock(td->cache->lock); 201 nssHash_Destroy(td->cache->issuerAndSN); 202 nssHash_Destroy(td->cache->subject); 203 nssHash_Destroy(td->cache->nickname); 204 nssHash_Destroy(td->cache->email); 205 nssArena_Destroy(td->cache->arena); 206 td->cache = NULL; 207 #ifdef DEBUG_CACHE 208 PR_LOG(s_log, PR_LOG_DEBUG, ("Cache destroyed.")); 209 #endif 210 return PR_SUCCESS; 211 } 212 213 static PRStatus 214 remove_issuer_and_serial_entry( 215 nssTDCertificateCache *cache, 216 NSSCertificate *cert) 217 { 218 /* Remove the cert from the issuer/serial hash */ 219 nssHash_Remove(cache->issuerAndSN, cert); 220 #ifdef DEBUG_CACHE 221 log_cert_ref("removed issuer/sn", cert); 222 #endif 223 return PR_SUCCESS; 224 } 225 226 static PRStatus 227 remove_subject_entry( 228 nssTDCertificateCache *cache, 229 NSSCertificate *cert, 230 nssList **subjectList, 231 NSSUTF8 **nickname, 232 NSSArena **arena) 233 { 234 PRStatus nssrv; 235 cache_entry *ce; 236 *subjectList = NULL; 237 *arena = NULL; 238 /* Get the subject list for the cert's subject */ 239 ce = (cache_entry *)nssHash_Lookup(cache->subject, &cert->subject); 240 if (ce) { 241 /* Remove the cert from the subject hash */ 242 nssList_Remove(ce->entry.list, cert); 243 *subjectList = ce->entry.list; 244 *nickname = ce->nickname; 245 *arena = ce->arena; 246 nssrv = PR_SUCCESS; 247 #ifdef DEBUG_CACHE 248 log_cert_ref("removed cert", cert); 249 log_item_dump("from subject list", &cert->subject); 250 #endif 251 } else { 252 nssrv = PR_FAILURE; 253 } 254 return nssrv; 255 } 256 257 static PRStatus 258 remove_nickname_entry( 259 nssTDCertificateCache *cache, 260 NSSUTF8 *nickname, 261 nssList *subjectList) 262 { 263 PRStatus nssrv; 264 if (nickname) { 265 nssHash_Remove(cache->nickname, nickname); 266 nssrv = PR_SUCCESS; 267 #ifdef DEBUG_CACHE 268 PR_LOG(s_log, PR_LOG_DEBUG, ("removed nickname %s", nickname)); 269 #endif 270 } else { 271 nssrv = PR_FAILURE; 272 } 273 return nssrv; 274 } 275 276 static PRStatus 277 remove_email_entry( 278 nssTDCertificateCache *cache, 279 NSSCertificate *cert, 280 nssList *subjectList) 281 { 282 PRStatus nssrv = PR_FAILURE; 283 cache_entry *ce; 284 /* Find the subject list in the email hash */ 285 if (cert->email) { 286 ce = (cache_entry *)nssHash_Lookup(cache->email, cert->email); 287 if (ce) { 288 nssList *subjects = ce->entry.list; 289 /* Remove the subject list from the email hash */ 290 if (subjects) { 291 nssList_Remove(subjects, subjectList); 292 #ifdef DEBUG_CACHE 293 log_item_dump("removed subject list", &cert->subject); 294 PR_LOG(s_log, PR_LOG_DEBUG, ("for email %s", cert->email)); 295 #endif 296 if (nssList_Count(subjects) == 0) { 297 /* No more subject lists for email, delete list and 298 * remove hash entry 299 */ 300 (void)nssList_Destroy(subjects); 301 nssHash_Remove(cache->email, cert->email); 302 /* there are no entries left for this address, free space 303 * used for email entries 304 */ 305 nssArena_Destroy(ce->arena); 306 #ifdef DEBUG_CACHE 307 PR_LOG(s_log, PR_LOG_DEBUG, ("removed email %s", cert->email)); 308 #endif 309 } 310 } 311 nssrv = PR_SUCCESS; 312 } 313 } 314 return nssrv; 315 } 316 317 NSS_IMPLEMENT void 318 nssTrustDomain_RemoveCertFromCacheLOCKED( 319 NSSTrustDomain *td, 320 NSSCertificate *cert) 321 { 322 nssList *subjectList; 323 cache_entry *ce; 324 NSSArena *arena; 325 NSSUTF8 *nickname = NULL; 326 327 #ifdef DEBUG_CACHE 328 log_cert_ref("attempt to remove cert", cert); 329 #endif 330 ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, cert); 331 if (!ce || ce->entry.cert != cert) { 332 /* If it's not in the cache, or a different cert is (this is really 333 * for safety reasons, though it shouldn't happen), do nothing 334 */ 335 #ifdef DEBUG_CACHE 336 PR_LOG(s_log, PR_LOG_DEBUG, ("but it wasn't in the cache")); 337 #endif 338 return; 339 } 340 (void)remove_issuer_and_serial_entry(td->cache, cert); 341 (void)remove_subject_entry(td->cache, cert, &subjectList, 342 &nickname, &arena); 343 if (nssList_Count(subjectList) == 0) { 344 (void)remove_nickname_entry(td->cache, nickname, subjectList); 345 (void)remove_email_entry(td->cache, cert, subjectList); 346 (void)nssList_Destroy(subjectList); 347 nssHash_Remove(td->cache->subject, &cert->subject); 348 /* there are no entries left for this subject, free the space used 349 * for both the nickname and subject entries 350 */ 351 if (arena) { 352 nssArena_Destroy(arena); 353 } 354 } 355 } 356 357 NSS_IMPLEMENT void 358 nssTrustDomain_LockCertCache(NSSTrustDomain *td) 359 { 360 PZ_Lock(td->cache->lock); 361 } 362 363 NSS_IMPLEMENT void 364 nssTrustDomain_UnlockCertCache(NSSTrustDomain *td) 365 { 366 PZ_Unlock(td->cache->lock); 367 } 368 369 struct token_cert_dtor { 370 NSSToken *token; 371 nssTDCertificateCache *cache; 372 NSSCertificate **certs; 373 PRUint32 numCerts, arrSize; 374 }; 375 376 static void 377 remove_token_certs(const void *k, void *v, void *a) 378 { 379 NSSCertificate *c = (NSSCertificate *)k; 380 nssPKIObject *object = &c->object; 381 struct token_cert_dtor *dtor = a; 382 PRUint32 i; 383 nssPKIObject_AddRef(object); 384 nssPKIObject_Lock(object); 385 for (i = 0; i < object->numInstances; i++) { 386 if (object->instances[i]->token == dtor->token) { 387 nssCryptokiObject_Destroy(object->instances[i]); 388 object->instances[i] = object->instances[object->numInstances - 1]; 389 object->instances[object->numInstances - 1] = NULL; 390 object->numInstances--; 391 dtor->certs[dtor->numCerts++] = c; 392 if (dtor->numCerts == dtor->arrSize) { 393 dtor->arrSize *= 2; 394 dtor->certs = nss_ZREALLOCARRAY(dtor->certs, 395 NSSCertificate *, 396 dtor->arrSize); 397 } 398 break; 399 } 400 } 401 nssPKIObject_Unlock(object); 402 nssPKIObject_Destroy(object); 403 return; 404 } 405 406 /* 407 * Remove all certs for the given token from the cache. This is 408 * needed if the token is removed. 409 */ 410 NSS_IMPLEMENT PRStatus 411 nssTrustDomain_RemoveTokenCertsFromCache( 412 NSSTrustDomain *td, 413 NSSToken *token) 414 { 415 NSSCertificate **certs; 416 PRUint32 i, arrSize = 10; 417 struct token_cert_dtor dtor; 418 certs = nss_ZNEWARRAY(NULL, NSSCertificate *, arrSize); 419 if (!certs) { 420 return PR_FAILURE; 421 } 422 dtor.cache = td->cache; 423 dtor.token = token; 424 dtor.certs = certs; 425 dtor.numCerts = 0; 426 dtor.arrSize = arrSize; 427 PZ_Lock(td->cache->lock); 428 nssHash_Iterate(td->cache->issuerAndSN, remove_token_certs, &dtor); 429 for (i = 0; i < dtor.numCerts; i++) { 430 if (dtor.certs[i]->object.numInstances == 0) { 431 nssTrustDomain_RemoveCertFromCacheLOCKED(td, dtor.certs[i]); 432 dtor.certs[i] = NULL; /* skip this cert in the second for loop */ 433 } else { 434 /* make sure it doesn't disappear on us before we finish */ 435 nssCertificate_AddRef(dtor.certs[i]); 436 } 437 } 438 PZ_Unlock(td->cache->lock); 439 for (i = 0; i < dtor.numCerts; i++) { 440 if (dtor.certs[i]) { 441 STAN_ForceCERTCertificateUpdate(dtor.certs[i]); 442 nssCertificate_Destroy(dtor.certs[i]); 443 } 444 } 445 nss_ZFreeIf(dtor.certs); 446 return PR_SUCCESS; 447 } 448 449 NSS_IMPLEMENT PRStatus 450 nssTrustDomain_UpdateCachedTokenCerts( 451 NSSTrustDomain *td, 452 NSSToken *token) 453 { 454 NSSCertificate **cp, **cached = NULL; 455 nssList *certList; 456 PRUint32 count; 457 certList = nssList_Create(NULL, PR_FALSE); 458 if (!certList) 459 return PR_FAILURE; 460 (void)nssTrustDomain_GetCertsFromCache(td, certList); 461 count = nssList_Count(certList); 462 if (count > 0) { 463 cached = nss_ZNEWARRAY(NULL, NSSCertificate *, count + 1); 464 if (!cached) { 465 nssList_Destroy(certList); 466 return PR_FAILURE; 467 } 468 nssList_GetArray(certList, (void **)cached, count); 469 for (cp = cached; *cp; cp++) { 470 nssCryptokiObject *instance; 471 NSSCertificate *c = *cp; 472 nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; 473 instance = nssToken_FindCertificateByIssuerAndSerialNumber( 474 token, 475 NULL, 476 &c->issuer, 477 &c->serial, 478 tokenOnly, 479 NULL); 480 if (instance) { 481 nssPKIObject_AddInstance(&c->object, instance); 482 STAN_ForceCERTCertificateUpdate(c); 483 } 484 } 485 nssCertificateArray_Destroy(cached); 486 } 487 nssList_Destroy(certList); 488 return PR_SUCCESS; 489 } 490 491 static PRStatus 492 add_issuer_and_serial_entry( 493 NSSArena *arena, 494 nssTDCertificateCache *cache, 495 NSSCertificate *cert) 496 { 497 cache_entry *ce; 498 ce = new_cache_entry(arena, (void *)cert, PR_FALSE); 499 #ifdef DEBUG_CACHE 500 log_cert_ref("added to issuer/sn", cert); 501 #endif 502 return nssHash_Add(cache->issuerAndSN, cert, (void *)ce); 503 } 504 505 static PRStatus 506 add_subject_entry( 507 NSSArena *arena, 508 nssTDCertificateCache *cache, 509 NSSCertificate *cert, 510 NSSUTF8 *nickname, 511 nssList **subjectList) 512 { 513 PRStatus nssrv; 514 nssList *list; 515 cache_entry *ce; 516 *subjectList = NULL; /* this is only set if a new one is created */ 517 ce = (cache_entry *)nssHash_Lookup(cache->subject, &cert->subject); 518 if (ce) { 519 ce->hits++; 520 ce->lastHit = PR_Now(); 521 /* The subject is already in, add this cert to the list */ 522 nssrv = nssList_AddUnique(ce->entry.list, cert); 523 #ifdef DEBUG_CACHE 524 log_cert_ref("added to existing subject list", cert); 525 #endif 526 } else { 527 NSSDER *subject; 528 /* Create a new subject list for the subject */ 529 list = nssList_Create(arena, PR_FALSE); 530 if (!list) { 531 return PR_FAILURE; 532 } 533 ce = new_cache_entry(arena, (void *)list, PR_TRUE); 534 if (!ce) { 535 return PR_FAILURE; 536 } 537 if (nickname) { 538 ce->nickname = nssUTF8_Duplicate(nickname, arena); 539 } 540 nssList_SetSortFunction(list, nssCertificate_SubjectListSort); 541 /* Add the cert entry to this list of subjects */ 542 nssrv = nssList_AddUnique(list, cert); 543 if (nssrv != PR_SUCCESS) { 544 return nssrv; 545 } 546 /* Add the subject list to the cache */ 547 subject = nssItem_Duplicate(&cert->subject, arena, NULL); 548 if (!subject) { 549 return PR_FAILURE; 550 } 551 nssrv = nssHash_Add(cache->subject, subject, ce); 552 if (nssrv != PR_SUCCESS) { 553 return nssrv; 554 } 555 *subjectList = list; 556 #ifdef DEBUG_CACHE 557 log_cert_ref("created subject list", cert); 558 #endif 559 } 560 return nssrv; 561 } 562 563 static PRStatus 564 add_nickname_entry( 565 NSSArena *arena, 566 nssTDCertificateCache *cache, 567 NSSUTF8 *certNickname, 568 nssList *subjectList) 569 { 570 PRStatus nssrv = PR_SUCCESS; 571 cache_entry *ce; 572 ce = (cache_entry *)nssHash_Lookup(cache->nickname, certNickname); 573 if (ce) { 574 /* This is a collision. A nickname entry already exists for this 575 * subject, but a subject entry didn't. This would imply there are 576 * two subjects using the same nickname, which is not allowed. 577 */ 578 return PR_FAILURE; 579 } else { 580 NSSUTF8 *nickname; 581 ce = new_cache_entry(arena, subjectList, PR_FALSE); 582 if (!ce) { 583 return PR_FAILURE; 584 } 585 nickname = nssUTF8_Duplicate(certNickname, arena); 586 if (!nickname) { 587 return PR_FAILURE; 588 } 589 nssrv = nssHash_Add(cache->nickname, nickname, ce); 590 #ifdef DEBUG_CACHE 591 log_cert_ref("created nickname for", cert); 592 #endif 593 } 594 return nssrv; 595 } 596 597 static PRStatus 598 add_email_entry( 599 nssTDCertificateCache *cache, 600 NSSCertificate *cert, 601 nssList *subjectList) 602 { 603 PRStatus nssrv = PR_SUCCESS; 604 nssList *subjects; 605 cache_entry *ce; 606 ce = (cache_entry *)nssHash_Lookup(cache->email, cert->email); 607 if (ce) { 608 /* Already have an entry for this email address, but not subject */ 609 subjects = ce->entry.list; 610 nssrv = nssList_AddUnique(subjects, subjectList); 611 ce->hits++; 612 ce->lastHit = PR_Now(); 613 #ifdef DEBUG_CACHE 614 log_cert_ref("added subject to email for", cert); 615 #endif 616 } else { 617 NSSASCII7 *email; 618 NSSArena *arena; 619 arena = nssArena_Create(); 620 if (!arena) { 621 return PR_FAILURE; 622 } 623 /* Create a new list of subject lists, add this subject */ 624 subjects = nssList_Create(arena, PR_TRUE); 625 if (!subjects) { 626 nssArena_Destroy(arena); 627 return PR_FAILURE; 628 } 629 /* Add the new subject to the list */ 630 nssrv = nssList_AddUnique(subjects, subjectList); 631 if (nssrv != PR_SUCCESS) { 632 nssArena_Destroy(arena); 633 return nssrv; 634 } 635 /* Add the new entry to the cache */ 636 ce = new_cache_entry(arena, (void *)subjects, PR_TRUE); 637 if (!ce) { 638 nssArena_Destroy(arena); 639 return PR_FAILURE; 640 } 641 email = nssUTF8_Duplicate(cert->email, arena); 642 if (!email) { 643 nssArena_Destroy(arena); 644 return PR_FAILURE; 645 } 646 nssrv = nssHash_Add(cache->email, email, ce); 647 if (nssrv != PR_SUCCESS) { 648 nssArena_Destroy(arena); 649 return nssrv; 650 } 651 #ifdef DEBUG_CACHE 652 log_cert_ref("created email for", cert); 653 #endif 654 } 655 return nssrv; 656 } 657 658 extern const NSSError NSS_ERROR_CERTIFICATE_IN_CACHE; 659 660 static void 661 remove_object_instances( 662 nssPKIObject *object, 663 nssCryptokiObject **instances, 664 int numInstances) 665 { 666 int i; 667 668 for (i = 0; i < numInstances; i++) { 669 nssPKIObject_RemoveInstanceForToken(object, instances[i]->token); 670 } 671 } 672 673 static SECStatus 674 merge_object_instances( 675 nssPKIObject *to, 676 nssPKIObject *from) 677 { 678 nssCryptokiObject **instances, **ci; 679 int i; 680 SECStatus rv = SECSuccess; 681 682 instances = nssPKIObject_GetInstances(from); 683 if (instances == NULL) { 684 return SECFailure; 685 } 686 for (ci = instances, i = 0; *ci; ci++, i++) { 687 nssCryptokiObject *instance = nssCryptokiObject_Clone(*ci); 688 if (instance) { 689 if (nssPKIObject_AddInstance(to, instance) == PR_SUCCESS) { 690 continue; 691 } 692 nssCryptokiObject_Destroy(instance); 693 } 694 remove_object_instances(to, instances, i); 695 rv = SECFailure; 696 break; 697 } 698 nssCryptokiObjectArray_Destroy(instances); 699 return rv; 700 } 701 702 static NSSCertificate * 703 add_cert_to_cache( 704 NSSTrustDomain *td, 705 NSSCertificate *cert) 706 { 707 NSSArena *arena = NULL; 708 nssList *subjectList = NULL; 709 PRStatus nssrv; 710 PRUint32 added = 0; 711 cache_entry *ce; 712 NSSCertificate *rvCert = NULL; 713 NSSUTF8 *certNickname = nssCertificate_GetNickname(cert, NULL); 714 715 /* Set cc->trust and cc->nssCertificate before taking td->cache->lock. 716 * Otherwise, the sorter in add_subject_entry may eventually call 717 * nssSlot_IsTokenPresent, which must not occur while the cache lock 718 * is held. See bugs 1625791 and 1651564 for details. */ 719 if (cert->type == NSSCertificateType_PKIX) { 720 (void)STAN_GetCERTCertificate(cert); 721 } 722 723 PZ_Lock(td->cache->lock); 724 /* If it exists in the issuer/serial hash, it's already in all */ 725 ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, cert); 726 if (ce) { 727 ce->hits++; 728 ce->lastHit = PR_Now(); 729 rvCert = nssCertificate_AddRef(ce->entry.cert); 730 #ifdef DEBUG_CACHE 731 log_cert_ref("attempted to add cert already in cache", cert); 732 #endif 733 PZ_Unlock(td->cache->lock); 734 nss_ZFreeIf(certNickname); 735 /* collision - somebody else already added the cert 736 * to the cache before this thread got around to it. 737 */ 738 /* merge the instances of the cert */ 739 if (merge_object_instances(&rvCert->object, &cert->object) != SECSuccess) { 740 nssCertificate_Destroy(rvCert); 741 return NULL; 742 } 743 STAN_ForceCERTCertificateUpdate(rvCert); 744 nssCertificate_Destroy(cert); 745 return rvCert; 746 } 747 /* create a new cache entry for this cert within the cert's arena*/ 748 nssrv = add_issuer_and_serial_entry(cert->object.arena, td->cache, cert); 749 if (nssrv != PR_SUCCESS) { 750 goto loser; 751 } 752 added++; 753 /* create an arena for the nickname and subject entries */ 754 arena = nssArena_Create(); 755 if (!arena) { 756 goto loser; 757 } 758 /* create a new subject list for this cert, or add to existing */ 759 nssrv = add_subject_entry(arena, td->cache, cert, 760 certNickname, &subjectList); 761 if (nssrv != PR_SUCCESS) { 762 goto loser; 763 } 764 added++; 765 /* If a new subject entry was created, also need nickname and/or email */ 766 if (subjectList != NULL) { 767 #ifdef nodef 768 PRBool handle = PR_FALSE; 769 #endif 770 if (certNickname) { 771 nssrv = add_nickname_entry(arena, td->cache, 772 certNickname, subjectList); 773 if (nssrv != PR_SUCCESS) { 774 goto loser; 775 } 776 #ifdef nodef 777 handle = PR_TRUE; 778 #endif 779 added++; 780 } 781 if (cert->email) { 782 nssrv = add_email_entry(td->cache, cert, subjectList); 783 if (nssrv != PR_SUCCESS) { 784 goto loser; 785 } 786 #ifdef nodef 787 handle = PR_TRUE; 788 #endif 789 added += 2; 790 } 791 #ifdef nodef 792 /* I think either a nickname or email address must be associated 793 * with the cert. However, certs are passed to NewTemp without 794 * either. This worked in the old code, so it must work now. 795 */ 796 if (!handle) { 797 /* Require either nickname or email handle */ 798 nssrv = PR_FAILURE; 799 goto loser; 800 } 801 #endif 802 } else { 803 /* A new subject entry was not created. arena is unused. */ 804 nssArena_Destroy(arena); 805 } 806 rvCert = cert; 807 PZ_Unlock(td->cache->lock); 808 nss_ZFreeIf(certNickname); 809 return rvCert; 810 loser: 811 nss_ZFreeIf(certNickname); 812 certNickname = NULL; 813 /* Remove any handles that have been created */ 814 subjectList = NULL; 815 if (added >= 1) { 816 (void)remove_issuer_and_serial_entry(td->cache, cert); 817 } 818 if (added >= 2) { 819 (void)remove_subject_entry(td->cache, cert, &subjectList, 820 &certNickname, &arena); 821 } 822 if (added == 3 || added == 5) { 823 (void)remove_nickname_entry(td->cache, certNickname, subjectList); 824 } 825 if (added >= 4) { 826 (void)remove_email_entry(td->cache, cert, subjectList); 827 } 828 if (subjectList) { 829 nssHash_Remove(td->cache->subject, &cert->subject); 830 nssList_Destroy(subjectList); 831 } 832 if (arena) { 833 nssArena_Destroy(arena); 834 } 835 PZ_Unlock(td->cache->lock); 836 return NULL; 837 } 838 839 NSS_IMPLEMENT PRStatus 840 nssTrustDomain_AddCertsToCache( 841 NSSTrustDomain *td, 842 NSSCertificate **certs, 843 PRUint32 numCerts) 844 { 845 PRUint32 i; 846 NSSCertificate *c; 847 for (i = 0; i < numCerts && certs[i]; i++) { 848 c = add_cert_to_cache(td, certs[i]); 849 if (c == NULL) { 850 return PR_FAILURE; 851 } else { 852 certs[i] = c; 853 } 854 } 855 return PR_SUCCESS; 856 } 857 858 static NSSCertificate ** 859 collect_subject_certs( 860 nssList *subjectList, 861 nssList *rvCertListOpt) 862 { 863 NSSCertificate *c; 864 NSSCertificate **rvArray = NULL; 865 PRUint32 count; 866 nssCertificateList_AddReferences(subjectList); 867 if (rvCertListOpt) { 868 nssListIterator *iter = nssList_CreateIterator(subjectList); 869 if (!iter) { 870 return (NSSCertificate **)NULL; 871 } 872 for (c = (NSSCertificate *)nssListIterator_Start(iter); 873 c != (NSSCertificate *)NULL; 874 c = (NSSCertificate *)nssListIterator_Next(iter)) { 875 nssList_Add(rvCertListOpt, c); 876 } 877 nssListIterator_Finish(iter); 878 nssListIterator_Destroy(iter); 879 } else { 880 count = nssList_Count(subjectList); 881 rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count + 1); 882 if (!rvArray) { 883 return (NSSCertificate **)NULL; 884 } 885 nssList_GetArray(subjectList, (void **)rvArray, count); 886 } 887 return rvArray; 888 } 889 890 /* 891 * Find all cached certs with this subject. 892 */ 893 NSS_IMPLEMENT NSSCertificate ** 894 nssTrustDomain_GetCertsForSubjectFromCache( 895 NSSTrustDomain *td, 896 NSSDER *subject, 897 nssList *certListOpt) 898 { 899 NSSCertificate **rvArray = NULL; 900 cache_entry *ce; 901 #ifdef DEBUG_CACHE 902 log_item_dump("looking for cert by subject", subject); 903 #endif 904 PZ_Lock(td->cache->lock); 905 ce = (cache_entry *)nssHash_Lookup(td->cache->subject, subject); 906 if (ce) { 907 ce->hits++; 908 ce->lastHit = PR_Now(); 909 #ifdef DEBUG_CACHE 910 PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits)); 911 #endif 912 rvArray = collect_subject_certs(ce->entry.list, certListOpt); 913 } 914 PZ_Unlock(td->cache->lock); 915 return rvArray; 916 } 917 918 /* 919 * Find all cached certs with this label. 920 */ 921 NSS_IMPLEMENT NSSCertificate ** 922 nssTrustDomain_GetCertsForNicknameFromCache( 923 NSSTrustDomain *td, 924 const NSSUTF8 *nickname, 925 nssList *certListOpt) 926 { 927 NSSCertificate **rvArray = NULL; 928 cache_entry *ce; 929 #ifdef DEBUG_CACHE 930 PR_LOG(s_log, PR_LOG_DEBUG, ("looking for cert by nick %s", nickname)); 931 #endif 932 PZ_Lock(td->cache->lock); 933 ce = (cache_entry *)nssHash_Lookup(td->cache->nickname, nickname); 934 if (ce) { 935 ce->hits++; 936 ce->lastHit = PR_Now(); 937 #ifdef DEBUG_CACHE 938 PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits)); 939 #endif 940 rvArray = collect_subject_certs(ce->entry.list, certListOpt); 941 } 942 PZ_Unlock(td->cache->lock); 943 return rvArray; 944 } 945 946 /* 947 * Find all cached certs with this email address. 948 */ 949 NSS_IMPLEMENT NSSCertificate ** 950 nssTrustDomain_GetCertsForEmailAddressFromCache( 951 NSSTrustDomain *td, 952 NSSASCII7 *email, 953 nssList *certListOpt) 954 { 955 NSSCertificate **rvArray = NULL; 956 cache_entry *ce; 957 nssList *collectList = NULL; 958 nssListIterator *iter = NULL; 959 nssList *subjectList; 960 #ifdef DEBUG_CACHE 961 PR_LOG(s_log, PR_LOG_DEBUG, ("looking for cert by email %s", email)); 962 #endif 963 PZ_Lock(td->cache->lock); 964 ce = (cache_entry *)nssHash_Lookup(td->cache->email, email); 965 if (ce) { 966 ce->hits++; 967 ce->lastHit = PR_Now(); 968 #ifdef DEBUG_CACHE 969 PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits)); 970 #endif 971 /* loop over subject lists and get refs for certs */ 972 if (certListOpt) { 973 collectList = certListOpt; 974 } else { 975 collectList = nssList_Create(NULL, PR_FALSE); 976 if (!collectList) { 977 PZ_Unlock(td->cache->lock); 978 return NULL; 979 } 980 } 981 iter = nssList_CreateIterator(ce->entry.list); 982 if (!iter) { 983 PZ_Unlock(td->cache->lock); 984 if (!certListOpt) { 985 nssList_Destroy(collectList); 986 } 987 return NULL; 988 } 989 for (subjectList = (nssList *)nssListIterator_Start(iter); 990 subjectList != (nssList *)NULL; 991 subjectList = (nssList *)nssListIterator_Next(iter)) { 992 (void)collect_subject_certs(subjectList, collectList); 993 } 994 nssListIterator_Finish(iter); 995 nssListIterator_Destroy(iter); 996 } 997 PZ_Unlock(td->cache->lock); 998 if (!certListOpt && collectList) { 999 PRUint32 count = nssList_Count(collectList); 1000 rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count); 1001 if (rvArray) { 1002 nssList_GetArray(collectList, (void **)rvArray, count); 1003 } 1004 nssList_Destroy(collectList); 1005 } 1006 return rvArray; 1007 } 1008 1009 /* 1010 * Look for a specific cert in the cache 1011 */ 1012 NSS_IMPLEMENT NSSCertificate * 1013 nssTrustDomain_GetCertForIssuerAndSNFromCache( 1014 NSSTrustDomain *td, 1015 NSSDER *issuer, 1016 NSSDER *serial) 1017 { 1018 NSSCertificate certkey; 1019 NSSCertificate *rvCert = NULL; 1020 cache_entry *ce; 1021 certkey.issuer.data = issuer->data; 1022 certkey.issuer.size = issuer->size; 1023 certkey.serial.data = serial->data; 1024 certkey.serial.size = serial->size; 1025 #ifdef DEBUG_CACHE 1026 log_item_dump("looking for cert by issuer/sn, issuer", issuer); 1027 log_item_dump(" serial", serial); 1028 #endif 1029 PZ_Lock(td->cache->lock); 1030 ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, &certkey); 1031 if (ce) { 1032 ce->hits++; 1033 ce->lastHit = PR_Now(); 1034 rvCert = nssCertificate_AddRef(ce->entry.cert); 1035 #ifdef DEBUG_CACHE 1036 PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits)); 1037 #endif 1038 } 1039 PZ_Unlock(td->cache->lock); 1040 return rvCert; 1041 } 1042 1043 /* 1044 * Look for a specific cert in the cache 1045 */ 1046 NSS_IMPLEMENT NSSCertificate * 1047 nssTrustDomain_GetCertByDERFromCache( 1048 NSSTrustDomain *td, 1049 NSSDER *der) 1050 { 1051 PRStatus nssrv = PR_FAILURE; 1052 NSSDER issuer, serial; 1053 NSSCertificate *rvCert; 1054 nssrv = nssPKIX509_GetIssuerAndSerialFromDER(der, &issuer, &serial); 1055 if (nssrv != PR_SUCCESS) { 1056 return NULL; 1057 } 1058 #ifdef DEBUG_CACHE 1059 log_item_dump("looking for cert by DER", der); 1060 #endif 1061 rvCert = nssTrustDomain_GetCertForIssuerAndSNFromCache(td, 1062 &issuer, &serial); 1063 PORT_Free(issuer.data); 1064 PORT_Free(serial.data); 1065 return rvCert; 1066 } 1067 1068 static void 1069 cert_iter(const void *k, void *v, void *a) 1070 { 1071 nssList *certList = (nssList *)a; 1072 NSSCertificate *c = (NSSCertificate *)k; 1073 nssList_Add(certList, nssCertificate_AddRef(c)); 1074 } 1075 1076 NSS_EXTERN NSSCertificate ** 1077 nssTrustDomain_GetCertsFromCache( 1078 NSSTrustDomain *td, 1079 nssList *certListOpt) 1080 { 1081 NSSCertificate **rvArray = NULL; 1082 nssList *certList; 1083 if (certListOpt) { 1084 certList = certListOpt; 1085 } else { 1086 certList = nssList_Create(NULL, PR_FALSE); 1087 if (!certList) { 1088 return NULL; 1089 } 1090 } 1091 PZ_Lock(td->cache->lock); 1092 nssHash_Iterate(td->cache->issuerAndSN, cert_iter, (void *)certList); 1093 PZ_Unlock(td->cache->lock); 1094 if (!certListOpt) { 1095 PRUint32 count = nssList_Count(certList); 1096 rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count); 1097 nssList_GetArray(certList, (void **)rvArray, count); 1098 /* array takes the references */ 1099 nssList_Destroy(certList); 1100 } 1101 return rvArray; 1102 } 1103 1104 NSS_IMPLEMENT void 1105 nssTrustDomain_DumpCacheInfo( 1106 NSSTrustDomain *td, 1107 void (*cert_dump_iter)(const void *, void *, void *), 1108 void *arg) 1109 { 1110 PZ_Lock(td->cache->lock); 1111 nssHash_Iterate(td->cache->issuerAndSN, cert_dump_iter, arg); 1112 PZ_Unlock(td->cache->lock); 1113 }