Source code

Revision control

Copy as Markdown

Other Tools

/* 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/. */
/*
* This file manages object type indepentent functions.
*/
#include <limits.h>
#include <stddef.h>
#include "seccomon.h"
#include "secder.h"
#include "secmod.h"
#include "secmodi.h"
#include "secmodti.h"
#include "pkcs11.h"
#include "pkcs11t.h"
#include "pk11func.h"
#include "keyhi.h"
#include "secitem.h"
#include "secerr.h"
#include "sslerr.h"
#define PK11_SEARCH_CHUNKSIZE 10
/*
* Build a block big enough to hold the data
*/
SECItem *
PK11_BlockData(SECItem *data, unsigned long size)
{
SECItem *newData;
if (size == 0u)
return NULL;
newData = (SECItem *)PORT_Alloc(sizeof(SECItem));
if (newData == NULL)
return NULL;
newData->len = (data->len + (size - 1)) / size;
newData->len *= size;
newData->data = (unsigned char *)PORT_ZAlloc(newData->len);
if (newData->data == NULL) {
PORT_Free(newData);
return NULL;
}
PORT_Memset(newData->data, newData->len - data->len, newData->len);
PORT_Memcpy(newData->data, data->data, data->len);
return newData;
}
SECStatus
PK11_DestroyObject(PK11SlotInfo *slot, CK_OBJECT_HANDLE object)
{
CK_RV crv;
PK11_EnterSlotMonitor(slot);
crv = PK11_GETTAB(slot)->C_DestroyObject(slot->session, object);
PK11_ExitSlotMonitor(slot);
if (crv != CKR_OK) {
return SECFailure;
}
return SECSuccess;
}
SECStatus
PK11_DestroyTokenObject(PK11SlotInfo *slot, CK_OBJECT_HANDLE object)
{
CK_RV crv;
SECStatus rv = SECSuccess;
CK_SESSION_HANDLE rwsession;
rwsession = PK11_GetRWSession(slot);
if (rwsession == CK_INVALID_HANDLE) {
PORT_SetError(SEC_ERROR_BAD_DATA);
return SECFailure;
}
crv = PK11_GETTAB(slot)->C_DestroyObject(rwsession, object);
if (crv != CKR_OK) {
rv = SECFailure;
PORT_SetError(PK11_MapError(crv));
}
PK11_RestoreROSession(slot, rwsession);
return rv;
}
/*
* Read in a single attribute into a SECItem. Allocate space for it with
* PORT_Alloc unless an arena is supplied. In the latter case use the arena
* to allocate the space.
*
* PK11_ReadAttribute sets the 'data' and 'len' fields of the SECItem but
* does not modify its 'type' field.
*/
SECStatus
PK11_ReadAttribute(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
CK_ATTRIBUTE_TYPE type, PLArenaPool *arena, SECItem *result)
{
CK_ATTRIBUTE attr = { 0, NULL, 0 };
CK_RV crv;
attr.type = type;
PK11_EnterSlotMonitor(slot);
crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session, id, &attr, 1);
if (crv != CKR_OK) {
PK11_ExitSlotMonitor(slot);
PORT_SetError(PK11_MapError(crv));
return SECFailure;
}
if (arena) {
attr.pValue = PORT_ArenaAlloc(arena, attr.ulValueLen);
} else {
attr.pValue = PORT_Alloc(attr.ulValueLen);
}
if (attr.pValue == NULL) {
PK11_ExitSlotMonitor(slot);
return SECFailure;
}
crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session, id, &attr, 1);
PK11_ExitSlotMonitor(slot);
if (crv != CKR_OK) {
PORT_SetError(PK11_MapError(crv));
if (!arena)
PORT_Free(attr.pValue);
return SECFailure;
}
result->data = (unsigned char *)attr.pValue;
result->len = attr.ulValueLen;
return SECSuccess;
}
/*
* Read in a single attribute into a Ulong.
*/
CK_ULONG
PK11_ReadULongAttribute(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
CK_ATTRIBUTE_TYPE type)
{
CK_ATTRIBUTE attr;
CK_ULONG value = CK_UNAVAILABLE_INFORMATION;
CK_RV crv;
PK11_SETATTRS(&attr, type, &value, sizeof(value));
PK11_EnterSlotMonitor(slot);
crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session, id, &attr, 1);
PK11_ExitSlotMonitor(slot);
if (crv != CKR_OK) {
PORT_SetError(PK11_MapError(crv));
}
return value;
}
/*
* check to see if a bool has been set.
*/
CK_BBOOL
pk11_HasAttributeSet_Lock(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
CK_ATTRIBUTE_TYPE type, PRBool haslock)
{
CK_BBOOL ckvalue = CK_FALSE;
CK_ATTRIBUTE theTemplate;
CK_RV crv;
/* Prepare to retrieve the attribute. */
PK11_SETATTRS(&theTemplate, type, &ckvalue, sizeof(CK_BBOOL));
/* Retrieve attribute value. */
if (!haslock)
PK11_EnterSlotMonitor(slot);
crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session, id,
&theTemplate, 1);
if (!haslock)
PK11_ExitSlotMonitor(slot);
if (crv != CKR_OK) {
PORT_SetError(PK11_MapError(crv));
return CK_FALSE;
}
return ckvalue;
}
CK_BBOOL
PK11_HasAttributeSet(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
CK_ATTRIBUTE_TYPE type, PRBool haslock)
{
PR_ASSERT(haslock == PR_FALSE);
return pk11_HasAttributeSet_Lock(slot, id, type, PR_FALSE);
}
/*
* returns a full list of attributes. Allocate space for them. If an arena is
* provided, allocate space out of the arena.
*/
CK_RV
PK11_GetAttributes(PLArenaPool *arena, PK11SlotInfo *slot,
CK_OBJECT_HANDLE obj, CK_ATTRIBUTE *attr, int count)
{
int i;
/* make pedantic happy... note that it's only used arena != NULL */
void *mark = NULL;
CK_RV crv;
if (slot->session == CK_INVALID_HANDLE)
return CKR_SESSION_HANDLE_INVALID;
/*
* first get all the lengths of the parameters.
*/
PK11_EnterSlotMonitor(slot);
crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session, obj, attr, count);
if (crv != CKR_OK) {
PK11_ExitSlotMonitor(slot);
return crv;
}
if (arena) {
mark = PORT_ArenaMark(arena);
if (mark == NULL)
return CKR_HOST_MEMORY;
}
/*
* now allocate space to store the results.
*/
for (i = 0; i < count; i++) {
if (attr[i].ulValueLen == 0)
continue;
if (arena) {
attr[i].pValue = PORT_ArenaAlloc(arena, attr[i].ulValueLen);
if (attr[i].pValue == NULL) {
/* arena failures, just release the mark */
PORT_ArenaRelease(arena, mark);
PK11_ExitSlotMonitor(slot);
return CKR_HOST_MEMORY;
}
} else {
attr[i].pValue = PORT_Alloc(attr[i].ulValueLen);
if (attr[i].pValue == NULL) {
/* Separate malloc failures, loop to release what we have
* so far */
int j;
for (j = 0; j < i; j++) {
PORT_Free(attr[j].pValue);
/* don't give the caller pointers to freed memory */
attr[j].pValue = NULL;
}
PK11_ExitSlotMonitor(slot);
return CKR_HOST_MEMORY;
}
}
}
/*
* finally get the results.
*/
crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session, obj, attr, count);
PK11_ExitSlotMonitor(slot);
if (crv != CKR_OK) {
if (arena) {
PORT_ArenaRelease(arena, mark);
} else {
for (i = 0; i < count; i++) {
PORT_Free(attr[i].pValue);
/* don't give the caller pointers to freed memory */
attr[i].pValue = NULL;
}
}
} else if (arena && mark) {
PORT_ArenaUnmark(arena, mark);
}
return crv;
}
PRBool
PK11_IsPermObject(PK11SlotInfo *slot, CK_OBJECT_HANDLE handle)
{
return (PRBool)PK11_HasAttributeSet(slot, handle, CKA_TOKEN, PR_FALSE);
}
char *
PK11_GetObjectNickname(PK11SlotInfo *slot, CK_OBJECT_HANDLE id)
{
char *nickname = NULL;
SECItem result;
SECStatus rv;
rv = PK11_ReadAttribute(slot, id, CKA_LABEL, NULL, &result);
if (rv != SECSuccess) {
return NULL;
}
nickname = PORT_ZAlloc(result.len + 1);
if (nickname == NULL) {
PORT_Free(result.data);
return NULL;
}
PORT_Memcpy(nickname, result.data, result.len);
PORT_Free(result.data);
return nickname;
}
SECStatus
PK11_SetObjectNickname(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
const char *nickname)
{
int len = PORT_Strlen(nickname);
CK_ATTRIBUTE setTemplate;
CK_RV crv;
CK_SESSION_HANDLE rwsession;
if (len < 0) {
return SECFailure;
}
PK11_SETATTRS(&setTemplate, CKA_LABEL, (CK_CHAR *)nickname, len);
rwsession = PK11_GetRWSession(slot);
if (rwsession == CK_INVALID_HANDLE) {
PORT_SetError(SEC_ERROR_BAD_DATA);
return SECFailure;
}
crv = PK11_GETTAB(slot)->C_SetAttributeValue(rwsession, id,
&setTemplate, 1);
PK11_RestoreROSession(slot, rwsession);
if (crv != CKR_OK) {
PORT_SetError(PK11_MapError(crv));
return SECFailure;
}
return SECSuccess;
}
/*
* strip leading zero's from key material
*/
void
pk11_SignedToUnsigned(CK_ATTRIBUTE *attrib)
{
char *ptr = (char *)attrib->pValue;
unsigned long len = attrib->ulValueLen;
while ((len > 1) && (*ptr == 0)) {
len--;
ptr++;
}
attrib->pValue = ptr;
attrib->ulValueLen = len;
}
/*
* get a new session on a slot. If we run out of session, use the slot's
* 'exclusive' session. In this case owner becomes false.
*/
CK_SESSION_HANDLE
pk11_GetNewSession(PK11SlotInfo *slot, PRBool *owner)
{
CK_SESSION_HANDLE session;
*owner = PR_TRUE;
if (!slot->isThreadSafe)
PK11_EnterSlotMonitor(slot);
if (PK11_GETTAB(slot)->C_OpenSession(slot->slotID, CKF_SERIAL_SESSION,
slot, pk11_notify, &session) != CKR_OK) {
*owner = PR_FALSE;
session = slot->session;
}
if (!slot->isThreadSafe)
PK11_ExitSlotMonitor(slot);
return session;
}
void
pk11_CloseSession(PK11SlotInfo *slot, CK_SESSION_HANDLE session, PRBool owner)
{
if (!owner)
return;
if (!slot->isThreadSafe)
PK11_EnterSlotMonitor(slot);
(void)PK11_GETTAB(slot)->C_CloseSession(session);
if (!slot->isThreadSafe)
PK11_ExitSlotMonitor(slot);
}
SECStatus
PK11_CreateNewObject(PK11SlotInfo *slot, CK_SESSION_HANDLE session,
const CK_ATTRIBUTE *theTemplate, int count,
PRBool token, CK_OBJECT_HANDLE *objectID)
{
CK_SESSION_HANDLE rwsession;
CK_RV crv;
SECStatus rv = SECSuccess;
rwsession = session;
if (token) {
rwsession = PK11_GetRWSession(slot);
} else if (rwsession == CK_INVALID_HANDLE) {
rwsession = slot->session;
if (rwsession != CK_INVALID_HANDLE)
PK11_EnterSlotMonitor(slot);
}
if (rwsession == CK_INVALID_HANDLE) {
PORT_SetError(SEC_ERROR_BAD_DATA);
return SECFailure;
}
crv = PK11_GETTAB(slot)->C_CreateObject(rwsession,
/* cast away const :-( */ (CK_ATTRIBUTE_PTR)theTemplate,
count, objectID);
if (crv != CKR_OK) {
PORT_SetError(PK11_MapError(crv));
rv = SECFailure;
}
if (token) {
PK11_RestoreROSession(slot, rwsession);
} else if (session == CK_INVALID_HANDLE) {
PK11_ExitSlotMonitor(slot);
}
return rv;
}
/* This function may add a maximum of 9 attributes. */
unsigned int
pk11_OpFlagsToAttributes(CK_FLAGS flags, CK_ATTRIBUTE *attrs, CK_BBOOL *ckTrue)
{
const static CK_ATTRIBUTE_TYPE attrTypes[12] = {
CKA_ENCRYPT, CKA_DECRYPT, 0 /* DIGEST */, CKA_SIGN,
CKA_SIGN_RECOVER, CKA_VERIFY, CKA_VERIFY_RECOVER, 0 /* GEN */,
0 /* GEN PAIR */, CKA_WRAP, CKA_UNWRAP, CKA_DERIVE
};
const CK_ATTRIBUTE_TYPE *pType = attrTypes;
CK_ATTRIBUTE *attr = attrs;
CK_FLAGS test = CKF_ENCRYPT;
PR_ASSERT(!(flags & ~CKF_KEY_OPERATION_FLAGS));
flags &= CKF_KEY_OPERATION_FLAGS;
for (; flags && test <= CKF_DERIVE; test <<= 1, ++pType) {
if (test & flags) {
flags ^= test;
PR_ASSERT(*pType);
PK11_SETATTRS(attr, *pType, ckTrue, sizeof *ckTrue);
++attr;
}
}
return (attr - attrs);
}
/*
* Check for conflicting flags, for example, if both PK11_ATTR_PRIVATE
* and PK11_ATTR_PUBLIC are set.
*/
PRBool
pk11_BadAttrFlags(PK11AttrFlags attrFlags)
{
PK11AttrFlags trueFlags = attrFlags & 0x55555555;
PK11AttrFlags falseFlags = (attrFlags >> 1) & 0x55555555;
return ((trueFlags & falseFlags) != 0);
}
/*
* This function may add a maximum of 5 attributes.
* The caller must make sure the attribute flags don't have conflicts.
*/
unsigned int
pk11_AttrFlagsToAttributes(PK11AttrFlags attrFlags, CK_ATTRIBUTE *attrs,
CK_BBOOL *ckTrue, CK_BBOOL *ckFalse)
{
const static CK_ATTRIBUTE_TYPE attrTypes[5] = {
CKA_TOKEN, CKA_PRIVATE, CKA_MODIFIABLE, CKA_SENSITIVE,
CKA_EXTRACTABLE
};
const CK_ATTRIBUTE_TYPE *pType = attrTypes;
CK_ATTRIBUTE *attr = attrs;
PK11AttrFlags test = PK11_ATTR_TOKEN;
PR_ASSERT(!pk11_BadAttrFlags(attrFlags));
/* we test two related bitflags in each iteration */
for (; attrFlags && test <= PK11_ATTR_EXTRACTABLE; test <<= 2, ++pType) {
if (test & attrFlags) {
attrFlags ^= test;
PK11_SETATTRS(attr, *pType, ckTrue, sizeof *ckTrue);
++attr;
} else if ((test << 1) & attrFlags) {
attrFlags ^= (test << 1);
PK11_SETATTRS(attr, *pType, ckFalse, sizeof *ckFalse);
++attr;
}
}
return (attr - attrs);
}
/*
* Some non-compliant PKCS #11 vendors do not give us the modulus, so actually
* set up a signature to get the signaure length.
*/
static int
pk11_backupGetSignLength(SECKEYPrivateKey *key)
{
PK11SlotInfo *slot = key->pkcs11Slot;
CK_MECHANISM mech = { 0, NULL, 0 };
PRBool owner = PR_TRUE;
CK_SESSION_HANDLE session;
CK_ULONG len;
CK_RV crv;
unsigned char h_data[20] = { 0 };
unsigned char buf[20]; /* obviously to small */
CK_ULONG smallLen = sizeof(buf);
mech.mechanism = PK11_MapSignKeyType(key->keyType);
session = pk11_GetNewSession(slot, &owner);
if (!owner || !(slot->isThreadSafe))
PK11_EnterSlotMonitor(slot);
crv = PK11_GETTAB(slot)->C_SignInit(session, &mech, key->pkcs11ID);
if (crv != CKR_OK) {
if (!owner || !(slot->isThreadSafe))
PK11_ExitSlotMonitor(slot);
pk11_CloseSession(slot, session, owner);
PORT_SetError(PK11_MapError(crv));
return -1;
}
len = 0;
crv = PK11_GETTAB(slot)->C_Sign(session, h_data, sizeof(h_data),
NULL, &len);
/* now call C_Sign with too small a buffer to clear the session state */
(void)PK11_GETTAB(slot)->C_Sign(session, h_data, sizeof(h_data), buf, &smallLen);
if (!owner || !(slot->isThreadSafe))
PK11_ExitSlotMonitor(slot);
pk11_CloseSession(slot, session, owner);
if (crv != CKR_OK) {
PORT_SetError(PK11_MapError(crv));
return -1;
}
return len;
}
/*
* get the length of a signature object based on the key
*/
int
PK11_SignatureLen(SECKEYPrivateKey *key)
{
int val;
SECItem attributeItem = { siBuffer, NULL, 0 };
SECStatus rv;
int length;
switch (key->keyType) {
case rsaKey:
val = PK11_GetPrivateModulusLen(key);
if (val == -1) {
return pk11_backupGetSignLength(key);
}
return (unsigned long)val;
case fortezzaKey:
return 40;
case dsaKey:
rv = PK11_ReadAttribute(key->pkcs11Slot, key->pkcs11ID, CKA_SUBPRIME,
NULL, &attributeItem);
if (rv == SECSuccess) {
length = attributeItem.len;
if ((length > 0) && attributeItem.data[0] == 0) {
length--;
}
PORT_Free(attributeItem.data);
return length * 2;
}
return pk11_backupGetSignLength(key);
case edKey:
case ecKey:
rv = PK11_ReadAttribute(key->pkcs11Slot, key->pkcs11ID, CKA_EC_PARAMS,
NULL, &attributeItem);
if (rv == SECSuccess) {
length = SECKEY_ECParamsToBasePointOrderLen(&attributeItem);
PORT_Free(attributeItem.data);
if (length != 0) {
length = ((length + 7) / 8) * 2;
return length;
}
}
return pk11_backupGetSignLength(key);
default:
break;
}
PORT_SetError(SEC_ERROR_INVALID_KEY);
return 0;
}
/*
* copy a key (or any other object) on a token
*/
CK_OBJECT_HANDLE
PK11_CopyKey(PK11SlotInfo *slot, CK_OBJECT_HANDLE srcObject)
{
CK_OBJECT_HANDLE destObject;
CK_RV crv;
PK11_EnterSlotMonitor(slot);
crv = PK11_GETTAB(slot)->C_CopyObject(slot->session, srcObject, NULL, 0,
&destObject);
PK11_ExitSlotMonitor(slot);
if (crv == CKR_OK)
return destObject;
PORT_SetError(PK11_MapError(crv));
return CK_INVALID_HANDLE;
}
PRBool
pk11_FindAttrInTemplate(CK_ATTRIBUTE *attr, unsigned int numAttrs,
CK_ATTRIBUTE_TYPE target)
{
for (; numAttrs > 0; ++attr, --numAttrs) {
if (attr->type == target)
return PR_TRUE;
}
return PR_FALSE;
}
/*
* Recover the Signed data. We need this because our old verify can't
* figure out which hash algorithm to use until we decryptted this.
*/
SECStatus
PK11_VerifyRecover(SECKEYPublicKey *key, const SECItem *sig,
SECItem *dsig, void *wincx)
{
PK11SlotInfo *slot = key->pkcs11Slot;
CK_OBJECT_HANDLE id = key->pkcs11ID;
CK_MECHANISM mech = { 0, NULL, 0 };
PRBool owner = PR_TRUE;
CK_SESSION_HANDLE session;
CK_ULONG len;
CK_RV crv;
mech.mechanism = PK11_MapSignKeyType(key->keyType);
if (slot == NULL) {
slot = PK11_GetBestSlotWithAttributes(mech.mechanism,
CKF_VERIFY_RECOVER, 0, wincx);
if (slot == NULL) {
PORT_SetError(SEC_ERROR_NO_MODULE);
return SECFailure;
}
id = PK11_ImportPublicKey(slot, key, PR_FALSE);
} else {
PK11_ReferenceSlot(slot);
}
if (id == CK_INVALID_HANDLE) {
PK11_FreeSlot(slot);
PORT_SetError(SEC_ERROR_BAD_KEY);
return SECFailure;
}
session = pk11_GetNewSession(slot, &owner);
if (!owner || !(slot->isThreadSafe))
PK11_EnterSlotMonitor(slot);
crv = PK11_GETTAB(slot)->C_VerifyRecoverInit(session, &mech, id);
if (crv != CKR_OK) {
if (!owner || !(slot->isThreadSafe))
PK11_ExitSlotMonitor(slot);
pk11_CloseSession(slot, session, owner);
PORT_SetError(PK11_MapError(crv));
PK11_FreeSlot(slot);
return SECFailure;
}
len = dsig->len;
crv = PK11_GETTAB(slot)->C_VerifyRecover(session, sig->data,
sig->len, dsig->data, &len);
if (!owner || !(slot->isThreadSafe))
PK11_ExitSlotMonitor(slot);
pk11_CloseSession(slot, session, owner);
dsig->len = len;
if (crv != CKR_OK) {
PORT_SetError(PK11_MapError(crv));
PK11_FreeSlot(slot);
return SECFailure;
}
PK11_FreeSlot(slot);
return SECSuccess;
}
/*
* verify a signature from its hash.
*/
SECStatus
PK11_Verify(SECKEYPublicKey *key, const SECItem *sig, const SECItem *hash,
void *wincx)
{
CK_MECHANISM_TYPE mech = PK11_MapSignKeyType(key->keyType);
return PK11_VerifyWithMechanism(key, mech, NULL, sig, hash, wincx);
}
/*
* Verify a signature from its hash using the given algorithm.
*/
SECStatus
PK11_VerifyWithMechanism(SECKEYPublicKey *key, CK_MECHANISM_TYPE mechanism,
const SECItem *param, const SECItem *sig,
const SECItem *hash, void *wincx)
{
PK11SlotInfo *slot = key->pkcs11Slot;
CK_OBJECT_HANDLE id = key->pkcs11ID;
CK_MECHANISM mech = { 0, NULL, 0 };
PRBool owner = PR_TRUE;
CK_SESSION_HANDLE session;
CK_RV crv;
mech.mechanism = mechanism;
if (param) {
mech.pParameter = param->data;
mech.ulParameterLen = param->len;
}
if (slot == NULL) {
unsigned int length = 0;
if ((mech.mechanism == CKM_DSA) &&
/* 129 is 1024 bits translated to bytes and
* padded with an optional '0' to maintain a
* positive sign */
(key->u.dsa.params.prime.len > 129)) {
/* we need to get a slot that not only can do DSA, but can do DSA2
* key lengths */
length = key->u.dsa.params.prime.len;
if (key->u.dsa.params.prime.data[0] == 0) {
length--;
}
/* convert keysize to bits for slot lookup */
length *= 8;
}
slot = PK11_GetBestSlotWithAttributes(mech.mechanism,
CKF_VERIFY, length, wincx);
if (slot == NULL) {
PORT_SetError(SEC_ERROR_NO_MODULE);
return SECFailure;
}
id = PK11_ImportPublicKey(slot, key, PR_FALSE);
} else {
PK11_ReferenceSlot(slot);
}
if (id == CK_INVALID_HANDLE) {
PK11_FreeSlot(slot);
PORT_SetError(SEC_ERROR_BAD_KEY);
return SECFailure;
}
session = pk11_GetNewSession(slot, &owner);
if (!owner || !(slot->isThreadSafe))
PK11_EnterSlotMonitor(slot);
crv = PK11_GETTAB(slot)->C_VerifyInit(session, &mech, id);
if (crv != CKR_OK) {
if (!owner || !(slot->isThreadSafe))
PK11_ExitSlotMonitor(slot);
pk11_CloseSession(slot, session, owner);
PK11_FreeSlot(slot);
PORT_SetError(PK11_MapError(crv));
return SECFailure;
}
crv = PK11_GETTAB(slot)->C_Verify(session, hash->data,
hash->len, sig->data, sig->len);
if (!owner || !(slot->isThreadSafe))
PK11_ExitSlotMonitor(slot);
pk11_CloseSession(slot, session, owner);
PK11_FreeSlot(slot);
if (crv != CKR_OK) {
PORT_SetError(PK11_MapError(crv));
return SECFailure;
}
return SECSuccess;
}
/*
* sign a hash. The algorithm is determined by the key.
*/
SECStatus
PK11_Sign(SECKEYPrivateKey *key, SECItem *sig, const SECItem *hash)
{
CK_MECHANISM_TYPE mech = PK11_MapSignKeyType(key->keyType);
return PK11_SignWithMechanism(key, mech, NULL, sig, hash);
}
/*
* Sign a hash using the given algorithm.
*/
SECStatus
PK11_SignWithMechanism(SECKEYPrivateKey *key, CK_MECHANISM_TYPE mechanism,
const SECItem *param, SECItem *sig, const SECItem *hash)
{
PK11SlotInfo *slot = key->pkcs11Slot;
CK_MECHANISM mech = { 0, NULL, 0 };
PRBool owner = PR_TRUE;
CK_SESSION_HANDLE session;
PRBool haslock = PR_FALSE;
CK_ULONG len;
CK_RV crv;
mech.mechanism = mechanism;
if (param) {
mech.pParameter = param->data;
mech.ulParameterLen = param->len;
}
if (SECKEY_HAS_ATTRIBUTE_SET(key, CKA_PRIVATE)) {
PK11_HandlePasswordCheck(slot, key->wincx);
}
session = pk11_GetNewSession(slot, &owner);
haslock = (!owner || !(slot->isThreadSafe));
if (haslock)
PK11_EnterSlotMonitor(slot);
crv = PK11_GETTAB(slot)->C_SignInit(session, &mech, key->pkcs11ID);
if (crv != CKR_OK) {
if (haslock)
PK11_ExitSlotMonitor(slot);
pk11_CloseSession(slot, session, owner);
PORT_SetError(PK11_MapError(crv));
return SECFailure;
}
/* PKCS11 2.20 says if CKA_ALWAYS_AUTHENTICATE then
* do C_Login with CKU_CONTEXT_SPECIFIC
* between C_SignInit and C_Sign */
if (SECKEY_HAS_ATTRIBUTE_SET_LOCK(key, CKA_ALWAYS_AUTHENTICATE, haslock)) {
PK11_DoPassword(slot, session, PR_FALSE, key->wincx, haslock, PR_TRUE);
}
len = sig->len;
crv = PK11_GETTAB(slot)->C_Sign(session, hash->data,
hash->len, sig->data, &len);
if (haslock)
PK11_ExitSlotMonitor(slot);
pk11_CloseSession(slot, session, owner);
sig->len = len;
if (crv != CKR_OK) {
PORT_SetError(PK11_MapError(crv));
return SECFailure;
}
return SECSuccess;
}
/*
* sign data with a MAC key.
*/
SECStatus
PK11_SignWithSymKey(PK11SymKey *symKey, CK_MECHANISM_TYPE mechanism,
SECItem *param, SECItem *sig, const SECItem *data)
{
PK11SlotInfo *slot = symKey->slot;
CK_MECHANISM mech = { 0, NULL, 0 };
PRBool owner = PR_TRUE;
CK_SESSION_HANDLE session;
PRBool haslock = PR_FALSE;
CK_ULONG len;
CK_RV crv;
mech.mechanism = mechanism;
if (param) {
mech.pParameter = param->data;
mech.ulParameterLen = param->len;
}
session = pk11_GetNewSession(slot, &owner);
haslock = (!owner || !(slot->isThreadSafe));
if (haslock)
PK11_EnterSlotMonitor(slot);
crv = PK11_GETTAB(slot)->C_SignInit(session, &mech, symKey->objectID);
if (crv != CKR_OK) {
if (haslock)
PK11_ExitSlotMonitor(slot);
pk11_CloseSession(slot, session, owner);
PORT_SetError(PK11_MapError(crv));
return SECFailure;
}
len = sig->len;
crv = PK11_GETTAB(slot)->C_Sign(session, data->data,
data->len, sig->data, &len);
if (haslock)
PK11_ExitSlotMonitor(slot);
pk11_CloseSession(slot, session, owner);
sig->len = len;
if (crv != CKR_OK) {
PORT_SetError(PK11_MapError(crv));
return SECFailure;
}
return SECSuccess;
}
SECStatus
PK11_Decrypt(PK11SymKey *symKey,
CK_MECHANISM_TYPE mechanism, SECItem *param,
unsigned char *out, unsigned int *outLen,
unsigned int maxLen,
const unsigned char *enc, unsigned encLen)
{
PK11SlotInfo *slot = symKey->slot;
CK_MECHANISM mech = { 0, NULL, 0 };
CK_ULONG len = maxLen;
PRBool owner = PR_TRUE;
CK_SESSION_HANDLE session;
PRBool haslock = PR_FALSE;
CK_RV crv;
mech.mechanism = mechanism;
if (param) {
mech.pParameter = param->data;
mech.ulParameterLen = param->len;
}
session = pk11_GetNewSession(slot, &owner);
haslock = (!owner || !slot->isThreadSafe);
if (haslock)
PK11_EnterSlotMonitor(slot);
crv = PK11_GETTAB(slot)->C_DecryptInit(session, &mech, symKey->objectID);
if (crv != CKR_OK) {
if (haslock)
PK11_ExitSlotMonitor(slot);
pk11_CloseSession(slot, session, owner);
PORT_SetError(PK11_MapError(crv));
return SECFailure;
}
crv = PK11_GETTAB(slot)->C_Decrypt(session, (unsigned char *)enc, encLen,
out, &len);
if (haslock)
PK11_ExitSlotMonitor(slot);
pk11_CloseSession(slot, session, owner);
if (crv != CKR_OK) {
PORT_SetError(PK11_MapError(crv));
return SECFailure;
}
*outLen = len;
return SECSuccess;
}
SECStatus
PK11_Encrypt(PK11SymKey *symKey,
CK_MECHANISM_TYPE mechanism, SECItem *param,
unsigned char *out, unsigned int *outLen,
unsigned int maxLen,
const unsigned char *data, unsigned int dataLen)
{
PK11SlotInfo *slot = symKey->slot;
CK_MECHANISM mech = { 0, NULL, 0 };
CK_ULONG len = maxLen;
PRBool owner = PR_TRUE;
CK_SESSION_HANDLE session;
PRBool haslock = PR_FALSE;
CK_RV crv;
mech.mechanism = mechanism;
if (param) {
mech.pParameter = param->data;
mech.ulParameterLen = param->len;
}
session = pk11_GetNewSession(slot, &owner);
haslock = (!owner || !slot->isThreadSafe);
if (haslock)
PK11_EnterSlotMonitor(slot);
crv = PK11_GETTAB(slot)->C_EncryptInit(session, &mech, symKey->objectID);
if (crv != CKR_OK) {
if (haslock)
PK11_ExitSlotMonitor(slot);
pk11_CloseSession(slot, session, owner);
PORT_SetError(PK11_MapError(crv));
return SECFailure;
}
crv = PK11_GETTAB(slot)->C_Encrypt(session, (unsigned char *)data,
dataLen, out, &len);
if (haslock)
PK11_ExitSlotMonitor(slot);
pk11_CloseSession(slot, session, owner);
if (crv != CKR_OK) {
PORT_SetError(PK11_MapError(crv));
return SECFailure;
}
*outLen = len;
return SECSuccess;
}
static SECStatus
pk11_PrivDecryptRaw(SECKEYPrivateKey *key,
unsigned char *data, unsigned *outLen, unsigned int maxLen,
const unsigned char *enc, unsigned encLen,
CK_MECHANISM_PTR mech)
{
PK11SlotInfo *slot = key->pkcs11Slot;
CK_ULONG out = maxLen;
PRBool owner = PR_TRUE;
CK_SESSION_HANDLE session;
PRBool haslock = PR_FALSE;
CK_RV crv;
if (key->keyType != rsaKey) {
PORT_SetError(SEC_ERROR_INVALID_KEY);
return SECFailure;
}
/* Why do we do a PK11_handle check here? for simple
* decryption? .. because the user may have asked for 'ask always'
* and this is a private key operation. In practice, thought, it's mute
* since only servers wind up using this function */
if (SECKEY_HAS_ATTRIBUTE_SET(key, CKA_PRIVATE)) {
PK11_HandlePasswordCheck(slot, key->wincx);
}
session = pk11_GetNewSession(slot, &owner);
haslock = (!owner || !(slot->isThreadSafe));
if (haslock)
PK11_EnterSlotMonitor(slot);
crv = PK11_GETTAB(slot)->C_DecryptInit(session, mech, key->pkcs11ID);
if (crv != CKR_OK) {
if (haslock)
PK11_ExitSlotMonitor(slot);
pk11_CloseSession(slot, session, owner);
PORT_SetError(PK11_MapError(crv));
return SECFailure;
}
/* PKCS11 2.20 says if CKA_ALWAYS_AUTHENTICATE then
* do C_Login with CKU_CONTEXT_SPECIFIC
* between C_DecryptInit and C_Decrypt
* ... But see note above about servers */
if (SECKEY_HAS_ATTRIBUTE_SET_LOCK(key, CKA_ALWAYS_AUTHENTICATE, haslock)) {
PK11_DoPassword(slot, session, PR_FALSE, key->wincx, haslock, PR_TRUE);
}
crv = PK11_GETTAB(slot)->C_Decrypt(session, (unsigned char *)enc, encLen,
data, &out);
if (haslock)
PK11_ExitSlotMonitor(slot);
pk11_CloseSession(slot, session, owner);
*outLen = out;
if (crv != CKR_OK) {
PORT_SetError(PK11_MapError(crv));
return SECFailure;
}
return SECSuccess;
}
SECStatus
PK11_PubDecryptRaw(SECKEYPrivateKey *key,
unsigned char *data, unsigned *outLen, unsigned int maxLen,
const unsigned char *enc, unsigned encLen)
{
CK_MECHANISM mech = { CKM_RSA_X_509, NULL, 0 };
return pk11_PrivDecryptRaw(key, data, outLen, maxLen, enc, encLen, &mech);
}
SECStatus
PK11_PrivDecryptPKCS1(SECKEYPrivateKey *key,
unsigned char *data, unsigned *outLen, unsigned int maxLen,
const unsigned char *enc, unsigned encLen)
{
CK_MECHANISM mech = { CKM_RSA_PKCS, NULL, 0 };
return pk11_PrivDecryptRaw(key, data, outLen, maxLen, enc, encLen, &mech);
}
static SECStatus
pk11_PubEncryptRaw(SECKEYPublicKey *key,
unsigned char *out, unsigned int *outLen,
unsigned int maxLen,
const unsigned char *data, unsigned dataLen,
CK_MECHANISM_PTR mech, void *wincx)
{
PK11SlotInfo *slot;
CK_OBJECT_HANDLE id;
CK_ULONG len = maxLen;
PRBool owner = PR_TRUE;
CK_SESSION_HANDLE session;
CK_RV crv;
slot = PK11_GetBestSlotWithAttributes(mech->mechanism, CKF_ENCRYPT, 0, wincx);
if (slot == NULL) {
PORT_SetError(SEC_ERROR_NO_MODULE);
return SECFailure;
}
id = PK11_ImportPublicKey(slot, key, PR_FALSE);
if (id == CK_INVALID_HANDLE) {
PK11_FreeSlot(slot);
PORT_SetError(SEC_ERROR_BAD_KEY);
return SECFailure;
}
session = pk11_GetNewSession(slot, &owner);
if (!owner || !(slot->isThreadSafe))
PK11_EnterSlotMonitor(slot);
crv = PK11_GETTAB(slot)->C_EncryptInit(session, mech, id);
if (crv != CKR_OK) {
if (!owner || !(slot->isThreadSafe))
PK11_ExitSlotMonitor(slot);
pk11_CloseSession(slot, session, owner);
PK11_FreeSlot(slot);
PORT_SetError(PK11_MapError(crv));
return SECFailure;
}
crv = PK11_GETTAB(slot)->C_Encrypt(session, (unsigned char *)data, dataLen,
out, &len);
if (!owner || !(slot->isThreadSafe))
PK11_ExitSlotMonitor(slot);
pk11_CloseSession(slot, session, owner);
PK11_FreeSlot(slot);
*outLen = len;
if (crv != CKR_OK) {
PORT_SetError(PK11_MapError(crv));
return SECFailure;
}
return SECSuccess;
}
SECStatus
PK11_PubEncryptRaw(SECKEYPublicKey *key,
unsigned char *enc,
const unsigned char *data, unsigned dataLen,
void *wincx)
{
CK_MECHANISM mech = { CKM_RSA_X_509, NULL, 0 };
unsigned int outLen;
if (!key || key->keyType != rsaKey) {
PORT_SetError(SEC_ERROR_BAD_KEY);
return SECFailure;
}
outLen = SECKEY_PublicKeyStrength(key);
return pk11_PubEncryptRaw(key, enc, &outLen, outLen, data, dataLen, &mech,
wincx);
}
SECStatus
PK11_PubEncryptPKCS1(SECKEYPublicKey *key,
unsigned char *enc,
const unsigned char *data, unsigned dataLen,
void *wincx)
{
CK_MECHANISM mech = { CKM_RSA_PKCS, NULL, 0 };
unsigned int outLen;
if (!key || key->keyType != rsaKey) {
PORT_SetError(SEC_ERROR_BAD_KEY);
return SECFailure;
}
outLen = SECKEY_PublicKeyStrength(key);
return pk11_PubEncryptRaw(key, enc, &outLen, outLen, data, dataLen, &mech,
wincx);
}
SECStatus
PK11_PrivDecrypt(SECKEYPrivateKey *key,
CK_MECHANISM_TYPE mechanism, SECItem *param,
unsigned char *out, unsigned int *outLen,
unsigned int maxLen,
const unsigned char *enc, unsigned encLen)
{
CK_MECHANISM mech = { mechanism, NULL, 0 };
if (param) {
mech.pParameter = param->data;
mech.ulParameterLen = param->len;
}
return pk11_PrivDecryptRaw(key, out, outLen, maxLen, enc, encLen, &mech);
}
SECStatus
PK11_PubEncrypt(SECKEYPublicKey *key,
CK_MECHANISM_TYPE mechanism, SECItem *param,
unsigned char *out, unsigned int *outLen,
unsigned int maxLen,
const unsigned char *data, unsigned dataLen,
void *wincx)
{
CK_MECHANISM mech = { mechanism, NULL, 0 };
if (param) {
mech.pParameter = param->data;
mech.ulParameterLen = param->len;
}
return pk11_PubEncryptRaw(key, out, outLen, maxLen, data, dataLen, &mech,
wincx);
}
SECKEYPrivateKey *
PK11_UnwrapPrivKey(PK11SlotInfo *slot, PK11SymKey *wrappingKey,
CK_MECHANISM_TYPE wrapType, SECItem *param,
SECItem *wrappedKey, SECItem *label,
SECItem *idValue, PRBool perm, PRBool sensitive,
CK_KEY_TYPE keyType, CK_ATTRIBUTE_TYPE *usage,
int usageCount, void *wincx)
{
CK_BBOOL cktrue = CK_TRUE;
CK_BBOOL ckfalse = CK_FALSE;
CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
CK_ATTRIBUTE keyTemplate[15];
int templateCount = 0;
CK_OBJECT_HANDLE privKeyID;
CK_MECHANISM mechanism;
CK_ATTRIBUTE *attrs = keyTemplate;
SECItem *param_free = NULL, *ck_id = NULL;
CK_RV crv;
CK_SESSION_HANDLE rwsession;
PK11SymKey *newKey = NULL;
int i;
if (!slot || !wrappedKey || !idValue) {
/* SET AN ERROR!!! */
return NULL;
}
ck_id = PK11_MakeIDFromPubKey(idValue);
if (!ck_id) {
return NULL;
}
PK11_SETATTRS(attrs, CKA_TOKEN, perm ? &cktrue : &ckfalse,
sizeof(cktrue));
attrs++;
PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass));
attrs++;
PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType));
attrs++;
PK11_SETATTRS(attrs, CKA_PRIVATE, sensitive ? &cktrue : &ckfalse,
sizeof(cktrue));
attrs++;
PK11_SETATTRS(attrs, CKA_SENSITIVE, sensitive ? &cktrue : &ckfalse,
sizeof(cktrue));
attrs++;
if (label && label->data) {
PK11_SETATTRS(attrs, CKA_LABEL, label->data, label->len);
attrs++;
}
PK11_SETATTRS(attrs, CKA_ID, ck_id->data, ck_id->len);
attrs++;
for (i = 0; i < usageCount; i++) {
PK11_SETATTRS(attrs, usage[i], &cktrue, sizeof(cktrue));
attrs++;
}
if (PK11_IsInternal(slot)) {
PK11_SETATTRS(attrs, CKA_NSS_DB, idValue->data,
idValue->len);
attrs++;
}
templateCount = attrs - keyTemplate;
PR_ASSERT(templateCount <= (sizeof(keyTemplate) / sizeof(CK_ATTRIBUTE)));
mechanism.mechanism = wrapType;
if (!param)
param = param_free = PK11_ParamFromIV(wrapType, NULL);
if (param) {
mechanism.pParameter = param->data;
mechanism.ulParameterLen = param->len;
} else {
mechanism.pParameter = NULL;
mechanism.ulParameterLen = 0;
}
if (wrappingKey->slot != slot) {
newKey = pk11_CopyToSlot(slot, wrapType, CKA_UNWRAP, wrappingKey);
} else {
newKey = PK11_ReferenceSymKey(wrappingKey);
}
if (newKey) {
if (perm) {
/* Get RW Session will either lock the monitor if necessary,
* or return a thread safe session handle, or fail. */
rwsession = PK11_GetRWSession(slot);
} else {
rwsession = slot->session;
if (rwsession != CK_INVALID_HANDLE)
PK11_EnterSlotMonitor(slot);
}
/* This is a lot a work to deal with fussy PKCS #11 modules
* that can't bother to return BAD_DATA when presented with an
* invalid session! */
if (rwsession == CK_INVALID_HANDLE) {
PORT_SetError(SEC_ERROR_BAD_DATA);
goto loser;
}
crv = PK11_GETTAB(slot)->C_UnwrapKey(rwsession, &mechanism,
newKey->objectID,
wrappedKey->data,
wrappedKey->len, keyTemplate,
templateCount, &privKeyID);
if (perm) {
PK11_RestoreROSession(slot, rwsession);
} else {
PK11_ExitSlotMonitor(slot);
}
PK11_FreeSymKey(newKey);
newKey = NULL;
} else {
crv = CKR_FUNCTION_NOT_SUPPORTED;
}
SECITEM_FreeItem(ck_id, PR_TRUE);
ck_id = NULL;
if (crv != CKR_OK) {
/* we couldn't unwrap the key, use the internal module to do the
* unwrap, then load the new key into the token */
PK11SlotInfo *int_slot = PK11_GetInternalSlot();
if (int_slot && (slot != int_slot)) {
SECKEYPrivateKey *privKey = PK11_UnwrapPrivKey(int_slot,
wrappingKey, wrapType, param, wrappedKey, label,
idValue, PR_FALSE, PR_FALSE,
keyType, usage, usageCount, wincx);
if (privKey) {
SECKEYPrivateKey *newPrivKey = PK11_LoadPrivKey(slot, privKey,
NULL, perm, sensitive);
SECKEY_DestroyPrivateKey(privKey);
PK11_FreeSlot(int_slot);
SECITEM_FreeItem(param_free, PR_TRUE);
return newPrivKey;
}
}
if (int_slot)
PK11_FreeSlot(int_slot);
PORT_SetError(PK11_MapError(crv));
SECITEM_FreeItem(param_free, PR_TRUE);
return NULL;
}
SECITEM_FreeItem(param_free, PR_TRUE);
return PK11_MakePrivKey(slot, nullKey, PR_FALSE, privKeyID, wincx);
loser:
PK11_FreeSymKey(newKey);
SECITEM_FreeItem(ck_id, PR_TRUE);
SECITEM_FreeItem(param_free, PR_TRUE);
return NULL;
}
/*
* Now we're going to wrap a SECKEYPrivateKey with a PK11SymKey
* The strategy is to get both keys to reside in the same slot,
* one that can perform the desired crypto mechanism and then
* call C_WrapKey after all the setup has taken place.
*/
SECStatus
PK11_WrapPrivKey(PK11SlotInfo *slot, PK11SymKey *wrappingKey,
SECKEYPrivateKey *privKey, CK_MECHANISM_TYPE wrapType,
SECItem *param, SECItem *wrappedKey, void *wincx)
{
PK11SlotInfo *privSlot = privKey->pkcs11Slot; /* The slot where
* the private key
* we are going to
* wrap lives.
*/
PK11SymKey *newSymKey = NULL;
SECKEYPrivateKey *newPrivKey = NULL;
SECItem *param_free = NULL;
CK_ULONG len = wrappedKey->len;
CK_MECHANISM mech;
CK_RV crv;
if (!privSlot || !PK11_DoesMechanism(privSlot, wrapType)) {
/* Figure out a slot that does the mechanism and try to import
* the private key onto that slot.
*/
PK11SlotInfo *int_slot = PK11_GetInternalSlot();
privSlot = int_slot; /* The private key has a new home */
newPrivKey = PK11_LoadPrivKey(privSlot, privKey, NULL, PR_FALSE, PR_FALSE);
/* newPrivKey has allocated its own reference to the slot, so it's
* safe until we destroy newPrivkey.
*/
PK11_FreeSlot(int_slot);
if (newPrivKey == NULL) {
return SECFailure;
}
privKey = newPrivKey;
}
if (privSlot != wrappingKey->slot) {
newSymKey = pk11_CopyToSlot(privSlot, wrapType, CKA_WRAP,
wrappingKey);
wrappingKey = newSymKey;
}
if (wrappingKey == NULL) {
if (newPrivKey) {
SECKEY_DestroyPrivateKey(newPrivKey);
}
return SECFailure;
}
mech.mechanism = wrapType;
if (!param) {
param = param_free = PK11_ParamFromIV(wrapType, NULL);
}
if (param) {
mech.pParameter = param->data;
mech.ulParameterLen = param->len;
} else {
mech.pParameter = NULL;
mech.ulParameterLen = 0;
}
PK11_EnterSlotMonitor(privSlot);
crv = PK11_GETTAB(privSlot)->C_WrapKey(privSlot->session, &mech,
wrappingKey->objectID,
privKey->pkcs11ID,
wrappedKey->data, &len);
PK11_ExitSlotMonitor(privSlot);
if (newSymKey) {
PK11_FreeSymKey(newSymKey);
}
if (newPrivKey) {
SECKEY_DestroyPrivateKey(newPrivKey);
}
if (param_free) {
SECITEM_FreeItem(param_free, PR_TRUE);
}
if (crv != CKR_OK) {
PORT_SetError(PK11_MapError(crv));
return SECFailure;
}
wrappedKey->len = len;
return SECSuccess;
}
#if 0
/*
* Sample code relating to linked list returned by PK11_FindGenericObjects
*/
/*
* You can walk the list with the following code:
*/
firstObj = PK11_FindGenericObjects(slot, objClass);
for (thisObj=firstObj;
thisObj;
thisObj=PK11_GetNextGenericObject(thisObj)) {
/* operate on thisObj */
}
/*
* If you want a particular object from the list...
*/
firstObj = PK11_FindGenericObjects(slot, objClass);
for (thisObj=firstObj;
thisObj;
thisObj=PK11_GetNextGenericObject(thisObj)) {
if (isMyObj(thisObj)) {
if ( thisObj == firstObj) {
/* NOTE: firstObj could be NULL at this point */
firstObj = PK11_GetNextGenericObject(thsObj);
}
PK11_UnlinkGenericObject(thisObj);
myObj = thisObj;
break;
}
}
PK11_DestroyGenericObjects(firstObj);
/* use myObj */
PK11_DestroyGenericObject(myObj);
#endif /* sample code */
/*
* return a linked, non-circular list of generic objects.
* If you are only interested
* in one object, just use the first object in the list. To find the
* rest of the list use PK11_GetNextGenericObject() to return the next object.
*/
PK11GenericObject *
PK11_FindGenericObjects(PK11SlotInfo *slot, CK_OBJECT_CLASS objClass)
{
CK_ATTRIBUTE template[1];
CK_ATTRIBUTE *attrs = template;
CK_OBJECT_HANDLE *objectIDs = NULL;
PK11GenericObject *lastObj = NULL, *obj;
PK11GenericObject *firstObj = NULL;
int i, count = 0;
PK11_SETATTRS(attrs, CKA_CLASS, &objClass, sizeof(objClass));
attrs++;
objectIDs = pk11_FindObjectsByTemplate(slot, template, 1, &count);
if (objectIDs == NULL) {
return NULL;
}
/* where we connect our object once we've created it.. */
for (i = 0; i < count; i++) {
obj = PORT_New(PK11GenericObject);
if (!obj) {
if (firstObj) {
PK11_DestroyGenericObjects(firstObj);
}
PORT_Free(objectIDs);
return NULL;
}
/* initialize it */
obj->slot = PK11_ReferenceSlot(slot);
obj->objectID = objectIDs[i];
obj->owner = PR_FALSE;
obj->next = NULL;
obj->prev = NULL;
/* link it in */
if (firstObj == NULL) {
firstObj = obj;
} else {
PK11_LinkGenericObject(lastObj, obj);
}
lastObj = obj;
}
PORT_Free(objectIDs);
return firstObj;
}
/*
* get the Next Object in the list.
*/
PK11GenericObject *
PK11_GetNextGenericObject(PK11GenericObject *object)
{
return object->next;
}
PK11GenericObject *
PK11_GetPrevGenericObject(PK11GenericObject *object)
{
return object->prev;
}
/*
* Link a single object into a new list.
* if the object is already in another list, remove it first.
*/
SECStatus
PK11_LinkGenericObject(PK11GenericObject *list, PK11GenericObject *object)
{
PK11_UnlinkGenericObject(object);
object->prev = list;
object->next = list->next;
list->next = object;
if (object->next != NULL) {
object->next->prev = object;
}
return SECSuccess;
}
/*
* remove an object from the list. If the object isn't already in
* a list unlink becomes a noop.
*/
SECStatus
PK11_UnlinkGenericObject(PK11GenericObject *object)
{
if (object->prev != NULL) {
object->prev->next = object->next;
}
if (object->next != NULL) {
object->next->prev = object->prev;
}
object->next = NULL;
object->prev = NULL;
return SECSuccess;
}
/*
* This function removes a single object from the list and destroys it.
* For an already unlinked object there is no difference between
* PK11_DestroyGenericObject and PK11_DestroyGenericObjects
*/
SECStatus
PK11_DestroyGenericObject(PK11GenericObject *object)
{
if (object == NULL) {
return SECSuccess;
}
PK11_UnlinkGenericObject(object);
if (object->slot) {
if (object->owner) {
PK11_DestroyObject(object->slot, object->objectID);
}
PK11_FreeSlot(object->slot);
}
PORT_Free(object);
return SECSuccess;
}
/*
* walk down a link list of generic objects destroying them.
* This will destroy all objects in a list that the object is linked into.
* (the list is traversed in both directions).
*/
SECStatus
PK11_DestroyGenericObjects(PK11GenericObject *objects)
{
PK11GenericObject *nextObject;
PK11GenericObject *prevObject;
if (objects == NULL) {
return SECSuccess;
}
nextObject = objects->next;
prevObject = objects->prev;
/* delete all the objects after it in the list */
for (; objects; objects = nextObject) {
nextObject = objects->next;
PK11_DestroyGenericObject(objects);
}
/* delete all the objects before it in the list */
for (objects = prevObject; objects; objects = prevObject) {
prevObject = objects->prev;
PK11_DestroyGenericObject(objects);
}
return SECSuccess;
}
/*
* Hand Create a new object and return the Generic object for our new object.
*/
PK11GenericObject *
pk11_CreateGenericObjectHelper(PK11SlotInfo *slot,
const CK_ATTRIBUTE *pTemplate,
int count, PRBool token, PRBool owner)
{
CK_OBJECT_HANDLE objectID;
PK11GenericObject *obj;
CK_RV crv;
PK11_EnterSlotMonitor(slot);
crv = PK11_CreateNewObject(slot, slot->session, pTemplate, count,
token, &objectID);
PK11_ExitSlotMonitor(slot);
if (crv != CKR_OK) {
PORT_SetError(PK11_MapError(crv));
return NULL;
}
obj = PORT_New(PK11GenericObject);
if (!obj) {
/* error set by PORT_New */
return NULL;
}
/* initialize it */
obj->slot = PK11_ReferenceSlot(slot);
obj->objectID = objectID;
obj->owner = owner;
obj->next = NULL;
obj->prev = NULL;
return obj;
}
/* This is the classic interface. Applications would call this function to
* create new object that would not be destroyed later. This lead to resource
* leaks (and thus memory leaks in the PKCS #11 module). To solve this we have
* a new interface that automatically marks objects created on the fly to be
* destroyed later.
* The old interface is preserved because applications like Mozilla purposefully
* leak the reference to be found later with PK11_FindGenericObjects. New
* applications should use the new interface PK11_CreateManagedGenericObject */
PK11GenericObject *
PK11_CreateGenericObject(PK11SlotInfo *slot, const CK_ATTRIBUTE *pTemplate,
int count, PRBool token)
{
return pk11_CreateGenericObjectHelper(slot, pTemplate, count, token,
PR_FALSE);
}
/* Use this interface. It will automatically destroy any temporary objects
* (token = PR_FALSE) when the PK11GenericObject is freed. Permanent objects still
* need to be destroyed by hand with PK11_DestroyTokenObject.
*/
PK11GenericObject *
PK11_CreateManagedGenericObject(PK11SlotInfo *slot,
const CK_ATTRIBUTE *pTemplate, int count, PRBool token)
{
return pk11_CreateGenericObjectHelper(slot, pTemplate, count, token,
!token);
}
CK_OBJECT_HANDLE
PK11_GetObjectHandle(PK11ObjectType objType, void *objSpec,
PK11SlotInfo **slotp)
{
CK_OBJECT_HANDLE handle = CK_INVALID_HANDLE;
PK11SlotInfo *slot = NULL;
switch (objType) {
case PK11_TypeGeneric:
slot = ((PK11GenericObject *)objSpec)->slot;
handle = ((PK11GenericObject *)objSpec)->objectID;
break;
case PK11_TypePrivKey:
slot = ((SECKEYPrivateKey *)objSpec)->pkcs11Slot;
handle = ((SECKEYPrivateKey *)objSpec)->pkcs11ID;
break;
case PK11_TypePubKey:
slot = ((SECKEYPublicKey *)objSpec)->pkcs11Slot;
handle = ((SECKEYPublicKey *)objSpec)->pkcs11ID;
break;
case PK11_TypeSymKey:
slot = ((PK11SymKey *)objSpec)->slot;
handle = ((PK11SymKey *)objSpec)->objectID;
break;
case PK11_TypeCert:
handle = PK11_FindObjectForCert((CERTCertificate *)objSpec, NULL,
&slot);
break;
default:
PORT_SetError(SEC_ERROR_UNKNOWN_OBJECT_TYPE);
break;
}
if (slotp) {
*slotp = slot;
}
/* paranoia. If the object doesn't have a slot, then it's handle isn't
* valid either */
if (slot == NULL) {
handle = CK_INVALID_HANDLE;
}
return handle;
}
/*
* Change an attribute on a raw object
*/
SECStatus
PK11_WriteRawAttribute(PK11ObjectType objType, void *objSpec,
CK_ATTRIBUTE_TYPE attrType, SECItem *item)
{
PK11SlotInfo *slot = NULL;
CK_OBJECT_HANDLE handle = 0;
CK_ATTRIBUTE setTemplate;
CK_RV crv;
CK_SESSION_HANDLE rwsession;
handle = PK11_GetObjectHandle(objType, objSpec, &slot);
if (handle == CK_INVALID_HANDLE) {
PORT_SetError(SEC_ERROR_UNKNOWN_OBJECT_TYPE);
return SECFailure;
}
PK11_SETATTRS(&setTemplate, attrType, (CK_CHAR *)item->data, item->len);
rwsession = PK11_GetRWSession(slot);
if (rwsession == CK_INVALID_HANDLE) {
PORT_SetError(SEC_ERROR_BAD_DATA);
return SECFailure;
}
crv = PK11_GETTAB(slot)->C_SetAttributeValue(rwsession, handle,
&setTemplate, 1);
PK11_RestoreROSession(slot, rwsession);
if (crv != CKR_OK) {
PORT_SetError(PK11_MapError(crv));
return SECFailure;
}
return SECSuccess;
}
SECStatus
PK11_ReadRawAttribute(PK11ObjectType objType, void *objSpec,
CK_ATTRIBUTE_TYPE attrType, SECItem *item)
{
PK11SlotInfo *slot = NULL;
CK_OBJECT_HANDLE handle = 0;
handle = PK11_GetObjectHandle(objType, objSpec, &slot);
if (handle == CK_INVALID_HANDLE) {
PORT_SetError(SEC_ERROR_UNKNOWN_OBJECT_TYPE);
return SECFailure;
}
return PK11_ReadAttribute(slot, handle, attrType, NULL, item);