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