1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
38 static const char CVS_ID
[] = "@(#) $RCSfile: pkistore.c,v $ $Revision: 1.33 $ $Date: 2008/06/06 01:19:30 $";
59 #endif /* PKISTORE_H */
68 * This differs from the cache in that it is a true storage facility. Items
69 * stay in until they are explicitly removed. It is only used by crypto
70 * contexts at this time, but may be more generally useful...
74 struct nssCertificateStoreStr
76 PRBool i_alloced_arena
;
80 nssHash
*issuer_and_serial
;
83 typedef struct certificate_hash_entry_str certificate_hash_entry
;
85 struct certificate_hash_entry_str
89 nssSMIMEProfile
*profile
;
92 /* forward static declarations */
93 static NSSCertificate
*
94 nssCertStore_FindCertByIssuerAndSerialNumberLocked (
95 nssCertificateStore
*store
,
100 NSS_IMPLEMENT nssCertificateStore
*
101 nssCertificateStore_Create (
106 nssCertificateStore
*store
;
107 PRBool i_alloced_arena
;
110 i_alloced_arena
= PR_FALSE
;
112 arena
= nssArena_Create();
116 i_alloced_arena
= PR_TRUE
;
118 store
= nss_ZNEW(arena
, nssCertificateStore
);
122 store
->lock
= PZ_NewLock(nssILockOther
);
126 /* Create the issuer/serial --> {cert, trust, S/MIME profile } hash */
127 store
->issuer_and_serial
= nssHash_CreateCertificate(arena
, 0);
128 if (!store
->issuer_and_serial
) {
131 /* Create the subject DER --> subject list hash */
132 store
->subject
= nssHash_CreateItem(arena
, 0);
133 if (!store
->subject
) {
136 store
->arena
= arena
;
137 store
->i_alloced_arena
= i_alloced_arena
;
142 PZ_DestroyLock(store
->lock
);
144 if (store
->issuer_and_serial
) {
145 nssHash_Destroy(store
->issuer_and_serial
);
147 if (store
->subject
) {
148 nssHash_Destroy(store
->subject
);
151 if (i_alloced_arena
) {
152 nssArena_Destroy(arena
);
157 extern const NSSError NSS_ERROR_BUSY
;
159 NSS_IMPLEMENT PRStatus
160 nssCertificateStore_Destroy (
161 nssCertificateStore
*store
164 if (nssHash_Count(store
->issuer_and_serial
) > 0) {
165 nss_SetError(NSS_ERROR_BUSY
);
168 PZ_DestroyLock(store
->lock
);
169 nssHash_Destroy(store
->issuer_and_serial
);
170 nssHash_Destroy(store
->subject
);
171 if (store
->i_alloced_arena
) {
172 nssArena_Destroy(store
->arena
);
180 add_certificate_entry (
181 nssCertificateStore
*store
,
186 certificate_hash_entry
*entry
;
187 entry
= nss_ZNEW(cert
->object
.arena
, certificate_hash_entry
);
192 nssrv
= nssHash_Add(store
->issuer_and_serial
, cert
, entry
);
193 if (nssrv
!= PR_SUCCESS
) {
201 nssCertificateStore
*store
,
206 nssList
*subjectList
;
207 subjectList
= (nssList
*)nssHash_Lookup(store
->subject
, &cert
->subject
);
209 /* The subject is already in, add this cert to the list */
210 nssrv
= nssList_AddUnique(subjectList
, cert
);
212 /* Create a new subject list for the subject */
213 subjectList
= nssList_Create(NULL
, PR_FALSE
);
217 nssList_SetSortFunction(subjectList
, nssCertificate_SubjectListSort
);
218 /* Add the cert entry to this list of subjects */
219 nssrv
= nssList_Add(subjectList
, cert
);
220 if (nssrv
!= PR_SUCCESS
) {
223 /* Add the subject list to the cache */
224 nssrv
= nssHash_Add(store
->subject
, &cert
->subject
, subjectList
);
231 remove_certificate_entry (
232 nssCertificateStore
*store
,
236 /* Caller must hold store->lock */
238 nssCertificateStore_AddLocked (
239 nssCertificateStore
*store
,
243 PRStatus nssrv
= add_certificate_entry(store
, cert
);
244 if (nssrv
== PR_SUCCESS
) {
245 nssrv
= add_subject_entry(store
, cert
);
246 if (nssrv
== PR_FAILURE
) {
247 remove_certificate_entry(store
, cert
);
254 NSS_IMPLEMENT NSSCertificate
*
255 nssCertificateStore_FindOrAdd (
256 nssCertificateStore
*store
,
261 NSSCertificate
*rvCert
= NULL
;
263 PZ_Lock(store
->lock
);
264 rvCert
= nssCertStore_FindCertByIssuerAndSerialNumberLocked(
265 store
, &c
->issuer
, &c
->serial
);
267 nssrv
= nssCertificateStore_AddLocked(store
, c
);
268 if (PR_SUCCESS
== nssrv
) {
269 rvCert
= nssCertificate_AddRef(c
);
272 PZ_Unlock(store
->lock
);
277 remove_certificate_entry (
278 nssCertificateStore
*store
,
282 certificate_hash_entry
*entry
;
283 entry
= (certificate_hash_entry
*)
284 nssHash_Lookup(store
->issuer_and_serial
, cert
);
286 nssHash_Remove(store
->issuer_and_serial
, cert
);
288 nssTrust_Destroy(entry
->trust
);
290 if (entry
->profile
) {
291 nssSMIMEProfile_Destroy(entry
->profile
);
298 remove_subject_entry (
299 nssCertificateStore
*store
,
303 nssList
*subjectList
;
304 /* Get the subject list for the cert's subject */
305 subjectList
= (nssList
*)nssHash_Lookup(store
->subject
, &cert
->subject
);
307 /* Remove the cert from the subject hash */
308 nssList_Remove(subjectList
, cert
);
309 nssHash_Remove(store
->subject
, &cert
->subject
);
310 if (nssList_Count(subjectList
) == 0) {
311 nssList_Destroy(subjectList
);
313 /* The cert being released may have keyed the subject entry.
314 * Since there are still subject certs around, get another and
315 * rekey the entry just in case.
317 NSSCertificate
*subjectCert
;
318 (void)nssList_GetArray(subjectList
, (void **)&subjectCert
, 1);
319 nssHash_Add(store
->subject
, &subjectCert
->subject
, subjectList
);
325 nssCertificateStore_RemoveCertLOCKED (
326 nssCertificateStore
*store
,
330 certificate_hash_entry
*entry
;
331 entry
= (certificate_hash_entry
*)
332 nssHash_Lookup(store
->issuer_and_serial
, cert
);
333 if (entry
&& entry
->cert
== cert
) {
334 remove_certificate_entry(store
, cert
);
335 remove_subject_entry(store
, cert
);
340 nssCertificateStore_Lock (
341 nssCertificateStore
*store
, nssCertificateStoreTrace
* out
347 out
->lock
= store
->lock
;
348 out
->locked
= PR_TRUE
;
351 PZ_Lock(store
->lock
);
356 nssCertificateStore_Unlock (
357 nssCertificateStore
*store
, const nssCertificateStoreTrace
* in
,
358 nssCertificateStoreTrace
* out
365 out
->lock
= store
->lock
;
366 PORT_Assert(!out
->locked
);
367 out
->unlocked
= PR_TRUE
;
369 PORT_Assert(in
->store
== out
->store
);
370 PORT_Assert(in
->lock
== out
->lock
);
371 PORT_Assert(in
->locked
);
372 PORT_Assert(!in
->unlocked
);
374 PZ_Unlock(out
->lock
);
376 PZ_Unlock(store
->lock
);
380 static NSSCertificate
**
381 get_array_from_list (
383 NSSCertificate
*rvOpt
[],
389 NSSCertificate
**rvArray
= NULL
;
390 count
= nssList_Count(certList
);
394 if (maximumOpt
> 0) {
395 count
= PR_MIN(maximumOpt
, count
);
398 nssList_GetArray(certList
, (void **)rvOpt
, count
);
400 rvArray
= nss_ZNEWARRAY(arenaOpt
, NSSCertificate
*, count
+ 1);
402 nssList_GetArray(certList
, (void **)rvArray
, count
);
408 NSS_IMPLEMENT NSSCertificate
**
409 nssCertificateStore_FindCertificatesBySubject (
410 nssCertificateStore
*store
,
412 NSSCertificate
*rvOpt
[],
417 NSSCertificate
**rvArray
= NULL
;
418 nssList
*subjectList
;
419 PZ_Lock(store
->lock
);
420 subjectList
= (nssList
*)nssHash_Lookup(store
->subject
, subject
);
422 nssCertificateList_AddReferences(subjectList
);
423 rvArray
= get_array_from_list(subjectList
,
424 rvOpt
, maximumOpt
, arenaOpt
);
426 PZ_Unlock(store
->lock
);
430 /* Because only subject indexing is implemented, all other lookups require
431 * full traversal (unfortunately, PLHashTable doesn't allow you to exit
432 * early from the enumeration). The assumptions are that 1) lookups by
433 * fields other than subject will be rare, and 2) the hash will not have
434 * a large number of entries. These assumptions will be tested.
437 * For NSS 3.4, it is worth consideration to do all forms of indexing,
438 * because the only crypto context is global and persistent.
441 struct nickname_template_str
444 nssList
*subjectList
;
447 static void match_nickname(const void *k
, void *v
, void *a
)
452 nssList
*subjectList
= (nssList
*)v
;
453 struct nickname_template_str
*nt
= (struct nickname_template_str
*)a
;
454 nssrv
= nssList_GetArray(subjectList
, (void **)&c
, 1);
455 nickname
= nssCertificate_GetNickname(c
, NULL
);
456 if (nssrv
== PR_SUCCESS
&& nickname
&&
457 nssUTF8_Equal(nickname
, nt
->nickname
, &nssrv
))
459 nt
->subjectList
= subjectList
;
464 * Find all cached certs with this label.
466 NSS_IMPLEMENT NSSCertificate
**
467 nssCertificateStore_FindCertificatesByNickname (
468 nssCertificateStore
*store
,
469 const NSSUTF8
*nickname
,
470 NSSCertificate
*rvOpt
[],
475 NSSCertificate
**rvArray
= NULL
;
476 struct nickname_template_str nt
;
477 nt
.nickname
= (char*) nickname
;
478 nt
.subjectList
= NULL
;
479 PZ_Lock(store
->lock
);
480 nssHash_Iterate(store
->subject
, match_nickname
, &nt
);
481 if (nt
.subjectList
) {
482 nssCertificateList_AddReferences(nt
.subjectList
);
483 rvArray
= get_array_from_list(nt
.subjectList
,
484 rvOpt
, maximumOpt
, arenaOpt
);
486 PZ_Unlock(store
->lock
);
490 struct email_template_str
496 static void match_email(const void *k
, void *v
, void *a
)
500 nssList
*subjectList
= (nssList
*)v
;
501 struct email_template_str
*et
= (struct email_template_str
*)a
;
502 nssrv
= nssList_GetArray(subjectList
, (void **)&c
, 1);
503 if (nssrv
== PR_SUCCESS
&&
504 nssUTF8_Equal(c
->email
, et
->email
, &nssrv
))
506 nssListIterator
*iter
= nssList_CreateIterator(subjectList
);
508 for (c
= (NSSCertificate
*)nssListIterator_Start(iter
);
509 c
!= (NSSCertificate
*)NULL
;
510 c
= (NSSCertificate
*)nssListIterator_Next(iter
))
512 nssList_Add(et
->emailList
, c
);
514 nssListIterator_Finish(iter
);
515 nssListIterator_Destroy(iter
);
521 * Find all cached certs with this email address.
523 NSS_IMPLEMENT NSSCertificate
**
524 nssCertificateStore_FindCertificatesByEmail (
525 nssCertificateStore
*store
,
527 NSSCertificate
*rvOpt
[],
532 NSSCertificate
**rvArray
= NULL
;
533 struct email_template_str et
;
535 et
.emailList
= nssList_Create(NULL
, PR_FALSE
);
539 PZ_Lock(store
->lock
);
540 nssHash_Iterate(store
->subject
, match_email
, &et
);
542 /* get references before leaving the store's lock protection */
543 nssCertificateList_AddReferences(et
.emailList
);
545 PZ_Unlock(store
->lock
);
547 rvArray
= get_array_from_list(et
.emailList
,
548 rvOpt
, maximumOpt
, arenaOpt
);
549 nssList_Destroy(et
.emailList
);
554 /* Caller holds store->lock */
555 static NSSCertificate
*
556 nssCertStore_FindCertByIssuerAndSerialNumberLocked (
557 nssCertificateStore
*store
,
562 certificate_hash_entry
*entry
;
563 NSSCertificate
*rvCert
= NULL
;
564 NSSCertificate index
;
566 index
.issuer
= *issuer
;
567 index
.serial
= *serial
;
568 entry
= (certificate_hash_entry
*)
569 nssHash_Lookup(store
->issuer_and_serial
, &index
);
571 rvCert
= nssCertificate_AddRef(entry
->cert
);
576 NSS_IMPLEMENT NSSCertificate
*
577 nssCertificateStore_FindCertificateByIssuerAndSerialNumber (
578 nssCertificateStore
*store
,
583 NSSCertificate
*rvCert
= NULL
;
585 PZ_Lock(store
->lock
);
586 rvCert
= nssCertStore_FindCertByIssuerAndSerialNumberLocked (
587 store
, issuer
, serial
);
588 PZ_Unlock(store
->lock
);
593 issuer_and_serial_from_encoding (
599 SECItem derCert
, derIssuer
, derSerial
;
601 derCert
.data
= (unsigned char *)encoding
->data
;
602 derCert
.len
= encoding
->size
;
603 secrv
= CERT_IssuerNameFromDERCert(&derCert
, &derIssuer
);
604 if (secrv
!= SECSuccess
) {
607 secrv
= CERT_SerialNumberFromDERCert(&derCert
, &derSerial
);
608 if (secrv
!= SECSuccess
) {
609 PORT_Free(derIssuer
.data
);
612 issuer
->data
= derIssuer
.data
;
613 issuer
->size
= derIssuer
.len
;
614 serial
->data
= derSerial
.data
;
615 serial
->size
= derSerial
.len
;
619 NSS_IMPLEMENT NSSCertificate
*
620 nssCertificateStore_FindCertificateByEncodedCertificate (
621 nssCertificateStore
*store
,
625 PRStatus nssrv
= PR_FAILURE
;
626 NSSDER issuer
, serial
;
627 NSSCertificate
*rvCert
= NULL
;
628 nssrv
= issuer_and_serial_from_encoding(encoding
, &issuer
, &serial
);
629 if (nssrv
!= PR_SUCCESS
) {
632 rvCert
= nssCertificateStore_FindCertificateByIssuerAndSerialNumber(store
,
635 PORT_Free(issuer
.data
);
636 PORT_Free(serial
.data
);
641 nssCertificateStore_AddTrust (
642 nssCertificateStore
*store
,
646 NSSCertificate
*cert
;
647 certificate_hash_entry
*entry
;
648 cert
= trust
->certificate
;
649 PZ_Lock(store
->lock
);
650 entry
= (certificate_hash_entry
*)
651 nssHash_Lookup(store
->issuer_and_serial
, cert
);
653 entry
->trust
= nssTrust_AddRef(trust
);
655 PZ_Unlock(store
->lock
);
656 return (entry
) ? PR_SUCCESS
: PR_FAILURE
;
659 NSS_IMPLEMENT NSSTrust
*
660 nssCertificateStore_FindTrustForCertificate (
661 nssCertificateStore
*store
,
665 certificate_hash_entry
*entry
;
666 NSSTrust
*rvTrust
= NULL
;
667 PZ_Lock(store
->lock
);
668 entry
= (certificate_hash_entry
*)
669 nssHash_Lookup(store
->issuer_and_serial
, cert
);
670 if (entry
&& entry
->trust
) {
671 rvTrust
= nssTrust_AddRef(entry
->trust
);
673 PZ_Unlock(store
->lock
);
678 nssCertificateStore_AddSMIMEProfile (
679 nssCertificateStore
*store
,
680 nssSMIMEProfile
*profile
683 NSSCertificate
*cert
;
684 certificate_hash_entry
*entry
;
685 cert
= profile
->certificate
;
686 PZ_Lock(store
->lock
);
687 entry
= (certificate_hash_entry
*)
688 nssHash_Lookup(store
->issuer_and_serial
, cert
);
690 entry
->profile
= nssSMIMEProfile_AddRef(profile
);
692 PZ_Unlock(store
->lock
);
693 return (entry
) ? PR_SUCCESS
: PR_FAILURE
;
696 NSS_IMPLEMENT nssSMIMEProfile
*
697 nssCertificateStore_FindSMIMEProfileForCertificate (
698 nssCertificateStore
*store
,
702 certificate_hash_entry
*entry
;
703 nssSMIMEProfile
*rvProfile
= NULL
;
704 PZ_Lock(store
->lock
);
705 entry
= (certificate_hash_entry
*)
706 nssHash_Lookup(store
->issuer_and_serial
, cert
);
707 if (entry
&& entry
->profile
) {
708 rvProfile
= nssSMIMEProfile_AddRef(entry
->profile
);
710 PZ_Unlock(store
->lock
);
714 /* XXX this is also used by cache and should be somewhere else */
717 nss_certificate_hash (
723 NSSCertificate
*c
= (NSSCertificate
*)key
;
725 for (i
=0; i
<c
->issuer
.size
; i
++)
726 h
= PR_ROTATE_LEFT32(h
, 4) ^ ((unsigned char *)c
->issuer
.data
)[i
];
727 for (i
=0; i
<c
->serial
.size
; i
++)
728 h
= PR_ROTATE_LEFT32(h
, 4) ^ ((unsigned char *)c
->serial
.data
)[i
];
733 nss_compare_certs(const void *v1
, const void *v2
)
736 NSSCertificate
*c1
= (NSSCertificate
*)v1
;
737 NSSCertificate
*c2
= (NSSCertificate
*)v2
;
738 return (int)(nssItem_Equal(&c1
->issuer
, &c2
->issuer
, &ignore
) &&
739 nssItem_Equal(&c1
->serial
, &c2
->serial
, &ignore
));
742 NSS_IMPLEMENT nssHash
*
743 nssHash_CreateCertificate (
748 return nssHash_Create(arenaOpt
,
750 nss_certificate_hash
,
756 nssCertificateStore_DumpStoreInfo (
757 nssCertificateStore
*store
,
758 void (* cert_dump_iter
)(const void *, void *, void *),
762 PZ_Lock(store
->lock
);
763 nssHash_Iterate(store
->issuer_and_serial
, cert_dump_iter
, arg
);
764 PZ_Unlock(store
->lock
);