summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/pki/pkibase.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/pki/pkibase.c')
-rw-r--r--security/nss/lib/pki/pkibase.c1222
1 files changed, 1222 insertions, 0 deletions
diff --git a/security/nss/lib/pki/pkibase.c b/security/nss/lib/pki/pkibase.c
new file mode 100644
index 000000000..4082a37bd
--- /dev/null
+++ b/security/nss/lib/pki/pkibase.c
@@ -0,0 +1,1222 @@
+/* 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 DEV_H
+#include "dev.h"
+#endif /* DEV_H */
+
+#ifndef PKIM_H
+#include "pkim.h"
+#endif /* PKIM_H */
+
+#include "pki3hack.h"
+
+extern const NSSError NSS_ERROR_NOT_FOUND;
+
+NSS_IMPLEMENT void
+nssPKIObject_Lock(nssPKIObject *object)
+{
+ switch (object->lockType) {
+ case nssPKIMonitor:
+ PZ_EnterMonitor(object->sync.mlock);
+ break;
+ case nssPKILock:
+ PZ_Lock(object->sync.lock);
+ break;
+ default:
+ PORT_Assert(0);
+ }
+}
+
+NSS_IMPLEMENT void
+nssPKIObject_Unlock(nssPKIObject *object)
+{
+ switch (object->lockType) {
+ case nssPKIMonitor:
+ PZ_ExitMonitor(object->sync.mlock);
+ break;
+ case nssPKILock:
+ PZ_Unlock(object->sync.lock);
+ break;
+ default:
+ PORT_Assert(0);
+ }
+}
+
+NSS_IMPLEMENT PRStatus
+nssPKIObject_NewLock(nssPKIObject *object, nssPKILockType lockType)
+{
+ object->lockType = lockType;
+ switch (lockType) {
+ case nssPKIMonitor:
+ object->sync.mlock = PZ_NewMonitor(nssILockSSL);
+ return (object->sync.mlock ? PR_SUCCESS : PR_FAILURE);
+ case nssPKILock:
+ object->sync.lock = PZ_NewLock(nssILockSSL);
+ return (object->sync.lock ? PR_SUCCESS : PR_FAILURE);
+ default:
+ PORT_Assert(0);
+ return PR_FAILURE;
+ }
+}
+
+NSS_IMPLEMENT void
+nssPKIObject_DestroyLock(nssPKIObject *object)
+{
+ switch (object->lockType) {
+ case nssPKIMonitor:
+ PZ_DestroyMonitor(object->sync.mlock);
+ object->sync.mlock = NULL;
+ break;
+ case nssPKILock:
+ PZ_DestroyLock(object->sync.lock);
+ object->sync.lock = NULL;
+ break;
+ default:
+ PORT_Assert(0);
+ }
+}
+
+NSS_IMPLEMENT nssPKIObject *
+nssPKIObject_Create(
+ NSSArena *arenaOpt,
+ nssCryptokiObject *instanceOpt,
+ NSSTrustDomain *td,
+ NSSCryptoContext *cc,
+ nssPKILockType lockType)
+{
+ NSSArena *arena;
+ nssArenaMark *mark = NULL;
+ nssPKIObject *object;
+ if (arenaOpt) {
+ arena = arenaOpt;
+ mark = nssArena_Mark(arena);
+ } else {
+ arena = nssArena_Create();
+ if (!arena) {
+ return (nssPKIObject *)NULL;
+ }
+ }
+ object = nss_ZNEW(arena, nssPKIObject);
+ if (!object) {
+ goto loser;
+ }
+ object->arena = arena;
+ object->trustDomain = td; /* XXX */
+ object->cryptoContext = cc;
+ if (PR_SUCCESS != nssPKIObject_NewLock(object, lockType)) {
+ goto loser;
+ }
+ if (instanceOpt) {
+ if (nssPKIObject_AddInstance(object, instanceOpt) != PR_SUCCESS) {
+ goto loser;
+ }
+ }
+ PR_ATOMIC_INCREMENT(&object->refCount);
+ if (mark) {
+ nssArena_Unmark(arena, mark);
+ }
+ return object;
+loser:
+ if (mark) {
+ nssArena_Release(arena, mark);
+ } else {
+ nssArena_Destroy(arena);
+ }
+ return (nssPKIObject *)NULL;
+}
+
+NSS_IMPLEMENT PRBool
+nssPKIObject_Destroy(
+ nssPKIObject *object)
+{
+ PRUint32 i;
+ PR_ASSERT(object->refCount > 0);
+ if (PR_ATOMIC_DECREMENT(&object->refCount) == 0) {
+ for (i = 0; i < object->numInstances; i++) {
+ nssCryptokiObject_Destroy(object->instances[i]);
+ }
+ nssPKIObject_DestroyLock(object);
+ nssArena_Destroy(object->arena);
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+NSS_IMPLEMENT nssPKIObject *
+nssPKIObject_AddRef(
+ nssPKIObject *object)
+{
+ PR_ATOMIC_INCREMENT(&object->refCount);
+ return object;
+}
+
+NSS_IMPLEMENT PRStatus
+nssPKIObject_AddInstance(
+ nssPKIObject *object,
+ nssCryptokiObject *instance)
+{
+ nssCryptokiObject **newInstances = NULL;
+
+ nssPKIObject_Lock(object);
+ if (object->numInstances == 0) {
+ newInstances = nss_ZNEWARRAY(object->arena,
+ nssCryptokiObject *,
+ object->numInstances + 1);
+ } else {
+ PRBool found = PR_FALSE;
+ PRUint32 i;
+ for (i = 0; i < object->numInstances; i++) {
+ if (nssCryptokiObject_Equal(object->instances[i], instance)) {
+ found = PR_TRUE;
+ break;
+ }
+ }
+ if (found) {
+ /* The new instance is identical to one in the array, except
+ * perhaps that the label may be different. So replace
+ * the label in the array instance with the label from the
+ * new instance, and discard the new instance.
+ */
+ nss_ZFreeIf(object->instances[i]->label);
+ object->instances[i]->label = instance->label;
+ nssPKIObject_Unlock(object);
+ instance->label = NULL;
+ nssCryptokiObject_Destroy(instance);
+ return PR_SUCCESS;
+ }
+ newInstances = nss_ZREALLOCARRAY(object->instances,
+ nssCryptokiObject *,
+ object->numInstances + 1);
+ }
+ if (newInstances) {
+ object->instances = newInstances;
+ newInstances[object->numInstances++] = instance;
+ }
+ nssPKIObject_Unlock(object);
+ return (newInstances ? PR_SUCCESS : PR_FAILURE);
+}
+
+NSS_IMPLEMENT PRBool
+nssPKIObject_HasInstance(
+ nssPKIObject *object,
+ nssCryptokiObject *instance)
+{
+ PRUint32 i;
+ PRBool hasIt = PR_FALSE;
+ ;
+ nssPKIObject_Lock(object);
+ for (i = 0; i < object->numInstances; i++) {
+ if (nssCryptokiObject_Equal(object->instances[i], instance)) {
+ hasIt = PR_TRUE;
+ break;
+ }
+ }
+ nssPKIObject_Unlock(object);
+ return hasIt;
+}
+
+NSS_IMPLEMENT PRStatus
+nssPKIObject_RemoveInstanceForToken(
+ nssPKIObject *object,
+ NSSToken *token)
+{
+ PRUint32 i;
+ nssCryptokiObject *instanceToRemove = NULL;
+ nssPKIObject_Lock(object);
+ if (object->numInstances == 0) {
+ nssPKIObject_Unlock(object);
+ return PR_SUCCESS;
+ }
+ for (i = 0; i < object->numInstances; i++) {
+ if (object->instances[i]->token == token) {
+ instanceToRemove = object->instances[i];
+ object->instances[i] = object->instances[object->numInstances - 1];
+ object->instances[object->numInstances - 1] = NULL;
+ break;
+ }
+ }
+ if (--object->numInstances > 0) {
+ nssCryptokiObject **instances = nss_ZREALLOCARRAY(object->instances,
+ nssCryptokiObject *,
+ object->numInstances);
+ if (instances) {
+ object->instances = instances;
+ }
+ } else {
+ nss_ZFreeIf(object->instances);
+ }
+ nssCryptokiObject_Destroy(instanceToRemove);
+ nssPKIObject_Unlock(object);
+ return PR_SUCCESS;
+}
+
+/* this needs more thought on what will happen when there are multiple
+ * instances
+ */
+NSS_IMPLEMENT PRStatus
+nssPKIObject_DeleteStoredObject(
+ nssPKIObject *object,
+ NSSCallback *uhh,
+ PRBool isFriendly)
+{
+ PRUint32 i, numNotDestroyed;
+ PRStatus status = PR_SUCCESS;
+ numNotDestroyed = 0;
+ nssPKIObject_Lock(object);
+ for (i = 0; i < object->numInstances; i++) {
+ nssCryptokiObject *instance = object->instances[i];
+ status = nssToken_DeleteStoredObject(instance);
+ object->instances[i] = NULL;
+ if (status == PR_SUCCESS) {
+ nssCryptokiObject_Destroy(instance);
+ } else {
+ object->instances[numNotDestroyed++] = instance;
+ }
+ }
+ if (numNotDestroyed == 0) {
+ nss_ZFreeIf(object->instances);
+ object->numInstances = 0;
+ } else {
+ object->numInstances = numNotDestroyed;
+ }
+ nssPKIObject_Unlock(object);
+ return status;
+}
+
+NSS_IMPLEMENT NSSToken **
+nssPKIObject_GetTokens(
+ nssPKIObject *object,
+ PRStatus *statusOpt)
+{
+ NSSToken **tokens = NULL;
+ nssPKIObject_Lock(object);
+ if (object->numInstances > 0) {
+ tokens = nss_ZNEWARRAY(NULL, NSSToken *, object->numInstances + 1);
+ if (tokens) {
+ PRUint32 i;
+ for (i = 0; i < object->numInstances; i++) {
+ tokens[i] = nssToken_AddRef(object->instances[i]->token);
+ }
+ }
+ }
+ nssPKIObject_Unlock(object);
+ if (statusOpt)
+ *statusOpt = PR_SUCCESS; /* until more logic here */
+ return tokens;
+}
+
+NSS_IMPLEMENT NSSUTF8 *
+nssPKIObject_GetNicknameForToken(
+ nssPKIObject *object,
+ NSSToken *tokenOpt)
+{
+ PRUint32 i;
+ NSSUTF8 *nickname = NULL;
+ nssPKIObject_Lock(object);
+ for (i = 0; i < object->numInstances; i++) {
+ if ((!tokenOpt && object->instances[i]->label) ||
+ (object->instances[i]->token == tokenOpt)) {
+ /* Must copy, see bug 745548 */
+ nickname = nssUTF8_Duplicate(object->instances[i]->label, NULL);
+ break;
+ }
+ }
+ nssPKIObject_Unlock(object);
+ return nickname;
+}
+
+NSS_IMPLEMENT nssCryptokiObject **
+nssPKIObject_GetInstances(
+ nssPKIObject *object)
+{
+ nssCryptokiObject **instances = NULL;
+ PRUint32 i;
+ if (object->numInstances == 0) {
+ return (nssCryptokiObject **)NULL;
+ }
+ nssPKIObject_Lock(object);
+ instances = nss_ZNEWARRAY(NULL, nssCryptokiObject *,
+ object->numInstances + 1);
+ if (instances) {
+ for (i = 0; i < object->numInstances; i++) {
+ instances[i] = nssCryptokiObject_Clone(object->instances[i]);
+ }
+ }
+ nssPKIObject_Unlock(object);
+ return instances;
+}
+
+NSS_IMPLEMENT void
+nssCertificateArray_Destroy(
+ NSSCertificate **certs)
+{
+ if (certs) {
+ NSSCertificate **certp;
+ for (certp = certs; *certp; certp++) {
+ if ((*certp)->decoding) {
+ CERTCertificate *cc = STAN_GetCERTCertificate(*certp);
+ if (cc) {
+ CERT_DestroyCertificate(cc);
+ }
+ continue;
+ }
+ nssCertificate_Destroy(*certp);
+ }
+ nss_ZFreeIf(certs);
+ }
+}
+
+NSS_IMPLEMENT void
+NSSCertificateArray_Destroy(
+ NSSCertificate **certs)
+{
+ nssCertificateArray_Destroy(certs);
+}
+
+NSS_IMPLEMENT NSSCertificate **
+nssCertificateArray_Join(
+ NSSCertificate **certs1,
+ NSSCertificate **certs2)
+{
+ if (certs1 && certs2) {
+ NSSCertificate **certs, **cp;
+ PRUint32 count = 0;
+ PRUint32 count1 = 0;
+ cp = certs1;
+ while (*cp++)
+ count1++;
+ count = count1;
+ cp = certs2;
+ while (*cp++)
+ count++;
+ certs = nss_ZREALLOCARRAY(certs1, NSSCertificate *, count + 1);
+ if (!certs) {
+ nss_ZFreeIf(certs1);
+ nss_ZFreeIf(certs2);
+ return (NSSCertificate **)NULL;
+ }
+ for (cp = certs2; *cp; cp++, count1++) {
+ certs[count1] = *cp;
+ }
+ nss_ZFreeIf(certs2);
+ return certs;
+ } else if (certs1) {
+ return certs1;
+ } else {
+ return certs2;
+ }
+}
+
+NSS_IMPLEMENT NSSCertificate *
+nssCertificateArray_FindBestCertificate(
+ NSSCertificate **certs,
+ NSSTime *timeOpt,
+ const NSSUsage *usage,
+ NSSPolicies *policiesOpt)
+{
+ NSSCertificate *bestCert = NULL;
+ nssDecodedCert *bestdc = NULL;
+ NSSTime *time, sTime;
+ PRBool bestCertMatches = PR_FALSE;
+ PRBool thisCertMatches;
+ PRBool bestCertIsValidAtTime = PR_FALSE;
+ PRBool bestCertIsTrusted = PR_FALSE;
+
+ if (timeOpt) {
+ time = timeOpt;
+ } else {
+ NSSTime_Now(&sTime);
+ time = &sTime;
+ }
+ if (!certs) {
+ return (NSSCertificate *)NULL;
+ }
+ for (; *certs; certs++) {
+ nssDecodedCert *dc;
+ NSSCertificate *c = *certs;
+ dc = nssCertificate_GetDecoding(c);
+ if (!dc)
+ continue;
+ thisCertMatches = dc->matchUsage(dc, usage);
+ if (!bestCert) {
+ /* always take the first cert, but remember whether or not
+ * the usage matched
+ */
+ bestCert = nssCertificate_AddRef(c);
+ bestCertMatches = thisCertMatches;
+ bestdc = dc;
+ continue;
+ } else {
+ if (bestCertMatches && !thisCertMatches) {
+ /* if already have a cert for this usage, and if this cert
+ * doesn't have the correct usage, continue
+ */
+ continue;
+ } else if (!bestCertMatches && thisCertMatches) {
+ /* this one does match usage, replace the other */
+ nssCertificate_Destroy(bestCert);
+ bestCert = nssCertificate_AddRef(c);
+ bestCertMatches = thisCertMatches;
+ bestdc = dc;
+ continue;
+ }
+ /* this cert match as well as any cert we've found so far,
+ * defer to time/policies
+ * */
+ }
+ /* time */
+ if (bestCertIsValidAtTime || bestdc->isValidAtTime(bestdc, time)) {
+ /* The current best cert is valid at time */
+ bestCertIsValidAtTime = PR_TRUE;
+ if (!dc->isValidAtTime(dc, time)) {
+ /* If the new cert isn't valid at time, it's not better */
+ continue;
+ }
+ } else {
+ /* The current best cert is not valid at time */
+ if (dc->isValidAtTime(dc, time)) {
+ /* If the new cert is valid at time, it's better */
+ nssCertificate_Destroy(bestCert);
+ bestCert = nssCertificate_AddRef(c);
+ bestdc = dc;
+ bestCertIsValidAtTime = PR_TRUE;
+ continue;
+ }
+ }
+ /* Either they are both valid at time, or neither valid.
+ * If only one is trusted for this usage, take it.
+ */
+ if (bestCertIsTrusted || bestdc->isTrustedForUsage(bestdc, usage)) {
+ bestCertIsTrusted = PR_TRUE;
+ if (!dc->isTrustedForUsage(dc, usage)) {
+ continue;
+ }
+ } else {
+ /* The current best cert is not trusted */
+ if (dc->isTrustedForUsage(dc, usage)) {
+ /* If the new cert is trusted, it's better */
+ nssCertificate_Destroy(bestCert);
+ bestCert = nssCertificate_AddRef(c);
+ bestdc = dc;
+ bestCertIsTrusted = PR_TRUE;
+ continue;
+ }
+ }
+ /* Otherwise, take the newer one. */
+ if (!bestdc->isNewerThan(bestdc, dc)) {
+ nssCertificate_Destroy(bestCert);
+ bestCert = nssCertificate_AddRef(c);
+ bestdc = dc;
+ continue;
+ }
+ /* policies */
+ /* XXX later -- defer to policies */
+ }
+ return bestCert;
+}
+
+NSS_IMPLEMENT PRStatus
+nssCertificateArray_Traverse(
+ NSSCertificate **certs,
+ PRStatus (*callback)(NSSCertificate *c, void *arg),
+ void *arg)
+{
+ PRStatus status = PR_SUCCESS;
+ if (certs) {
+ NSSCertificate **certp;
+ for (certp = certs; *certp; certp++) {
+ status = (*callback)(*certp, arg);
+ if (status != PR_SUCCESS) {
+ break;
+ }
+ }
+ }
+ return status;
+}
+
+NSS_IMPLEMENT void
+nssCRLArray_Destroy(
+ NSSCRL **crls)
+{
+ if (crls) {
+ NSSCRL **crlp;
+ for (crlp = crls; *crlp; crlp++) {
+ nssCRL_Destroy(*crlp);
+ }
+ nss_ZFreeIf(crls);
+ }
+}
+
+/*
+ * Object collections
+ */
+
+typedef enum {
+ pkiObjectType_Certificate = 0,
+ pkiObjectType_CRL = 1,
+ pkiObjectType_PrivateKey = 2,
+ pkiObjectType_PublicKey = 3
+} pkiObjectType;
+
+/* Each object is defined by a set of items that uniquely identify it.
+ * Here are the uid sets:
+ *
+ * NSSCertificate ==> { issuer, serial }
+ * NSSPrivateKey
+ * (RSA) ==> { modulus, public exponent }
+ *
+ */
+#define MAX_ITEMS_FOR_UID 2
+
+/* pkiObjectCollectionNode
+ *
+ * A node in the collection is the set of unique identifiers for a single
+ * object, along with either the actual object or a proto-object.
+ */
+typedef struct
+{
+ PRCList link;
+ PRBool haveObject;
+ nssPKIObject *object;
+ NSSItem uid[MAX_ITEMS_FOR_UID];
+} pkiObjectCollectionNode;
+
+/* nssPKIObjectCollection
+ *
+ * The collection is the set of all objects, plus the interfaces needed
+ * to manage the objects.
+ *
+ */
+struct nssPKIObjectCollectionStr {
+ NSSArena *arena;
+ NSSTrustDomain *td;
+ NSSCryptoContext *cc;
+ PRCList head; /* list of pkiObjectCollectionNode's */
+ PRUint32 size;
+ pkiObjectType objectType;
+ void (*destroyObject)(nssPKIObject *o);
+ PRStatus (*getUIDFromObject)(nssPKIObject *o, NSSItem *uid);
+ PRStatus (*getUIDFromInstance)(nssCryptokiObject *co, NSSItem *uid,
+ NSSArena *arena);
+ nssPKIObject *(*createObject)(nssPKIObject *o);
+ nssPKILockType lockType; /* type of lock to use for new proto-objects */
+};
+
+static nssPKIObjectCollection *
+nssPKIObjectCollection_Create(
+ NSSTrustDomain *td,
+ NSSCryptoContext *ccOpt,
+ nssPKILockType lockType)
+{
+ NSSArena *arena;
+ nssPKIObjectCollection *rvCollection = NULL;
+ arena = nssArena_Create();
+ if (!arena) {
+ return (nssPKIObjectCollection *)NULL;
+ }
+ rvCollection = nss_ZNEW(arena, nssPKIObjectCollection);
+ if (!rvCollection) {
+ goto loser;
+ }
+ PR_INIT_CLIST(&rvCollection->head);
+ rvCollection->arena = arena;
+ rvCollection->td = td; /* XXX */
+ rvCollection->cc = ccOpt;
+ rvCollection->lockType = lockType;
+ return rvCollection;
+loser:
+ nssArena_Destroy(arena);
+ return (nssPKIObjectCollection *)NULL;
+}
+
+NSS_IMPLEMENT void
+nssPKIObjectCollection_Destroy(
+ nssPKIObjectCollection *collection)
+{
+ if (collection) {
+ PRCList *link;
+ pkiObjectCollectionNode *node;
+ /* first destroy any objects in the collection */
+ link = PR_NEXT_LINK(&collection->head);
+ while (link != &collection->head) {
+ node = (pkiObjectCollectionNode *)link;
+ if (node->haveObject) {
+ (*collection->destroyObject)(node->object);
+ } else {
+ nssPKIObject_Destroy(node->object);
+ }
+ link = PR_NEXT_LINK(link);
+ }
+ /* then destroy it */
+ nssArena_Destroy(collection->arena);
+ }
+}
+
+NSS_IMPLEMENT PRUint32
+nssPKIObjectCollection_Count(
+ nssPKIObjectCollection *collection)
+{
+ return collection->size;
+}
+
+NSS_IMPLEMENT PRStatus
+nssPKIObjectCollection_AddObject(
+ nssPKIObjectCollection *collection,
+ nssPKIObject *object)
+{
+ pkiObjectCollectionNode *node;
+ node = nss_ZNEW(collection->arena, pkiObjectCollectionNode);
+ if (!node) {
+ return PR_FAILURE;
+ }
+ node->haveObject = PR_TRUE;
+ node->object = nssPKIObject_AddRef(object);
+ (*collection->getUIDFromObject)(object, node->uid);
+ PR_INIT_CLIST(&node->link);
+ PR_INSERT_BEFORE(&node->link, &collection->head);
+ collection->size++;
+ return PR_SUCCESS;
+}
+
+static pkiObjectCollectionNode *
+find_instance_in_collection(
+ nssPKIObjectCollection *collection,
+ nssCryptokiObject *instance)
+{
+ PRCList *link;
+ pkiObjectCollectionNode *node;
+ link = PR_NEXT_LINK(&collection->head);
+ while (link != &collection->head) {
+ node = (pkiObjectCollectionNode *)link;
+ if (nssPKIObject_HasInstance(node->object, instance)) {
+ return node;
+ }
+ link = PR_NEXT_LINK(link);
+ }
+ return (pkiObjectCollectionNode *)NULL;
+}
+
+static pkiObjectCollectionNode *
+find_object_in_collection(
+ nssPKIObjectCollection *collection,
+ NSSItem *uid)
+{
+ PRUint32 i;
+ PRStatus status;
+ PRCList *link;
+ pkiObjectCollectionNode *node;
+ link = PR_NEXT_LINK(&collection->head);
+ while (link != &collection->head) {
+ node = (pkiObjectCollectionNode *)link;
+ for (i = 0; i < MAX_ITEMS_FOR_UID; i++) {
+ if (!nssItem_Equal(&node->uid[i], &uid[i], &status)) {
+ break;
+ }
+ }
+ if (i == MAX_ITEMS_FOR_UID) {
+ return node;
+ }
+ link = PR_NEXT_LINK(link);
+ }
+ return (pkiObjectCollectionNode *)NULL;
+}
+
+static pkiObjectCollectionNode *
+add_object_instance(
+ nssPKIObjectCollection *collection,
+ nssCryptokiObject *instance,
+ PRBool *foundIt)
+{
+ PRUint32 i;
+ PRStatus status;
+ pkiObjectCollectionNode *node;
+ nssArenaMark *mark = NULL;
+ NSSItem uid[MAX_ITEMS_FOR_UID];
+ nsslibc_memset(uid, 0, sizeof uid);
+ /* The list is traversed twice, first (here) looking to match the
+ * { token, handle } tuple, and if that is not found, below a search
+ * for unique identifier is done. Here, a match means this exact object
+ * instance is already in the collection, and we have nothing to do.
+ */
+ *foundIt = PR_FALSE;
+ node = find_instance_in_collection(collection, instance);
+ if (node) {
+ /* The collection is assumed to take over the instance. Since we
+ * are not using it, it must be destroyed.
+ */
+ nssCryptokiObject_Destroy(instance);
+ *foundIt = PR_TRUE;
+ return node;
+ }
+ mark = nssArena_Mark(collection->arena);
+ if (!mark) {
+ goto loser;
+ }
+ status = (*collection->getUIDFromInstance)(instance, uid,
+ collection->arena);
+ if (status != PR_SUCCESS) {
+ goto loser;
+ }
+ /* Search for unique identifier. A match here means the object exists
+ * in the collection, but does not have this instance, so the instance
+ * needs to be added.
+ */
+ node = find_object_in_collection(collection, uid);
+ if (node) {
+ /* This is an object with multiple instances */
+ status = nssPKIObject_AddInstance(node->object, instance);
+ } else {
+ /* This is a completely new object. Create a node for it. */
+ node = nss_ZNEW(collection->arena, pkiObjectCollectionNode);
+ if (!node) {
+ goto loser;
+ }
+ node->object = nssPKIObject_Create(NULL, instance,
+ collection->td, collection->cc,
+ collection->lockType);
+ if (!node->object) {
+ goto loser;
+ }
+ for (i = 0; i < MAX_ITEMS_FOR_UID; i++) {
+ node->uid[i] = uid[i];
+ }
+ node->haveObject = PR_FALSE;
+ PR_INIT_CLIST(&node->link);
+ PR_INSERT_BEFORE(&node->link, &collection->head);
+ collection->size++;
+ status = PR_SUCCESS;
+ }
+ nssArena_Unmark(collection->arena, mark);
+ return node;
+loser:
+ if (mark) {
+ nssArena_Release(collection->arena, mark);
+ }
+ nssCryptokiObject_Destroy(instance);
+ return (pkiObjectCollectionNode *)NULL;
+}
+
+NSS_IMPLEMENT PRStatus
+nssPKIObjectCollection_AddInstances(
+ nssPKIObjectCollection *collection,
+ nssCryptokiObject **instances,
+ PRUint32 numInstances)
+{
+ PRStatus status = PR_SUCCESS;
+ PRUint32 i = 0;
+ PRBool foundIt;
+ pkiObjectCollectionNode *node;
+ if (instances) {
+ while ((!numInstances || i < numInstances) && *instances) {
+ if (status == PR_SUCCESS) {
+ node = add_object_instance(collection, *instances, &foundIt);
+ if (node == NULL) {
+ /* add_object_instance freed the current instance */
+ /* free the remaining instances */
+ status = PR_FAILURE;
+ }
+ } else {
+ nssCryptokiObject_Destroy(*instances);
+ }
+ instances++;
+ i++;
+ }
+ }
+ return status;
+}
+
+static void
+nssPKIObjectCollection_RemoveNode(
+ nssPKIObjectCollection *collection,
+ pkiObjectCollectionNode *node)
+{
+ PR_REMOVE_LINK(&node->link);
+ collection->size--;
+}
+
+static PRStatus
+nssPKIObjectCollection_GetObjects(
+ nssPKIObjectCollection *collection,
+ nssPKIObject **rvObjects,
+ PRUint32 rvSize)
+{
+ PRUint32 i = 0;
+ PRCList *link = PR_NEXT_LINK(&collection->head);
+ pkiObjectCollectionNode *node;
+ int error = 0;
+ while ((i < rvSize) && (link != &collection->head)) {
+ node = (pkiObjectCollectionNode *)link;
+ if (!node->haveObject) {
+ /* Convert the proto-object to an object */
+ node->object = (*collection->createObject)(node->object);
+ if (!node->object) {
+ link = PR_NEXT_LINK(link);
+ /*remove bogus object from list*/
+ nssPKIObjectCollection_RemoveNode(collection, node);
+ error++;
+ continue;
+ }
+ node->haveObject = PR_TRUE;
+ }
+ rvObjects[i++] = nssPKIObject_AddRef(node->object);
+ link = PR_NEXT_LINK(link);
+ }
+ if (!error && *rvObjects == NULL) {
+ nss_SetError(NSS_ERROR_NOT_FOUND);
+ }
+ return PR_SUCCESS;
+}
+
+NSS_IMPLEMENT PRStatus
+nssPKIObjectCollection_Traverse(
+ nssPKIObjectCollection *collection,
+ nssPKIObjectCallback *callback)
+{
+ PRCList *link = PR_NEXT_LINK(&collection->head);
+ pkiObjectCollectionNode *node;
+ while (link != &collection->head) {
+ node = (pkiObjectCollectionNode *)link;
+ if (!node->haveObject) {
+ node->object = (*collection->createObject)(node->object);
+ if (!node->object) {
+ link = PR_NEXT_LINK(link);
+ /*remove bogus object from list*/
+ nssPKIObjectCollection_RemoveNode(collection, node);
+ continue;
+ }
+ node->haveObject = PR_TRUE;
+ }
+ switch (collection->objectType) {
+ case pkiObjectType_Certificate:
+ (void)(*callback->func.cert)((NSSCertificate *)node->object,
+ callback->arg);
+ break;
+ case pkiObjectType_CRL:
+ (void)(*callback->func.crl)((NSSCRL *)node->object,
+ callback->arg);
+ break;
+ case pkiObjectType_PrivateKey:
+ (void)(*callback->func.pvkey)((NSSPrivateKey *)node->object,
+ callback->arg);
+ break;
+ case pkiObjectType_PublicKey:
+ (void)(*callback->func.pbkey)((NSSPublicKey *)node->object,
+ callback->arg);
+ break;
+ }
+ link = PR_NEXT_LINK(link);
+ }
+ return PR_SUCCESS;
+}
+
+NSS_IMPLEMENT PRStatus
+nssPKIObjectCollection_AddInstanceAsObject(
+ nssPKIObjectCollection *collection,
+ nssCryptokiObject *instance)
+{
+ pkiObjectCollectionNode *node;
+ PRBool foundIt;
+ node = add_object_instance(collection, instance, &foundIt);
+ if (node == NULL) {
+ return PR_FAILURE;
+ }
+ if (!node->haveObject) {
+ node->object = (*collection->createObject)(node->object);
+ if (!node->object) {
+ /*remove bogus object from list*/
+ nssPKIObjectCollection_RemoveNode(collection, node);
+ return PR_FAILURE;
+ }
+ node->haveObject = PR_TRUE;
+ } else if (!foundIt) {
+ /* The instance was added to a pre-existing node. This
+ * function is *only* being used for certificates, and having
+ * multiple instances of certs in 3.X requires updating the
+ * CERTCertificate.
+ * But only do it if it was a new instance!!! If the same instance
+ * is encountered, we set *foundIt to true. Detect that here and
+ * ignore it.
+ */
+ STAN_ForceCERTCertificateUpdate((NSSCertificate *)node->object);
+ }
+ return PR_SUCCESS;
+}
+
+/*
+ * Certificate collections
+ */
+
+static void
+cert_destroyObject(nssPKIObject *o)
+{
+ NSSCertificate *c = (NSSCertificate *)o;
+ if (c->decoding) {
+ CERTCertificate *cc = STAN_GetCERTCertificate(c);
+ if (cc) {
+ CERT_DestroyCertificate(cc);
+ return;
+ } /* else destroy it as NSSCertificate below */
+ }
+ nssCertificate_Destroy(c);
+}
+
+static PRStatus
+cert_getUIDFromObject(nssPKIObject *o, NSSItem *uid)
+{
+ NSSCertificate *c = (NSSCertificate *)o;
+ /* The builtins are still returning decoded serial numbers. Until
+ * this compatibility issue is resolved, use the full DER of the
+ * cert to uniquely identify it.
+ */
+ NSSDER *derCert;
+ derCert = nssCertificate_GetEncoding(c);
+ uid[0].data = NULL;
+ uid[0].size = 0;
+ uid[1].data = NULL;
+ uid[1].size = 0;
+ if (derCert != NULL) {
+ uid[0] = *derCert;
+ }
+ return PR_SUCCESS;
+}
+
+static PRStatus
+cert_getUIDFromInstance(nssCryptokiObject *instance, NSSItem *uid,
+ NSSArena *arena)
+{
+ /* The builtins are still returning decoded serial numbers. Until
+ * this compatibility issue is resolved, use the full DER of the
+ * cert to uniquely identify it.
+ */
+ uid[1].data = NULL;
+ uid[1].size = 0;
+ return nssCryptokiCertificate_GetAttributes(instance,
+ NULL, /* XXX sessionOpt */
+ arena, /* arena */
+ NULL, /* type */
+ NULL, /* id */
+ &uid[0], /* encoding */
+ NULL, /* issuer */
+ NULL, /* serial */
+ NULL); /* subject */
+}
+
+static nssPKIObject *
+cert_createObject(nssPKIObject *o)
+{
+ NSSCertificate *cert;
+ cert = nssCertificate_Create(o);
+ /* if (STAN_GetCERTCertificate(cert) == NULL) {
+ nssCertificate_Destroy(cert);
+ return (nssPKIObject *)NULL;
+ } */
+ /* In 3.4, have to maintain uniqueness of cert pointers by caching all
+ * certs. Cache the cert here, before returning. If it is already
+ * cached, take the cached entry.
+ */
+ {
+ NSSTrustDomain *td = o->trustDomain;
+ nssTrustDomain_AddCertsToCache(td, &cert, 1);
+ }
+ return (nssPKIObject *)cert;
+}
+
+NSS_IMPLEMENT nssPKIObjectCollection *
+nssCertificateCollection_Create(
+ NSSTrustDomain *td,
+ NSSCertificate **certsOpt)
+{
+ nssPKIObjectCollection *collection;
+ collection = nssPKIObjectCollection_Create(td, NULL, nssPKIMonitor);
+ if (!collection) {
+ return NULL;
+ }
+ collection->objectType = pkiObjectType_Certificate;
+ collection->destroyObject = cert_destroyObject;
+ collection->getUIDFromObject = cert_getUIDFromObject;
+ collection->getUIDFromInstance = cert_getUIDFromInstance;
+ collection->createObject = cert_createObject;
+ if (certsOpt) {
+ for (; *certsOpt; certsOpt++) {
+ nssPKIObject *object = (nssPKIObject *)(*certsOpt);
+ (void)nssPKIObjectCollection_AddObject(collection, object);
+ }
+ }
+ return collection;
+}
+
+NSS_IMPLEMENT NSSCertificate **
+nssPKIObjectCollection_GetCertificates(
+ nssPKIObjectCollection *collection,
+ NSSCertificate **rvOpt,
+ PRUint32 maximumOpt,
+ NSSArena *arenaOpt)
+{
+ PRStatus status;
+ PRUint32 rvSize;
+ PRBool allocated = PR_FALSE;
+ if (collection->size == 0) {
+ return (NSSCertificate **)NULL;
+ }
+ if (maximumOpt == 0) {
+ rvSize = collection->size;
+ } else {
+ rvSize = PR_MIN(collection->size, maximumOpt);
+ }
+ if (!rvOpt) {
+ rvOpt = nss_ZNEWARRAY(arenaOpt, NSSCertificate *, rvSize + 1);
+ if (!rvOpt) {
+ return (NSSCertificate **)NULL;
+ }
+ allocated = PR_TRUE;
+ }
+ status = nssPKIObjectCollection_GetObjects(collection,
+ (nssPKIObject **)rvOpt,
+ rvSize);
+ if (status != PR_SUCCESS) {
+ if (allocated) {
+ nss_ZFreeIf(rvOpt);
+ }
+ return (NSSCertificate **)NULL;
+ }
+ return rvOpt;
+}
+
+/*
+ * CRL/KRL collections
+ */
+
+static void
+crl_destroyObject(nssPKIObject *o)
+{
+ NSSCRL *crl = (NSSCRL *)o;
+ nssCRL_Destroy(crl);
+}
+
+static PRStatus
+crl_getUIDFromObject(nssPKIObject *o, NSSItem *uid)
+{
+ NSSCRL *crl = (NSSCRL *)o;
+ NSSDER *encoding;
+ encoding = nssCRL_GetEncoding(crl);
+ if (!encoding) {
+ nss_SetError(NSS_ERROR_INVALID_ARGUMENT);
+ return PR_FALSE;
+ }
+ uid[0] = *encoding;
+ uid[1].data = NULL;
+ uid[1].size = 0;
+ return PR_SUCCESS;
+}
+
+static PRStatus
+crl_getUIDFromInstance(nssCryptokiObject *instance, NSSItem *uid,
+ NSSArena *arena)
+{
+ return nssCryptokiCRL_GetAttributes(instance,
+ NULL, /* XXX sessionOpt */
+ arena, /* arena */
+ &uid[0], /* encoding */
+ NULL, /* subject */
+ NULL, /* class */
+ NULL, /* url */
+ NULL); /* isKRL */
+}
+
+static nssPKIObject *
+crl_createObject(nssPKIObject *o)
+{
+ return (nssPKIObject *)nssCRL_Create(o);
+}
+
+NSS_IMPLEMENT nssPKIObjectCollection *
+nssCRLCollection_Create(
+ NSSTrustDomain *td,
+ NSSCRL **crlsOpt)
+{
+ nssPKIObjectCollection *collection;
+ collection = nssPKIObjectCollection_Create(td, NULL, nssPKILock);
+ if (!collection) {
+ return NULL;
+ }
+ collection->objectType = pkiObjectType_CRL;
+ collection->destroyObject = crl_destroyObject;
+ collection->getUIDFromObject = crl_getUIDFromObject;
+ collection->getUIDFromInstance = crl_getUIDFromInstance;
+ collection->createObject = crl_createObject;
+ if (crlsOpt) {
+ for (; *crlsOpt; crlsOpt++) {
+ nssPKIObject *object = (nssPKIObject *)(*crlsOpt);
+ (void)nssPKIObjectCollection_AddObject(collection, object);
+ }
+ }
+ return collection;
+}
+
+NSS_IMPLEMENT NSSCRL **
+nssPKIObjectCollection_GetCRLs(
+ nssPKIObjectCollection *collection,
+ NSSCRL **rvOpt,
+ PRUint32 maximumOpt,
+ NSSArena *arenaOpt)
+{
+ PRStatus status;
+ PRUint32 rvSize;
+ PRBool allocated = PR_FALSE;
+ if (collection->size == 0) {
+ return (NSSCRL **)NULL;
+ }
+ if (maximumOpt == 0) {
+ rvSize = collection->size;
+ } else {
+ rvSize = PR_MIN(collection->size, maximumOpt);
+ }
+ if (!rvOpt) {
+ rvOpt = nss_ZNEWARRAY(arenaOpt, NSSCRL *, rvSize + 1);
+ if (!rvOpt) {
+ return (NSSCRL **)NULL;
+ }
+ allocated = PR_TRUE;
+ }
+ status = nssPKIObjectCollection_GetObjects(collection,
+ (nssPKIObject **)rvOpt,
+ rvSize);
+ if (status != PR_SUCCESS) {
+ if (allocated) {
+ nss_ZFreeIf(rvOpt);
+ }
+ return (NSSCRL **)NULL;
+ }
+ return rvOpt;
+}
+
+/* how bad would it be to have a static now sitting around, updated whenever
+ * this was called? would avoid repeated allocs...
+ */
+NSS_IMPLEMENT NSSTime *
+NSSTime_Now(NSSTime *timeOpt)
+{
+ return NSSTime_SetPRTime(timeOpt, PR_Now());
+}
+
+NSS_IMPLEMENT NSSTime *
+NSSTime_SetPRTime(
+ NSSTime *timeOpt,
+ PRTime prTime)
+{
+ NSSTime *rvTime;
+ rvTime = (timeOpt) ? timeOpt : nss_ZNEW(NULL, NSSTime);
+ if (rvTime) {
+ rvTime->prTime = prTime;
+ }
+ return rvTime;
+}
+
+NSS_IMPLEMENT PRTime
+NSSTime_GetPRTime(
+ NSSTime *time)
+{
+ return time->prTime;
+}