Bug 460926 A11y hierachy is broken on Ubuntu 8.10 (GNOME 2.24), r=Evan.Yan sr=roc
[wine-gecko.git] / security / nss / lib / pki / pkistore.c
blob90840e44ab22007383d5fa906e5ebbe31346bdef
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
12 * License.
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.
21 * Contributor(s):
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 ***** */
37 #ifdef DEBUG
38 static const char CVS_ID[] = "@(#) $RCSfile: pkistore.c,v $ $Revision: 1.33 $ $Date: 2008/06/06 01:19:30 $";
39 #endif /* DEBUG */
41 #ifndef PKIM_H
42 #include "pkim.h"
43 #endif /* PKIM_H */
45 #ifndef PKI_H
46 #include "pki.h"
47 #endif /* PKI_H */
49 #ifndef NSSPKI_H
50 #include "nsspki.h"
51 #endif /* NSSPKI_H */
53 #ifndef BASE_H
54 #include "base.h"
55 #endif /* BASE_H */
57 #ifndef PKISTORE_H
58 #include "pkistore.h"
59 #endif /* PKISTORE_H */
61 #include "cert.h"
63 #include "prbit.h"
65 /*
66 * Certificate Store
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;
77 NSSArena *arena;
78 PZLock *lock;
79 nssHash *subject;
80 nssHash *issuer_and_serial;
83 typedef struct certificate_hash_entry_str certificate_hash_entry;
85 struct certificate_hash_entry_str
87 NSSCertificate *cert;
88 NSSTrust *trust;
89 nssSMIMEProfile *profile;
92 /* forward static declarations */
93 static NSSCertificate *
94 nssCertStore_FindCertByIssuerAndSerialNumberLocked (
95 nssCertificateStore *store,
96 NSSDER *issuer,
97 NSSDER *serial
100 NSS_IMPLEMENT nssCertificateStore *
101 nssCertificateStore_Create (
102 NSSArena *arenaOpt
105 NSSArena *arena;
106 nssCertificateStore *store;
107 PRBool i_alloced_arena;
108 if (arenaOpt) {
109 arena = arenaOpt;
110 i_alloced_arena = PR_FALSE;
111 } else {
112 arena = nssArena_Create();
113 if (!arena) {
114 return NULL;
116 i_alloced_arena = PR_TRUE;
118 store = nss_ZNEW(arena, nssCertificateStore);
119 if (!store) {
120 goto loser;
122 store->lock = PZ_NewLock(nssILockOther);
123 if (!store->lock) {
124 goto loser;
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) {
129 goto loser;
131 /* Create the subject DER --> subject list hash */
132 store->subject = nssHash_CreateItem(arena, 0);
133 if (!store->subject) {
134 goto loser;
136 store->arena = arena;
137 store->i_alloced_arena = i_alloced_arena;
138 return store;
139 loser:
140 if (store) {
141 if (store->lock) {
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);
154 return NULL;
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);
166 return PR_FAILURE;
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);
173 } else {
174 nss_ZFreeIf(store);
176 return PR_SUCCESS;
179 static PRStatus
180 add_certificate_entry (
181 nssCertificateStore *store,
182 NSSCertificate *cert
185 PRStatus nssrv;
186 certificate_hash_entry *entry;
187 entry = nss_ZNEW(cert->object.arena, certificate_hash_entry);
188 if (!entry) {
189 return PR_FAILURE;
191 entry->cert = cert;
192 nssrv = nssHash_Add(store->issuer_and_serial, cert, entry);
193 if (nssrv != PR_SUCCESS) {
194 nss_ZFreeIf(entry);
196 return nssrv;
199 static PRStatus
200 add_subject_entry (
201 nssCertificateStore *store,
202 NSSCertificate *cert
205 PRStatus nssrv;
206 nssList *subjectList;
207 subjectList = (nssList *)nssHash_Lookup(store->subject, &cert->subject);
208 if (subjectList) {
209 /* The subject is already in, add this cert to the list */
210 nssrv = nssList_AddUnique(subjectList, cert);
211 } else {
212 /* Create a new subject list for the subject */
213 subjectList = nssList_Create(NULL, PR_FALSE);
214 if (!subjectList) {
215 return PR_FAILURE;
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) {
221 return nssrv;
223 /* Add the subject list to the cache */
224 nssrv = nssHash_Add(store->subject, &cert->subject, subjectList);
226 return nssrv;
229 /* declared below */
230 static void
231 remove_certificate_entry (
232 nssCertificateStore *store,
233 NSSCertificate *cert
236 /* Caller must hold store->lock */
237 static PRStatus
238 nssCertificateStore_AddLocked (
239 nssCertificateStore *store,
240 NSSCertificate *cert
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);
250 return nssrv;
254 NSS_IMPLEMENT NSSCertificate *
255 nssCertificateStore_FindOrAdd (
256 nssCertificateStore *store,
257 NSSCertificate *c
260 PRStatus nssrv;
261 NSSCertificate *rvCert = NULL;
263 PZ_Lock(store->lock);
264 rvCert = nssCertStore_FindCertByIssuerAndSerialNumberLocked(
265 store, &c->issuer, &c->serial);
266 if (!rvCert) {
267 nssrv = nssCertificateStore_AddLocked(store, c);
268 if (PR_SUCCESS == nssrv) {
269 rvCert = nssCertificate_AddRef(c);
272 PZ_Unlock(store->lock);
273 return rvCert;
276 static void
277 remove_certificate_entry (
278 nssCertificateStore *store,
279 NSSCertificate *cert
282 certificate_hash_entry *entry;
283 entry = (certificate_hash_entry *)
284 nssHash_Lookup(store->issuer_and_serial, cert);
285 if (entry) {
286 nssHash_Remove(store->issuer_and_serial, cert);
287 if (entry->trust) {
288 nssTrust_Destroy(entry->trust);
290 if (entry->profile) {
291 nssSMIMEProfile_Destroy(entry->profile);
293 nss_ZFreeIf(entry);
297 static void
298 remove_subject_entry (
299 nssCertificateStore *store,
300 NSSCertificate *cert
303 nssList *subjectList;
304 /* Get the subject list for the cert's subject */
305 subjectList = (nssList *)nssHash_Lookup(store->subject, &cert->subject);
306 if (subjectList) {
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);
312 } else {
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);
324 NSS_IMPLEMENT void
325 nssCertificateStore_RemoveCertLOCKED (
326 nssCertificateStore *store,
327 NSSCertificate *cert
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);
339 NSS_IMPLEMENT void
340 nssCertificateStore_Lock (
341 nssCertificateStore *store, nssCertificateStoreTrace* out
344 #ifdef DEBUG
345 PORT_Assert(out);
346 out->store = store;
347 out->lock = store->lock;
348 out->locked = PR_TRUE;
349 PZ_Lock(out->lock);
350 #else
351 PZ_Lock(store->lock);
352 #endif
355 NSS_IMPLEMENT void
356 nssCertificateStore_Unlock (
357 nssCertificateStore *store, const nssCertificateStoreTrace* in,
358 nssCertificateStoreTrace* out
361 #ifdef DEBUG
362 PORT_Assert(in);
363 PORT_Assert(out);
364 out->store = store;
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);
375 #else
376 PZ_Unlock(store->lock);
377 #endif
380 static NSSCertificate **
381 get_array_from_list (
382 nssList *certList,
383 NSSCertificate *rvOpt[],
384 PRUint32 maximumOpt,
385 NSSArena *arenaOpt
388 PRUint32 count;
389 NSSCertificate **rvArray = NULL;
390 count = nssList_Count(certList);
391 if (count == 0) {
392 return NULL;
394 if (maximumOpt > 0) {
395 count = PR_MIN(maximumOpt, count);
397 if (rvOpt) {
398 nssList_GetArray(certList, (void **)rvOpt, count);
399 } else {
400 rvArray = nss_ZNEWARRAY(arenaOpt, NSSCertificate *, count + 1);
401 if (rvArray) {
402 nssList_GetArray(certList, (void **)rvArray, count);
405 return rvArray;
408 NSS_IMPLEMENT NSSCertificate **
409 nssCertificateStore_FindCertificatesBySubject (
410 nssCertificateStore *store,
411 NSSDER *subject,
412 NSSCertificate *rvOpt[],
413 PRUint32 maximumOpt,
414 NSSArena *arenaOpt
417 NSSCertificate **rvArray = NULL;
418 nssList *subjectList;
419 PZ_Lock(store->lock);
420 subjectList = (nssList *)nssHash_Lookup(store->subject, subject);
421 if (subjectList) {
422 nssCertificateList_AddReferences(subjectList);
423 rvArray = get_array_from_list(subjectList,
424 rvOpt, maximumOpt, arenaOpt);
426 PZ_Unlock(store->lock);
427 return rvArray;
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.
436 * XXX
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
443 NSSUTF8 *nickname;
444 nssList *subjectList;
447 static void match_nickname(const void *k, void *v, void *a)
449 PRStatus nssrv;
450 NSSCertificate *c;
451 NSSUTF8 *nickname;
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[],
471 PRUint32 maximumOpt,
472 NSSArena *arenaOpt
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);
487 return rvArray;
490 struct email_template_str
492 NSSASCII7 *email;
493 nssList *emailList;
496 static void match_email(const void *k, void *v, void *a)
498 PRStatus nssrv;
499 NSSCertificate *c;
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);
507 if (iter) {
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,
526 NSSASCII7 *email,
527 NSSCertificate *rvOpt[],
528 PRUint32 maximumOpt,
529 NSSArena *arenaOpt
532 NSSCertificate **rvArray = NULL;
533 struct email_template_str et;
534 et.email = email;
535 et.emailList = nssList_Create(NULL, PR_FALSE);
536 if (!et.emailList) {
537 return NULL;
539 PZ_Lock(store->lock);
540 nssHash_Iterate(store->subject, match_email, &et);
541 if (et.emailList) {
542 /* get references before leaving the store's lock protection */
543 nssCertificateList_AddReferences(et.emailList);
545 PZ_Unlock(store->lock);
546 if (et.emailList) {
547 rvArray = get_array_from_list(et.emailList,
548 rvOpt, maximumOpt, arenaOpt);
549 nssList_Destroy(et.emailList);
551 return rvArray;
554 /* Caller holds store->lock */
555 static NSSCertificate *
556 nssCertStore_FindCertByIssuerAndSerialNumberLocked (
557 nssCertificateStore *store,
558 NSSDER *issuer,
559 NSSDER *serial
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);
570 if (entry) {
571 rvCert = nssCertificate_AddRef(entry->cert);
573 return rvCert;
576 NSS_IMPLEMENT NSSCertificate *
577 nssCertificateStore_FindCertificateByIssuerAndSerialNumber (
578 nssCertificateStore *store,
579 NSSDER *issuer,
580 NSSDER *serial
583 NSSCertificate *rvCert = NULL;
585 PZ_Lock(store->lock);
586 rvCert = nssCertStore_FindCertByIssuerAndSerialNumberLocked (
587 store, issuer, serial);
588 PZ_Unlock(store->lock);
589 return rvCert;
592 static PRStatus
593 issuer_and_serial_from_encoding (
594 NSSBER *encoding,
595 NSSDER *issuer,
596 NSSDER *serial
599 SECItem derCert, derIssuer, derSerial;
600 SECStatus secrv;
601 derCert.data = (unsigned char *)encoding->data;
602 derCert.len = encoding->size;
603 secrv = CERT_IssuerNameFromDERCert(&derCert, &derIssuer);
604 if (secrv != SECSuccess) {
605 return PR_FAILURE;
607 secrv = CERT_SerialNumberFromDERCert(&derCert, &derSerial);
608 if (secrv != SECSuccess) {
609 PORT_Free(derIssuer.data);
610 return PR_FAILURE;
612 issuer->data = derIssuer.data;
613 issuer->size = derIssuer.len;
614 serial->data = derSerial.data;
615 serial->size = derSerial.len;
616 return PR_SUCCESS;
619 NSS_IMPLEMENT NSSCertificate *
620 nssCertificateStore_FindCertificateByEncodedCertificate (
621 nssCertificateStore *store,
622 NSSDER *encoding
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) {
630 return NULL;
632 rvCert = nssCertificateStore_FindCertificateByIssuerAndSerialNumber(store,
633 &issuer,
634 &serial);
635 PORT_Free(issuer.data);
636 PORT_Free(serial.data);
637 return rvCert;
640 NSS_EXTERN PRStatus
641 nssCertificateStore_AddTrust (
642 nssCertificateStore *store,
643 NSSTrust *trust
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);
652 if (entry) {
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,
662 NSSCertificate *cert
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);
674 return rvTrust;
677 NSS_EXTERN PRStatus
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);
689 if (entry) {
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,
699 NSSCertificate *cert
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);
711 return rvProfile;
714 /* XXX this is also used by cache and should be somewhere else */
716 static PLHashNumber
717 nss_certificate_hash (
718 const void *key
721 unsigned int i;
722 PLHashNumber h;
723 NSSCertificate *c = (NSSCertificate *)key;
724 h = 0;
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];
729 return h;
732 static int
733 nss_compare_certs(const void *v1, const void *v2)
735 PRStatus ignore;
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 (
744 NSSArena *arenaOpt,
745 PRUint32 numBuckets
748 return nssHash_Create(arenaOpt,
749 numBuckets,
750 nss_certificate_hash,
751 nss_compare_certs,
752 PL_CompareValues);
755 NSS_IMPLEMENT void
756 nssCertificateStore_DumpStoreInfo (
757 nssCertificateStore *store,
758 void (* cert_dump_iter)(const void *, void *, void *),
759 void *arg
762 PZ_Lock(store->lock);
763 nssHash_Iterate(store->issuer_and_serial, cert_dump_iter, arg);
764 PZ_Unlock(store->lock);