diff options
Diffstat (limited to 'security/nss/lib/dev/devutil.c')
-rw-r--r-- | security/nss/lib/dev/devutil.c | 1445 |
1 files changed, 1445 insertions, 0 deletions
diff --git a/security/nss/lib/dev/devutil.c b/security/nss/lib/dev/devutil.c new file mode 100644 index 000000000..99218f83d --- /dev/null +++ b/security/nss/lib/dev/devutil.c @@ -0,0 +1,1445 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#ifdef DEBUG +static const char CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$"; +#endif /* DEBUG */ + +#ifndef DEVM_H +#include "devm.h" +#endif /* DEVM_H */ + +#ifndef CKHELPER_H +#include "ckhelper.h" +#endif /* CKHELPER_H */ + +NSS_IMPLEMENT nssCryptokiObject * +nssCryptokiObject_Create +( + NSSToken *t, + nssSession *session, + CK_OBJECT_HANDLE h +) +{ + PRStatus status; + NSSSlot *slot; + nssCryptokiObject *object; + CK_BBOOL *isTokenObject; + CK_ATTRIBUTE cert_template[] = { + { CKA_TOKEN, NULL, 0 }, + { CKA_LABEL, NULL, 0 } + }; + slot = nssToken_GetSlot(t); + status = nssCKObject_GetAttributes(h, cert_template, 2, + NULL, session, slot); + nssSlot_Destroy(slot); + if (status != PR_SUCCESS) { + /* a failure here indicates a device error */ + return (nssCryptokiObject *)NULL; + } + object = nss_ZNEW(NULL, nssCryptokiObject); + if (!object) { + return (nssCryptokiObject *)NULL; + } + object->handle = h; + object->token = nssToken_AddRef(t); + isTokenObject = (CK_BBOOL *)cert_template[0].pValue; + object->isTokenObject = *isTokenObject; + nss_ZFreeIf(isTokenObject); + NSS_CK_ATTRIBUTE_TO_UTF8(&cert_template[1], object->label); + return object; +} + +NSS_IMPLEMENT void +nssCryptokiObject_Destroy +( + nssCryptokiObject *object +) +{ + if (object) { + nssToken_Destroy(object->token); + nss_ZFreeIf(object->label); + nss_ZFreeIf(object); + } +} + +NSS_IMPLEMENT nssCryptokiObject * +nssCryptokiObject_Clone +( + nssCryptokiObject *object +) +{ + nssCryptokiObject *rvObject; + rvObject = nss_ZNEW(NULL, nssCryptokiObject); + if (rvObject) { + rvObject->handle = object->handle; + rvObject->token = nssToken_AddRef(object->token); + rvObject->isTokenObject = object->isTokenObject; + if (object->label) { + rvObject->label = nssUTF8_Duplicate(object->label, NULL); + } + } + return rvObject; +} + +NSS_EXTERN PRBool +nssCryptokiObject_Equal +( + nssCryptokiObject *o1, + nssCryptokiObject *o2 +) +{ + return (o1->token == o2->token && o1->handle == o2->handle); +} + +NSS_IMPLEMENT PRUint32 +nssPKCS11String_Length(CK_CHAR *pkcs11Str, PRUint32 bufLen) +{ + PRInt32 i; + for (i = bufLen - 1; i>=0; ) { + if (pkcs11Str[i] != ' ' && pkcs11Str[i] != '\0') break; + --i; + } + return (PRUint32)(i + 1); +} + +/* + * Slot arrays + */ + +NSS_IMPLEMENT NSSSlot ** +nssSlotArray_Clone +( + NSSSlot **slots +) +{ + NSSSlot **rvSlots = NULL; + NSSSlot **sp = slots; + PRUint32 count = 0; + while (sp && *sp) count++; + if (count > 0) { + rvSlots = nss_ZNEWARRAY(NULL, NSSSlot *, count + 1); + if (rvSlots) { + sp = slots; + count = 0; + for (sp = slots; *sp; sp++) { + rvSlots[count++] = nssSlot_AddRef(*sp); + } + } + } + return rvSlots; +} + +#ifdef PURE_STAN_BUILD +NSS_IMPLEMENT void +nssModuleArray_Destroy +( + NSSModule **modules +) +{ + if (modules) { + NSSModule **mp; + for (mp = modules; *mp; mp++) { + nssModule_Destroy(*mp); + } + nss_ZFreeIf(modules); + } +} +#endif + +NSS_IMPLEMENT void +nssSlotArray_Destroy +( + NSSSlot **slots +) +{ + if (slots) { + NSSSlot **slotp; + for (slotp = slots; *slotp; slotp++) { + nssSlot_Destroy(*slotp); + } + nss_ZFreeIf(slots); + } +} + +NSS_IMPLEMENT void +NSSSlotArray_Destroy +( + NSSSlot **slots +) +{ + nssSlotArray_Destroy(slots); +} + +NSS_IMPLEMENT void +nssTokenArray_Destroy +( + NSSToken **tokens +) +{ + if (tokens) { + NSSToken **tokenp; + for (tokenp = tokens; *tokenp; tokenp++) { + nssToken_Destroy(*tokenp); + } + nss_ZFreeIf(tokens); + } +} + +NSS_IMPLEMENT void +NSSTokenArray_Destroy +( + NSSToken **tokens +) +{ + nssTokenArray_Destroy(tokens); +} + +NSS_IMPLEMENT void +nssCryptokiObjectArray_Destroy +( + nssCryptokiObject **objects +) +{ + if (objects) { + nssCryptokiObject **op; + for (op = objects; *op; op++) { + nssCryptokiObject_Destroy(*op); + } + nss_ZFreeIf(objects); + } +} + +#ifdef PURE_STAN_BUILD +/* + * Slot lists + */ + +struct nssSlotListNodeStr +{ + PRCList link; + NSSSlot *slot; + PRUint32 order; +}; + +/* XXX separate slots with non-present tokens? */ +struct nssSlotListStr +{ + NSSArena *arena; + PRBool i_allocated_arena; + PZLock *lock; + PRCList head; + PRUint32 count; +}; + +NSS_IMPLEMENT nssSlotList * +nssSlotList_Create +( + NSSArena *arenaOpt +) +{ + nssSlotList *rvList; + NSSArena *arena; + nssArenaMark *mark; + if (arenaOpt) { + arena = arenaOpt; + mark = nssArena_Mark(arena); + if (!mark) { + return (nssSlotList *)NULL; + } + } else { + arena = nssArena_Create(); + if (!arena) { + return (nssSlotList *)NULL; + } + } + rvList = nss_ZNEW(arena, nssSlotList); + if (!rvList) { + goto loser; + } + rvList->lock = PZ_NewLock(nssILockOther); /* XXX */ + if (!rvList->lock) { + goto loser; + } + PR_INIT_CLIST(&rvList->head); + rvList->arena = arena; + rvList->i_allocated_arena = (arenaOpt == NULL); + nssArena_Unmark(arena, mark); + return rvList; +loser: + if (arenaOpt) { + nssArena_Release(arena, mark); + } else { + nssArena_Destroy(arena); + } + return (nssSlotList *)NULL; +} + +NSS_IMPLEMENT void +nssSlotList_Destroy +( + nssSlotList *slotList +) +{ + PRCList *link; + struct nssSlotListNodeStr *node; + if (slotList) { + link = PR_NEXT_LINK(&slotList->head); + while (link != &slotList->head) { + node = (struct nssSlotListNodeStr *)link; + nssSlot_Destroy(node->slot); + link = PR_NEXT_LINK(link); + } + if (slotList->i_allocated_arena) { + nssArena_Destroy(slotList->arena); + } + } +} + +/* XXX should do allocs outside of lock */ +NSS_IMPLEMENT PRStatus +nssSlotList_Add +( + nssSlotList *slotList, + NSSSlot *slot, + PRUint32 order +) +{ + PRCList *link; + struct nssSlotListNodeStr *node; + PZ_Lock(slotList->lock); + link = PR_NEXT_LINK(&slotList->head); + while (link != &slotList->head) { + node = (struct nssSlotListNodeStr *)link; + if (order < node->order) { + break; + } + link = PR_NEXT_LINK(link); + } + node = nss_ZNEW(slotList->arena, struct nssSlotListNodeStr); + if (!node) { + return PR_FAILURE; + } + PR_INIT_CLIST(&node->link); + node->slot = nssSlot_AddRef(slot); + node->order = order; + PR_INSERT_AFTER(&node->link, link); + slotList->count++; + PZ_Unlock(slotList->lock); + return PR_SUCCESS; +} + +NSS_IMPLEMENT PRStatus +nssSlotList_AddModuleSlots +( + nssSlotList *slotList, + NSSModule *module, + PRUint32 order +) +{ + nssArenaMark *mark = NULL; + NSSSlot **sp, **slots = NULL; + PRCList *link; + struct nssSlotListNodeStr *node; + PZ_Lock(slotList->lock); + link = PR_NEXT_LINK(&slotList->head); + while (link != &slotList->head) { + node = (struct nssSlotListNodeStr *)link; + if (order < node->order) { + break; + } + link = PR_NEXT_LINK(link); + } + slots = nssModule_GetSlots(module); + if (!slots) { + PZ_Unlock(slotList->lock); + return PR_SUCCESS; + } + mark = nssArena_Mark(slotList->arena); + if (!mark) { + goto loser; + } + for (sp = slots; *sp; sp++) { + node = nss_ZNEW(slotList->arena, struct nssSlotListNodeStr); + if (!node) { + goto loser; + } + PR_INIT_CLIST(&node->link); + node->slot = *sp; /* have ref from nssModule_GetSlots */ + node->order = order; + PR_INSERT_AFTER(&node->link, link); + slotList->count++; + } + PZ_Unlock(slotList->lock); + nssArena_Unmark(slotList->arena, mark); + return PR_SUCCESS; +loser: + PZ_Unlock(slotList->lock); + if (mark) { + nssArena_Release(slotList->arena, mark); + } + if (slots) { + nssSlotArray_Destroy(slots); + } + return PR_FAILURE; +} + +NSS_IMPLEMENT NSSSlot ** +nssSlotList_GetSlots +( + nssSlotList *slotList +) +{ + PRUint32 i; + PRCList *link; + struct nssSlotListNodeStr *node; + NSSSlot **rvSlots = NULL; + PZ_Lock(slotList->lock); + rvSlots = nss_ZNEWARRAY(NULL, NSSSlot *, slotList->count + 1); + if (!rvSlots) { + PZ_Unlock(slotList->lock); + return (NSSSlot **)NULL; + } + i = 0; + link = PR_NEXT_LINK(&slotList->head); + while (link != &slotList->head) { + node = (struct nssSlotListNodeStr *)link; + rvSlots[i] = nssSlot_AddRef(node->slot); + link = PR_NEXT_LINK(link); + i++; + } + PZ_Unlock(slotList->lock); + return rvSlots; +} + +#if 0 +NSS_IMPLEMENT NSSSlot * +nssSlotList_GetBestSlotForAlgorithmAndParameters +( + nssSlotList *slotList, + NSSAlgorithmAndParameters *ap +) +{ + PRCList *link; + struct nssSlotListNodeStr *node; + NSSSlot *rvSlot = NULL; + PZ_Lock(slotList->lock); + link = PR_NEXT_LINK(&slotList->head); + while (link != &slotList->head) { + node = (struct nssSlotListNodeStr *)link; + if (nssSlot_DoesAlgorithmAndParameters(ap)) { + rvSlot = nssSlot_AddRef(node->slot); /* XXX check isPresent? */ + } + link = PR_NEXT_LINK(link); + } + PZ_Unlock(slotList->lock); + return rvSlot; +} +#endif + +NSS_IMPLEMENT NSSSlot * +nssSlotList_GetBestSlot +( + nssSlotList *slotList +) +{ + PRCList *link; + struct nssSlotListNodeStr *node; + NSSSlot *rvSlot = NULL; + PZ_Lock(slotList->lock); + if (PR_CLIST_IS_EMPTY(&slotList->head)) { + PZ_Unlock(slotList->lock); + return (NSSSlot *)NULL; + } + link = PR_NEXT_LINK(&slotList->head); + node = (struct nssSlotListNodeStr *)link; + rvSlot = nssSlot_AddRef(node->slot); /* XXX check isPresent? */ + PZ_Unlock(slotList->lock); + return rvSlot; +} + +NSS_IMPLEMENT NSSSlot * +nssSlotList_FindSlotByName +( + nssSlotList *slotList, + NSSUTF8 *slotName +) +{ + PRCList *link; + struct nssSlotListNodeStr *node; + NSSSlot *rvSlot = NULL; + PZ_Lock(slotList->lock); + link = PR_NEXT_LINK(&slotList->head); + while (link != &slotList->head) { + NSSUTF8 *sName; + node = (struct nssSlotListNodeStr *)link; + sName = nssSlot_GetName(node->slot); + if (nssUTF8_Equal(sName, slotName, NULL)) { + rvSlot = nssSlot_AddRef(node->slot); + break; + } + link = PR_NEXT_LINK(link); + } + PZ_Unlock(slotList->lock); + return rvSlot; +} + +NSS_IMPLEMENT NSSToken * +nssSlotList_FindTokenByName +( + nssSlotList *slotList, + NSSUTF8 *tokenName +) +{ + PRCList *link; + struct nssSlotListNodeStr *node; + NSSToken *rvToken = NULL; + PZ_Lock(slotList->lock); + link = PR_NEXT_LINK(&slotList->head); + while (link != &slotList->head) { + NSSUTF8 *tName; + node = (struct nssSlotListNodeStr *)link; + tName = nssSlot_GetTokenName(node->slot); + if (nssUTF8_Equal(tName, tokenName, NULL)) { + rvToken = nssSlot_GetToken(node->slot); + break; + } + link = PR_NEXT_LINK(link); + } + PZ_Unlock(slotList->lock); + return rvToken; +} +#endif /* PURE_STAN_BUILD */ + +/* object cache for token */ + +typedef struct +{ + NSSArena *arena; + nssCryptokiObject *object; + CK_ATTRIBUTE_PTR attributes; + CK_ULONG numAttributes; +} +nssCryptokiObjectAndAttributes; + +enum { + cachedCerts = 0, + cachedTrust = 1, + cachedCRLs = 2 +} cachedObjectType; + +struct nssTokenObjectCacheStr +{ + NSSToken *token; + PZLock *lock; + PRBool loggedIn; + PRBool doObjectType[3]; + PRBool searchedObjectType[3]; + nssCryptokiObjectAndAttributes **objects[3]; +}; + +NSS_IMPLEMENT nssTokenObjectCache * +nssTokenObjectCache_Create +( + NSSToken *token, + PRBool cacheCerts, + PRBool cacheTrust, + PRBool cacheCRLs +) +{ + nssTokenObjectCache *rvCache; + rvCache = nss_ZNEW(NULL, nssTokenObjectCache); + if (!rvCache) { + goto loser; + } + rvCache->lock = PZ_NewLock(nssILockOther); /* XXX */ + if (!rvCache->lock) { + goto loser; + } + rvCache->doObjectType[cachedCerts] = cacheCerts; + rvCache->doObjectType[cachedTrust] = cacheTrust; + rvCache->doObjectType[cachedCRLs] = cacheCRLs; + rvCache->token = token; /* cache goes away with token */ + return rvCache; +loser: + return (nssTokenObjectCache *)NULL; +} + +static void +clear_cache +( + nssTokenObjectCache *cache +) +{ + nssCryptokiObjectAndAttributes **oa; + PRUint32 objectType; + for (objectType = cachedCerts; objectType <= cachedCRLs; objectType++) { + if (!cache->objects[objectType]) { + continue; + } + for (oa = cache->objects[objectType]; *oa; oa++) { + /* prevent the token from being destroyed */ + (*oa)->object->token = NULL; + nssCryptokiObject_Destroy((*oa)->object); + nssArena_Destroy((*oa)->arena); + } + nss_ZFreeIf(cache->objects[objectType]); + cache->objects[objectType] = NULL; + cache->searchedObjectType[objectType] = PR_FALSE; + } +} + +NSS_IMPLEMENT void +nssTokenObjectCache_Clear +( + nssTokenObjectCache *cache +) +{ + if (cache) { + clear_cache(cache); + } +} + +NSS_IMPLEMENT void +nssTokenObjectCache_Destroy +( + nssTokenObjectCache *cache +) +{ + if (cache) { + clear_cache(cache); + PZ_DestroyLock(cache->lock); + nss_ZFreeIf(cache); + } +} + +NSS_IMPLEMENT PRBool +nssTokenObjectCache_HaveObjectClass +( + nssTokenObjectCache *cache, + CK_OBJECT_CLASS objclass +) +{ + PRBool haveIt; + PZ_Lock(cache->lock); + switch (objclass) { + case CKO_CERTIFICATE: haveIt = cache->doObjectType[cachedCerts]; break; + case CKO_NETSCAPE_TRUST: haveIt = cache->doObjectType[cachedTrust]; break; + case CKO_NETSCAPE_CRL: haveIt = cache->doObjectType[cachedCRLs]; break; + default: haveIt = PR_FALSE; + } + PZ_Unlock(cache->lock); + return haveIt; +} + +static nssCryptokiObjectAndAttributes ** +create_object_array +( + nssCryptokiObject **objects, + PRBool *doObjects, + PRUint32 *numObjects, + PRStatus *status +) +{ + nssCryptokiObject **op = objects; + nssCryptokiObjectAndAttributes **rvOandA = NULL; + *numObjects = 0; + /* There are no objects for this type */ + if (!objects) { + return (nssCryptokiObjectAndAttributes **)NULL; + } + while (*op++) (*numObjects)++; + if (*numObjects == MAX_LOCAL_CACHE_OBJECTS) { + /* Hit the maximum allowed, so don't use a cache (there are + * too many objects to make caching worthwhile, presumably, if + * the token can handle that many objects, it can handle searching. + */ + *doObjects = PR_FALSE; + *status = PR_FAILURE; + *numObjects = 0; + } else if (*numObjects > 0) { + rvOandA = nss_ZNEWARRAY(NULL, + nssCryptokiObjectAndAttributes *, + *numObjects + 1); + *status = rvOandA ? PR_SUCCESS : PR_FALSE; + } + return rvOandA; +} + +static nssCryptokiObjectAndAttributes * +create_object +( + nssCryptokiObject *object, + CK_ATTRIBUTE_TYPE *types, + PRUint32 numTypes, + PRStatus *status +) +{ + PRUint32 j; + NSSArena *arena; + NSSSlot *slot = NULL; + nssSession *session = NULL; + nssCryptokiObjectAndAttributes *rvCachedObject = NULL; + + slot = nssToken_GetSlot(object->token); + session = nssToken_GetDefaultSession(object->token); + + arena = nssArena_Create(); + if (!arena) { + nssSlot_Destroy(slot); + return (nssCryptokiObjectAndAttributes *)NULL; + } + rvCachedObject = nss_ZNEW(arena, nssCryptokiObjectAndAttributes); + if (!rvCachedObject) { + goto loser; + } + rvCachedObject->arena = arena; + /* The cache is tied to the token, and therefore the objects + * in it should not hold references to the token. + */ + nssToken_Destroy(object->token); + rvCachedObject->object = object; + rvCachedObject->attributes = nss_ZNEWARRAY(arena, CK_ATTRIBUTE, numTypes); + if (!rvCachedObject->attributes) { + goto loser; + } + for (j=0; j<numTypes; j++) { + rvCachedObject->attributes[j].type = types[j]; + } + *status = nssCKObject_GetAttributes(object->handle, + rvCachedObject->attributes, + numTypes, + arena, + session, + slot); + if (*status != PR_SUCCESS) { + goto loser; + } + rvCachedObject->numAttributes = numTypes; + *status = PR_SUCCESS; + if (slot) { + nssSlot_Destroy(slot); + } + return rvCachedObject; +loser: + *status = PR_FAILURE; + if (slot) { + nssSlot_Destroy(slot); + } + nssArena_Destroy(arena); + return (nssCryptokiObjectAndAttributes *)NULL; +} + +/* + * + * State diagram for cache: + * + * token !present token removed + * +-------------------------+<----------------------+ + * | ^ | + * v | | + * +----------+ slot friendly | token present +----------+ + * | cache | -----------------> % ---------------> | cache | + * | unloaded | | loaded | + * +----------+ +----------+ + * ^ | ^ | + * | | slot !friendly slot logged in | | + * | +-----------------------> % ----------------------+ | + * | | | + * | slot logged out v slot !friendly | + * +-----------------------------+<--------------------------+ + * + */ +static PRBool +search_for_objects +( + nssTokenObjectCache *cache +) +{ + PRBool doSearch = PR_FALSE; + NSSSlot *slot = nssToken_GetSlot(cache->token); + if (!nssSlot_IsTokenPresent(slot)) { + /* The token is no longer present, destroy any cached objects */ + /* clear_cache(cache); */ + nssSlot_Destroy(slot); + return PR_FALSE; + } + /* Handle non-friendly slots (slots which require login for objects) */ + if (!nssSlot_IsFriendly(slot)) { + if (nssSlot_IsLoggedIn(slot)) { + /* Either no state change, or went from !logged in -> logged in */ + cache->loggedIn = PR_TRUE; + doSearch = PR_TRUE; + } else { + if (cache->loggedIn) { + /* went from logged in -> !logged in, destroy cached objects */ + clear_cache(cache); + cache->loggedIn = PR_FALSE; + } /* else no state change, still not logged in, so exit */ + } + } else { + /* slot is friendly, thus always available for search */ + doSearch = PR_TRUE; + } + nssSlot_Destroy(slot); + return doSearch; +} + +static nssCryptokiObjectAndAttributes * +create_cert +( + nssCryptokiObject *object, + PRStatus *status +) +{ + CK_ATTRIBUTE_TYPE certAttr[] = { + CKA_CLASS, + CKA_TOKEN, + CKA_LABEL, + CKA_CERTIFICATE_TYPE, + CKA_ID, + CKA_VALUE, + CKA_ISSUER, + CKA_SERIAL_NUMBER, + CKA_SUBJECT, + CKA_NETSCAPE_EMAIL + }; + PRUint32 numCertAttr = sizeof(certAttr) / sizeof(certAttr[0]); + return create_object(object, certAttr, numCertAttr, status); +} + +static PRStatus +get_token_certs_for_cache +( + nssTokenObjectCache *cache +) +{ + PRStatus status; + nssCryptokiObject **objects; + PRBool *doIt = &cache->doObjectType[cachedCerts]; + PRUint32 i, numObjects; + + if (!search_for_objects(cache) || + cache->searchedObjectType[cachedCerts] || + !cache->doObjectType[cachedCerts]) + { + /* Either there was a state change that prevents a search + * (token removed or logged out), or the search was already done, + * or certs are not being cached. + */ + return PR_SUCCESS; + } + objects = nssToken_FindCertificates(cache->token, NULL, + nssTokenSearchType_TokenForced, + MAX_LOCAL_CACHE_OBJECTS, &status); + if (status != PR_SUCCESS) { + return status; + } + cache->objects[cachedCerts] = create_object_array(objects, + doIt, + &numObjects, + &status); + if (status != PR_SUCCESS) { + return status; + } + for (i=0; i<numObjects; i++) { + cache->objects[cachedCerts][i] = create_cert(objects[i], &status); + if (status != PR_SUCCESS) { + break; + } + } + if (status == PR_SUCCESS) { + nss_ZFreeIf(objects); + } else { + PRUint32 j; + for (j=0; j<i; j++) { + /* sigh */ + nssToken_AddRef(cache->objects[cachedCerts][i]->object->token); + nssArena_Destroy(cache->objects[cachedCerts][i]->arena); + } + nssCryptokiObjectArray_Destroy(objects); + } + cache->searchedObjectType[cachedCerts] = PR_TRUE; + return status; +} + +static nssCryptokiObjectAndAttributes * +create_trust +( + nssCryptokiObject *object, + PRStatus *status +) +{ + CK_ATTRIBUTE_TYPE trustAttr[] = { + CKA_CLASS, + CKA_TOKEN, + CKA_LABEL, + CKA_CERT_SHA1_HASH, + CKA_CERT_MD5_HASH, + CKA_ISSUER, + CKA_SUBJECT, + CKA_TRUST_SERVER_AUTH, + CKA_TRUST_CLIENT_AUTH, + CKA_TRUST_EMAIL_PROTECTION, + CKA_TRUST_CODE_SIGNING + }; + PRUint32 numTrustAttr = sizeof(trustAttr) / sizeof(trustAttr[0]); + return create_object(object, trustAttr, numTrustAttr, status); +} + +static PRStatus +get_token_trust_for_cache +( + nssTokenObjectCache *cache +) +{ + PRStatus status; + nssCryptokiObject **objects; + PRBool *doIt = &cache->doObjectType[cachedTrust]; + PRUint32 i, numObjects; + + if (!search_for_objects(cache) || + cache->searchedObjectType[cachedTrust] || + !cache->doObjectType[cachedTrust]) + { + /* Either there was a state change that prevents a search + * (token removed or logged out), or the search was already done, + * or trust is not being cached. + */ + return PR_SUCCESS; + } + objects = nssToken_FindTrustObjects(cache->token, NULL, + nssTokenSearchType_TokenForced, + MAX_LOCAL_CACHE_OBJECTS, &status); + if (status != PR_SUCCESS) { + return status; + } + cache->objects[cachedTrust] = create_object_array(objects, + doIt, + &numObjects, + &status); + if (status != PR_SUCCESS) { + return status; + } + for (i=0; i<numObjects; i++) { + cache->objects[cachedTrust][i] = create_trust(objects[i], &status); + if (status != PR_SUCCESS) { + break; + } + } + if (status == PR_SUCCESS) { + nss_ZFreeIf(objects); + } else { + PRUint32 j; + for (j=0; j<i; j++) { + /* sigh */ + nssToken_AddRef(cache->objects[cachedTrust][i]->object->token); + nssArena_Destroy(cache->objects[cachedTrust][i]->arena); + } + nssCryptokiObjectArray_Destroy(objects); + } + cache->searchedObjectType[cachedTrust] = PR_TRUE; + return status; +} + +static nssCryptokiObjectAndAttributes * +create_crl +( + nssCryptokiObject *object, + PRStatus *status +) +{ + CK_ATTRIBUTE_TYPE crlAttr[] = { + CKA_CLASS, + CKA_TOKEN, + CKA_LABEL, + CKA_VALUE, + CKA_SUBJECT, + CKA_NETSCAPE_KRL, + CKA_NETSCAPE_URL + }; + PRUint32 numCRLAttr = sizeof(crlAttr) / sizeof(crlAttr[0]); + return create_object(object, crlAttr, numCRLAttr, status); +} + +static PRStatus +get_token_crls_for_cache +( + nssTokenObjectCache *cache +) +{ + PRStatus status; + nssCryptokiObject **objects; + PRBool *doIt = &cache->doObjectType[cachedCRLs]; + PRUint32 i, numObjects; + + if (!search_for_objects(cache) || + cache->searchedObjectType[cachedCRLs] || + !cache->doObjectType[cachedCRLs]) + { + /* Either there was a state change that prevents a search + * (token removed or logged out), or the search was already done, + * or CRLs are not being cached. + */ + return PR_SUCCESS; + } + objects = nssToken_FindCRLs(cache->token, NULL, + nssTokenSearchType_TokenForced, + MAX_LOCAL_CACHE_OBJECTS, &status); + if (status != PR_SUCCESS) { + return status; + } + cache->objects[cachedCRLs] = create_object_array(objects, + doIt, + &numObjects, + &status); + if (status != PR_SUCCESS) { + return status; + } + for (i=0; i<numObjects; i++) { + cache->objects[cachedCRLs][i] = create_crl(objects[i], &status); + if (status != PR_SUCCESS) { + break; + } + } + if (status == PR_SUCCESS) { + nss_ZFreeIf(objects); + } else { + PRUint32 j; + for (j=0; j<i; j++) { + /* sigh */ + nssToken_AddRef(cache->objects[cachedCRLs][i]->object->token); + nssArena_Destroy(cache->objects[cachedCRLs][i]->arena); + } + nssCryptokiObjectArray_Destroy(objects); + } + cache->searchedObjectType[cachedCRLs] = PR_TRUE; + return status; +} + +static nssCryptokiObject ** +find_objects_in_array +( + nssCryptokiObjectAndAttributes **objArray, + CK_ATTRIBUTE_PTR ot, + CK_ULONG otlen, + PRUint32 maximumOpt +) +{ + PRIntn oi; + PRUint32 i, j; + PRBool match; + NSSArena *arena; + PRUint32 size = 8; + PRUint32 numMatches = 0; + nssCryptokiObject **objects = NULL; + nssCryptokiObjectAndAttributes **matches = NULL; + if (!objArray) { + return (nssCryptokiObject **)NULL; + } + arena = nssArena_Create(); + if (!arena) { + return (nssCryptokiObject **)NULL; + } + matches = nss_ZNEWARRAY(arena, nssCryptokiObjectAndAttributes *, size); + if (!matches) { + goto loser; + } + if (maximumOpt == 0) maximumOpt = ~0; + for (; *objArray && numMatches < maximumOpt; objArray++) { + nssCryptokiObjectAndAttributes *obj = *objArray; + for (i=0; i<otlen; i++) { + for (j=0; j<obj->numAttributes; j++) { + if (ot[i].type == obj->attributes[j].type) { + if (ot[i].ulValueLen == obj->attributes[j].ulValueLen && + nsslibc_memequal(ot[i].pValue, + obj->attributes[j].pValue, + ot[i].ulValueLen, NULL)) + { + match = PR_TRUE; + } else { + match = PR_FALSE; + } + break; + } + } + if (j == obj->numAttributes || !match) { + break; + } + } + if (match) { + matches[numMatches++] = obj; + if (numMatches == size) { + size *= 2; + matches = nss_ZREALLOCARRAY(matches, + nssCryptokiObjectAndAttributes *, + size); + if (!matches) { + goto loser; + } + } + } + } + if (numMatches > 0) { + objects = nss_ZNEWARRAY(NULL, nssCryptokiObject *, numMatches + 1); + if (!objects) { + goto loser; + } + for (oi=0; oi<(PRIntn)numMatches; oi++) { + objects[oi] = nssCryptokiObject_Clone(matches[oi]->object); + if (!objects[oi]) { + goto loser; + } + } + } + nssArena_Destroy(arena); + return objects; +loser: + if (objects) { + for (--oi; oi>=0; --oi) { + nssCryptokiObject_Destroy(objects[oi]); + } + } + nssArena_Destroy(arena); + return (nssCryptokiObject **)NULL; +} + +NSS_IMPLEMENT nssCryptokiObject ** +nssTokenObjectCache_FindObjectsByTemplate +( + nssTokenObjectCache *cache, + CK_OBJECT_CLASS objclass, + CK_ATTRIBUTE_PTR otemplate, + CK_ULONG otlen, + PRUint32 maximumOpt, + PRStatus *statusOpt +) +{ + PRStatus status = PR_FAILURE; + nssCryptokiObject **rvObjects = NULL; + PZ_Lock(cache->lock); + switch (objclass) { + case CKO_CERTIFICATE: + if (cache->doObjectType[cachedCerts]) { + status = get_token_certs_for_cache(cache); + if (status != PR_SUCCESS) { + goto finish; + } + rvObjects = find_objects_in_array(cache->objects[cachedCerts], + otemplate, otlen, maximumOpt); + } + break; + case CKO_NETSCAPE_TRUST: + if (cache->doObjectType[cachedTrust]) { + status = get_token_trust_for_cache(cache); + if (status != PR_SUCCESS) { + goto finish; + } + rvObjects = find_objects_in_array(cache->objects[cachedTrust], + otemplate, otlen, maximumOpt); + } + break; + case CKO_NETSCAPE_CRL: + if (cache->doObjectType[cachedCRLs]) { + status = get_token_crls_for_cache(cache); + if (status != PR_SUCCESS) { + goto finish; + } + rvObjects = find_objects_in_array(cache->objects[cachedCRLs], + otemplate, otlen, maximumOpt); + } + break; + default: break; + } +finish: + PZ_Unlock(cache->lock); + if (statusOpt) { + *statusOpt = status; + } + return rvObjects; +} + +static PRBool +cache_available_for_object_type +( + nssTokenObjectCache *cache, + PRUint32 objectType +) +{ + if (!cache->doObjectType[objectType]) { + /* not caching this object kind */ + return PR_FALSE; + } + if (!cache->searchedObjectType[objectType]) { + /* objects are not cached yet */ + return PR_FALSE; + } + if (!search_for_objects(cache)) { + /* not logged in or removed */ + return PR_FALSE; + } + return PR_TRUE; +} + +NSS_IMPLEMENT PRStatus +nssTokenObjectCache_GetObjectAttributes +( + nssTokenObjectCache *cache, + NSSArena *arenaOpt, + nssCryptokiObject *object, + CK_OBJECT_CLASS objclass, + CK_ATTRIBUTE_PTR atemplate, + CK_ULONG atlen +) +{ + PRUint32 i, j; + NSSArena *arena = NULL; + nssArenaMark *mark = NULL; + nssCryptokiObjectAndAttributes *cachedOA = NULL; + nssCryptokiObjectAndAttributes **oa = NULL; + PRUint32 objectType; + PZ_Lock(cache->lock); + switch (objclass) { + case CKO_CERTIFICATE: objectType = cachedCerts; break; + case CKO_NETSCAPE_TRUST: objectType = cachedTrust; break; + case CKO_NETSCAPE_CRL: objectType = cachedCRLs; break; + default: goto loser; + } + if (!cache_available_for_object_type(cache, objectType)) { + goto loser; + } + oa = cache->objects[objectType]; + if (!oa) { + goto loser; + } + for (; *oa; oa++) { + if (nssCryptokiObject_Equal((*oa)->object, object)) { + cachedOA = *oa; + break; + } + } + if (!cachedOA) { + goto loser; /* don't have this object */ + } + if (arenaOpt) { + arena = arenaOpt; + mark = nssArena_Mark(arena); + } + for (i=0; i<atlen; i++) { + for (j=0; j<cachedOA->numAttributes; j++) { + if (atemplate[i].type == cachedOA->attributes[j].type) { + CK_ATTRIBUTE_PTR attr = &cachedOA->attributes[j]; + if (cachedOA->attributes[j].ulValueLen == 0 || + cachedOA->attributes[j].ulValueLen == (CK_ULONG)-1) + { + break; /* invalid attribute */ + } + if (atemplate[i].ulValueLen > 0) { + if (atemplate[i].pValue == NULL || + atemplate[i].ulValueLen < attr->ulValueLen) + { + goto loser; + } + } else { + atemplate[i].pValue = nss_ZAlloc(arena, attr->ulValueLen); + if (!atemplate[i].pValue) { + goto loser; + } + } + nsslibc_memcpy(atemplate[i].pValue, + attr->pValue, attr->ulValueLen); + atemplate[i].ulValueLen = attr->ulValueLen; + break; + } + } + if (j == cachedOA->numAttributes) { + atemplate[i].ulValueLen = (CK_ULONG)-1; + } + } + PZ_Unlock(cache->lock); + if (mark) { + nssArena_Unmark(arena, mark); + } + return PR_SUCCESS; +loser: + PZ_Unlock(cache->lock); + if (mark) { + nssArena_Release(arena, mark); + } + return PR_FAILURE; +} + +NSS_IMPLEMENT PRStatus +nssTokenObjectCache_ImportObject +( + nssTokenObjectCache *cache, + nssCryptokiObject *object, + CK_OBJECT_CLASS objclass, + CK_ATTRIBUTE_PTR ot, + CK_ULONG otlen +) +{ + PRStatus status = PR_SUCCESS; + PRUint32 count; + nssCryptokiObjectAndAttributes **oa, ***otype; + PRUint32 objectType; + PRBool haveIt = PR_FALSE; + + PZ_Lock(cache->lock); + switch (objclass) { + case CKO_CERTIFICATE: objectType = cachedCerts; break; + case CKO_NETSCAPE_TRUST: objectType = cachedTrust; break; + case CKO_NETSCAPE_CRL: objectType = cachedCRLs; break; + default: + PZ_Unlock(cache->lock); + return PR_SUCCESS; /* don't need to import it here */ + } + if (!cache_available_for_object_type(cache, objectType)) { + PZ_Unlock(cache->lock); + return PR_SUCCESS; /* cache not active, ignored */ + } + count = 0; + otype = &cache->objects[objectType]; /* index into array of types */ + oa = *otype; /* the array of objects for this type */ + while (oa && *oa) { + if (nssCryptokiObject_Equal((*oa)->object, object)) { + haveIt = PR_TRUE; + break; + } + count++; + oa++; + } + if (haveIt) { + /* Destroy the old entry */ + (*oa)->object->token = NULL; + nssCryptokiObject_Destroy((*oa)->object); + nssArena_Destroy((*oa)->arena); + } else { + /* Create space for a new entry */ + if (count > 0) { + *otype = nss_ZREALLOCARRAY(*otype, + nssCryptokiObjectAndAttributes *, + count + 2); + } else { + *otype = nss_ZNEWARRAY(NULL, nssCryptokiObjectAndAttributes *, 2); + } + } + if (*otype) { + nssCryptokiObject *copyObject = nssCryptokiObject_Clone(object); + if (objectType == cachedCerts) { + (*otype)[count] = create_cert(copyObject, &status); + } else if (objectType == cachedTrust) { + (*otype)[count] = create_trust(copyObject, &status); + } else if (objectType == cachedCRLs) { + (*otype)[count] = create_crl(copyObject, &status); + } + } else { + status = PR_FAILURE; + } + PZ_Unlock(cache->lock); + return status; +} + +NSS_IMPLEMENT void +nssTokenObjectCache_RemoveObject +( + nssTokenObjectCache *cache, + nssCryptokiObject *object +) +{ + PRUint32 oType; + nssCryptokiObjectAndAttributes **oa, **swp = NULL; + PZ_Lock(cache->lock); + for (oType=0; oType<3; oType++) { + if (!cache_available_for_object_type(cache, oType) || + !cache->objects[oType]) + { + continue; + } + for (oa = cache->objects[oType]; *oa; oa++) { + if (nssCryptokiObject_Equal((*oa)->object, object)) { + swp = oa; /* the entry to remove */ + while (oa[1]) oa++; /* go to the tail */ + (*swp)->object->token = NULL; + nssCryptokiObject_Destroy((*swp)->object); + nssArena_Destroy((*swp)->arena); /* destroy it */ + *swp = *oa; /* swap the last with the removed */ + *oa = NULL; /* null-terminate the array */ + break; + } + } + if (swp) { + break; + } + } + if ((oType <3) && + cache->objects[oType] && cache->objects[oType][0] == NULL) { + nss_ZFreeIf(cache->objects[oType]); /* no entries remaining */ + cache->objects[oType] = NULL; + } + PZ_Unlock(cache->lock); +} + +/* XXX of course this doesn't belong here */ +NSS_IMPLEMENT NSSAlgorithmAndParameters * +NSSAlgorithmAndParameters_CreateSHA1Digest +( + NSSArena *arenaOpt +) +{ + NSSAlgorithmAndParameters *rvAP = NULL; + rvAP = nss_ZNEW(arenaOpt, NSSAlgorithmAndParameters); + if (rvAP) { + rvAP->mechanism.mechanism = CKM_SHA_1; + rvAP->mechanism.pParameter = NULL; + rvAP->mechanism.ulParameterLen = 0; + } + return rvAP; +} + +NSS_IMPLEMENT NSSAlgorithmAndParameters * +NSSAlgorithmAndParameters_CreateMD5Digest +( + NSSArena *arenaOpt +) +{ + NSSAlgorithmAndParameters *rvAP = NULL; + rvAP = nss_ZNEW(arenaOpt, NSSAlgorithmAndParameters); + if (rvAP) { + rvAP->mechanism.mechanism = CKM_MD5; + rvAP->mechanism.pParameter = NULL; + rvAP->mechanism.ulParameterLen = 0; + } + return rvAP; +} + |