xca/lib/pkcs11.cpp
Christian Hohnstaedt fe8c2c2660 improve filename handling for databases
- allow "umlauts" and other non ASCII char in database filenames
   using the OS dependent handling QString2filename, using
   8Bit codepage encoding on Windows and UTF8 on linux and OS X
2009-11-25 09:06:22 +01:00

399 lines
9.4 KiB
C++

#include "pkcs11.h"
#include "pk11_attribute.h"
#include "exception.h"
#include "db_base.h"
#include "func.h"
#include <qmessagebox.h>
#include <ltdl.h>
CK_FUNCTION_LIST *pkcs11::p11 = NULL;
lt_dlhandle pkcs11::dl_handle = NULL;
pkcs11::pkcs11()
{
session = CK_INVALID_HANDLE;
object = CK_INVALID_HANDLE;
slot_id = 0;
}
pkcs11::~pkcs11()
{
if (session != CK_INVALID_HANDLE && p11)
p11->C_CloseSession(session);
}
void pkcs11::startSession(unsigned long slot, bool rw)
{
CK_RV rv;
unsigned long flags = CKF_SERIAL_SESSION | (rw ? CKF_RW_SESSION : 0);
if (session != CK_INVALID_HANDLE) {
rv = p11->C_CloseSession(session);
if (rv != CKR_OK)
pk11error("C_OpenSession", rv);
}
rv = p11->C_OpenSession(slot, flags, NULL, NULL, &session);
if (rv != CKR_OK)
pk11error("C_OpenSession", rv);
slot_id = slot;
}
QList<unsigned long> pkcs11::getSlotList()
{
CK_RV rv;
CK_SLOT_ID *p11_slots = NULL;
QList<unsigned long> sl;
unsigned long i, num_slots = 0;
/* This one helps to avoid errors.
* Fist time it fails, 2nd time it works */
p11->C_GetSlotList(CK_TRUE, p11_slots, &num_slots);
while (1) {
rv = p11->C_GetSlotList(CK_TRUE, p11_slots, &num_slots);
if (rv != CKR_OK && rv != CKR_BUFFER_TOO_SMALL)
pk11error("C_GetSlotList", rv);
if (num_slots == 0)
break;
if ((rv == CKR_OK) && p11_slots)
break;
p11_slots = (CK_SLOT_ID *)realloc(p11_slots,
num_slots *sizeof(CK_SLOT_ID));
check_oom(p11_slots);
}
for (i=0; i<num_slots; i++) {
sl << p11_slots[i];
}
if (p11_slots)
free(p11_slots);
return sl;
}
QList<CK_MECHANISM_TYPE> pkcs11::mechanismList(unsigned long slot)
{
CK_RV rv;
CK_MECHANISM_TYPE *m;
QList<CK_MECHANISM_TYPE> ml;
unsigned long count;
rv = p11->C_GetMechanismList(slot, NULL, &count);
if (count != 0) {
m = (CK_MECHANISM_TYPE *)malloc(count *sizeof(*m));
check_oom(m);
rv = p11->C_GetMechanismList(slot, m, &count);
if (rv != CKR_OK)
pk11error("C_GetMechanismList", rv);
for (unsigned i=0; i<count; i++) {
ml << m[i];
}
}
return ml;
}
void pkcs11::logout()
{
CK_RV rv;
rv = p11->C_Logout(session);
if (rv != CKR_OK && rv != CKR_USER_NOT_LOGGED_IN)
pk11error("C_Logout", rv);
}
bool pkcs11::needsLogin(bool so)
{
CK_SESSION_INFO sinfo;
CK_RV rv;
rv = p11->C_GetSessionInfo(session, &sinfo);
if (rv != CKR_OK)
pk11error("C_GetSessionInfo", rv);
switch (sinfo.state) {
case CKS_RO_PUBLIC_SESSION:
case CKS_RW_PUBLIC_SESSION:
return true;
case CKS_RW_SO_FUNCTIONS:
if (so) {
return false;
} else {
logout();
return true;
}
case CKS_RO_USER_FUNCTIONS:
case CKS_RW_USER_FUNCTIONS:
if (!so) {
return false;
} else {
logout();
return true;
}
}
return true;
}
void pkcs11::login(unsigned char *pin, unsigned long pinlen, bool so)
{
unsigned long user = so ? CKU_SO : CKU_USER;
CK_RV rv;
rv = p11->C_Login(session, user, pin, pinlen);
if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN)
pk11error("C_Login", rv);
}
void pkcs11::setPin(unsigned char *oldPin, unsigned long oldPinLen,
unsigned char *pin, unsigned long pinLen)
{
CK_RV rv = p11->C_SetPIN(session, oldPin, oldPinLen, pin, pinLen);
if (rv != CKR_OK)
pk11error("C_SetPIN", rv);
}
void pkcs11::initPin(unsigned char *pin, unsigned long pinLen)
{
CK_RV rv = p11->C_InitPIN(session, pin, pinLen);
if (rv != CKR_OK)
pk11error("C_InitPIN", rv);
}
QStringList pkcs11::tokenInfo(CK_SLOT_ID slot)
{
QStringList l;
CK_TOKEN_INFO token_info;
CK_RV rv;
rv = p11->C_GetTokenInfo(slot, &token_info);
if (rv != CKR_OK) {
pk11error("C_GetTokenInfo", rv);
}
l << UTF8QSTRING(token_info.label, 32);
l << UTF8QSTRING(token_info.manufacturerID, 32);
l << UTF8QSTRING(token_info.serialNumber, 16);
return l;
}
QStringList pkcs11::tokenInfo()
{
return tokenInfo(slot_id);
}
bool pkcs11::protAuthPath(CK_SLOT_ID slot)
{
CK_TOKEN_INFO token_info;
CK_RV rv;
rv = p11->C_GetTokenInfo(slot, &token_info);
if (rv != CKR_OK) {
pk11error("C_GetTokenInfo", rv);
}
return !!(token_info.flags & CKF_PROTECTED_AUTHENTICATION_PATH);
}
bool pkcs11::protAuthPath()
{
return protAuthPath(slot_id);
}
void pkcs11::loadAttribute(pk11_attribute &attribute, CK_OBJECT_HANDLE object)
{
attribute.load(session, object);
}
void pkcs11::storeAttribute(pk11_attribute &attribute, CK_OBJECT_HANDLE object)
{
attribute.store(session, object);
}
CK_OBJECT_HANDLE pkcs11::createObject(pk11_attlist &attrs)
{
CK_RV rv;
CK_ATTRIBUTE *attributes;
unsigned long num;
CK_OBJECT_HANDLE obj;
num = attrs.get(&attributes);
rv = p11->C_CreateObject(session, attributes, num, &obj);
if (rv != CKR_OK) {
pk11error("C_CreateObject", rv);
}
return obj;
}
QList<CK_OBJECT_HANDLE> pkcs11::objectList(pk11_attlist &atts)
{
CK_RV rv;
CK_OBJECT_HANDLE objects[256];
QList<CK_OBJECT_HANDLE> list;
unsigned long len, i, att_num;
CK_ATTRIBUTE *attribute;
att_num = atts.get(&attribute);
rv = p11->C_FindObjectsInit(session, attribute, att_num);
if (rv != CKR_OK)
pk11error("C_FindObjectsInit", rv);
do {
rv = p11->C_FindObjects(session, objects, 256, &len);
if (rv != CKR_OK)
pk11error("C_FindObjects", rv);
for (i=0; i<len; i++)
list += objects[i];
} while (len);
rv = p11->C_FindObjectsFinal(session);
if (rv != CKR_OK)
pk11error("C_FindObjectsFinal", rv);
return list;
}
bool pkcs11::load_lib(QString file, bool silent)
{
CK_RV (*c_get_function_list)(CK_FUNCTION_LIST_PTR_PTR);
lt_dlinit();
if (dl_handle) {
if (lt_dlclose(dl_handle) < 0) {
if (silent)
return false;
throw errorEx("Failed to close PKCS11 library: " + file);
}
}
p11 = NULL;
dl_handle = NULL;
if (file.isEmpty()) {
if (silent)
return false;
throw errorEx("PKCS11 library filename empty");
}
dl_handle = lt_dlopen(QString2filename(file));
if (dl_handle == NULL) {
if (silent)
return false;
throw errorEx("Failed to open PKCS11 library: " + file);
}
/* Get the list of function pointers */
c_get_function_list = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR))
lt_dlsym(dl_handle, "C_GetFunctionList");
if (c_get_function_list) {
if (c_get_function_list(&p11) == CKR_OK)
return true;
}
/* This state is always worth an error ! */
if (lt_dlclose(dl_handle) == 0)
dl_handle = NULL;
throw errorEx("Failed to open PKCS11 library: " + file);
return false;
}
static const char *CKR2Str(unsigned long rv)
{
#define PK11_ERR(x) case x : return #x;
switch (rv) {
PK11_ERR(CKR_OK)
PK11_ERR(CKR_CANCEL)
PK11_ERR(CKR_HOST_MEMORY)
PK11_ERR(CKR_SLOT_ID_INVALID)
PK11_ERR(CKR_GENERAL_ERROR)
PK11_ERR(CKR_FUNCTION_FAILED)
PK11_ERR(CKR_ARGUMENTS_BAD)
PK11_ERR(CKR_NO_EVENT)
PK11_ERR(CKR_NEED_TO_CREATE_THREADS)
PK11_ERR(CKR_CANT_LOCK)
PK11_ERR(CKR_ATTRIBUTE_READ_ONLY)
PK11_ERR(CKR_ATTRIBUTE_SENSITIVE)
PK11_ERR(CKR_ATTRIBUTE_TYPE_INVALID)
PK11_ERR(CKR_ATTRIBUTE_VALUE_INVALID)
PK11_ERR(CKR_DATA_INVALID)
PK11_ERR(CKR_DATA_LEN_RANGE)
PK11_ERR(CKR_DEVICE_ERROR)
PK11_ERR(CKR_DEVICE_MEMORY)
PK11_ERR(CKR_DEVICE_REMOVED)
PK11_ERR(CKR_ENCRYPTED_DATA_INVALID)
PK11_ERR(CKR_ENCRYPTED_DATA_LEN_RANGE)
PK11_ERR(CKR_FUNCTION_CANCELED)
PK11_ERR(CKR_FUNCTION_NOT_PARALLEL)
PK11_ERR(CKR_FUNCTION_NOT_SUPPORTED)
PK11_ERR(CKR_KEY_HANDLE_INVALID)
PK11_ERR(CKR_KEY_SIZE_RANGE)
PK11_ERR(CKR_KEY_TYPE_INCONSISTENT)
PK11_ERR(CKR_KEY_NOT_NEEDED)
PK11_ERR(CKR_KEY_CHANGED)
PK11_ERR(CKR_KEY_NEEDED)
PK11_ERR(CKR_KEY_INDIGESTIBLE)
PK11_ERR(CKR_KEY_FUNCTION_NOT_PERMITTED)
PK11_ERR(CKR_KEY_NOT_WRAPPABLE)
PK11_ERR(CKR_KEY_UNEXTRACTABLE)
PK11_ERR(CKR_MECHANISM_INVALID)
PK11_ERR(CKR_MECHANISM_PARAM_INVALID)
PK11_ERR(CKR_OBJECT_HANDLE_INVALID)
PK11_ERR(CKR_OPERATION_ACTIVE)
PK11_ERR(CKR_OPERATION_NOT_INITIALIZED)
PK11_ERR(CKR_PIN_INCORRECT)
PK11_ERR(CKR_PIN_INVALID)
PK11_ERR(CKR_PIN_LEN_RANGE)
PK11_ERR(CKR_PIN_EXPIRED)
PK11_ERR(CKR_PIN_LOCKED)
PK11_ERR(CKR_SESSION_CLOSED)
PK11_ERR(CKR_SESSION_COUNT)
PK11_ERR(CKR_SESSION_HANDLE_INVALID)
PK11_ERR(CKR_SESSION_PARALLEL_NOT_SUPPORTED)
PK11_ERR(CKR_SESSION_READ_ONLY)
PK11_ERR(CKR_SESSION_EXISTS)
PK11_ERR(CKR_SESSION_READ_ONLY_EXISTS)
PK11_ERR(CKR_SESSION_READ_WRITE_SO_EXISTS)
PK11_ERR(CKR_SIGNATURE_INVALID)
PK11_ERR(CKR_SIGNATURE_LEN_RANGE)
PK11_ERR(CKR_TEMPLATE_INCOMPLETE)
PK11_ERR(CKR_TEMPLATE_INCONSISTENT)
PK11_ERR(CKR_TOKEN_NOT_PRESENT)
PK11_ERR(CKR_TOKEN_NOT_RECOGNIZED)
PK11_ERR(CKR_TOKEN_WRITE_PROTECTED)
PK11_ERR(CKR_UNWRAPPING_KEY_HANDLE_INVALID)
PK11_ERR(CKR_UNWRAPPING_KEY_SIZE_RANGE)
PK11_ERR(CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT)
PK11_ERR(CKR_USER_ALREADY_LOGGED_IN)
PK11_ERR(CKR_USER_NOT_LOGGED_IN)
PK11_ERR(CKR_USER_PIN_NOT_INITIALIZED)
PK11_ERR(CKR_USER_TYPE_INVALID)
PK11_ERR(CKR_USER_ANOTHER_ALREADY_LOGGED_IN)
PK11_ERR(CKR_USER_TOO_MANY_TYPES)
PK11_ERR(CKR_WRAPPED_KEY_INVALID)
PK11_ERR(CKR_WRAPPED_KEY_LEN_RANGE)
PK11_ERR(CKR_WRAPPING_KEY_HANDLE_INVALID)
PK11_ERR(CKR_WRAPPING_KEY_SIZE_RANGE)
PK11_ERR(CKR_WRAPPING_KEY_TYPE_INCONSISTENT)
PK11_ERR(CKR_RANDOM_SEED_NOT_SUPPORTED)
PK11_ERR(CKR_RANDOM_NO_RNG)
PK11_ERR(CKR_DOMAIN_PARAMS_INVALID)
PK11_ERR(CKR_BUFFER_TOO_SMALL)
PK11_ERR(CKR_SAVED_STATE_INVALID)
PK11_ERR(CKR_INFORMATION_SENSITIVE)
PK11_ERR(CKR_STATE_UNSAVEABLE)
PK11_ERR(CKR_CRYPTOKI_NOT_INITIALIZED)
PK11_ERR(CKR_CRYPTOKI_ALREADY_INITIALIZED)
PK11_ERR(CKR_MUTEX_BAD)
PK11_ERR(CKR_MUTEX_NOT_LOCKED)
PK11_ERR(CKR_VENDOR_DEFINED)
}
return "unknown PKCS11 error";
}
void pkcs11::pk11error(QString func, int rv)
{
errorEx err("PKCS#11 function " + func + " failed: " +
CKR2Str(rv) + "\n");
throw err;
}