diff nss/lib/pki/pkistore.c @ 0:1e5118fa0cb1

This is NSS with a Cmake Buildsyste To compile a static NSS library for Windows we've used the Chromium-NSS fork and added a Cmake buildsystem to compile it statically for Windows. See README.chromium for chromium changes and README.trustbridge for our modifications.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 28 Jul 2014 10:47:06 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/pki/pkistore.c	Mon Jul 28 10:47:06 2014 +0200
@@ -0,0 +1,739 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef PKIM_H
+#include "pkim.h"
+#endif /* PKIM_H */
+
+#ifndef PKI_H
+#include "pki.h"
+#endif /* PKI_H */
+
+#ifndef NSSPKI_H
+#include "nsspki.h"
+#endif /* NSSPKI_H */
+
+#ifndef BASE_H
+#include "base.h"
+#endif /* BASE_H */
+
+#ifndef PKISTORE_H
+#include "pkistore.h"
+#endif /* PKISTORE_H */
+
+#include "cert.h"
+
+#include "prbit.h"
+
+/* 
+ * Certificate Store
+ *
+ * This differs from the cache in that it is a true storage facility.  Items
+ * stay in until they are explicitly removed.  It is only used by crypto
+ * contexts at this time, but may be more generally useful...
+ *
+ */
+
+struct nssCertificateStoreStr 
+{
+    PRBool i_alloced_arena;
+    NSSArena *arena;
+    PZLock *lock;
+    nssHash *subject;
+    nssHash *issuer_and_serial;
+};
+
+typedef struct certificate_hash_entry_str certificate_hash_entry;
+
+struct certificate_hash_entry_str
+{
+    NSSCertificate *cert;
+    NSSTrust *trust;
+    nssSMIMEProfile *profile;
+};
+
+/* forward static declarations */
+static NSSCertificate *
+nssCertStore_FindCertByIssuerAndSerialNumberLocked (
+  nssCertificateStore *store,
+  NSSDER *issuer,
+  NSSDER *serial
+);
+
+NSS_IMPLEMENT nssCertificateStore *
+nssCertificateStore_Create (
+  NSSArena *arenaOpt
+)
+{
+    NSSArena *arena;
+    nssCertificateStore *store;
+    PRBool i_alloced_arena;
+    if (arenaOpt) {
+	arena = arenaOpt;
+	i_alloced_arena = PR_FALSE;
+    } else {
+	arena = nssArena_Create();
+	if (!arena) {
+	    return NULL;
+	}
+	i_alloced_arena = PR_TRUE;
+    }
+    store = nss_ZNEW(arena, nssCertificateStore);
+    if (!store) {
+	goto loser;
+    }
+    store->lock = PZ_NewLock(nssILockOther);
+    if (!store->lock) {
+	goto loser;
+    }
+    /* Create the issuer/serial --> {cert, trust, S/MIME profile } hash */
+    store->issuer_and_serial = nssHash_CreateCertificate(arena, 0);
+    if (!store->issuer_and_serial) {
+	goto loser;
+    }
+    /* Create the subject DER --> subject list hash */
+    store->subject = nssHash_CreateItem(arena, 0);
+    if (!store->subject) {
+	goto loser;
+    }
+    store->arena = arena;
+    store->i_alloced_arena = i_alloced_arena;
+    return store;
+loser:
+    if (store) {
+	if (store->lock) {
+	    PZ_DestroyLock(store->lock);
+	}
+	if (store->issuer_and_serial) {
+	    nssHash_Destroy(store->issuer_and_serial);
+	}
+	if (store->subject) {
+	    nssHash_Destroy(store->subject);
+	}
+    }
+    if (i_alloced_arena) {
+	nssArena_Destroy(arena);
+    }
+    return NULL;
+}
+
+extern const NSSError NSS_ERROR_BUSY;
+
+NSS_IMPLEMENT PRStatus
+nssCertificateStore_Destroy (
+  nssCertificateStore *store
+)
+{
+    if (nssHash_Count(store->issuer_and_serial) > 0) {
+	nss_SetError(NSS_ERROR_BUSY);
+	return PR_FAILURE;
+    }
+    PZ_DestroyLock(store->lock);
+    nssHash_Destroy(store->issuer_and_serial);
+    nssHash_Destroy(store->subject);
+    if (store->i_alloced_arena) {
+	nssArena_Destroy(store->arena);
+    } else {
+	nss_ZFreeIf(store);
+    }
+    return PR_SUCCESS;
+}
+
+static PRStatus
+add_certificate_entry (
+  nssCertificateStore *store,
+  NSSCertificate *cert
+)
+{
+    PRStatus nssrv;
+    certificate_hash_entry *entry;
+    entry = nss_ZNEW(cert->object.arena, certificate_hash_entry);
+    if (!entry) {
+	return PR_FAILURE;
+    }
+    entry->cert = cert;
+    nssrv = nssHash_Add(store->issuer_and_serial, cert, entry);
+    if (nssrv != PR_SUCCESS) {
+	nss_ZFreeIf(entry);
+    }
+    return nssrv;
+}
+
+static PRStatus
+add_subject_entry (
+  nssCertificateStore *store,
+  NSSCertificate *cert
+)
+{
+    PRStatus nssrv;
+    nssList *subjectList;
+    subjectList = (nssList *)nssHash_Lookup(store->subject, &cert->subject);
+    if (subjectList) {
+	/* The subject is already in, add this cert to the list */
+	nssrv = nssList_AddUnique(subjectList, cert);
+    } else {
+	/* Create a new subject list for the subject */
+	subjectList = nssList_Create(NULL, PR_FALSE);
+	if (!subjectList) {
+	    return PR_FAILURE;
+	}
+	nssList_SetSortFunction(subjectList, nssCertificate_SubjectListSort);
+	/* Add the cert entry to this list of subjects */
+	nssrv = nssList_Add(subjectList, cert);
+	if (nssrv != PR_SUCCESS) {
+	    return nssrv;
+	}
+	/* Add the subject list to the cache */
+	nssrv = nssHash_Add(store->subject, &cert->subject, subjectList);
+    }
+    return nssrv;
+}
+
+/* declared below */
+static void
+remove_certificate_entry (
+  nssCertificateStore *store,
+  NSSCertificate *cert
+);
+
+/* Caller must hold store->lock */
+static PRStatus
+nssCertificateStore_AddLocked (
+  nssCertificateStore *store,
+  NSSCertificate *cert
+)
+{
+    PRStatus nssrv = add_certificate_entry(store, cert);
+    if (nssrv == PR_SUCCESS) {
+	nssrv = add_subject_entry(store, cert);
+	if (nssrv == PR_FAILURE) {
+	    remove_certificate_entry(store, cert);
+	}
+    }
+    return nssrv;
+}
+
+
+NSS_IMPLEMENT NSSCertificate *
+nssCertificateStore_FindOrAdd (
+  nssCertificateStore *store,
+  NSSCertificate *c
+)
+{
+    PRStatus nssrv;
+    NSSCertificate *rvCert = NULL;
+
+    PZ_Lock(store->lock);
+    rvCert = nssCertStore_FindCertByIssuerAndSerialNumberLocked(
+					   store, &c->issuer, &c->serial);
+    if (!rvCert) {
+	nssrv = nssCertificateStore_AddLocked(store, c);
+	if (PR_SUCCESS == nssrv) {
+	    rvCert = nssCertificate_AddRef(c);
+	}
+    }
+    PZ_Unlock(store->lock);
+    return rvCert;
+}
+
+static void
+remove_certificate_entry (
+  nssCertificateStore *store,
+  NSSCertificate *cert
+)
+{
+    certificate_hash_entry *entry;
+    entry = (certificate_hash_entry *)
+                             nssHash_Lookup(store->issuer_and_serial, cert);
+    if (entry) {
+	nssHash_Remove(store->issuer_and_serial, cert);
+	if (entry->trust) {
+	    nssTrust_Destroy(entry->trust);
+	}
+	if (entry->profile) {
+	    nssSMIMEProfile_Destroy(entry->profile);
+	}
+	nss_ZFreeIf(entry);
+    }
+}
+
+static void
+remove_subject_entry (
+  nssCertificateStore *store,
+  NSSCertificate *cert
+)
+{
+    nssList *subjectList;
+    /* Get the subject list for the cert's subject */
+    subjectList = (nssList *)nssHash_Lookup(store->subject, &cert->subject);
+    if (subjectList) {
+	/* Remove the cert from the subject hash */
+	nssList_Remove(subjectList, cert);
+	nssHash_Remove(store->subject, &cert->subject);
+	if (nssList_Count(subjectList) == 0) {
+	    nssList_Destroy(subjectList);
+	} else {
+	    /* The cert being released may have keyed the subject entry.
+	     * Since there are still subject certs around, get another and
+	     * rekey the entry just in case.
+	     */
+	    NSSCertificate *subjectCert;
+	    (void)nssList_GetArray(subjectList, (void **)&subjectCert, 1);
+	    nssHash_Add(store->subject, &subjectCert->subject, subjectList);
+	}
+    }
+}
+
+NSS_IMPLEMENT void
+nssCertificateStore_RemoveCertLOCKED (
+  nssCertificateStore *store,
+  NSSCertificate *cert
+)
+{
+    certificate_hash_entry *entry;
+    entry = (certificate_hash_entry *)
+                              nssHash_Lookup(store->issuer_and_serial, cert);
+    if (entry && entry->cert == cert) {
+	remove_certificate_entry(store, cert);
+	remove_subject_entry(store, cert);
+    }
+}
+
+NSS_IMPLEMENT void
+nssCertificateStore_Lock (
+  nssCertificateStore *store, nssCertificateStoreTrace* out
+)
+{
+#ifdef DEBUG
+    PORT_Assert(out);
+    out->store = store;
+    out->lock = store->lock;
+    out->locked = PR_TRUE;
+    PZ_Lock(out->lock);
+#else
+    PZ_Lock(store->lock);
+#endif
+}
+
+NSS_IMPLEMENT void
+nssCertificateStore_Unlock (
+  nssCertificateStore *store, const nssCertificateStoreTrace* in,
+  nssCertificateStoreTrace* out
+)
+{
+#ifdef DEBUG
+    PORT_Assert(in);
+    PORT_Assert(out);
+    out->store = store;
+    out->lock = store->lock;
+    PORT_Assert(!out->locked);
+    out->unlocked = PR_TRUE;
+
+    PORT_Assert(in->store == out->store);
+    PORT_Assert(in->lock == out->lock);
+    PORT_Assert(in->locked);
+    PORT_Assert(!in->unlocked);
+
+    PZ_Unlock(out->lock);
+#else
+    PZ_Unlock(store->lock);
+#endif
+}
+
+static NSSCertificate **
+get_array_from_list (
+  nssList *certList,
+  NSSCertificate *rvOpt[],
+  PRUint32 maximumOpt,
+  NSSArena *arenaOpt
+)
+{
+    PRUint32 count;
+    NSSCertificate **rvArray = NULL;
+    count = nssList_Count(certList);
+    if (count == 0) {
+	return NULL;
+    }
+    if (maximumOpt > 0) {
+	count = PR_MIN(maximumOpt, count);
+    }
+    if (rvOpt) {
+	nssList_GetArray(certList, (void **)rvOpt, count);
+    } else {
+	rvArray = nss_ZNEWARRAY(arenaOpt, NSSCertificate *, count + 1);
+	if (rvArray) {
+	    nssList_GetArray(certList, (void **)rvArray, count);
+	}
+    }
+    return rvArray;
+}
+
+NSS_IMPLEMENT NSSCertificate **
+nssCertificateStore_FindCertificatesBySubject (
+  nssCertificateStore *store,
+  NSSDER *subject,
+  NSSCertificate *rvOpt[],
+  PRUint32 maximumOpt,
+  NSSArena *arenaOpt
+)
+{
+    NSSCertificate **rvArray = NULL;
+    nssList *subjectList;
+    PZ_Lock(store->lock);
+    subjectList = (nssList *)nssHash_Lookup(store->subject, subject);
+    if (subjectList) {
+	nssCertificateList_AddReferences(subjectList);
+	rvArray = get_array_from_list(subjectList, 
+	                              rvOpt, maximumOpt, arenaOpt);
+    }
+    PZ_Unlock(store->lock);
+    return rvArray;
+}
+
+/* Because only subject indexing is implemented, all other lookups require
+ * full traversal (unfortunately, PLHashTable doesn't allow you to exit
+ * early from the enumeration).  The assumptions are that 1) lookups by 
+ * fields other than subject will be rare, and 2) the hash will not have
+ * a large number of entries.  These assumptions will be tested.
+ *
+ * XXX
+ * For NSS 3.4, it is worth consideration to do all forms of indexing,
+ * because the only crypto context is global and persistent.
+ */
+
+struct nickname_template_str
+{
+    NSSUTF8 *nickname;
+    nssList *subjectList;
+};
+
+static void match_nickname(const void *k, void *v, void *a)
+{
+    PRStatus nssrv;
+    NSSCertificate *c;
+    NSSUTF8 *nickname;
+    nssList *subjectList = (nssList *)v;
+    struct nickname_template_str *nt = (struct nickname_template_str *)a;
+    nssrv = nssList_GetArray(subjectList, (void **)&c, 1);
+    nickname = nssCertificate_GetNickname(c, NULL);
+    if (nssrv == PR_SUCCESS && nickname &&
+         nssUTF8_Equal(nickname, nt->nickname, &nssrv)) 
+    {
+	nt->subjectList = subjectList;
+    }
+    nss_ZFreeIf(nickname);
+}
+
+/*
+ * Find all cached certs with this label.
+ */
+NSS_IMPLEMENT NSSCertificate **
+nssCertificateStore_FindCertificatesByNickname (
+  nssCertificateStore *store,
+  const NSSUTF8 *nickname,
+  NSSCertificate *rvOpt[],
+  PRUint32 maximumOpt,
+  NSSArena *arenaOpt
+)
+{
+    NSSCertificate **rvArray = NULL;
+    struct nickname_template_str nt;
+    nt.nickname = (char*) nickname;
+    nt.subjectList = NULL;
+    PZ_Lock(store->lock);
+    nssHash_Iterate(store->subject, match_nickname, &nt);
+    if (nt.subjectList) {
+	nssCertificateList_AddReferences(nt.subjectList);
+	rvArray = get_array_from_list(nt.subjectList, 
+	                              rvOpt, maximumOpt, arenaOpt);
+    }
+    PZ_Unlock(store->lock);
+    return rvArray;
+}
+
+struct email_template_str
+{
+    NSSASCII7 *email;
+    nssList *emailList;
+};
+
+static void match_email(const void *k, void *v, void *a)
+{
+    PRStatus nssrv;
+    NSSCertificate *c;
+    nssList *subjectList = (nssList *)v;
+    struct email_template_str *et = (struct email_template_str *)a;
+    nssrv = nssList_GetArray(subjectList, (void **)&c, 1);
+    if (nssrv == PR_SUCCESS && 
+         nssUTF8_Equal(c->email, et->email, &nssrv)) 
+    {
+	nssListIterator *iter = nssList_CreateIterator(subjectList);
+	if (iter) {
+	    for (c  = (NSSCertificate *)nssListIterator_Start(iter);
+	         c != (NSSCertificate *)NULL;
+	         c  = (NSSCertificate *)nssListIterator_Next(iter))
+	    {
+		nssList_Add(et->emailList, c);
+	    }
+	    nssListIterator_Finish(iter);
+	    nssListIterator_Destroy(iter);
+	}
+    }
+}
+
+/*
+ * Find all cached certs with this email address.
+ */
+NSS_IMPLEMENT NSSCertificate **
+nssCertificateStore_FindCertificatesByEmail (
+  nssCertificateStore *store,
+  NSSASCII7 *email,
+  NSSCertificate *rvOpt[],
+  PRUint32 maximumOpt,
+  NSSArena *arenaOpt
+)
+{
+    NSSCertificate **rvArray = NULL;
+    struct email_template_str et;
+    et.email = email;
+    et.emailList = nssList_Create(NULL, PR_FALSE);
+    if (!et.emailList) {
+	return NULL;
+    }
+    PZ_Lock(store->lock);
+    nssHash_Iterate(store->subject, match_email, &et);
+    if (et.emailList) {
+	/* get references before leaving the store's lock protection */
+	nssCertificateList_AddReferences(et.emailList);
+    }
+    PZ_Unlock(store->lock);
+    if (et.emailList) {
+	rvArray = get_array_from_list(et.emailList, 
+	                              rvOpt, maximumOpt, arenaOpt);
+	nssList_Destroy(et.emailList);
+    }
+    return rvArray;
+}
+
+/* Caller holds store->lock */
+static NSSCertificate *
+nssCertStore_FindCertByIssuerAndSerialNumberLocked (
+  nssCertificateStore *store,
+  NSSDER *issuer,
+  NSSDER *serial
+)
+{
+    certificate_hash_entry *entry;
+    NSSCertificate *rvCert = NULL;
+    NSSCertificate index;
+
+    index.issuer = *issuer;
+    index.serial = *serial;
+    entry = (certificate_hash_entry *)
+                           nssHash_Lookup(store->issuer_and_serial, &index);
+    if (entry) {
+	rvCert = nssCertificate_AddRef(entry->cert);
+    }
+    return rvCert;
+}
+
+NSS_IMPLEMENT NSSCertificate *
+nssCertificateStore_FindCertificateByIssuerAndSerialNumber (
+  nssCertificateStore *store,
+  NSSDER *issuer,
+  NSSDER *serial
+)
+{
+    NSSCertificate *rvCert = NULL;
+
+    PZ_Lock(store->lock);
+    rvCert = nssCertStore_FindCertByIssuerAndSerialNumberLocked (
+                           store, issuer, serial);
+    PZ_Unlock(store->lock);
+    return rvCert;
+}
+
+static PRStatus
+issuer_and_serial_from_encoding (
+  NSSBER *encoding, 
+  NSSDER *issuer, 
+  NSSDER *serial
+)
+{
+    SECItem derCert, derIssuer, derSerial;
+    SECStatus secrv;
+    derCert.data = (unsigned char *)encoding->data;
+    derCert.len = encoding->size;
+    secrv = CERT_IssuerNameFromDERCert(&derCert, &derIssuer);
+    if (secrv != SECSuccess) {
+	return PR_FAILURE;
+    }
+    secrv = CERT_SerialNumberFromDERCert(&derCert, &derSerial);
+    if (secrv != SECSuccess) {
+	PORT_Free(derIssuer.data);
+	return PR_FAILURE;
+    }
+    issuer->data = derIssuer.data;
+    issuer->size = derIssuer.len;
+    serial->data = derSerial.data;
+    serial->size = derSerial.len;
+    return PR_SUCCESS;
+}
+
+NSS_IMPLEMENT NSSCertificate *
+nssCertificateStore_FindCertificateByEncodedCertificate (
+  nssCertificateStore *store,
+  NSSDER *encoding
+)
+{
+    PRStatus nssrv = PR_FAILURE;
+    NSSDER issuer, serial;
+    NSSCertificate *rvCert = NULL;
+    nssrv = issuer_and_serial_from_encoding(encoding, &issuer, &serial);
+    if (nssrv != PR_SUCCESS) {
+	return NULL;
+    }
+    rvCert = nssCertificateStore_FindCertificateByIssuerAndSerialNumber(store, 
+                                                                     &issuer, 
+                                                                     &serial);
+    PORT_Free(issuer.data);
+    PORT_Free(serial.data);
+    return rvCert;
+}
+
+NSS_EXTERN PRStatus
+nssCertificateStore_AddTrust (
+  nssCertificateStore *store,
+  NSSTrust *trust
+)
+{
+    NSSCertificate *cert;
+    certificate_hash_entry *entry;
+    cert = trust->certificate;
+    PZ_Lock(store->lock);
+    entry = (certificate_hash_entry *)
+                              nssHash_Lookup(store->issuer_and_serial, cert);
+    if (entry) {
+	NSSTrust* newTrust = nssTrust_AddRef(trust);
+	if (entry->trust) {
+	    nssTrust_Destroy(entry->trust);
+	}
+	entry->trust = newTrust;
+    }
+    PZ_Unlock(store->lock);
+    return (entry) ? PR_SUCCESS : PR_FAILURE;
+}
+
+NSS_IMPLEMENT NSSTrust *
+nssCertificateStore_FindTrustForCertificate (
+  nssCertificateStore *store,
+  NSSCertificate *cert
+)
+{
+    certificate_hash_entry *entry;
+    NSSTrust *rvTrust = NULL;
+    PZ_Lock(store->lock);
+    entry = (certificate_hash_entry *)
+                              nssHash_Lookup(store->issuer_and_serial, cert);
+    if (entry && entry->trust) {
+	rvTrust = nssTrust_AddRef(entry->trust);
+    }
+    PZ_Unlock(store->lock);
+    return rvTrust;
+}
+
+NSS_EXTERN PRStatus
+nssCertificateStore_AddSMIMEProfile (
+  nssCertificateStore *store,
+  nssSMIMEProfile *profile
+)
+{
+    NSSCertificate *cert;
+    certificate_hash_entry *entry;
+    cert = profile->certificate;
+    PZ_Lock(store->lock);
+    entry = (certificate_hash_entry *)
+                              nssHash_Lookup(store->issuer_and_serial, cert);
+    if (entry) {
+	nssSMIMEProfile* newProfile = nssSMIMEProfile_AddRef(profile);
+	if (entry->profile) {
+	    nssSMIMEProfile_Destroy(entry->profile);
+	}
+	entry->profile = newProfile;
+    }
+    PZ_Unlock(store->lock);
+    return (entry) ? PR_SUCCESS : PR_FAILURE;
+}
+
+NSS_IMPLEMENT nssSMIMEProfile *
+nssCertificateStore_FindSMIMEProfileForCertificate (
+  nssCertificateStore *store,
+  NSSCertificate *cert
+)
+{
+    certificate_hash_entry *entry;
+    nssSMIMEProfile *rvProfile = NULL;
+    PZ_Lock(store->lock);
+    entry = (certificate_hash_entry *)
+                              nssHash_Lookup(store->issuer_and_serial, cert);
+    if (entry && entry->profile) {
+	rvProfile = nssSMIMEProfile_AddRef(entry->profile);
+    }
+    PZ_Unlock(store->lock);
+    return rvProfile;
+}
+
+/* XXX this is also used by cache and should be somewhere else */
+
+static PLHashNumber
+nss_certificate_hash (
+  const void *key
+)
+{
+    unsigned int i;
+    PLHashNumber h;
+    NSSCertificate *c = (NSSCertificate *)key;
+    h = 0;
+    for (i=0; i<c->issuer.size; i++)
+	h = PR_ROTATE_LEFT32(h, 4) ^ ((unsigned char *)c->issuer.data)[i];
+    for (i=0; i<c->serial.size; i++)
+	h = PR_ROTATE_LEFT32(h, 4) ^ ((unsigned char *)c->serial.data)[i];
+    return h;
+}
+
+static int
+nss_compare_certs(const void *v1, const void *v2)
+{
+    PRStatus ignore;
+    NSSCertificate *c1 = (NSSCertificate *)v1;
+    NSSCertificate *c2 = (NSSCertificate *)v2;
+    return (int)(nssItem_Equal(&c1->issuer, &c2->issuer, &ignore) &&
+                 nssItem_Equal(&c1->serial, &c2->serial, &ignore));
+}
+
+NSS_IMPLEMENT nssHash *
+nssHash_CreateCertificate (
+  NSSArena *arenaOpt,
+  PRUint32 numBuckets
+)
+{
+    return nssHash_Create(arenaOpt, 
+                          numBuckets, 
+                          nss_certificate_hash, 
+                          nss_compare_certs, 
+                          PL_CompareValues);
+}
+
+NSS_IMPLEMENT void
+nssCertificateStore_DumpStoreInfo (
+  nssCertificateStore *store,
+  void (* cert_dump_iter)(const void *, void *, void *),
+  void *arg
+)
+{
+    PZ_Lock(store->lock);
+    nssHash_Iterate(store->issuer_and_serial, cert_dump_iter, arg);
+    PZ_Unlock(store->lock);
+}
+
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)