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