summaryrefslogtreecommitdiff
path: root/security/nss/lib/dev/devutil.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/dev/devutil.c')
-rw-r--r--security/nss/lib/dev/devutil.c1445
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;
+}
+