summaryrefslogtreecommitdiff
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.c1268
1 files changed, 1268 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..3cbfebb23
--- /dev/null
+++ b/security/nss/lib/pki/pkibase.c
@@ -0,0 +1,1268 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifdef DEBUG
+static const char CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$";
+#endif /* DEBUG */
+
+#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))
+ {
+ /* XXX should be copy? safe as long as caller has reference */
+ nickname = object->instances[i]->label;
+ 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;
+ NSSTime *time, sTime;
+ PRBool haveUsageMatch = PR_FALSE;
+ PRBool thisCertMatches;
+
+ if (timeOpt) {
+ time = timeOpt;
+ } else {
+ NSSTime_Now(&sTime);
+ time = &sTime;
+ }
+ if (!certs) {
+ return (NSSCertificate *)NULL;
+ }
+ for (; *certs; certs++) {
+ nssDecodedCert *dc, *bestdc;
+ 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);
+ haveUsageMatch = thisCertMatches;
+ continue;
+ } else {
+ if (haveUsageMatch && !thisCertMatches) {
+ /* if already have a cert for this usage, and if this cert
+ * doesn't have the correct usage, continue
+ */
+ continue;
+ } else if (!haveUsageMatch && thisCertMatches) {
+ /* this one does match usage, replace the other */
+ nssCertificate_Destroy(bestCert);
+ bestCert = nssCertificate_AddRef(c);
+ haveUsageMatch = PR_TRUE;
+ continue;
+ }
+ /* this cert match as well as any cert we've found so far,
+ * defer to time/policies
+ * */
+ }
+ bestdc = nssCertificate_GetDecoding(bestCert);
+ if (!bestdc) {
+ nssCertificate_Destroy(bestCert);
+ bestCert = nssCertificate_AddRef(c);
+ continue;
+ }
+ /* time */
+ if (bestdc->isValidAtTime(bestdc, time)) {
+ /* The current best cert is valid at time */
+ 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);
+ }
+ }
+ /* either they are both valid at time, or neither valid;
+ * take the newer one
+ */
+ if (!bestdc->isNewerThan(bestdc, dc)) {
+ nssCertificate_Destroy(bestCert);
+ bestCert = nssCertificate_AddRef(c);
+ }
+ /* 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
+)
+{
+ PRStatus status;
+ 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:
+ status = (*callback->func.cert)((NSSCertificate *)node->object,
+ callback->arg);
+ break;
+ case pkiObjectType_CRL:
+ status = (*callback->func.crl)((NSSCRL *)node->object,
+ callback->arg);
+ break;
+ case pkiObjectType_PrivateKey:
+ status = (*callback->func.pvkey)((NSSPrivateKey *)node->object,
+ callback->arg);
+ break;
+ case pkiObjectType_PublicKey:
+ status = (*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
+)
+{
+ PRStatus status;
+ nssPKIObjectCollection *collection;
+ collection = nssPKIObjectCollection_Create(td, NULL, nssPKIMonitor);
+ 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);
+ status = 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
+)
+{
+ PRStatus status;
+ nssPKIObjectCollection *collection;
+ collection = nssPKIObjectCollection_Create(td, NULL, nssPKILock);
+ 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);
+ status = 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;
+}
+