| /* 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/. */ |
| #include "lgdb.h" |
| #include "secerr.h" |
| #include "lgglue.h" |
| |
| /* |
| * ******************** Attribute Utilities ******************************* |
| */ |
| |
| /* |
| * 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. |
| */ |
| const CK_ATTRIBUTE * |
| lg_FindAttribute(CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE *templ, |
| CK_ULONG count) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < count; i++) { |
| if (templ[i].type == type) { |
| return &templ[i]; |
| } |
| } |
| return NULL; |
| } |
| |
| /* |
| * return true if object has attribute |
| */ |
| PRBool |
| lg_hasAttribute(CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE *templ, |
| CK_ULONG count) |
| { |
| if (lg_FindAttribute(type, templ, count) == NULL) { |
| return PR_FALSE; |
| } |
| return PR_TRUE; |
| } |
| |
| /* |
| * copy an attribute into a SECItem. Secitem is allocated in the specified |
| * arena. |
| */ |
| CK_RV |
| lg_Attribute2SecItem(PLArenaPool *arena, CK_ATTRIBUTE_TYPE type, |
| const CK_ATTRIBUTE *templ, CK_ULONG count, |
| SECItem *item) |
| { |
| int len; |
| const CK_ATTRIBUTE *attribute; |
| |
| attribute = lg_FindAttribute(type, templ, count); |
| if (attribute == NULL) |
| return CKR_TEMPLATE_INCOMPLETE; |
| len = attribute->ulValueLen; |
| |
| if (arena) { |
| item->data = (unsigned char *)PORT_ArenaAlloc(arena, len); |
| } else { |
| item->data = (unsigned char *)PORT_Alloc(len); |
| } |
| if (item->data == NULL) { |
| return CKR_HOST_MEMORY; |
| } |
| item->len = len; |
| if (item->len) { |
| PORT_Memcpy(item->data, attribute->pValue, len); |
| } |
| return CKR_OK; |
| } |
| |
| /* |
| * copy an unsigned attribute into a SECItem. Secitem is allocated in |
| * the specified arena. |
| */ |
| CK_RV |
| lg_Attribute2SSecItem(PLArenaPool *arena, CK_ATTRIBUTE_TYPE type, |
| const CK_ATTRIBUTE *templ, CK_ULONG count, |
| SECItem *item) |
| { |
| const CK_ATTRIBUTE *attribute; |
| item->data = NULL; |
| |
| attribute = lg_FindAttribute(type, templ, count); |
| if (attribute == NULL) |
| return CKR_TEMPLATE_INCOMPLETE; |
| |
| (void)SECITEM_AllocItem(arena, item, attribute->ulValueLen); |
| if (item->data == NULL) { |
| return CKR_HOST_MEMORY; |
| } |
| PORT_Memcpy(item->data, attribute->pValue, item->len); |
| return CKR_OK; |
| } |
| |
| /* |
| * copy an unsigned attribute into a SECItem. Secitem is allocated in |
| * the specified arena. |
| */ |
| CK_RV |
| lg_PrivAttr2SSecItem(PLArenaPool *arena, CK_ATTRIBUTE_TYPE type, |
| const CK_ATTRIBUTE *templ, CK_ULONG count, |
| SECItem *item, SDB *sdbpw) |
| { |
| const CK_ATTRIBUTE *attribute; |
| SECItem epki, *dest = NULL; |
| SECStatus rv; |
| |
| item->data = NULL; |
| |
| attribute = lg_FindAttribute(type, templ, count); |
| if (attribute == NULL) |
| return CKR_TEMPLATE_INCOMPLETE; |
| |
| epki.data = attribute->pValue; |
| epki.len = attribute->ulValueLen; |
| |
| rv = lg_util_decrypt(sdbpw, &epki, &dest); |
| if (rv != SECSuccess) { |
| return CKR_USER_NOT_LOGGED_IN; |
| } |
| (void)SECITEM_AllocItem(arena, item, dest->len); |
| if (item->data == NULL) { |
| SECITEM_FreeItem(dest, PR_TRUE); |
| return CKR_HOST_MEMORY; |
| } |
| |
| PORT_Memcpy(item->data, dest->data, item->len); |
| SECITEM_FreeItem(dest, PR_TRUE); |
| return CKR_OK; |
| } |
| |
| CK_RV |
| lg_PrivAttr2SecItem(PLArenaPool *arena, CK_ATTRIBUTE_TYPE type, |
| const CK_ATTRIBUTE *templ, CK_ULONG count, |
| SECItem *item, SDB *sdbpw) |
| { |
| return lg_PrivAttr2SSecItem(arena, type, templ, count, item, sdbpw); |
| } |
| |
| /* |
| * this is only valid for CK_BBOOL type attributes. Return the state |
| * of that attribute. |
| */ |
| PRBool |
| lg_isTrue(CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE *templ, CK_ULONG count) |
| { |
| const CK_ATTRIBUTE *attribute; |
| PRBool tok = PR_FALSE; |
| |
| attribute = lg_FindAttribute(type, templ, count); |
| if (attribute == NULL) { |
| return PR_FALSE; |
| } |
| tok = (PRBool)(*(CK_BBOOL *)attribute->pValue); |
| |
| return tok; |
| } |
| |
| /* |
| * return a null terminated string from attribute 'type'. This string |
| * is allocated and needs to be freed with PORT_Free() When complete. |
| */ |
| char * |
| lg_getString(CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE *templ, CK_ULONG count) |
| { |
| const CK_ATTRIBUTE *attribute; |
| char *label = NULL; |
| |
| attribute = lg_FindAttribute(type, templ, count); |
| if (attribute == NULL) |
| return NULL; |
| |
| if (attribute->pValue != NULL) { |
| label = (char *)PORT_Alloc(attribute->ulValueLen + 1); |
| if (label == NULL) { |
| return NULL; |
| } |
| |
| PORT_Memcpy(label, attribute->pValue, attribute->ulValueLen); |
| label[attribute->ulValueLen] = 0; |
| } |
| return label; |
| } |
| |
| CK_RV |
| lg_GetULongAttribute(CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE *templ, |
| CK_ULONG count, CK_ULONG *longData) |
| { |
| const CK_ATTRIBUTE *attribute; |
| CK_ULONG value = 0; |
| const unsigned char *data; |
| int i; |
| |
| attribute = lg_FindAttribute(type, templ, count); |
| if (attribute == NULL) |
| return CKR_TEMPLATE_INCOMPLETE; |
| |
| if (attribute->ulValueLen != 4) { |
| return CKR_ATTRIBUTE_VALUE_INVALID; |
| } |
| data = (const unsigned char *)attribute->pValue; |
| for (i = 0; i < 4; i++) { |
| value |= (CK_ULONG)(data[i]) << ((3 - i) * 8); |
| } |
| |
| *longData = value; |
| return CKR_OK; |
| } |
| |
| /* |
| * ******************** Object Utilities ******************************* |
| */ |
| |
| SECStatus |
| lg_deleteTokenKeyByHandle(SDB *sdb, CK_OBJECT_HANDLE handle) |
| { |
| SECItem *item; |
| PRBool rem; |
| PLHashTable *hashTable = lg_GetHashTable(sdb); |
| |
| item = (SECItem *)PL_HashTableLookup(hashTable, (void *)handle); |
| rem = PL_HashTableRemove(hashTable, (void *)handle); |
| if (rem && item) { |
| SECITEM_FreeItem(item, PR_TRUE); |
| } |
| return rem ? SECSuccess : SECFailure; |
| } |
| |
| /* must be called holding lg_DBLock(sdb) */ |
| static SECStatus |
| lg_addTokenKeyByHandle(SDB *sdb, CK_OBJECT_HANDLE handle, SECItem *key) |
| { |
| PLHashEntry *entry; |
| SECItem *item; |
| PLHashTable *hashTable = lg_GetHashTable(sdb); |
| |
| item = SECITEM_DupItem(key); |
| if (item == NULL) { |
| return SECFailure; |
| } |
| entry = PL_HashTableAdd(hashTable, (void *)handle, item); |
| if (entry == NULL) { |
| SECITEM_FreeItem(item, PR_TRUE); |
| return SECFailure; |
| } |
| return SECSuccess; |
| } |
| |
| /* must be called holding lg_DBLock(sdb) */ |
| const SECItem * |
| lg_lookupTokenKeyByHandle(SDB *sdb, CK_OBJECT_HANDLE handle) |
| { |
| PLHashTable *hashTable = lg_GetHashTable(sdb); |
| return (const SECItem *)PL_HashTableLookup(hashTable, (void *)handle); |
| } |
| |
| static PRIntn |
| lg_freeHashItem(PLHashEntry *entry, PRIntn index, void *arg) |
| { |
| SECItem *item = (SECItem *)entry->value; |
| |
| SECITEM_FreeItem(item, PR_TRUE); |
| return HT_ENUMERATE_NEXT; |
| } |
| |
| CK_RV |
| lg_ClearTokenKeyHashTable(SDB *sdb) |
| { |
| PLHashTable *hashTable; |
| lg_DBLock(sdb); |
| hashTable = lg_GetHashTable(sdb); |
| PL_HashTableEnumerateEntries(hashTable, lg_freeHashItem, NULL); |
| lg_DBUnlock(sdb); |
| return CKR_OK; |
| } |
| |
| /* |
| * handle Token Object stuff |
| */ |
| static void |
| lg_XORHash(unsigned char *key, unsigned char *dbkey, int len) |
| { |
| int i; |
| |
| PORT_Memset(key, 0, 4); |
| |
| for (i = 0; i < len - 4; i += 4) { |
| key[0] ^= dbkey[i]; |
| key[1] ^= dbkey[i + 1]; |
| key[2] ^= dbkey[i + 2]; |
| key[3] ^= dbkey[i + 3]; |
| } |
| } |
| |
| /* Make a token handle for an object and record it so we can find it again */ |
| CK_OBJECT_HANDLE |
| lg_mkHandle(SDB *sdb, SECItem *dbKey, CK_OBJECT_HANDLE class) |
| { |
| unsigned char hashBuf[4]; |
| CK_OBJECT_HANDLE handle; |
| const SECItem *key; |
| |
| handle = class; |
| /* there is only one KRL, use a fixed handle for it */ |
| if (handle != LG_TOKEN_KRL_HANDLE) { |
| lg_XORHash(hashBuf, dbKey->data, dbKey->len); |
| handle = ((CK_OBJECT_HANDLE)hashBuf[0] << 24) | |
| ((CK_OBJECT_HANDLE)hashBuf[1] << 16) | |
| ((CK_OBJECT_HANDLE)hashBuf[2] << 8) | |
| (CK_OBJECT_HANDLE)hashBuf[3]; |
| handle = class | (handle & ~(LG_TOKEN_TYPE_MASK | LG_TOKEN_MASK)); |
| /* we have a CRL who's handle has randomly matched the reserved KRL |
| * handle, increment it */ |
| if (handle == LG_TOKEN_KRL_HANDLE) { |
| handle++; |
| } |
| } |
| |
| lg_DBLock(sdb); |
| while ((key = lg_lookupTokenKeyByHandle(sdb, handle)) != NULL) { |
| if (SECITEM_ItemsAreEqual(key, dbKey)) { |
| lg_DBUnlock(sdb); |
| return handle; |
| } |
| handle++; |
| } |
| lg_addTokenKeyByHandle(sdb, handle, dbKey); |
| lg_DBUnlock(sdb); |
| return handle; |
| } |
| |
| PRBool |
| lg_poisonHandle(SDB *sdb, SECItem *dbKey, CK_OBJECT_HANDLE class) |
| { |
| unsigned char hashBuf[4]; |
| CK_OBJECT_HANDLE handle; |
| const SECItem *key; |
| |
| handle = class; |
| /* there is only one KRL, use a fixed handle for it */ |
| if (handle != LG_TOKEN_KRL_HANDLE) { |
| lg_XORHash(hashBuf, dbKey->data, dbKey->len); |
| handle = (hashBuf[0] << 24) | (hashBuf[1] << 16) | |
| (hashBuf[2] << 8) | hashBuf[3]; |
| handle = class | (handle & ~(LG_TOKEN_TYPE_MASK | LG_TOKEN_MASK)); |
| /* we have a CRL who's handle has randomly matched the reserved KRL |
| * handle, increment it */ |
| if (handle == LG_TOKEN_KRL_HANDLE) { |
| handle++; |
| } |
| } |
| lg_DBLock(sdb); |
| while ((key = lg_lookupTokenKeyByHandle(sdb, handle)) != NULL) { |
| if (SECITEM_ItemsAreEqual(key, dbKey)) { |
| key->data[0] ^= 0x80; |
| lg_DBUnlock(sdb); |
| return PR_TRUE; |
| } |
| handle++; |
| } |
| lg_DBUnlock(sdb); |
| return PR_FALSE; |
| } |
| |
| static LGEncryptFunc lg_encrypt_stub = NULL; |
| static LGDecryptFunc lg_decrypt_stub = NULL; |
| |
| void |
| legacy_SetCryptFunctions(LGEncryptFunc enc, LGDecryptFunc dec) |
| { |
| lg_encrypt_stub = enc; |
| lg_decrypt_stub = dec; |
| } |
| |
| SECStatus |
| lg_util_encrypt(PLArenaPool *arena, SDB *sdb, |
| SECItem *plainText, SECItem **cipherText) |
| { |
| if (lg_encrypt_stub == NULL) { |
| PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
| return SECFailure; |
| } |
| return (*lg_encrypt_stub)(arena, sdb, plainText, cipherText); |
| } |
| |
| SECStatus |
| lg_util_decrypt(SDB *sdb, SECItem *cipherText, SECItem **plainText) |
| { |
| if (lg_decrypt_stub == NULL) { |
| PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
| return SECFailure; |
| } |
| return (*lg_decrypt_stub)(sdb, cipherText, plainText); |
| } |