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