| /* 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 "seccomon.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_SESSION) { |
| 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 As 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_SESSION) |
| 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_SESSION) { |
| 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_SESSION) { |
| rwsession = slot->session; |
| if (rwsession != CK_INVALID_SESSION) |
| PK11_EnterSlotMonitor(slot); |
| } |
| if (rwsession == CK_INVALID_SESSION) { |
| 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_SESSION) { |
| 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 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); |
| *outLen = len; |
| if (crv != CKR_OK) { |
| PORT_SetError(PK11_MapError(crv)); |
| return SECFailure; |
| } |
| 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); |
| *outLen = len; |
| if (crv != CKR_OK) { |
| PORT_SetError(PK11_MapError(crv)); |
| return SECFailure; |
| } |
| 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_NETSCAPE_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_SESSION) |
| 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_SESSION) { |
| 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); |
| return newPrivKey; |
| } |
| } |
| if (int_slot) |
| PK11_FreeSlot(int_slot); |
| PORT_SetError(PK11_MapError(crv)); |
| return NULL; |
| } |
| return PK11_MakePrivKey(slot, nullKey, PR_FALSE, privKeyID, wincx); |
| |
| loser: |
| if (newKey) { |
| PK11_FreeSymKey(newKey); |
| } |
| if (ck_id) { |
| SECITEM_FreeItem(ck_id, 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); |
| } |
| |
| /* |
| * 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; |
| |
| 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: /* don't handle cert case for now */ |
| default: |
| break; |
| } |
| if (slot == NULL) { |
| 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_SESSION) { |
| 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; |
| |
| 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: /* don't handle cert case for now */ |
| default: |
| break; |
| } |
| if (slot == NULL) { |
| PORT_SetError(SEC_ERROR_UNKNOWN_OBJECT_TYPE); |
| return SECFailure; |
| } |
| |
| return PK11_ReadAttribute(slot, handle, attrType, NULL, item); |
| } |
| |
| /* |
| * return the object handle that matches the template |
| */ |
| CK_OBJECT_HANDLE |
| pk11_FindObjectByTemplate(PK11SlotInfo *slot, CK_ATTRIBUTE *theTemplate, int tsize) |
| { |
| CK_OBJECT_HANDLE object; |
| CK_RV crv = CKR_SESSION_HANDLE_INVALID; |
| CK_ULONG objectCount; |
| |
| /* |
| * issue the find |
| */ |
| PK11_EnterSlotMonitor(slot); |
| if (slot->session != CK_INVALID_SESSION) { |
| crv = PK11_GETTAB(slot)->C_FindObjectsInit(slot->session, |
| theTemplate, tsize); |
| } |
| if (crv != CKR_OK) { |
| PK11_ExitSlotMonitor(slot); |
| PORT_SetError(PK11_MapError(crv)); |
| return CK_INVALID_HANDLE; |
| } |
| |
| crv = PK11_GETTAB(slot)->C_FindObjects(slot->session, &object, 1, &objectCount); |
| PK11_GETTAB(slot)->C_FindObjectsFinal(slot->session); |
| PK11_ExitSlotMonitor(slot); |
| if ((crv != CKR_OK) || (objectCount < 1)) { |
| /* shouldn't use SSL_ERROR... here */ |
| PORT_SetError(crv != CKR_OK ? PK11_MapError(crv) : SSL_ERROR_NO_CERTIFICATE); |
| return CK_INVALID_HANDLE; |
| } |
| |
| /* blow up if the PKCS #11 module returns us and invalid object handle */ |
| PORT_Assert(object != CK_INVALID_HANDLE); |
| return object; |
| } |
| |
| /* |
| * return all the object handles that matches the template |
| */ |
| CK_OBJECT_HANDLE * |
| pk11_FindObjectsByTemplate(PK11SlotInfo *slot, CK_ATTRIBUTE *findTemplate, |
| int templCount, int *object_count) |
| { |
| CK_OBJECT_HANDLE *objID = NULL; |
| CK_ULONG returned_count = 0; |
| CK_RV crv = CKR_SESSION_HANDLE_INVALID; |
| |
| PK11_EnterSlotMonitor(slot); |
| if (slot->session != CK_INVALID_SESSION) { |
| crv = PK11_GETTAB(slot)->C_FindObjectsInit(slot->session, |
| findTemplate, templCount); |
| } |
| if (crv != CKR_OK) { |
| PK11_ExitSlotMonitor(slot); |
| PORT_SetError(PK11_MapError(crv)); |
| *object_count = -1; |
| return NULL; |
| } |
| |
| /* |
| * collect all the Matching Objects |
| */ |
| do { |
| CK_OBJECT_HANDLE *oldObjID = objID; |
| |
| if (objID == NULL) { |
| objID = (CK_OBJECT_HANDLE *)PORT_Alloc(sizeof(CK_OBJECT_HANDLE) * |
| (*object_count + PK11_SEARCH_CHUNKSIZE)); |
| } else { |
| objID = (CK_OBJECT_HANDLE *)PORT_Realloc(objID, |
| sizeof(CK_OBJECT_HANDLE) * (*object_count + PK11_SEARCH_CHUNKSIZE)); |
| } |
| |
| if (objID == NULL) { |
| if (oldObjID) |
| PORT_Free(oldObjID); |
| break; |
| } |
| crv = PK11_GETTAB(slot)->C_FindObjects(slot->session, |
| &objID[*object_count], PK11_SEARCH_CHUNKSIZE, &returned_count); |
| if (crv != CKR_OK) { |
| PORT_SetError(PK11_MapError(crv)); |
| PORT_Free(objID); |
| objID = NULL; |
| break; |
| } |
| *object_count += returned_count; |
| } while (returned_count == PK11_SEARCH_CHUNKSIZE); |
| |
| PK11_GETTAB(slot)->C_FindObjectsFinal(slot->session); |
| PK11_ExitSlotMonitor(slot); |
| |
| if (objID && (*object_count == 0)) { |
| PORT_Free(objID); |
| return NULL; |
| } |
| if (objID == NULL) |
| *object_count = -1; |
| return objID; |
| } |
| /* |
| * given a PKCS #11 object, match it's peer based on the KeyID. searchID |
| * is typically a privateKey or a certificate while the peer is the opposite |
| */ |
| CK_OBJECT_HANDLE |
| PK11_MatchItem(PK11SlotInfo *slot, CK_OBJECT_HANDLE searchID, |
| CK_OBJECT_CLASS matchclass) |
| { |
| CK_ATTRIBUTE theTemplate[] = { |
| { CKA_ID, NULL, 0 }, |
| { CKA_CLASS, NULL, 0 } |
| }; |
| /* if you change the array, change the variable below as well */ |
| CK_ATTRIBUTE *keyclass = &theTemplate[1]; |
| int tsize = sizeof(theTemplate) / sizeof(theTemplate[0]); |
| /* if you change the array, change the variable below as well */ |
| CK_OBJECT_HANDLE peerID; |
| PORTCheapArenaPool tmpArena; |
| CK_RV crv; |
| |
| /* now we need to create space for the public key */ |
| PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE); |
| |
| crv = PK11_GetAttributes(&tmpArena.arena, slot, searchID, theTemplate, tsize); |
| if (crv != CKR_OK) { |
| PORT_DestroyCheapArena(&tmpArena); |
| PORT_SetError(PK11_MapError(crv)); |
| return CK_INVALID_HANDLE; |
| } |
| |
| if ((theTemplate[0].ulValueLen == 0) || (theTemplate[0].ulValueLen == -1)) { |
| PORT_DestroyCheapArena(&tmpArena); |
| if (matchclass == CKO_CERTIFICATE) |
| PORT_SetError(SEC_ERROR_BAD_KEY); |
| else |
| PORT_SetError(SEC_ERROR_NO_KEY); |
| return CK_INVALID_HANDLE; |
| } |
| |
| /* |
| * issue the find |
| */ |
| *(CK_OBJECT_CLASS *)(keyclass->pValue) = matchclass; |
| |
| peerID = pk11_FindObjectByTemplate(slot, theTemplate, tsize); |
| PORT_DestroyCheapArena(&tmpArena); |
| |
| return peerID; |
| } |
| |
| /* |
| * count the number of objects that match the template. |
| */ |
| int |
| PK11_NumberObjectsFor(PK11SlotInfo *slot, CK_ATTRIBUTE *findTemplate, |
| int templCount) |
| { |
| CK_OBJECT_HANDLE objID[PK11_SEARCH_CHUNKSIZE]; |
| int object_count = 0; |
| CK_ULONG returned_count = 0; |
| CK_RV crv = CKR_SESSION_HANDLE_INVALID; |
| |
| PK11_EnterSlotMonitor(slot); |
| if (slot->session != CK_INVALID_SESSION) { |
| crv = PK11_GETTAB(slot)->C_FindObjectsInit(slot->session, |
| findTemplate, templCount); |
| } |
| if (crv != CKR_OK) { |
| PK11_ExitSlotMonitor(slot); |
| PORT_SetError(PK11_MapError(crv)); |
| return object_count; |
| } |
| |
| /* |
| * collect all the Matching Objects |
| */ |
| do { |
| crv = PK11_GETTAB(slot)->C_FindObjects(slot->session, objID, |
| PK11_SEARCH_CHUNKSIZE, |
| &returned_count); |
| if (crv != CKR_OK) { |
| PORT_SetError(PK11_MapError(crv)); |
| break; |
| } |
| object_count += returned_count; |
| } while (returned_count == PK11_SEARCH_CHUNKSIZE); |
| |
| PK11_GETTAB(slot)->C_FindObjectsFinal(slot->session); |
| PK11_ExitSlotMonitor(slot); |
| return object_count; |
| } |
| |
| /* |
| * Traverse all the objects in a given slot. |
| */ |
| SECStatus |
| PK11_TraverseSlot(PK11SlotInfo *slot, void *arg) |
| { |
| int i; |
| CK_OBJECT_HANDLE *objID = NULL; |
| int object_count = 0; |
| pk11TraverseSlot *slotcb = (pk11TraverseSlot *)arg; |
| |
| objID = pk11_FindObjectsByTemplate(slot, slotcb->findTemplate, |
| slotcb->templateCount, &object_count); |
| |
| /*Actually this isn't a failure... there just were no objs to be found*/ |
| if (object_count == 0) { |
| return SECSuccess; |
| } |
| |
| if (objID == NULL) { |
| return SECFailure; |
| } |
| |
| for (i = 0; i < object_count; i++) { |
| (*slotcb->callback)(slot, objID[i], slotcb->callbackArg); |
| } |
| PORT_Free(objID); |
| return SECSuccess; |
| } |
| |
| /* |
| * Traverse all the objects in all slots. |
| */ |
| SECStatus |
| pk11_TraverseAllSlots(SECStatus (*callback)(PK11SlotInfo *, void *), |
| void *arg, PRBool forceLogin, void *wincx) |
| { |
| PK11SlotList *list; |
| PK11SlotListElement *le; |
| SECStatus rv; |
| |
| /* get them all! */ |
| list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, wincx); |
| if (list == NULL) |
| return SECFailure; |
| |
| /* look at each slot and authenticate as necessary */ |
| for (le = list->head; le; le = le->next) { |
| if (forceLogin) { |
| rv = pk11_AuthenticateUnfriendly(le->slot, PR_FALSE, wincx); |
| if (rv != SECSuccess) { |
| continue; |
| } |
| } |
| if (callback) { |
| (*callback)(le->slot, arg); |
| } |
| } |
| |
| PK11_FreeSlotList(list); |
| |
| return SECSuccess; |
| } |
| |
| CK_OBJECT_HANDLE * |
| PK11_FindObjectsFromNickname(char *nickname, PK11SlotInfo **slotptr, |
| CK_OBJECT_CLASS objclass, int *returnCount, void *wincx) |
| { |
| char *tokenName; |
| char *delimit; |
| PK11SlotInfo *slot; |
| CK_OBJECT_HANDLE *objID; |
| CK_ATTRIBUTE findTemplate[] = { |
| { CKA_LABEL, NULL, 0 }, |
| { CKA_CLASS, NULL, 0 }, |
| }; |
| int findCount = sizeof(findTemplate) / sizeof(findTemplate[0]); |
| SECStatus rv; |
| PK11_SETATTRS(&findTemplate[1], CKA_CLASS, &objclass, sizeof(objclass)); |
| |
| *slotptr = slot = NULL; |
| *returnCount = 0; |
| /* first find the slot associated with this nickname */ |
| if ((delimit = PORT_Strchr(nickname, ':')) != NULL) { |
| int len = delimit - nickname; |
| tokenName = (char *)PORT_Alloc(len + 1); |
| if (!tokenName) { |
| return CK_INVALID_HANDLE; |
| } |
| PORT_Memcpy(tokenName, nickname, len); |
| tokenName[len] = 0; |
| |
| slot = *slotptr = PK11_FindSlotByName(tokenName); |
| PORT_Free(tokenName); |
| /* if we couldn't find a slot, assume the nickname is an internal cert |
| * with no proceding slot name */ |
| if (slot == NULL) { |
| slot = *slotptr = PK11_GetInternalKeySlot(); |
| } else { |
| nickname = delimit + 1; |
| } |
| } else { |
| *slotptr = slot = PK11_GetInternalKeySlot(); |
| } |
| if (slot == NULL) { |
| return CK_INVALID_HANDLE; |
| } |
| |
| rv = pk11_AuthenticateUnfriendly(slot, PR_TRUE, wincx); |
| if (rv != SECSuccess) { |
| PK11_FreeSlot(slot); |
| *slotptr = NULL; |
| return CK_INVALID_HANDLE; |
| } |
| |
| findTemplate[0].pValue = nickname; |
| findTemplate[0].ulValueLen = PORT_Strlen(nickname); |
| objID = pk11_FindObjectsByTemplate(slot, findTemplate, findCount, returnCount); |
| if (objID == NULL) { |
| /* PKCS #11 isn't clear on whether or not the NULL is |
| * stored in the template.... try the find again with the |
| * full null terminated string. */ |
| findTemplate[0].ulValueLen += 1; |
| objID = pk11_FindObjectsByTemplate(slot, findTemplate, findCount, |
| returnCount); |
| if (objID == NULL) { |
| /* Well that's the best we can do. It's just not here */ |
| /* what about faked nicknames? */ |
| PK11_FreeSlot(slot); |
| *slotptr = NULL; |
| *returnCount = 0; |
| } |
| } |
| |
| return objID; |
| } |
| |
| SECItem * |
| pk11_GetLowLevelKeyFromHandle(PK11SlotInfo *slot, CK_OBJECT_HANDLE handle) |
| { |
| CK_ATTRIBUTE theTemplate[] = { |
| { CKA_ID, NULL, 0 }, |
| }; |
| int tsize = sizeof(theTemplate) / sizeof(theTemplate[0]); |
| CK_RV crv; |
| SECItem *item; |
| |
| item = SECITEM_AllocItem(NULL, NULL, 0); |
| |
| if (item == NULL) { |
| return NULL; |
| } |
| |
| crv = PK11_GetAttributes(NULL, slot, handle, theTemplate, tsize); |
| if (crv != CKR_OK) { |
| SECITEM_FreeItem(item, PR_TRUE); |
| PORT_SetError(PK11_MapError(crv)); |
| return NULL; |
| } |
| |
| item->data = (unsigned char *)theTemplate[0].pValue; |
| item->len = theTemplate[0].ulValueLen; |
| |
| return item; |
| } |