| /* 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; |
| } |