/* 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); }