diff options
Diffstat (limited to 'security/nss/lib/softoken/legacydb/lgutil.c')
-rw-r--r-- | security/nss/lib/softoken/legacydb/lgutil.c | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/security/nss/lib/softoken/legacydb/lgutil.c b/security/nss/lib/softoken/legacydb/lgutil.c new file mode 100644 index 000000000..d872bf4b3 --- /dev/null +++ b/security/nss/lib/softoken/legacydb/lgutil.c @@ -0,0 +1,399 @@ +/* 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); +} |