diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /security/nss/lib/softoken/pkcs11u.c | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'security/nss/lib/softoken/pkcs11u.c')
-rw-r--r-- | security/nss/lib/softoken/pkcs11u.c | 1994 |
1 files changed, 1994 insertions, 0 deletions
diff --git a/security/nss/lib/softoken/pkcs11u.c b/security/nss/lib/softoken/pkcs11u.c new file mode 100644 index 000000000..a5694ee38 --- /dev/null +++ b/security/nss/lib/softoken/pkcs11u.c @@ -0,0 +1,1994 @@ +/* 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/. */ +/* + * Internal PKCS #11 functions. Should only be called by pkcs11.c + */ +#include "pkcs11.h" +#include "pkcs11i.h" +#include "lowkeyi.h" +#include "secasn1.h" +#include "blapi.h" +#include "secerr.h" +#include "prnetdb.h" /* for PR_ntohl */ +#include "sftkdb.h" +#include "softoken.h" + +/* + * ******************** Attribute Utilities ******************************* + */ + +/* + * create a new attribute with type, value, and length. Space is allocated + * to hold value. + */ +static SFTKAttribute * +sftk_NewAttribute(SFTKObject *object, + CK_ATTRIBUTE_TYPE type, const void *value, CK_ULONG len) +{ + SFTKAttribute *attribute; + + SFTKSessionObject *so = sftk_narrowToSessionObject(object); + int index; + + if (so == NULL) { + /* allocate new attribute in a buffer */ + PORT_Assert(0); + return NULL; + } + /* + * We attempt to keep down contention on Malloc and Arena locks by + * limiting the number of these calls on high traversed paths. This + * is done for attributes by 'allocating' them from a pool already + * allocated by the parent object. + */ + PZ_Lock(so->attributeLock); + index = so->nextAttr++; + PZ_Unlock(so->attributeLock); + PORT_Assert(index < MAX_OBJS_ATTRS); + if (index >= MAX_OBJS_ATTRS) + return NULL; + + attribute = &so->attrList[index]; + attribute->attrib.type = type; + attribute->freeAttr = PR_FALSE; + attribute->freeData = PR_FALSE; + if (value) { + if (len <= ATTR_SPACE) { + attribute->attrib.pValue = attribute->space; + } else { + attribute->attrib.pValue = PORT_Alloc(len); + attribute->freeData = PR_TRUE; + } + if (attribute->attrib.pValue == NULL) { + return NULL; + } + PORT_Memcpy(attribute->attrib.pValue, value, len); + attribute->attrib.ulValueLen = len; + } else { + attribute->attrib.pValue = NULL; + attribute->attrib.ulValueLen = 0; + } + attribute->attrib.type = type; + attribute->handle = type; + attribute->next = attribute->prev = NULL; + return attribute; +} + +/* + * Free up all the memory associated with an attribute. Reference count + * must be zero to call this. + */ +static void +sftk_DestroyAttribute(SFTKAttribute *attribute) +{ + if (attribute->freeData) { + if (attribute->attrib.pValue) { + /* clear out the data in the attribute value... it may have been + * sensitive data */ + PORT_Memset(attribute->attrib.pValue, 0, + attribute->attrib.ulValueLen); + } + PORT_Free(attribute->attrib.pValue); + } + PORT_Free(attribute); +} + +/* + * release a reference to an attribute structure + */ +void +sftk_FreeAttribute(SFTKAttribute *attribute) +{ + if (attribute->freeAttr) { + sftk_DestroyAttribute(attribute); + return; + } +} + +static SFTKAttribute * +sftk_FindTokenAttribute(SFTKTokenObject *object, CK_ATTRIBUTE_TYPE type) +{ + SFTKAttribute *myattribute = NULL; + SFTKDBHandle *dbHandle = NULL; + CK_RV crv = CKR_HOST_MEMORY; + + myattribute = (SFTKAttribute *)PORT_Alloc(sizeof(SFTKAttribute)); + if (myattribute == NULL) { + goto loser; + } + + dbHandle = sftk_getDBForTokenObject(object->obj.slot, object->obj.handle); + + myattribute->handle = type; + myattribute->attrib.type = type; + myattribute->attrib.pValue = myattribute->space; + myattribute->attrib.ulValueLen = ATTR_SPACE; + myattribute->next = myattribute->prev = NULL; + myattribute->freeAttr = PR_TRUE; + myattribute->freeData = PR_FALSE; + + crv = sftkdb_GetAttributeValue(dbHandle, object->obj.handle, + &myattribute->attrib, 1); + + /* attribute is bigger than our attribute space buffer, malloc it */ + if (crv == CKR_BUFFER_TOO_SMALL) { + myattribute->attrib.pValue = NULL; + crv = sftkdb_GetAttributeValue(dbHandle, object->obj.handle, + &myattribute->attrib, 1); + if (crv != CKR_OK) { + goto loser; + } + myattribute->attrib.pValue = PORT_Alloc(myattribute->attrib.ulValueLen); + if (myattribute->attrib.pValue == NULL) { + crv = CKR_HOST_MEMORY; + goto loser; + } + myattribute->freeData = PR_TRUE; + crv = sftkdb_GetAttributeValue(dbHandle, object->obj.handle, + &myattribute->attrib, 1); + } +loser: + if (dbHandle) { + sftk_freeDB(dbHandle); + } + if (crv != CKR_OK) { + if (myattribute) { + myattribute->attrib.ulValueLen = 0; + sftk_FreeAttribute(myattribute); + myattribute = NULL; + } + } + return myattribute; +} + +/* + * look up and attribute structure from a type and Object structure. + * The returned attribute is referenced and needs to be freed when + * it is no longer needed. + */ +SFTKAttribute * +sftk_FindAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type) +{ + SFTKAttribute *attribute; + SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object); + + if (sessObject == NULL) { + return sftk_FindTokenAttribute(sftk_narrowToTokenObject(object), type); + } + + PZ_Lock(sessObject->attributeLock); + sftkqueue_find(attribute, type, sessObject->head, sessObject->hashSize); + PZ_Unlock(sessObject->attributeLock); + + return (attribute); +} + +/* + * Take a buffer and it's length and return it's true size in bits; + */ +unsigned int +sftk_GetLengthInBits(unsigned char *buf, unsigned int bufLen) +{ + unsigned int size = bufLen * 8; + unsigned int i; + + /* Get the real length in bytes */ + for (i = 0; i < bufLen; i++) { + unsigned char c = *buf++; + if (c != 0) { + unsigned char m; + for (m = 0x80; m > 0; m = m >> 1) { + if ((c & m) != 0) { + break; + } + size--; + } + break; + } + size -= 8; + } + return size; +} + +/* + * Constrain a big num attribute. to size and padding + * minLength means length of the object must be greater than equal to minLength + * maxLength means length of the object must be less than equal to maxLength + * minMultiple means that object length mod minMultiple must equal 0. + * all input sizes are in bits. + * if any constraint is '0' that constraint is not checked. + */ +CK_RV +sftk_ConstrainAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type, + int minLength, int maxLength, int minMultiple) +{ + SFTKAttribute *attribute; + int size; + unsigned char *ptr; + + attribute = sftk_FindAttribute(object, type); + if (!attribute) { + return CKR_TEMPLATE_INCOMPLETE; + } + ptr = (unsigned char *)attribute->attrib.pValue; + if (ptr == NULL) { + sftk_FreeAttribute(attribute); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + size = sftk_GetLengthInBits(ptr, attribute->attrib.ulValueLen); + sftk_FreeAttribute(attribute); + + if ((minLength != 0) && (size < minLength)) { + return CKR_ATTRIBUTE_VALUE_INVALID; + } + if ((maxLength != 0) && (size > maxLength)) { + return CKR_ATTRIBUTE_VALUE_INVALID; + } + if ((minMultiple != 0) && ((size % minMultiple) != 0)) { + return CKR_ATTRIBUTE_VALUE_INVALID; + } + return CKR_OK; +} + +PRBool +sftk_hasAttributeToken(SFTKTokenObject *object, CK_ATTRIBUTE_TYPE type) +{ + CK_ATTRIBUTE template; + CK_RV crv; + SFTKDBHandle *dbHandle; + + dbHandle = sftk_getDBForTokenObject(object->obj.slot, object->obj.handle); + template.type = type; + template.pValue = NULL; + template.ulValueLen = 0; + + crv = sftkdb_GetAttributeValue(dbHandle, object->obj.handle, &template, 1); + sftk_freeDB(dbHandle); + + /* attribute is bigger than our attribute space buffer, malloc it */ + return (crv == CKR_OK) ? PR_TRUE : PR_FALSE; +} + +/* + * return true if object has attribute + */ +PRBool +sftk_hasAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type) +{ + SFTKAttribute *attribute; + SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object); + + if (sessObject == NULL) { + return sftk_hasAttributeToken(sftk_narrowToTokenObject(object), type); + } + + PZ_Lock(sessObject->attributeLock); + sftkqueue_find(attribute, type, sessObject->head, sessObject->hashSize); + PZ_Unlock(sessObject->attributeLock); + + return (PRBool)(attribute != NULL); +} + +/* + * add an attribute to an object + */ +static void +sftk_AddAttribute(SFTKObject *object, SFTKAttribute *attribute) +{ + SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object); + + if (sessObject == NULL) + return; + PZ_Lock(sessObject->attributeLock); + sftkqueue_add(attribute, attribute->handle, + sessObject->head, sessObject->hashSize); + PZ_Unlock(sessObject->attributeLock); +} + +/* + * copy an unsigned attribute into a SECItem. Secitem is allocated in + * the specified arena. + */ +CK_RV +sftk_Attribute2SSecItem(PLArenaPool *arena, SECItem *item, SFTKObject *object, + CK_ATTRIBUTE_TYPE type) +{ + SFTKAttribute *attribute; + + item->data = NULL; + + attribute = sftk_FindAttribute(object, type); + if (attribute == NULL) + return CKR_TEMPLATE_INCOMPLETE; + + (void)SECITEM_AllocItem(arena, item, attribute->attrib.ulValueLen); + if (item->data == NULL) { + sftk_FreeAttribute(attribute); + return CKR_HOST_MEMORY; + } + PORT_Memcpy(item->data, attribute->attrib.pValue, item->len); + sftk_FreeAttribute(attribute); + return CKR_OK; +} + +/* + * fetch multiple attributes into SECItems. Secitem data is allocated in + * the specified arena. + */ +CK_RV +sftk_MultipleAttribute2SecItem(PLArenaPool *arena, SFTKObject *object, + SFTKItemTemplate *itemTemplate, int itemTemplateCount) +{ + + CK_RV crv = CKR_OK; + CK_ATTRIBUTE templateSpace[SFTK_MAX_ITEM_TEMPLATE]; + CK_ATTRIBUTE *template; + SFTKTokenObject *tokObject; + SFTKDBHandle *dbHandle = NULL; + int i; + + tokObject = sftk_narrowToTokenObject(object); + + /* session objects, just loop through the list */ + if (tokObject == NULL) { + for (i = 0; i < itemTemplateCount; i++) { + crv = sftk_Attribute2SecItem(arena, itemTemplate[i].item, object, + itemTemplate[i].type); + if (crv != CKR_OK) { + return crv; + } + } + return CKR_OK; + } + + /* don't do any work if none is required */ + if (itemTemplateCount == 0) { + return CKR_OK; + } + + /* don't allocate the template unless we need it */ + if (itemTemplateCount > SFTK_MAX_ITEM_TEMPLATE) { + template = PORT_NewArray(CK_ATTRIBUTE, itemTemplateCount); + } else { + template = templateSpace; + } + + if (template == NULL) { + crv = CKR_HOST_MEMORY; + goto loser; + } + + dbHandle = sftk_getDBForTokenObject(object->slot, object->handle); + if (dbHandle == NULL) { + crv = CKR_OBJECT_HANDLE_INVALID; + goto loser; + } + + /* set up the PKCS #11 template */ + for (i = 0; i < itemTemplateCount; i++) { + template[i].type = itemTemplate[i].type; + template[i].pValue = NULL; + template[i].ulValueLen = 0; + } + + /* fetch the attribute lengths */ + crv = sftkdb_GetAttributeValue(dbHandle, object->handle, + template, itemTemplateCount); + if (crv != CKR_OK) { + goto loser; + } + + /* allocate space for the attributes */ + for (i = 0; i < itemTemplateCount; i++) { + template[i].pValue = PORT_ArenaAlloc(arena, template[i].ulValueLen); + if (template[i].pValue == NULL) { + crv = CKR_HOST_MEMORY; + goto loser; + } + } + + /* fetch the attributes */ + crv = sftkdb_GetAttributeValue(dbHandle, object->handle, + template, itemTemplateCount); + if (crv != CKR_OK) { + goto loser; + } + + /* Fill in the items */ + for (i = 0; i < itemTemplateCount; i++) { + itemTemplate[i].item->data = template[i].pValue; + itemTemplate[i].item->len = template[i].ulValueLen; + } + +loser: + if (template != templateSpace) { + PORT_Free(template); + } + if (dbHandle) { + sftk_freeDB(dbHandle); + } + + return crv; +} + +/* + * delete an attribute from an object + */ +static void +sftk_DeleteAttribute(SFTKObject *object, SFTKAttribute *attribute) +{ + SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object); + + if (sessObject == NULL) { + return; + } + PZ_Lock(sessObject->attributeLock); + if (sftkqueue_is_queued(attribute, attribute->handle, + sessObject->head, sessObject->hashSize)) { + sftkqueue_delete(attribute, attribute->handle, + sessObject->head, sessObject->hashSize); + } + PZ_Unlock(sessObject->attributeLock); +} + +/* + * this is only valid for CK_BBOOL type attributes. Return the state + * of that attribute. + */ +PRBool +sftk_isTrue(SFTKObject *object, CK_ATTRIBUTE_TYPE type) +{ + SFTKAttribute *attribute; + PRBool tok = PR_FALSE; + + attribute = sftk_FindAttribute(object, type); + if (attribute == NULL) { + return PR_FALSE; + } + tok = (PRBool)(*(CK_BBOOL *)attribute->attrib.pValue); + sftk_FreeAttribute(attribute); + + return tok; +} + +/* + * force an attribute to null. + * this is for sensitive keys which are stored in the database, we don't + * want to keep this info around in memory in the clear. + */ +void +sftk_nullAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type) +{ + SFTKAttribute *attribute; + + attribute = sftk_FindAttribute(object, type); + if (attribute == NULL) + return; + + if (attribute->attrib.pValue != NULL) { + PORT_Memset(attribute->attrib.pValue, 0, attribute->attrib.ulValueLen); + if (attribute->freeData) { + PORT_Free(attribute->attrib.pValue); + } + attribute->freeData = PR_FALSE; + attribute->attrib.pValue = NULL; + attribute->attrib.ulValueLen = 0; + } + sftk_FreeAttribute(attribute); +} + +static CK_RV +sftk_forceTokenAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type, + const void *value, unsigned int len) +{ + CK_ATTRIBUTE attribute; + SFTKDBHandle *dbHandle = NULL; + SFTKTokenObject *to = sftk_narrowToTokenObject(object); + CK_RV crv; + + PORT_Assert(to); + if (to == NULL) { + return CKR_DEVICE_ERROR; + } + + dbHandle = sftk_getDBForTokenObject(object->slot, object->handle); + + attribute.type = type; + attribute.pValue = (void *)value; + attribute.ulValueLen = len; + + crv = sftkdb_SetAttributeValue(dbHandle, object, &attribute, 1); + sftk_freeDB(dbHandle); + return crv; +} + +/* + * force an attribute to a specifc value. + */ +CK_RV +sftk_forceAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type, + const void *value, unsigned int len) +{ + SFTKAttribute *attribute; + void *att_val = NULL; + PRBool freeData = PR_FALSE; + + PORT_Assert(object); + PORT_Assert(object->refCount); + PORT_Assert(object->slot); + if (!object || + !object->refCount || + !object->slot) { + return CKR_DEVICE_ERROR; + } + if (sftk_isToken(object->handle)) { + return sftk_forceTokenAttribute(object, type, value, len); + } + attribute = sftk_FindAttribute(object, type); + if (attribute == NULL) + return sftk_AddAttributeType(object, type, value, len); + + if (value) { + if (len <= ATTR_SPACE) { + att_val = attribute->space; + } else { + att_val = PORT_Alloc(len); + freeData = PR_TRUE; + } + if (att_val == NULL) { + return CKR_HOST_MEMORY; + } + if (attribute->attrib.pValue == att_val) { + PORT_Memset(attribute->attrib.pValue, 0, + attribute->attrib.ulValueLen); + } + PORT_Memcpy(att_val, value, len); + } + if (attribute->attrib.pValue != NULL) { + if (attribute->attrib.pValue != att_val) { + PORT_Memset(attribute->attrib.pValue, 0, + attribute->attrib.ulValueLen); + } + if (attribute->freeData) { + PORT_Free(attribute->attrib.pValue); + } + attribute->freeData = PR_FALSE; + attribute->attrib.pValue = NULL; + attribute->attrib.ulValueLen = 0; + } + if (att_val) { + attribute->attrib.pValue = att_val; + attribute->attrib.ulValueLen = len; + attribute->freeData = freeData; + } + sftk_FreeAttribute(attribute); + return CKR_OK; +} + +/* + * return a null terminated string from attribute 'type'. This string + * is allocated and needs to be freed with PORT_Free() When complete. + */ +char * +sftk_getString(SFTKObject *object, CK_ATTRIBUTE_TYPE type) +{ + SFTKAttribute *attribute; + char *label = NULL; + + attribute = sftk_FindAttribute(object, type); + if (attribute == NULL) + return NULL; + + if (attribute->attrib.pValue != NULL) { + label = (char *)PORT_Alloc(attribute->attrib.ulValueLen + 1); + if (label == NULL) { + sftk_FreeAttribute(attribute); + return NULL; + } + + PORT_Memcpy(label, attribute->attrib.pValue, + attribute->attrib.ulValueLen); + label[attribute->attrib.ulValueLen] = 0; + } + sftk_FreeAttribute(attribute); + return label; +} + +/* + * decode when a particular attribute may be modified + * SFTK_NEVER: This attribute must be set at object creation time and + * can never be modified. + * SFTK_ONCOPY: This attribute may be modified only when you copy the + * object. + * SFTK_SENSITIVE: The CKA_SENSITIVE attribute can only be changed from + * CK_FALSE to CK_TRUE. + * SFTK_ALWAYS: This attribute can always be modified. + * Some attributes vary their modification type based on the class of the + * object. + */ +SFTKModifyType +sftk_modifyType(CK_ATTRIBUTE_TYPE type, CK_OBJECT_CLASS inClass) +{ + /* if we don't know about it, user user defined, always allow modify */ + SFTKModifyType mtype = SFTK_ALWAYS; + + switch (type) { + /* NEVER */ + case CKA_CLASS: + case CKA_CERTIFICATE_TYPE: + case CKA_KEY_TYPE: + case CKA_MODULUS: + case CKA_MODULUS_BITS: + case CKA_PUBLIC_EXPONENT: + case CKA_PRIVATE_EXPONENT: + case CKA_PRIME: + case CKA_SUBPRIME: + case CKA_BASE: + case CKA_PRIME_1: + case CKA_PRIME_2: + case CKA_EXPONENT_1: + case CKA_EXPONENT_2: + case CKA_COEFFICIENT: + case CKA_VALUE_LEN: + case CKA_ALWAYS_SENSITIVE: + case CKA_NEVER_EXTRACTABLE: + case CKA_NETSCAPE_DB: + mtype = SFTK_NEVER; + break; + + /* ONCOPY */ + case CKA_TOKEN: + case CKA_PRIVATE: + case CKA_MODIFIABLE: + mtype = SFTK_ONCOPY; + break; + + /* SENSITIVE */ + case CKA_SENSITIVE: + case CKA_EXTRACTABLE: + mtype = SFTK_SENSITIVE; + break; + + /* ALWAYS */ + case CKA_LABEL: + case CKA_APPLICATION: + case CKA_ID: + case CKA_SERIAL_NUMBER: + case CKA_START_DATE: + case CKA_END_DATE: + case CKA_DERIVE: + case CKA_ENCRYPT: + case CKA_DECRYPT: + case CKA_SIGN: + case CKA_VERIFY: + case CKA_SIGN_RECOVER: + case CKA_VERIFY_RECOVER: + case CKA_WRAP: + case CKA_UNWRAP: + mtype = SFTK_ALWAYS; + break; + + /* DEPENDS ON CLASS */ + case CKA_VALUE: + mtype = (inClass == CKO_DATA) ? SFTK_ALWAYS : SFTK_NEVER; + break; + + case CKA_SUBJECT: + mtype = (inClass == CKO_CERTIFICATE) ? SFTK_NEVER : SFTK_ALWAYS; + break; + default: + break; + } + return mtype; +} + +/* decode if a particular attribute is sensitive (cannot be read + * back to the user of if the object is set to SENSITIVE) */ +PRBool +sftk_isSensitive(CK_ATTRIBUTE_TYPE type, CK_OBJECT_CLASS inClass) +{ + switch (type) { + /* ALWAYS */ + case CKA_PRIVATE_EXPONENT: + case CKA_PRIME_1: + case CKA_PRIME_2: + case CKA_EXPONENT_1: + case CKA_EXPONENT_2: + case CKA_COEFFICIENT: + return PR_TRUE; + + /* DEPENDS ON CLASS */ + case CKA_VALUE: + /* PRIVATE and SECRET KEYS have SENSITIVE values */ + return (PRBool)((inClass == CKO_PRIVATE_KEY) || (inClass == CKO_SECRET_KEY)); + + default: + break; + } + return PR_FALSE; +} + +/* + * copy an attribute into a SECItem. Secitem is allocated in the specified + * arena. + */ +CK_RV +sftk_Attribute2SecItem(PLArenaPool *arena, SECItem *item, SFTKObject *object, + CK_ATTRIBUTE_TYPE type) +{ + int len; + SFTKAttribute *attribute; + + attribute = sftk_FindAttribute(object, type); + if (attribute == NULL) + return CKR_TEMPLATE_INCOMPLETE; + len = attribute->attrib.ulValueLen; + + if (arena) { + item->data = (unsigned char *)PORT_ArenaAlloc(arena, len); + } else { + item->data = (unsigned char *)PORT_Alloc(len); + } + if (item->data == NULL) { + sftk_FreeAttribute(attribute); + return CKR_HOST_MEMORY; + } + item->len = len; + PORT_Memcpy(item->data, attribute->attrib.pValue, len); + sftk_FreeAttribute(attribute); + return CKR_OK; +} + +CK_RV +sftk_GetULongAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type, + CK_ULONG *longData) +{ + SFTKAttribute *attribute; + + attribute = sftk_FindAttribute(object, type); + if (attribute == NULL) + return CKR_TEMPLATE_INCOMPLETE; + + if (attribute->attrib.ulValueLen != sizeof(CK_ULONG)) { + return CKR_ATTRIBUTE_VALUE_INVALID; + } + + *longData = *(CK_ULONG *)attribute->attrib.pValue; + sftk_FreeAttribute(attribute); + return CKR_OK; +} + +void +sftk_DeleteAttributeType(SFTKObject *object, CK_ATTRIBUTE_TYPE type) +{ + SFTKAttribute *attribute; + attribute = sftk_FindAttribute(object, type); + if (attribute == NULL) + return; + sftk_DeleteAttribute(object, attribute); + sftk_FreeAttribute(attribute); +} + +CK_RV +sftk_AddAttributeType(SFTKObject *object, CK_ATTRIBUTE_TYPE type, + const void *valPtr, CK_ULONG length) +{ + SFTKAttribute *attribute; + attribute = sftk_NewAttribute(object, type, valPtr, length); + if (attribute == NULL) { + return CKR_HOST_MEMORY; + } + sftk_AddAttribute(object, attribute); + return CKR_OK; +} + +/* + * ******************** Object Utilities ******************************* + */ + +/* must be called holding sftk_tokenKeyLock(slot) */ +static SECItem * +sftk_lookupTokenKeyByHandle(SFTKSlot *slot, CK_OBJECT_HANDLE handle) +{ + return (SECItem *)PL_HashTableLookup(slot->tokObjHashTable, (void *)handle); +} + +/* + * use the refLock. This operations should be very rare, so the added + * contention on the ref lock should be lower than the overhead of adding + * a new lock. We use separate functions for this just in case I'm wrong. + */ +static void +sftk_tokenKeyLock(SFTKSlot *slot) +{ + SKIP_AFTER_FORK(PZ_Lock(slot->objectLock)); +} + +static void +sftk_tokenKeyUnlock(SFTKSlot *slot) +{ + SKIP_AFTER_FORK(PZ_Unlock(slot->objectLock)); +} + +static PRIntn +sftk_freeHashItem(PLHashEntry *entry, PRIntn index, void *arg) +{ + SECItem *item = (SECItem *)entry->value; + + SECITEM_FreeItem(item, PR_TRUE); + return HT_ENUMERATE_NEXT; +} + +CK_RV +SFTK_ClearTokenKeyHashTable(SFTKSlot *slot) +{ + sftk_tokenKeyLock(slot); + PORT_Assert(!slot->present); + PL_HashTableEnumerateEntries(slot->tokObjHashTable, sftk_freeHashItem, NULL); + sftk_tokenKeyUnlock(slot); + return CKR_OK; +} + +/* allocation hooks that allow us to recycle old object structures */ +static SFTKObjectFreeList sessionObjectList = { NULL, NULL, 0 }; +static SFTKObjectFreeList tokenObjectList = { NULL, NULL, 0 }; + +SFTKObject * +sftk_GetObjectFromList(PRBool *hasLocks, PRBool optimizeSpace, + SFTKObjectFreeList *list, unsigned int hashSize, PRBool isSessionObject) +{ + SFTKObject *object; + int size = 0; + + if (!optimizeSpace) { + PZ_Lock(list->lock); + object = list->head; + if (object) { + list->head = object->next; + list->count--; + } + PZ_Unlock(list->lock); + if (object) { + object->next = object->prev = NULL; + *hasLocks = PR_TRUE; + return object; + } + } + size = isSessionObject ? sizeof(SFTKSessionObject) + hashSize * sizeof(SFTKAttribute *) : sizeof(SFTKTokenObject); + + object = (SFTKObject *)PORT_ZAlloc(size); + if (isSessionObject && object) { + ((SFTKSessionObject *)object)->hashSize = hashSize; + } + *hasLocks = PR_FALSE; + return object; +} + +static void +sftk_PutObjectToList(SFTKObject *object, SFTKObjectFreeList *list, + PRBool isSessionObject) +{ + + /* the code below is equivalent to : + * optimizeSpace = isSessionObject ? object->optimizeSpace : PR_FALSE; + * just faster. + */ + PRBool optimizeSpace = isSessionObject && + ((SFTKSessionObject *)object)->optimizeSpace; + if (object->refLock && !optimizeSpace && (list->count < MAX_OBJECT_LIST_SIZE)) { + PZ_Lock(list->lock); + object->next = list->head; + list->head = object; + list->count++; + PZ_Unlock(list->lock); + return; + } + if (isSessionObject) { + SFTKSessionObject *so = (SFTKSessionObject *)object; + PZ_DestroyLock(so->attributeLock); + so->attributeLock = NULL; + } + if (object->refLock) { + PZ_DestroyLock(object->refLock); + object->refLock = NULL; + } + PORT_Free(object); +} + +static SFTKObject * +sftk_freeObjectData(SFTKObject *object) +{ + SFTKObject *next = object->next; + + PORT_Free(object); + return next; +} + +static void +sftk_InitFreeList(SFTKObjectFreeList *list) +{ + if (!list->lock) { + list->lock = PZ_NewLock(nssILockObject); + } +} + +void +sftk_InitFreeLists(void) +{ + sftk_InitFreeList(&sessionObjectList); + sftk_InitFreeList(&tokenObjectList); +} + +static void +sftk_CleanupFreeList(SFTKObjectFreeList *list, PRBool isSessionList) +{ + SFTKObject *object; + + if (!list->lock) { + return; + } + SKIP_AFTER_FORK(PZ_Lock(list->lock)); + for (object = list->head; object != NULL; + object = sftk_freeObjectData(object)) { + PZ_DestroyLock(object->refLock); + if (isSessionList) { + PZ_DestroyLock(((SFTKSessionObject *)object)->attributeLock); + } + } + list->count = 0; + list->head = NULL; + SKIP_AFTER_FORK(PZ_Unlock(list->lock)); + SKIP_AFTER_FORK(PZ_DestroyLock(list->lock)); + list->lock = NULL; +} + +void +sftk_CleanupFreeLists(void) +{ + sftk_CleanupFreeList(&sessionObjectList, PR_TRUE); + sftk_CleanupFreeList(&tokenObjectList, PR_FALSE); +} + +/* + * Create a new object + */ +SFTKObject * +sftk_NewObject(SFTKSlot *slot) +{ + SFTKObject *object; + SFTKSessionObject *sessObject; + PRBool hasLocks = PR_FALSE; + unsigned int i; + unsigned int hashSize = 0; + + hashSize = (slot->optimizeSpace) ? SPACE_ATTRIBUTE_HASH_SIZE : TIME_ATTRIBUTE_HASH_SIZE; + + object = sftk_GetObjectFromList(&hasLocks, slot->optimizeSpace, + &sessionObjectList, hashSize, PR_TRUE); + if (object == NULL) { + return NULL; + } + sessObject = (SFTKSessionObject *)object; + sessObject->nextAttr = 0; + + for (i = 0; i < MAX_OBJS_ATTRS; i++) { + sessObject->attrList[i].attrib.pValue = NULL; + sessObject->attrList[i].freeData = PR_FALSE; + } + sessObject->optimizeSpace = slot->optimizeSpace; + + object->handle = 0; + object->next = object->prev = NULL; + object->slot = slot; + + object->refCount = 1; + sessObject->sessionList.next = NULL; + sessObject->sessionList.prev = NULL; + sessObject->sessionList.parent = object; + sessObject->session = NULL; + sessObject->wasDerived = PR_FALSE; + if (!hasLocks) + object->refLock = PZ_NewLock(nssILockRefLock); + if (object->refLock == NULL) { + PORT_Free(object); + return NULL; + } + if (!hasLocks) + sessObject->attributeLock = PZ_NewLock(nssILockAttribute); + if (sessObject->attributeLock == NULL) { + PZ_DestroyLock(object->refLock); + PORT_Free(object); + return NULL; + } + for (i = 0; i < sessObject->hashSize; i++) { + sessObject->head[i] = NULL; + } + object->objectInfo = NULL; + object->infoFree = NULL; + return object; +} + +static CK_RV +sftk_DestroySessionObjectData(SFTKSessionObject *so) +{ + int i; + + for (i = 0; i < MAX_OBJS_ATTRS; i++) { + unsigned char *value = so->attrList[i].attrib.pValue; + if (value) { + PORT_Memset(value, 0, so->attrList[i].attrib.ulValueLen); + if (so->attrList[i].freeData) { + PORT_Free(value); + } + so->attrList[i].attrib.pValue = NULL; + so->attrList[i].freeData = PR_FALSE; + } + } + /* PZ_DestroyLock(so->attributeLock);*/ + return CKR_OK; +} + +/* + * free all the data associated with an object. Object reference count must + * be 'zero'. + */ +static CK_RV +sftk_DestroyObject(SFTKObject *object) +{ + CK_RV crv = CKR_OK; + SFTKSessionObject *so = sftk_narrowToSessionObject(object); + SFTKTokenObject *to = sftk_narrowToTokenObject(object); + + PORT_Assert(object->refCount == 0); + + /* delete the database value */ + if (to) { + if (to->dbKey.data) { + PORT_Free(to->dbKey.data); + to->dbKey.data = NULL; + } + } + if (so) { + sftk_DestroySessionObjectData(so); + } + if (object->objectInfo) { + (*object->infoFree)(object->objectInfo); + object->objectInfo = NULL; + object->infoFree = NULL; + } + if (so) { + sftk_PutObjectToList(object, &sessionObjectList, PR_TRUE); + } else { + sftk_PutObjectToList(object, &tokenObjectList, PR_FALSE); + } + return crv; +} + +void +sftk_ReferenceObject(SFTKObject *object) +{ + PZ_Lock(object->refLock); + object->refCount++; + PZ_Unlock(object->refLock); +} + +static SFTKObject * +sftk_ObjectFromHandleOnSlot(CK_OBJECT_HANDLE handle, SFTKSlot *slot) +{ + SFTKObject *object; + PRUint32 index = sftk_hash(handle, slot->sessObjHashSize); + + if (sftk_isToken(handle)) { + return sftk_NewTokenObject(slot, NULL, handle); + } + + PZ_Lock(slot->objectLock); + sftkqueue_find2(object, handle, index, slot->sessObjHashTable); + if (object) { + sftk_ReferenceObject(object); + } + PZ_Unlock(slot->objectLock); + + return (object); +} +/* + * look up and object structure from a handle. OBJECT_Handles only make + * sense in terms of a given session. make a reference to that object + * structure returned. + */ +SFTKObject * +sftk_ObjectFromHandle(CK_OBJECT_HANDLE handle, SFTKSession *session) +{ + SFTKSlot *slot = sftk_SlotFromSession(session); + + return sftk_ObjectFromHandleOnSlot(handle, slot); +} + +/* + * release a reference to an object handle + */ +SFTKFreeStatus +sftk_FreeObject(SFTKObject *object) +{ + PRBool destroy = PR_FALSE; + CK_RV crv; + + PZ_Lock(object->refLock); + if (object->refCount == 1) + destroy = PR_TRUE; + object->refCount--; + PZ_Unlock(object->refLock); + + if (destroy) { + crv = sftk_DestroyObject(object); + if (crv != CKR_OK) { + return SFTK_DestroyFailure; + } + return SFTK_Destroyed; + } + return SFTK_Busy; +} + +/* + * add an object to a slot and session queue. These two functions + * adopt the object. + */ +void +sftk_AddSlotObject(SFTKSlot *slot, SFTKObject *object) +{ + PRUint32 index = sftk_hash(object->handle, slot->sessObjHashSize); + sftkqueue_init_element(object); + PZ_Lock(slot->objectLock); + sftkqueue_add2(object, object->handle, index, slot->sessObjHashTable); + PZ_Unlock(slot->objectLock); +} + +void +sftk_AddObject(SFTKSession *session, SFTKObject *object) +{ + SFTKSlot *slot = sftk_SlotFromSession(session); + SFTKSessionObject *so = sftk_narrowToSessionObject(object); + + if (so) { + PZ_Lock(session->objectLock); + sftkqueue_add(&so->sessionList, 0, session->objects, 0); + so->session = session; + PZ_Unlock(session->objectLock); + } + sftk_AddSlotObject(slot, object); + sftk_ReferenceObject(object); +} + +/* + * delete an object from a slot and session queue + */ +CK_RV +sftk_DeleteObject(SFTKSession *session, SFTKObject *object) +{ + SFTKSlot *slot = sftk_SlotFromSession(session); + SFTKSessionObject *so = sftk_narrowToSessionObject(object); + CK_RV crv = CKR_OK; + PRUint32 index = sftk_hash(object->handle, slot->sessObjHashSize); + + /* Handle Token case */ + if (so && so->session) { + SFTKSession *session = so->session; + PZ_Lock(session->objectLock); + sftkqueue_delete(&so->sessionList, 0, session->objects, 0); + PZ_Unlock(session->objectLock); + PZ_Lock(slot->objectLock); + sftkqueue_delete2(object, object->handle, index, slot->sessObjHashTable); + PZ_Unlock(slot->objectLock); + sftkqueue_clear_deleted_element(object); + sftk_FreeObject(object); /* free the reference owned by the queue */ + } else { + SFTKDBHandle *handle = sftk_getDBForTokenObject(slot, object->handle); +#ifdef DEBUG + SFTKTokenObject *to = sftk_narrowToTokenObject(object); + PORT_Assert(to); +#endif + crv = sftkdb_DestroyObject(handle, object->handle); + sftk_freeDB(handle); + } + return crv; +} + +/* + * Token objects don't explicitly store their attributes, so we need to know + * what attributes make up a particular token object before we can copy it. + * below are the tables by object type. + */ +static const CK_ATTRIBUTE_TYPE commonAttrs[] = { + CKA_CLASS, CKA_TOKEN, CKA_PRIVATE, CKA_LABEL, CKA_MODIFIABLE +}; +static const CK_ULONG commonAttrsCount = + sizeof(commonAttrs) / sizeof(commonAttrs[0]); + +static const CK_ATTRIBUTE_TYPE commonKeyAttrs[] = { + CKA_ID, CKA_START_DATE, CKA_END_DATE, CKA_DERIVE, CKA_LOCAL, CKA_KEY_TYPE +}; +static const CK_ULONG commonKeyAttrsCount = + sizeof(commonKeyAttrs) / sizeof(commonKeyAttrs[0]); + +static const CK_ATTRIBUTE_TYPE secretKeyAttrs[] = { + CKA_SENSITIVE, CKA_EXTRACTABLE, CKA_ENCRYPT, CKA_DECRYPT, CKA_SIGN, + CKA_VERIFY, CKA_WRAP, CKA_UNWRAP, CKA_VALUE +}; +static const CK_ULONG secretKeyAttrsCount = + sizeof(secretKeyAttrs) / sizeof(secretKeyAttrs[0]); + +static const CK_ATTRIBUTE_TYPE commonPubKeyAttrs[] = { + CKA_ENCRYPT, CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_WRAP, CKA_SUBJECT +}; +static const CK_ULONG commonPubKeyAttrsCount = + sizeof(commonPubKeyAttrs) / sizeof(commonPubKeyAttrs[0]); + +static const CK_ATTRIBUTE_TYPE rsaPubKeyAttrs[] = { + CKA_MODULUS, CKA_PUBLIC_EXPONENT +}; +static const CK_ULONG rsaPubKeyAttrsCount = + sizeof(rsaPubKeyAttrs) / sizeof(rsaPubKeyAttrs[0]); + +static const CK_ATTRIBUTE_TYPE dsaPubKeyAttrs[] = { + CKA_SUBPRIME, CKA_PRIME, CKA_BASE, CKA_VALUE +}; +static const CK_ULONG dsaPubKeyAttrsCount = + sizeof(dsaPubKeyAttrs) / sizeof(dsaPubKeyAttrs[0]); + +static const CK_ATTRIBUTE_TYPE dhPubKeyAttrs[] = { + CKA_PRIME, CKA_BASE, CKA_VALUE +}; +static const CK_ULONG dhPubKeyAttrsCount = + sizeof(dhPubKeyAttrs) / sizeof(dhPubKeyAttrs[0]); +#ifndef NSS_DISABLE_ECC +static const CK_ATTRIBUTE_TYPE ecPubKeyAttrs[] = { + CKA_EC_PARAMS, CKA_EC_POINT +}; +static const CK_ULONG ecPubKeyAttrsCount = + sizeof(ecPubKeyAttrs) / sizeof(ecPubKeyAttrs[0]); +#endif + +static const CK_ATTRIBUTE_TYPE commonPrivKeyAttrs[] = { + CKA_DECRYPT, CKA_SIGN, CKA_SIGN_RECOVER, CKA_UNWRAP, CKA_SUBJECT, + CKA_SENSITIVE, CKA_EXTRACTABLE, CKA_NETSCAPE_DB +}; +static const CK_ULONG commonPrivKeyAttrsCount = + sizeof(commonPrivKeyAttrs) / sizeof(commonPrivKeyAttrs[0]); + +static const CK_ATTRIBUTE_TYPE rsaPrivKeyAttrs[] = { + CKA_MODULUS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT, + CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT +}; +static const CK_ULONG rsaPrivKeyAttrsCount = + sizeof(rsaPrivKeyAttrs) / sizeof(rsaPrivKeyAttrs[0]); + +static const CK_ATTRIBUTE_TYPE dsaPrivKeyAttrs[] = { + CKA_SUBPRIME, CKA_PRIME, CKA_BASE, CKA_VALUE +}; +static const CK_ULONG dsaPrivKeyAttrsCount = + sizeof(dsaPrivKeyAttrs) / sizeof(dsaPrivKeyAttrs[0]); + +static const CK_ATTRIBUTE_TYPE dhPrivKeyAttrs[] = { + CKA_PRIME, CKA_BASE, CKA_VALUE +}; +static const CK_ULONG dhPrivKeyAttrsCount = + sizeof(dhPrivKeyAttrs) / sizeof(dhPrivKeyAttrs[0]); +#ifndef NSS_DISABLE_ECC +static const CK_ATTRIBUTE_TYPE ecPrivKeyAttrs[] = { + CKA_EC_PARAMS, CKA_VALUE +}; +static const CK_ULONG ecPrivKeyAttrsCount = + sizeof(ecPrivKeyAttrs) / sizeof(ecPrivKeyAttrs[0]); +#endif + +static const CK_ATTRIBUTE_TYPE certAttrs[] = { + CKA_CERTIFICATE_TYPE, CKA_VALUE, CKA_SUBJECT, CKA_ISSUER, CKA_SERIAL_NUMBER +}; +static const CK_ULONG certAttrsCount = + sizeof(certAttrs) / sizeof(certAttrs[0]); + +static const CK_ATTRIBUTE_TYPE trustAttrs[] = { + CKA_ISSUER, CKA_SERIAL_NUMBER, CKA_CERT_SHA1_HASH, CKA_CERT_MD5_HASH, + CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH, CKA_TRUST_EMAIL_PROTECTION, + CKA_TRUST_CODE_SIGNING, CKA_TRUST_STEP_UP_APPROVED +}; +static const CK_ULONG trustAttrsCount = + sizeof(trustAttrs) / sizeof(trustAttrs[0]); + +static const CK_ATTRIBUTE_TYPE smimeAttrs[] = { + CKA_SUBJECT, CKA_NETSCAPE_EMAIL, CKA_NETSCAPE_SMIME_TIMESTAMP, CKA_VALUE +}; +static const CK_ULONG smimeAttrsCount = + sizeof(smimeAttrs) / sizeof(smimeAttrs[0]); + +static const CK_ATTRIBUTE_TYPE crlAttrs[] = { + CKA_SUBJECT, CKA_VALUE, CKA_NETSCAPE_URL, CKA_NETSCAPE_KRL +}; +static const CK_ULONG crlAttrsCount = + sizeof(crlAttrs) / sizeof(crlAttrs[0]); + +/* copy an object based on it's table */ +CK_RV +stfk_CopyTokenAttributes(SFTKObject *destObject, SFTKTokenObject *src_to, + const CK_ATTRIBUTE_TYPE *attrArray, CK_ULONG attrCount) +{ + SFTKAttribute *attribute; + SFTKAttribute *newAttribute; + CK_RV crv = CKR_OK; + unsigned int i; + + for (i = 0; i < attrCount; i++) { + if (!sftk_hasAttribute(destObject, attrArray[i])) { + attribute = sftk_FindAttribute(&src_to->obj, attrArray[i]); + if (!attribute) { + continue; /* return CKR_ATTRIBUTE_VALUE_INVALID; */ + } + /* we need to copy the attribute since each attribute + * only has one set of link list pointers */ + newAttribute = sftk_NewAttribute(destObject, + sftk_attr_expand(&attribute->attrib)); + sftk_FreeAttribute(attribute); /* free the old attribute */ + if (!newAttribute) { + return CKR_HOST_MEMORY; + } + sftk_AddAttribute(destObject, newAttribute); + } + } + return crv; +} + +CK_RV +stfk_CopyTokenPrivateKey(SFTKObject *destObject, SFTKTokenObject *src_to) +{ + CK_RV crv; + CK_KEY_TYPE key_type; + SFTKAttribute *attribute; + + /* copy the common attributes for all keys first */ + crv = stfk_CopyTokenAttributes(destObject, src_to, commonKeyAttrs, + commonKeyAttrsCount); + if (crv != CKR_OK) { + goto fail; + } + /* copy the common attributes for all private keys next */ + crv = stfk_CopyTokenAttributes(destObject, src_to, commonPrivKeyAttrs, + commonPrivKeyAttrsCount); + if (crv != CKR_OK) { + goto fail; + } + attribute = sftk_FindAttribute(&src_to->obj, CKA_KEY_TYPE); + PORT_Assert(attribute); /* if it wasn't here, ww should have failed + * copying the common attributes */ + if (!attribute) { + /* OK, so CKR_ATTRIBUTE_VALUE_INVALID is the immediate error, but + * the fact is, the only reason we couldn't get the attribute would + * be a memory error or database error (an error in the 'device'). + * if we have a database error code, we could return it here */ + crv = CKR_DEVICE_ERROR; + goto fail; + } + key_type = *(CK_KEY_TYPE *)attribute->attrib.pValue; + sftk_FreeAttribute(attribute); + + /* finally copy the attributes for various private key types */ + switch (key_type) { + case CKK_RSA: + crv = stfk_CopyTokenAttributes(destObject, src_to, rsaPrivKeyAttrs, + rsaPrivKeyAttrsCount); + break; + case CKK_DSA: + crv = stfk_CopyTokenAttributes(destObject, src_to, dsaPrivKeyAttrs, + dsaPrivKeyAttrsCount); + break; + case CKK_DH: + crv = stfk_CopyTokenAttributes(destObject, src_to, dhPrivKeyAttrs, + dhPrivKeyAttrsCount); + break; +#ifndef NSS_DISABLE_ECC + case CKK_EC: + crv = stfk_CopyTokenAttributes(destObject, src_to, ecPrivKeyAttrs, + ecPrivKeyAttrsCount); + break; +#endif + default: + crv = CKR_DEVICE_ERROR; /* shouldn't happen unless we store more types + * of token keys into our database. */ + } +fail: + return crv; +} + +CK_RV +stfk_CopyTokenPublicKey(SFTKObject *destObject, SFTKTokenObject *src_to) +{ + CK_RV crv; + CK_KEY_TYPE key_type; + SFTKAttribute *attribute; + + /* copy the common attributes for all keys first */ + crv = stfk_CopyTokenAttributes(destObject, src_to, commonKeyAttrs, + commonKeyAttrsCount); + if (crv != CKR_OK) { + goto fail; + } + + /* copy the common attributes for all public keys next */ + crv = stfk_CopyTokenAttributes(destObject, src_to, commonPubKeyAttrs, + commonPubKeyAttrsCount); + if (crv != CKR_OK) { + goto fail; + } + attribute = sftk_FindAttribute(&src_to->obj, CKA_KEY_TYPE); + PORT_Assert(attribute); /* if it wasn't here, ww should have failed + * copying the common attributes */ + if (!attribute) { + /* OK, so CKR_ATTRIBUTE_VALUE_INVALID is the immediate error, but + * the fact is, the only reason we couldn't get the attribute would + * be a memory error or database error (an error in the 'device'). + * if we have a database error code, we could return it here */ + crv = CKR_DEVICE_ERROR; + goto fail; + } + key_type = *(CK_KEY_TYPE *)attribute->attrib.pValue; + sftk_FreeAttribute(attribute); + + /* finally copy the attributes for various public key types */ + switch (key_type) { + case CKK_RSA: + crv = stfk_CopyTokenAttributes(destObject, src_to, rsaPubKeyAttrs, + rsaPubKeyAttrsCount); + break; + case CKK_DSA: + crv = stfk_CopyTokenAttributes(destObject, src_to, dsaPubKeyAttrs, + dsaPubKeyAttrsCount); + break; + case CKK_DH: + crv = stfk_CopyTokenAttributes(destObject, src_to, dhPubKeyAttrs, + dhPubKeyAttrsCount); + break; +#ifndef NSS_DISABLE_ECC + case CKK_EC: + crv = stfk_CopyTokenAttributes(destObject, src_to, ecPubKeyAttrs, + ecPubKeyAttrsCount); + break; +#endif + default: + crv = CKR_DEVICE_ERROR; /* shouldn't happen unless we store more types + * of token keys into our database. */ + } +fail: + return crv; +} +CK_RV +stfk_CopyTokenSecretKey(SFTKObject *destObject, SFTKTokenObject *src_to) +{ + CK_RV crv; + crv = stfk_CopyTokenAttributes(destObject, src_to, commonKeyAttrs, + commonKeyAttrsCount); + if (crv != CKR_OK) { + goto fail; + } + crv = stfk_CopyTokenAttributes(destObject, src_to, secretKeyAttrs, + secretKeyAttrsCount); +fail: + return crv; +} + +/* + * Copy a token object. We need to explicitly copy the relevant + * attributes since token objects don't store those attributes in + * the token itself. + */ +CK_RV +sftk_CopyTokenObject(SFTKObject *destObject, SFTKObject *srcObject) +{ + SFTKTokenObject *src_to = sftk_narrowToTokenObject(srcObject); + CK_RV crv; + + PORT_Assert(src_to); + if (src_to == NULL) { + return CKR_DEVICE_ERROR; /* internal state inconsistant */ + } + + crv = stfk_CopyTokenAttributes(destObject, src_to, commonAttrs, + commonAttrsCount); + if (crv != CKR_OK) { + goto fail; + } + switch (src_to->obj.objclass) { + case CKO_CERTIFICATE: + crv = stfk_CopyTokenAttributes(destObject, src_to, certAttrs, + certAttrsCount); + break; + case CKO_NETSCAPE_TRUST: + crv = stfk_CopyTokenAttributes(destObject, src_to, trustAttrs, + trustAttrsCount); + break; + case CKO_NETSCAPE_SMIME: + crv = stfk_CopyTokenAttributes(destObject, src_to, smimeAttrs, + smimeAttrsCount); + break; + case CKO_NETSCAPE_CRL: + crv = stfk_CopyTokenAttributes(destObject, src_to, crlAttrs, + crlAttrsCount); + break; + case CKO_PRIVATE_KEY: + crv = stfk_CopyTokenPrivateKey(destObject, src_to); + break; + case CKO_PUBLIC_KEY: + crv = stfk_CopyTokenPublicKey(destObject, src_to); + break; + case CKO_SECRET_KEY: + crv = stfk_CopyTokenSecretKey(destObject, src_to); + break; + default: + crv = CKR_DEVICE_ERROR; /* shouldn't happen unless we store more types + * of token keys into our database. */ + } +fail: + return crv; +} + +/* + * copy the attributes from one object to another. Don't overwrite existing + * attributes. NOTE: This is a pretty expensive operation since it + * grabs the attribute locks for the src object for a *long* time. + */ +CK_RV +sftk_CopyObject(SFTKObject *destObject, SFTKObject *srcObject) +{ + SFTKAttribute *attribute; + SFTKSessionObject *src_so = sftk_narrowToSessionObject(srcObject); + unsigned int i; + + if (src_so == NULL) { + return sftk_CopyTokenObject(destObject, srcObject); + } + + PZ_Lock(src_so->attributeLock); + for (i = 0; i < src_so->hashSize; i++) { + attribute = src_so->head[i]; + do { + if (attribute) { + if (!sftk_hasAttribute(destObject, attribute->handle)) { + /* we need to copy the attribute since each attribute + * only has one set of link list pointers */ + SFTKAttribute *newAttribute = sftk_NewAttribute( + destObject, sftk_attr_expand(&attribute->attrib)); + if (newAttribute == NULL) { + PZ_Unlock(src_so->attributeLock); + return CKR_HOST_MEMORY; + } + sftk_AddAttribute(destObject, newAttribute); + } + attribute = attribute->next; + } + } while (attribute != NULL); + } + PZ_Unlock(src_so->attributeLock); + + return CKR_OK; +} + +/* + * ******************** Search Utilities ******************************* + */ + +/* add an object to a search list */ +CK_RV +AddToList(SFTKObjectListElement **list, SFTKObject *object) +{ + SFTKObjectListElement *newElem = + (SFTKObjectListElement *)PORT_Alloc(sizeof(SFTKObjectListElement)); + + if (newElem == NULL) + return CKR_HOST_MEMORY; + + newElem->next = *list; + newElem->object = object; + sftk_ReferenceObject(object); + + *list = newElem; + return CKR_OK; +} + +/* return true if the object matches the template */ +PRBool +sftk_objectMatch(SFTKObject *object, CK_ATTRIBUTE_PTR theTemplate, int count) +{ + int i; + + for (i = 0; i < count; i++) { + SFTKAttribute *attribute = sftk_FindAttribute(object, theTemplate[i].type); + if (attribute == NULL) { + return PR_FALSE; + } + if (attribute->attrib.ulValueLen == theTemplate[i].ulValueLen) { + if (PORT_Memcmp(attribute->attrib.pValue, theTemplate[i].pValue, + theTemplate[i].ulValueLen) == 0) { + sftk_FreeAttribute(attribute); + continue; + } + } + sftk_FreeAttribute(attribute); + return PR_FALSE; + } + return PR_TRUE; +} + +/* search through all the objects in the queue and return the template matches + * in the object list. + */ +CK_RV +sftk_searchObjectList(SFTKSearchResults *search, SFTKObject **head, + unsigned int size, PZLock *lock, CK_ATTRIBUTE_PTR theTemplate, + int count, PRBool isLoggedIn) +{ + unsigned int i; + SFTKObject *object; + CK_RV crv = CKR_OK; + + for (i = 0; i < size; i++) { + /* We need to hold the lock to copy a consistant version of + * the linked list. */ + PZ_Lock(lock); + for (object = head[i]; object != NULL; object = object->next) { + if (sftk_objectMatch(object, theTemplate, count)) { + /* don't return objects that aren't yet visible */ + if ((!isLoggedIn) && sftk_isTrue(object, CKA_PRIVATE)) + continue; + sftk_addHandle(search, object->handle); + } + } + PZ_Unlock(lock); + } + return crv; +} + +/* + * free a single list element. Return the Next object in the list. + */ +SFTKObjectListElement * +sftk_FreeObjectListElement(SFTKObjectListElement *objectList) +{ + SFTKObjectListElement *ol = objectList->next; + + sftk_FreeObject(objectList->object); + PORT_Free(objectList); + return ol; +} + +/* free an entire object list */ +void +sftk_FreeObjectList(SFTKObjectListElement *objectList) +{ + SFTKObjectListElement *ol; + + for (ol = objectList; ol != NULL; ol = sftk_FreeObjectListElement(ol)) { + } +} + +/* + * free a search structure + */ +void +sftk_FreeSearch(SFTKSearchResults *search) +{ + if (search->handles) { + PORT_Free(search->handles); + } + PORT_Free(search); +} + +/* + * ******************** Session Utilities ******************************* + */ + +/* update the sessions state based in it's flags and wether or not it's + * logged in */ +void +sftk_update_state(SFTKSlot *slot, SFTKSession *session) +{ + if (slot->isLoggedIn) { + if (slot->ssoLoggedIn) { + session->info.state = CKS_RW_SO_FUNCTIONS; + } else if (session->info.flags & CKF_RW_SESSION) { + session->info.state = CKS_RW_USER_FUNCTIONS; + } else { + session->info.state = CKS_RO_USER_FUNCTIONS; + } + } else { + if (session->info.flags & CKF_RW_SESSION) { + session->info.state = CKS_RW_PUBLIC_SESSION; + } else { + session->info.state = CKS_RO_PUBLIC_SESSION; + } + } +} + +/* update the state of all the sessions on a slot */ +void +sftk_update_all_states(SFTKSlot *slot) +{ + unsigned int i; + SFTKSession *session; + + for (i = 0; i < slot->sessHashSize; i++) { + PZLock *lock = SFTK_SESSION_LOCK(slot, i); + PZ_Lock(lock); + for (session = slot->head[i]; session; session = session->next) { + sftk_update_state(slot, session); + } + PZ_Unlock(lock); + } +} + +/* + * context are cipher and digest contexts that are associated with a session + */ +void +sftk_FreeContext(SFTKSessionContext *context) +{ + if (context->cipherInfo) { + (*context->destroy)(context->cipherInfo, PR_TRUE); + } + if (context->hashInfo) { + (*context->hashdestroy)(context->hashInfo, PR_TRUE); + } + if (context->key) { + sftk_FreeObject(context->key); + context->key = NULL; + } + PORT_Free(context); +} + +/* + * create a new nession. NOTE: The session handle is not set, and the + * session is not added to the slot's session queue. + */ +SFTKSession * +sftk_NewSession(CK_SLOT_ID slotID, CK_NOTIFY notify, CK_VOID_PTR pApplication, + CK_FLAGS flags) +{ + SFTKSession *session; + SFTKSlot *slot = sftk_SlotFromID(slotID, PR_FALSE); + + if (slot == NULL) + return NULL; + + session = (SFTKSession *)PORT_Alloc(sizeof(SFTKSession)); + if (session == NULL) + return NULL; + + session->next = session->prev = NULL; + session->refCount = 1; + session->enc_context = NULL; + session->hash_context = NULL; + session->sign_context = NULL; + session->search = NULL; + session->objectIDCount = 1; + session->objectLock = PZ_NewLock(nssILockObject); + if (session->objectLock == NULL) { + PORT_Free(session); + return NULL; + } + session->objects[0] = NULL; + + session->slot = slot; + session->notify = notify; + session->appData = pApplication; + session->info.flags = flags; + session->info.slotID = slotID; + session->info.ulDeviceError = 0; + sftk_update_state(slot, session); + return session; +} + +/* free all the data associated with a session. */ +static void +sftk_DestroySession(SFTKSession *session) +{ + SFTKObjectList *op, *next; + PORT_Assert(session->refCount == 0); + + /* clean out the attributes */ + /* since no one is referencing us, it's safe to walk the chain + * without a lock */ + for (op = session->objects[0]; op != NULL; op = next) { + next = op->next; + /* paranoia */ + op->next = op->prev = NULL; + sftk_DeleteObject(session, op->parent); + } + PZ_DestroyLock(session->objectLock); + if (session->enc_context) { + sftk_FreeContext(session->enc_context); + } + if (session->hash_context) { + sftk_FreeContext(session->hash_context); + } + if (session->sign_context) { + sftk_FreeContext(session->sign_context); + } + if (session->search) { + sftk_FreeSearch(session->search); + } + PORT_Free(session); +} + +/* + * look up a session structure from a session handle + * generate a reference to it. + */ +SFTKSession * +sftk_SessionFromHandle(CK_SESSION_HANDLE handle) +{ + SFTKSlot *slot = sftk_SlotFromSessionHandle(handle); + SFTKSession *session; + PZLock *lock; + + if (!slot) + return NULL; + lock = SFTK_SESSION_LOCK(slot, handle); + + PZ_Lock(lock); + sftkqueue_find(session, handle, slot->head, slot->sessHashSize); + if (session) + session->refCount++; + PZ_Unlock(lock); + + return (session); +} + +/* + * release a reference to a session handle + */ +void +sftk_FreeSession(SFTKSession *session) +{ + PRBool destroy = PR_FALSE; + SFTKSlot *slot = sftk_SlotFromSession(session); + PZLock *lock = SFTK_SESSION_LOCK(slot, session->handle); + + PZ_Lock(lock); + if (session->refCount == 1) + destroy = PR_TRUE; + session->refCount--; + PZ_Unlock(lock); + + if (destroy) + sftk_DestroySession(session); +} + +void +sftk_addHandle(SFTKSearchResults *search, CK_OBJECT_HANDLE handle) +{ + if (search->handles == NULL) { + return; + } + if (search->size >= search->array_size) { + search->array_size += NSC_SEARCH_BLOCK_SIZE; + search->handles = (CK_OBJECT_HANDLE *)PORT_Realloc(search->handles, + sizeof(CK_OBJECT_HANDLE) * search->array_size); + if (search->handles == NULL) { + return; + } + } + search->handles[search->size] = handle; + search->size++; +} + +static CK_RV +handleToClass(SFTKSlot *slot, CK_OBJECT_HANDLE handle, + CK_OBJECT_CLASS *objClass) +{ + SFTKDBHandle *dbHandle = sftk_getDBForTokenObject(slot, handle); + CK_ATTRIBUTE objClassTemplate; + CK_RV crv; + + *objClass = CKO_DATA; + objClassTemplate.type = CKA_CLASS; + objClassTemplate.pValue = objClass; + objClassTemplate.ulValueLen = sizeof(*objClass); + crv = sftkdb_GetAttributeValue(dbHandle, handle, &objClassTemplate, 1); + sftk_freeDB(dbHandle); + return crv; +} + +SFTKObject * +sftk_NewTokenObject(SFTKSlot *slot, SECItem *dbKey, CK_OBJECT_HANDLE handle) +{ + SFTKObject *object = NULL; + PRBool hasLocks = PR_FALSE; + CK_RV crv; + + object = sftk_GetObjectFromList(&hasLocks, PR_FALSE, &tokenObjectList, 0, + PR_FALSE); + if (object == NULL) { + return NULL; + } + + object->handle = handle; + /* every object must have a class, if we can't get it, the object + * doesn't exist */ + crv = handleToClass(slot, handle, &object->objclass); + if (crv != CKR_OK) { + goto loser; + } + object->slot = slot; + object->objectInfo = NULL; + object->infoFree = NULL; + if (!hasLocks) { + object->refLock = PZ_NewLock(nssILockRefLock); + } + if (object->refLock == NULL) { + goto loser; + } + object->refCount = 1; + + return object; +loser: + (void)sftk_DestroyObject(object); + return NULL; +} + +SFTKTokenObject * +sftk_convertSessionToToken(SFTKObject *obj) +{ + SECItem *key; + SFTKSessionObject *so = (SFTKSessionObject *)obj; + SFTKTokenObject *to = sftk_narrowToTokenObject(obj); + SECStatus rv; + + sftk_DestroySessionObjectData(so); + PZ_DestroyLock(so->attributeLock); + if (to == NULL) { + return NULL; + } + sftk_tokenKeyLock(so->obj.slot); + key = sftk_lookupTokenKeyByHandle(so->obj.slot, so->obj.handle); + if (key == NULL) { + sftk_tokenKeyUnlock(so->obj.slot); + return NULL; + } + rv = SECITEM_CopyItem(NULL, &to->dbKey, key); + sftk_tokenKeyUnlock(so->obj.slot); + if (rv == SECFailure) { + return NULL; + } + + return to; +} + +SFTKSessionObject * +sftk_narrowToSessionObject(SFTKObject *obj) +{ + return !sftk_isToken(obj->handle) ? (SFTKSessionObject *)obj : NULL; +} + +SFTKTokenObject * +sftk_narrowToTokenObject(SFTKObject *obj) +{ + return sftk_isToken(obj->handle) ? (SFTKTokenObject *)obj : NULL; +} |