diff options
Diffstat (limited to '')
-rw-r--r-- | security/nss/lib/pki/pkibase.c | 1224 |
1 files changed, 1224 insertions, 0 deletions
diff --git a/security/nss/lib/pki/pkibase.c b/security/nss/lib/pki/pkibase.c new file mode 100644 index 0000000000..f58a262cf2 --- /dev/null +++ b/security/nss/lib/pki/pkibase.c @@ -0,0 +1,1224 @@ +/* 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) { + nssPKIObject *original = node->object; + node->object = (*collection->createObject)(node->object); + if (!node->object) { + /*remove bogus object from list*/ + nssPKIObject_Destroy(original); + 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; +} |