blob: d872bf4b396c45af0a1aecf89b27c1e64467b8c2 [file] [log] [blame]
/* 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);
}