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