pkistore.c (18509B)
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 PKI_H 10 #include "pki.h" 11 #endif /* PKI_H */ 12 13 #ifndef NSSPKI_H 14 #include "nsspki.h" 15 #endif /* NSSPKI_H */ 16 17 #ifndef BASE_H 18 #include "base.h" 19 #endif /* BASE_H */ 20 21 #ifndef PKISTORE_H 22 #include "pkistore.h" 23 #endif /* PKISTORE_H */ 24 25 #include "cert.h" 26 #include "pki3hack.h" 27 28 #include "prbit.h" 29 30 /* 31 * Certificate Store 32 * 33 * This differs from the cache in that it is a true storage facility. Items 34 * stay in until they are explicitly removed. It is only used by crypto 35 * contexts at this time, but may be more generally useful... 36 * 37 */ 38 39 struct nssCertificateStoreStr { 40 PRBool i_alloced_arena; 41 NSSArena *arena; 42 PZLock *lock; 43 nssHash *subject; 44 nssHash *issuer_and_serial; 45 }; 46 47 typedef struct certificate_hash_entry_str certificate_hash_entry; 48 49 struct certificate_hash_entry_str { 50 NSSCertificate *cert; 51 NSSTrust *trust; 52 nssSMIMEProfile *profile; 53 }; 54 55 /* forward static declarations */ 56 static NSSCertificate * 57 nssCertStore_FindCertByIssuerAndSerialNumberLocked( 58 nssCertificateStore *store, 59 NSSDER *issuer, 60 NSSDER *serial); 61 62 NSS_IMPLEMENT nssCertificateStore * 63 nssCertificateStore_Create(NSSArena *arenaOpt) 64 { 65 NSSArena *arena; 66 nssCertificateStore *store; 67 PRBool i_alloced_arena; 68 if (arenaOpt) { 69 arena = arenaOpt; 70 i_alloced_arena = PR_FALSE; 71 } else { 72 arena = nssArena_Create(); 73 if (!arena) { 74 return NULL; 75 } 76 i_alloced_arena = PR_TRUE; 77 } 78 store = nss_ZNEW(arena, nssCertificateStore); 79 if (!store) { 80 goto loser; 81 } 82 store->lock = PZ_NewLock(nssILockOther); 83 if (!store->lock) { 84 goto loser; 85 } 86 /* Create the issuer/serial --> {cert, trust, S/MIME profile } hash */ 87 store->issuer_and_serial = nssHash_CreateCertificate(arena, 0); 88 if (!store->issuer_and_serial) { 89 goto loser; 90 } 91 /* Create the subject DER --> subject list hash */ 92 store->subject = nssHash_CreateItem(arena, 0); 93 if (!store->subject) { 94 goto loser; 95 } 96 store->arena = arena; 97 store->i_alloced_arena = i_alloced_arena; 98 return store; 99 loser: 100 if (store) { 101 if (store->lock) { 102 PZ_DestroyLock(store->lock); 103 } 104 if (store->issuer_and_serial) { 105 nssHash_Destroy(store->issuer_and_serial); 106 } 107 if (store->subject) { 108 nssHash_Destroy(store->subject); 109 } 110 } 111 if (i_alloced_arena) { 112 nssArena_Destroy(arena); 113 } 114 return NULL; 115 } 116 117 extern const NSSError NSS_ERROR_BUSY; 118 119 NSS_IMPLEMENT PRStatus 120 nssCertificateStore_Destroy(nssCertificateStore *store) 121 { 122 if (nssHash_Count(store->issuer_and_serial) > 0) { 123 nss_SetError(NSS_ERROR_BUSY); 124 return PR_FAILURE; 125 } 126 PZ_DestroyLock(store->lock); 127 nssHash_Destroy(store->issuer_and_serial); 128 nssHash_Destroy(store->subject); 129 if (store->i_alloced_arena) { 130 nssArena_Destroy(store->arena); 131 } else { 132 nss_ZFreeIf(store); 133 } 134 return PR_SUCCESS; 135 } 136 137 static PRStatus 138 add_certificate_entry( 139 nssCertificateStore *store, 140 NSSCertificate *cert) 141 { 142 PRStatus nssrv; 143 certificate_hash_entry *entry; 144 entry = nss_ZNEW(cert->object.arena, certificate_hash_entry); 145 if (!entry) { 146 return PR_FAILURE; 147 } 148 entry->cert = cert; 149 nssrv = nssHash_Add(store->issuer_and_serial, cert, entry); 150 if (nssrv != PR_SUCCESS) { 151 nss_ZFreeIf(entry); 152 } 153 return nssrv; 154 } 155 156 static PRStatus 157 add_subject_entry( 158 nssCertificateStore *store, 159 NSSCertificate *cert) 160 { 161 PRStatus nssrv; 162 nssList *subjectList; 163 subjectList = (nssList *)nssHash_Lookup(store->subject, &cert->subject); 164 if (subjectList) { 165 /* The subject is already in, add this cert to the list */ 166 nssrv = nssList_AddUnique(subjectList, cert); 167 } else { 168 /* Create a new subject list for the subject */ 169 subjectList = nssList_Create(NULL, PR_FALSE); 170 if (!subjectList) { 171 return PR_FAILURE; 172 } 173 nssList_SetSortFunction(subjectList, nssCertificate_SubjectListSort); 174 /* Add the cert entry to this list of subjects */ 175 nssrv = nssList_Add(subjectList, cert); 176 if (nssrv != PR_SUCCESS) { 177 return nssrv; 178 } 179 /* Add the subject list to the cache */ 180 nssrv = nssHash_Add(store->subject, &cert->subject, subjectList); 181 } 182 return nssrv; 183 } 184 185 /* declared below */ 186 static void 187 remove_certificate_entry( 188 nssCertificateStore *store, 189 NSSCertificate *cert); 190 191 /* Caller must hold store->lock */ 192 static PRStatus 193 nssCertificateStore_AddLocked( 194 nssCertificateStore *store, 195 NSSCertificate *cert) 196 { 197 PRStatus nssrv = add_certificate_entry(store, cert); 198 if (nssrv == PR_SUCCESS) { 199 nssrv = add_subject_entry(store, cert); 200 if (nssrv == PR_FAILURE) { 201 remove_certificate_entry(store, cert); 202 } 203 } 204 return nssrv; 205 } 206 207 NSS_IMPLEMENT NSSCertificate * 208 nssCertificateStore_FindOrAdd( 209 nssCertificateStore *store, 210 NSSCertificate *c) 211 { 212 PRStatus nssrv; 213 NSSCertificate *rvCert = NULL; 214 215 PZ_Lock(store->lock); 216 rvCert = nssCertStore_FindCertByIssuerAndSerialNumberLocked( 217 store, &c->issuer, &c->serial); 218 if (!rvCert) { 219 nssrv = nssCertificateStore_AddLocked(store, c); 220 if (PR_SUCCESS == nssrv) { 221 rvCert = nssCertificate_AddRef(c); 222 } 223 } 224 PZ_Unlock(store->lock); 225 return rvCert; 226 } 227 228 static void 229 remove_certificate_entry( 230 nssCertificateStore *store, 231 NSSCertificate *cert) 232 { 233 certificate_hash_entry *entry; 234 entry = (certificate_hash_entry *) 235 nssHash_Lookup(store->issuer_and_serial, cert); 236 if (entry) { 237 nssHash_Remove(store->issuer_and_serial, cert); 238 if (entry->trust) { 239 nssTrust_Destroy(entry->trust); 240 } 241 if (entry->profile) { 242 nssSMIMEProfile_Destroy(entry->profile); 243 } 244 nss_ZFreeIf(entry); 245 } 246 } 247 248 static void 249 remove_subject_entry( 250 nssCertificateStore *store, 251 NSSCertificate *cert) 252 { 253 nssList *subjectList; 254 /* Get the subject list for the cert's subject */ 255 subjectList = (nssList *)nssHash_Lookup(store->subject, &cert->subject); 256 if (subjectList) { 257 /* Remove the cert from the subject hash */ 258 nssList_Remove(subjectList, cert); 259 nssHash_Remove(store->subject, &cert->subject); 260 if (nssList_Count(subjectList) == 0) { 261 nssList_Destroy(subjectList); 262 } else { 263 /* The cert being released may have keyed the subject entry. 264 * Since there are still subject certs around, get another and 265 * rekey the entry just in case. 266 */ 267 NSSCertificate *subjectCert; 268 (void)nssList_GetArray(subjectList, (void **)&subjectCert, 1); 269 nssHash_Add(store->subject, &subjectCert->subject, subjectList); 270 } 271 } 272 } 273 274 NSS_IMPLEMENT void 275 nssCertificateStore_RemoveCertLOCKED( 276 nssCertificateStore *store, 277 NSSCertificate *cert) 278 { 279 certificate_hash_entry *entry; 280 entry = (certificate_hash_entry *) 281 nssHash_Lookup(store->issuer_and_serial, cert); 282 if (entry && entry->cert == cert) { 283 remove_certificate_entry(store, cert); 284 remove_subject_entry(store, cert); 285 } 286 } 287 288 NSS_IMPLEMENT void 289 nssCertificateStore_Lock(nssCertificateStore *store, nssCertificateStoreTrace *out) 290 { 291 #ifdef DEBUG 292 PORT_Assert(out); 293 out->store = store; 294 out->lock = store->lock; 295 out->locked = PR_TRUE; 296 PZ_Lock(out->lock); 297 #else 298 PZ_Lock(store->lock); 299 #endif 300 } 301 302 NSS_IMPLEMENT void 303 nssCertificateStore_Unlock( 304 nssCertificateStore *store, const nssCertificateStoreTrace *in, 305 nssCertificateStoreTrace *out) 306 { 307 #ifdef DEBUG 308 PORT_Assert(in); 309 PORT_Assert(out); 310 out->store = store; 311 out->lock = store->lock; 312 PORT_Assert(!out->locked); 313 out->unlocked = PR_TRUE; 314 315 PORT_Assert(in->store == out->store); 316 PORT_Assert(in->lock == out->lock); 317 PORT_Assert(in->locked); 318 PORT_Assert(!in->unlocked); 319 320 PZ_Unlock(out->lock); 321 #else 322 PZ_Unlock(store->lock); 323 #endif 324 } 325 326 static NSSCertificate ** 327 get_array_from_list( 328 nssList *certList, 329 NSSCertificate *rvOpt[], 330 PRUint32 maximumOpt, 331 NSSArena *arenaOpt) 332 { 333 PRUint32 count; 334 NSSCertificate **rvArray = NULL; 335 count = nssList_Count(certList); 336 if (count == 0) { 337 return NULL; 338 } 339 if (maximumOpt > 0) { 340 count = PR_MIN(maximumOpt, count); 341 } 342 if (rvOpt) { 343 nssList_GetArray(certList, (void **)rvOpt, count); 344 } else { 345 rvArray = nss_ZNEWARRAY(arenaOpt, NSSCertificate *, count + 1); 346 if (rvArray) { 347 nssList_GetArray(certList, (void **)rvArray, count); 348 } 349 } 350 return rvArray; 351 } 352 353 NSS_IMPLEMENT NSSCertificate ** 354 nssCertificateStore_FindCertificatesBySubject( 355 nssCertificateStore *store, 356 NSSDER *subject, 357 NSSCertificate *rvOpt[], 358 PRUint32 maximumOpt, 359 NSSArena *arenaOpt) 360 { 361 NSSCertificate **rvArray = NULL; 362 nssList *subjectList; 363 PZ_Lock(store->lock); 364 subjectList = (nssList *)nssHash_Lookup(store->subject, subject); 365 if (subjectList) { 366 nssCertificateList_AddReferences(subjectList); 367 rvArray = get_array_from_list(subjectList, 368 rvOpt, maximumOpt, arenaOpt); 369 } 370 PZ_Unlock(store->lock); 371 return rvArray; 372 } 373 374 /* Because only subject indexing is implemented, all other lookups require 375 * full traversal (unfortunately, PLHashTable doesn't allow you to exit 376 * early from the enumeration). The assumptions are that 1) lookups by 377 * fields other than subject will be rare, and 2) the hash will not have 378 * a large number of entries. These assumptions will be tested. 379 * 380 * XXX 381 * For NSS 3.4, it is worth consideration to do all forms of indexing, 382 * because the only crypto context is global and persistent. 383 */ 384 385 struct nickname_template_str { 386 NSSUTF8 *nickname; 387 nssList *subjectList; 388 }; 389 390 static void 391 match_nickname(const void *k, void *v, void *a) 392 { 393 PRStatus nssrv; 394 NSSCertificate *c; 395 NSSUTF8 *nickname; 396 nssList *subjectList = (nssList *)v; 397 struct nickname_template_str *nt = (struct nickname_template_str *)a; 398 nssrv = nssList_GetArray(subjectList, (void **)&c, 1); 399 nickname = nssCertificate_GetNickname(c, NULL); 400 if (nssrv == PR_SUCCESS && nickname && 401 nssUTF8_Equal(nickname, nt->nickname, &nssrv)) { 402 nt->subjectList = subjectList; 403 } 404 nss_ZFreeIf(nickname); 405 } 406 407 /* 408 * Find all cached certs with this label. 409 */ 410 NSS_IMPLEMENT NSSCertificate ** 411 nssCertificateStore_FindCertificatesByNickname( 412 nssCertificateStore *store, 413 const NSSUTF8 *nickname, 414 NSSCertificate *rvOpt[], 415 PRUint32 maximumOpt, 416 NSSArena *arenaOpt) 417 { 418 NSSCertificate **rvArray = NULL; 419 struct nickname_template_str nt; 420 nt.nickname = (char *)nickname; 421 nt.subjectList = NULL; 422 PZ_Lock(store->lock); 423 nssHash_Iterate(store->subject, match_nickname, &nt); 424 if (nt.subjectList) { 425 nssCertificateList_AddReferences(nt.subjectList); 426 rvArray = get_array_from_list(nt.subjectList, 427 rvOpt, maximumOpt, arenaOpt); 428 } 429 PZ_Unlock(store->lock); 430 return rvArray; 431 } 432 433 struct email_template_str { 434 NSSASCII7 *email; 435 nssList *emailList; 436 }; 437 438 static void 439 match_email(const void *k, void *v, void *a) 440 { 441 PRStatus nssrv; 442 NSSCertificate *c; 443 nssList *subjectList = (nssList *)v; 444 struct email_template_str *et = (struct email_template_str *)a; 445 nssrv = nssList_GetArray(subjectList, (void **)&c, 1); 446 if (nssrv == PR_SUCCESS && 447 nssUTF8_Equal(c->email, et->email, &nssrv)) { 448 nssListIterator *iter = nssList_CreateIterator(subjectList); 449 if (iter) { 450 for (c = (NSSCertificate *)nssListIterator_Start(iter); 451 c != (NSSCertificate *)NULL; 452 c = (NSSCertificate *)nssListIterator_Next(iter)) { 453 nssList_Add(et->emailList, c); 454 } 455 nssListIterator_Finish(iter); 456 nssListIterator_Destroy(iter); 457 } 458 } 459 } 460 461 /* 462 * Find all cached certs with this email address. 463 */ 464 NSS_IMPLEMENT NSSCertificate ** 465 nssCertificateStore_FindCertificatesByEmail( 466 nssCertificateStore *store, 467 NSSASCII7 *email, 468 NSSCertificate *rvOpt[], 469 PRUint32 maximumOpt, 470 NSSArena *arenaOpt) 471 { 472 NSSCertificate **rvArray = NULL; 473 struct email_template_str et; 474 et.email = email; 475 et.emailList = nssList_Create(NULL, PR_FALSE); 476 if (!et.emailList) { 477 return NULL; 478 } 479 PZ_Lock(store->lock); 480 nssHash_Iterate(store->subject, match_email, &et); 481 if (et.emailList) { 482 /* get references before leaving the store's lock protection */ 483 nssCertificateList_AddReferences(et.emailList); 484 } 485 PZ_Unlock(store->lock); 486 if (et.emailList) { 487 rvArray = get_array_from_list(et.emailList, 488 rvOpt, maximumOpt, arenaOpt); 489 nssList_Destroy(et.emailList); 490 } 491 return rvArray; 492 } 493 494 /* Caller holds store->lock */ 495 static NSSCertificate * 496 nssCertStore_FindCertByIssuerAndSerialNumberLocked( 497 nssCertificateStore *store, 498 NSSDER *issuer, 499 NSSDER *serial) 500 { 501 certificate_hash_entry *entry; 502 NSSCertificate *rvCert = NULL; 503 NSSCertificate index; 504 505 index.issuer = *issuer; 506 index.serial = *serial; 507 entry = (certificate_hash_entry *) 508 nssHash_Lookup(store->issuer_and_serial, &index); 509 if (entry) { 510 rvCert = nssCertificate_AddRef(entry->cert); 511 } 512 return rvCert; 513 } 514 515 NSS_IMPLEMENT NSSCertificate * 516 nssCertificateStore_FindCertificateByIssuerAndSerialNumber( 517 nssCertificateStore *store, 518 NSSDER *issuer, 519 NSSDER *serial) 520 { 521 NSSCertificate *rvCert = NULL; 522 523 PZ_Lock(store->lock); 524 rvCert = nssCertStore_FindCertByIssuerAndSerialNumberLocked( 525 store, issuer, serial); 526 PZ_Unlock(store->lock); 527 return rvCert; 528 } 529 530 NSS_IMPLEMENT NSSCertificate * 531 nssCertificateStore_FindCertificateByEncodedCertificate( 532 nssCertificateStore *store, 533 NSSDER *encoding) 534 { 535 PRStatus nssrv = PR_FAILURE; 536 NSSDER issuer, serial; 537 NSSCertificate *rvCert = NULL; 538 nssrv = nssPKIX509_GetIssuerAndSerialFromDER(encoding, &issuer, &serial); 539 if (nssrv != PR_SUCCESS) { 540 return NULL; 541 } 542 rvCert = nssCertificateStore_FindCertificateByIssuerAndSerialNumber(store, 543 &issuer, 544 &serial); 545 PORT_Free(issuer.data); 546 PORT_Free(serial.data); 547 return rvCert; 548 } 549 550 NSS_EXTERN PRStatus 551 nssCertificateStore_AddTrust( 552 nssCertificateStore *store, 553 NSSTrust *trust) 554 { 555 NSSCertificate *cert; 556 certificate_hash_entry *entry; 557 cert = trust->certificate; 558 PZ_Lock(store->lock); 559 entry = (certificate_hash_entry *) 560 nssHash_Lookup(store->issuer_and_serial, cert); 561 if (entry) { 562 NSSTrust *newTrust = nssTrust_AddRef(trust); 563 if (entry->trust) { 564 nssTrust_Destroy(entry->trust); 565 } 566 entry->trust = newTrust; 567 } 568 PZ_Unlock(store->lock); 569 return (entry) ? PR_SUCCESS : PR_FAILURE; 570 } 571 572 NSS_IMPLEMENT NSSTrust * 573 nssCertificateStore_FindTrustForCertificate( 574 nssCertificateStore *store, 575 NSSCertificate *cert) 576 { 577 certificate_hash_entry *entry; 578 NSSTrust *rvTrust = NULL; 579 PZ_Lock(store->lock); 580 entry = (certificate_hash_entry *) 581 nssHash_Lookup(store->issuer_and_serial, cert); 582 if (entry && entry->trust) { 583 rvTrust = nssTrust_AddRef(entry->trust); 584 } 585 PZ_Unlock(store->lock); 586 return rvTrust; 587 } 588 589 NSS_EXTERN PRStatus 590 nssCertificateStore_AddSMIMEProfile( 591 nssCertificateStore *store, 592 nssSMIMEProfile *profile) 593 { 594 NSSCertificate *cert; 595 certificate_hash_entry *entry; 596 cert = profile->certificate; 597 PZ_Lock(store->lock); 598 entry = (certificate_hash_entry *) 599 nssHash_Lookup(store->issuer_and_serial, cert); 600 if (entry) { 601 nssSMIMEProfile *newProfile = nssSMIMEProfile_AddRef(profile); 602 if (entry->profile) { 603 nssSMIMEProfile_Destroy(entry->profile); 604 } 605 entry->profile = newProfile; 606 } 607 PZ_Unlock(store->lock); 608 return (entry) ? PR_SUCCESS : PR_FAILURE; 609 } 610 611 NSS_IMPLEMENT nssSMIMEProfile * 612 nssCertificateStore_FindSMIMEProfileForCertificate( 613 nssCertificateStore *store, 614 NSSCertificate *cert) 615 { 616 certificate_hash_entry *entry; 617 nssSMIMEProfile *rvProfile = NULL; 618 PZ_Lock(store->lock); 619 entry = (certificate_hash_entry *) 620 nssHash_Lookup(store->issuer_and_serial, cert); 621 if (entry && entry->profile) { 622 rvProfile = nssSMIMEProfile_AddRef(entry->profile); 623 } 624 PZ_Unlock(store->lock); 625 return rvProfile; 626 } 627 628 /* XXX this is also used by cache and should be somewhere else */ 629 630 static PLHashNumber 631 nss_certificate_hash(const void *key) 632 { 633 unsigned int i; 634 PLHashNumber h; 635 NSSCertificate *c = (NSSCertificate *)key; 636 h = 0; 637 for (i = 0; i < c->issuer.size; i++) 638 h = PR_ROTATE_LEFT32(h, 4) ^ ((unsigned char *)c->issuer.data)[i]; 639 for (i = 0; i < c->serial.size; i++) 640 h = PR_ROTATE_LEFT32(h, 4) ^ ((unsigned char *)c->serial.data)[i]; 641 return h; 642 } 643 644 static int 645 nss_compare_certs(const void *v1, const void *v2) 646 { 647 PRStatus ignore; 648 NSSCertificate *c1 = (NSSCertificate *)v1; 649 NSSCertificate *c2 = (NSSCertificate *)v2; 650 return (int)(nssItem_Equal(&c1->issuer, &c2->issuer, &ignore) && 651 nssItem_Equal(&c1->serial, &c2->serial, &ignore)); 652 } 653 654 NSS_IMPLEMENT nssHash * 655 nssHash_CreateCertificate( 656 NSSArena *arenaOpt, 657 PRUint32 numBuckets) 658 { 659 return nssHash_Create(arenaOpt, 660 numBuckets, 661 nss_certificate_hash, 662 nss_compare_certs, 663 PL_CompareValues); 664 } 665 666 NSS_IMPLEMENT void 667 nssCertificateStore_DumpStoreInfo( 668 nssCertificateStore *store, 669 void (*cert_dump_iter)(const void *, void *, void *), 670 void *arg) 671 { 672 PZ_Lock(store->lock); 673 nssHash_Iterate(store->issuer_and_serial, cert_dump_iter, arg); 674 PZ_Unlock(store->lock); 675 }