| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #ifndef 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; |
| } |
| if (cert_template[0].ulValueLen == 0 || !cert_template[0].pValue) { |
| nss_ZFreeIf(cert_template[1].pValue); |
| return (nssCryptokiObject *)NULL; |
| } |
| object = nss_ZNEW(NULL, nssCryptokiObject); |
| if (!object) { |
| nss_ZFreeIf(cert_template[0].pValue); |
| nss_ZFreeIf(cert_template[1].pValue); |
| return (nssCryptokiObject *)NULL; |
| } |
| object->handle = h; |
| object->token = nssToken_AddRef(t); |
| isTokenObject = (CK_BBOOL *)cert_template[0].pValue; |
| object->isTokenObject = *isTokenObject; |
| nss_ZFreeIf(cert_template[0].pValue); |
| 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) { |
| for (sp = slots, count = 0; *sp; sp++) { |
| rvSlots[count++] = nssSlot_AddRef(*sp); |
| } |
| } |
| } |
| return rvSlots; |
| } |
| |
| 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); |
| } |
| } |
| |
| /* 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: |
| nssTokenObjectCache_Destroy(rvCache); |
| return (nssTokenObjectCache *)NULL; |
| } |
| |
| static void |
| clear_cache( |
| nssTokenObjectCache *cache) |
| { |
| nssCryptokiObjectAndAttributes **oa; |
| PRUint32 objectType; |
| for (objectType = cachedCerts; objectType <= cachedCRLs; objectType++) { |
| cache->searchedObjectType[objectType] = PR_FALSE; |
| 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; |
| } |
| } |
| |
| NSS_IMPLEMENT void |
| nssTokenObjectCache_Clear( |
| nssTokenObjectCache *cache) |
| { |
| if (cache) { |
| PZ_Lock(cache->lock); |
| clear_cache(cache); |
| PZ_Unlock(cache->lock); |
| } |
| } |
| |
| NSS_IMPLEMENT void |
| nssTokenObjectCache_Destroy( |
| nssTokenObjectCache *cache) |
| { |
| if (cache) { |
| clear_cache(cache); |
| if (cache->lock) { |
| 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_NSS_TRUST: |
| haveIt = cache->doObjectType[cachedTrust]; |
| break; |
| case CKO_NSS_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) |
| { |
| nssCryptokiObjectAndAttributes **rvOandA = NULL; |
| *numObjects = 0; |
| /* There are no objects for this type */ |
| if (!objects || !*objects) { |
| *status = PR_SUCCESS; |
| return rvOandA; |
| } |
| while (*objects++) |
| (*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 { |
| rvOandA = nss_ZNEWARRAY(NULL, |
| nssCryptokiObjectAndAttributes *, |
| *numObjects + 1); |
| *status = rvOandA ? PR_SUCCESS : PR_FAILURE; |
| } |
| return rvOandA; |
| } |
| |
| static nssCryptokiObjectAndAttributes * |
| create_object( |
| nssCryptokiObject *object, |
| const CK_ATTRIBUTE_TYPE *types, |
| PRUint32 numTypes, |
| PRStatus *status) |
| { |
| PRUint32 j; |
| NSSArena *arena = NULL; |
| NSSSlot *slot = NULL; |
| nssSession *session = NULL; |
| nssCryptokiObjectAndAttributes *rvCachedObject = NULL; |
| |
| slot = nssToken_GetSlot(object->token); |
| if (!slot) { |
| nss_SetError(NSS_ERROR_INVALID_POINTER); |
| goto loser; |
| } |
| session = nssToken_GetDefaultSession(object->token); |
| if (!session) { |
| nss_SetError(NSS_ERROR_INVALID_POINTER); |
| goto loser; |
| } |
| arena = nssArena_Create(); |
| if (!arena) { |
| goto loser; |
| } |
| 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; |
| nssSlot_Destroy(slot); |
| |
| return rvCachedObject; |
| loser: |
| *status = PR_FAILURE; |
| if (slot) { |
| nssSlot_Destroy(slot); |
| } |
| if (arena) |
| 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 | |
| * +-----------------------------+<--------------------------+ |
| * |
| */ |
| |
| /* This function must not be called with cache->lock locked. */ |
| static PRBool |
| token_is_present( |
| nssTokenObjectCache *cache) |
| { |
| NSSSlot *slot = nssToken_GetSlot(cache->token); |
| PRBool tokenPresent = nssSlot_IsTokenPresent(slot); |
| nssSlot_Destroy(slot); |
| return tokenPresent; |
| } |
| |
| static PRBool |
| search_for_objects( |
| nssTokenObjectCache *cache) |
| { |
| PRBool doSearch = PR_FALSE; |
| NSSSlot *slot = nssToken_GetSlot(cache->token); |
| /* 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) |
| { |
| static const 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_NSS_EMAIL |
| }; |
| static const PRUint32 numCertAttr = sizeof(certAttr) / sizeof(certAttr[0]); |
| return create_object(object, certAttr, numCertAttr, status); |
| } |
| |
| static nssCryptokiObjectAndAttributes * |
| create_trust( |
| nssCryptokiObject *object, |
| PRStatus *status) |
| { |
| static const 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 |
| }; |
| static const PRUint32 numTrustAttr = sizeof(trustAttr) / sizeof(trustAttr[0]); |
| return create_object(object, trustAttr, numTrustAttr, status); |
| } |
| |
| static nssCryptokiObjectAndAttributes * |
| create_crl( |
| nssCryptokiObject *object, |
| PRStatus *status) |
| { |
| static const CK_ATTRIBUTE_TYPE crlAttr[] = { |
| CKA_CLASS, |
| CKA_TOKEN, |
| CKA_LABEL, |
| CKA_VALUE, |
| CKA_SUBJECT, |
| CKA_NSS_KRL, |
| CKA_NSS_URL |
| }; |
| static const PRUint32 numCRLAttr = sizeof(crlAttr) / sizeof(crlAttr[0]); |
| return create_object(object, crlAttr, numCRLAttr, status); |
| } |
| |
| /* Dispatch to the create function for the object type */ |
| static nssCryptokiObjectAndAttributes * |
| create_object_of_type( |
| nssCryptokiObject *object, |
| PRUint32 objectType, |
| PRStatus *status) |
| { |
| if (objectType == cachedCerts) { |
| return create_cert(object, status); |
| } |
| if (objectType == cachedTrust) { |
| return create_trust(object, status); |
| } |
| if (objectType == cachedCRLs) { |
| return create_crl(object, status); |
| } |
| return (nssCryptokiObjectAndAttributes *)NULL; |
| } |
| |
| static PRStatus |
| get_token_objects_for_cache( |
| nssTokenObjectCache *cache, |
| PRUint32 objectType, |
| CK_OBJECT_CLASS objclass) |
| { |
| PRStatus status; |
| nssCryptokiObject **objects; |
| PRBool *doIt = &cache->doObjectType[objectType]; |
| PRUint32 i, numObjects; |
| |
| if (!search_for_objects(cache) || |
| cache->searchedObjectType[objectType] || |
| !cache->doObjectType[objectType]) { |
| /* Either there was a state change that prevents a search |
| * (token logged out), or the search was already done, |
| * or objects of this type are not being cached. |
| */ |
| return PR_SUCCESS; |
| } |
| objects = nssToken_FindObjects(cache->token, NULL, objclass, |
| nssTokenSearchType_TokenForced, |
| MAX_LOCAL_CACHE_OBJECTS, &status); |
| if (status != PR_SUCCESS) { |
| return status; |
| } |
| cache->objects[objectType] = create_object_array(objects, |
| doIt, |
| &numObjects, |
| &status); |
| if (status != PR_SUCCESS) { |
| nss_ZFreeIf(objects); |
| return status; |
| } |
| for (i = 0; i < numObjects; i++) { |
| cache->objects[objectType][i] = create_object_of_type(objects[i], |
| objectType, |
| &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[objectType][j]->object->token); |
| nssArena_Destroy(cache->objects[objectType][j]->arena); |
| } |
| nss_ZFreeIf(cache->objects[objectType]); |
| cache->objects[objectType] = NULL; |
| nssCryptokiObjectArray_Destroy(objects); |
| } |
| cache->searchedObjectType[objectType] = PR_TRUE; |
| return status; |
| } |
| |
| static CK_ATTRIBUTE_PTR |
| find_attribute_in_object( |
| nssCryptokiObjectAndAttributes *obj, |
| CK_ATTRIBUTE_TYPE attrType) |
| { |
| PRUint32 j; |
| for (j = 0; j < obj->numAttributes; j++) { |
| if (attrType == obj->attributes[j].type) { |
| return &obj->attributes[j]; |
| } |
| } |
| return (CK_ATTRIBUTE_PTR)NULL; |
| } |
| |
| /* Find all objects in the array that match the supplied template */ |
| static nssCryptokiObject ** |
| find_objects_in_array( |
| nssCryptokiObjectAndAttributes **objArray, |
| CK_ATTRIBUTE_PTR ot, |
| CK_ULONG otlen, |
| PRUint32 maximumOpt) |
| { |
| PRIntn oi = 0; |
| PRUint32 i; |
| NSSArena *arena; |
| PRUint32 size = 8; |
| PRUint32 numMatches = 0; |
| nssCryptokiObject **objects = NULL; |
| nssCryptokiObjectAndAttributes **matches = NULL; |
| CK_ATTRIBUTE_PTR attr; |
| |
| 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; |
| /* loop over the cached objects */ |
| for (; *objArray && numMatches < maximumOpt; objArray++) { |
| nssCryptokiObjectAndAttributes *obj = *objArray; |
| /* loop over the test template */ |
| for (i = 0; i < otlen; i++) { |
| /* see if the object has the attribute */ |
| attr = find_attribute_in_object(obj, ot[i].type); |
| if (!attr) { |
| /* nope, match failed */ |
| break; |
| } |
| /* compare the attribute against the test value */ |
| if (ot[i].ulValueLen != attr->ulValueLen || |
| !nsslibc_memequal(ot[i].pValue, |
| attr->pValue, |
| attr->ulValueLen, NULL)) { |
| /* nope, match failed */ |
| break; |
| } |
| } |
| if (i == otlen) { |
| /* all of the attributes in the test template were found |
| * in the object's template, and they all matched |
| */ |
| 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: |
| nssCryptokiObjectArray_Destroy(objects); |
| 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; |
| PRUint32 objectType; |
| if (!token_is_present(cache)) { |
| status = PR_SUCCESS; |
| goto finish; |
| } |
| switch (objclass) { |
| case CKO_CERTIFICATE: |
| objectType = cachedCerts; |
| break; |
| case CKO_NSS_TRUST: |
| objectType = cachedTrust; |
| break; |
| case CKO_NSS_CRL: |
| objectType = cachedCRLs; |
| break; |
| default: |
| goto finish; |
| } |
| PZ_Lock(cache->lock); |
| if (cache->doObjectType[objectType]) { |
| status = get_token_objects_for_cache(cache, objectType, objclass); |
| if (status == PR_SUCCESS) { |
| rvObjects = find_objects_in_array(cache->objects[objectType], |
| otemplate, otlen, maximumOpt); |
| } |
| } |
| PZ_Unlock(cache->lock); |
| finish: |
| 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 */ |
| 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; |
| if (!token_is_present(cache)) { |
| return PR_FAILURE; |
| } |
| PZ_Lock(cache->lock); |
| switch (objclass) { |
| case CKO_CERTIFICATE: |
| objectType = cachedCerts; |
| break; |
| case CKO_NSS_TRUST: |
| objectType = cachedTrust; |
| break; |
| case CKO_NSS_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; |
| |
| if (!token_is_present(cache)) { |
| return PR_SUCCESS; /* cache not active, ignored */ |
| } |
| PZ_Lock(cache->lock); |
| switch (objclass) { |
| case CKO_CERTIFICATE: |
| objectType = cachedCerts; |
| break; |
| case CKO_NSS_TRUST: |
| objectType = cachedTrust; |
| break; |
| case CKO_NSS_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); |
| (*otype)[count] = create_object_of_type(copyObject, objectType, |
| &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; |
| if (!token_is_present(cache)) { |
| return; |
| } |
| 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); |
| } |
| |
| /* These two hash algorithms are presently sufficient. |
| ** They are used for fingerprints of certs which are stored as the |
| ** CKA_CERT_SHA1_HASH and CKA_CERT_MD5_HASH attributes. |
| ** We don't need to add SHAxxx to these now. |
| */ |
| /* 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; |
| } |