strongswan/src/libstrongswan/plugins/pkcs11/pkcs11_manager.c

392 lines
8.9 KiB
C

/*
* Copyright (C) 2010 Martin Willi
* Copyright (C) 2010 revosec AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "pkcs11_manager.h"
#include <utils/debug.h>
#include <collections/linked_list.h>
#include <threading/thread.h>
#include "pkcs11_library.h"
#include <processing/jobs/callback_job.h>
typedef struct private_pkcs11_manager_t private_pkcs11_manager_t;
/**
* Private data of an pkcs11_manager_t object.
*/
struct private_pkcs11_manager_t {
/**
* Public pkcs11_manager_t interface.
*/
pkcs11_manager_t public;
/**
* List of loaded libraries, as lib_entry_t
*/
linked_list_t *libs;
/**
* Slot event callback function
*/
pkcs11_manager_token_event_t cb;
/**
* Slot event user data
*/
void *data;
};
/**
* Entry for a loaded library
*/
typedef struct {
/* back reference to this */
private_pkcs11_manager_t *this;
/* associated library path */
char *path;
/* loaded library */
pkcs11_library_t *lib;
} lib_entry_t;
/**
* Destroy a lib_entry_t
*/
static void lib_entry_destroy(lib_entry_t *entry)
{
entry->lib->destroy(entry->lib);
free(entry);
}
/**
* Print supported mechanisms of a token in a slot
*/
static void print_mechs(lib_entry_t *entry, CK_SLOT_ID slot)
{
enumerator_t *enumerator;
CK_MECHANISM_TYPE type;
CK_MECHANISM_INFO info;
enumerator = entry->lib->create_mechanism_enumerator(entry->lib, slot);
while (enumerator->enumerate(enumerator, &type, &info))
{
DBG2(DBG_CFG, " %N %lu-%lu [ %s%s%s%s%s%s%s%s%s%s%s%s%s]",
ck_mech_names, type,
info.ulMinKeySize, info.ulMaxKeySize,
info.flags & CKF_HW ? "HW " : "",
info.flags & CKF_ENCRYPT ? "ENCR " : "",
info.flags & CKF_DECRYPT ? "DECR " : "",
info.flags & CKF_DIGEST ? "DGST " : "",
info.flags & CKF_SIGN ? "SIGN " : "",
info.flags & CKF_SIGN_RECOVER ? "SIGN_RCVR " : "",
info.flags & CKF_VERIFY ? "VRFY " : "",
info.flags & CKF_VERIFY_RECOVER ? "VRFY_RCVR " : "",
info.flags & CKF_GENERATE ? "GEN " : "",
info.flags & CKF_GENERATE_KEY_PAIR ? "GEN_KEY_PAIR " : "",
info.flags & CKF_WRAP ? "WRAP " : "",
info.flags & CKF_UNWRAP ? "UNWRAP " : "",
info.flags & CKF_DERIVE ? "DERIVE " : "");
}
enumerator->destroy(enumerator);
}
/**
* Handle a token
*/
static void handle_token(lib_entry_t *entry, CK_SLOT_ID slot)
{
CK_TOKEN_INFO info;
CK_RV rv;
rv = entry->lib->f->C_GetTokenInfo(slot, &info);
if (rv != CKR_OK)
{
DBG1(DBG_CFG, "C_GetTokenInfo failed: %N", ck_rv_names, rv);
return;
}
pkcs11_library_trim(info.label, sizeof(info.label));
pkcs11_library_trim(info.manufacturerID, sizeof(info.manufacturerID));
pkcs11_library_trim(info.model, sizeof(info.model));
DBG1(DBG_CFG, " %s (%s: %s)",
info.label, info.manufacturerID, info.model);
print_mechs(entry, slot);
}
/**
* Handle slot changes
*/
static void handle_slot(lib_entry_t *entry, CK_SLOT_ID slot, bool hot)
{
CK_SLOT_INFO info;
CK_RV rv;
rv = entry->lib->f->C_GetSlotInfo(slot, &info);
if (rv != CKR_OK)
{
DBG1(DBG_CFG, "C_GetSlotInfo failed: %N", ck_rv_names, rv);
return;
}
pkcs11_library_trim(info.slotDescription, sizeof(info.slotDescription));
if (info.flags & CKF_TOKEN_PRESENT)
{
DBG1(DBG_CFG, " found token in slot '%s':%lu (%s)",
entry->lib->get_name(entry->lib), slot, info.slotDescription);
handle_token(entry, slot);
if (hot)
{
entry->this->cb(entry->this->data, entry->lib, slot, TRUE);
}
}
else
{
DBG1(DBG_CFG, "token removed from slot '%s':%lu (%s)",
entry->lib->get_name(entry->lib), slot, info.slotDescription);
if (hot)
{
entry->this->cb(entry->this->data, entry->lib, slot, FALSE);
}
}
}
CALLBACK(dispatch_slot_events, job_requeue_t,
lib_entry_t *entry)
{
CK_SLOT_ID slot;
CK_RV rv;
rv = entry->lib->f->C_WaitForSlotEvent(0, &slot, NULL);
if (rv == CKR_FUNCTION_NOT_SUPPORTED || rv == CKR_NO_EVENT)
{
DBG1(DBG_CFG, "module '%s' does not support hot-plugging, canceled",
entry->lib->get_name(entry->lib));
return JOB_REQUEUE_NONE;
}
if (rv == CKR_CRYPTOKI_NOT_INITIALIZED)
{ /* C_Finalize called, abort */
return JOB_REQUEUE_NONE;
}
if (rv != CKR_OK)
{
DBG1(DBG_CFG, "error in C_WaitForSlotEvent: %N", ck_rv_names, rv);
}
handle_slot(entry, slot, TRUE);
return JOB_REQUEUE_DIRECT;
}
CALLBACK(cancel_events, bool,
lib_entry_t *entry)
{
/* it's possible other threads still use the API after this call, but we
* have no other way to return from C_WaitForSlotEvent() if we can't cancel
* the thread because libraries hold locks they don't release */
entry->lib->f->C_Finalize(NULL);
return TRUE;
}
/**
* Get the slot list of a library
*/
static CK_SLOT_ID_PTR get_slot_list(pkcs11_library_t *p11, CK_ULONG *out)
{
CK_SLOT_ID_PTR slots;
CK_ULONG count;
CK_RV rv;
rv = p11->f->C_GetSlotList(TRUE, NULL, &count);
if (rv != CKR_OK)
{
DBG1(DBG_CFG, "C_GetSlotList() failed: %N", ck_rv_names, rv);
return NULL;
}
if (count == 0)
{
return NULL;
}
slots = malloc(sizeof(CK_SLOT_ID) * count);
rv = p11->f->C_GetSlotList(TRUE, slots, &count);
if (rv != CKR_OK)
{
DBG1(DBG_CFG, "C_GetSlotList() failed: %N", ck_rv_names, rv);
free(slots);
return NULL;
}
*out = count;
return slots;
}
/**
* Query the slots for tokens
*/
static void query_slots(lib_entry_t *entry)
{
CK_ULONG count;
CK_SLOT_ID_PTR slots;
int i;
slots = get_slot_list(entry->lib, &count);
if (slots)
{
for (i = 0; i < count; i++)
{
handle_slot(entry, slots[i], FALSE);
}
free(slots);
}
}
/**
* Token enumerator
*/
typedef struct {
/* implements enumerator */
enumerator_t public;
/* inner enumerator over PKCS#11 libraries */
enumerator_t *inner;
/* active library entry */
lib_entry_t *entry;
/* slot list with tokens */
CK_SLOT_ID_PTR slots;
/* number of slots */
CK_ULONG count;
/* current slot */
int current;
} token_enumerator_t;
METHOD(enumerator_t, enumerate_token, bool,
token_enumerator_t *this, va_list args)
{
pkcs11_library_t **out;
CK_SLOT_ID *slot;
VA_ARGS_VGET(args, out, slot);
if (this->current >= this->count)
{
free(this->slots);
this->slots = NULL;
this->current = 0;
}
while (!this->slots)
{
if (!this->inner->enumerate(this->inner, &this->entry))
{
return FALSE;
}
this->slots = get_slot_list(this->entry->lib, &this->count);
}
*out = this->entry->lib;
*slot = this->slots[this->current++];
return TRUE;
}
METHOD(enumerator_t, destroy_token, void,
token_enumerator_t *this)
{
this->inner->destroy(this->inner);
free(this->slots);
free(this);
}
METHOD(pkcs11_manager_t, create_token_enumerator, enumerator_t*,
private_pkcs11_manager_t *this)
{
token_enumerator_t *enumerator;
INIT(enumerator,
.public = {
.enumerate = enumerator_enumerate_default,
.venumerate = _enumerate_token,
.destroy = _destroy_token,
},
.inner = this->libs->create_enumerator(this->libs),
);
return &enumerator->public;
}
METHOD(pkcs11_manager_t, destroy, void,
private_pkcs11_manager_t *this)
{
this->libs->destroy_function(this->libs, (void*)lib_entry_destroy);
free(this);
}
/**
* See header
*/
pkcs11_manager_t *pkcs11_manager_create(pkcs11_manager_token_event_t cb,
void *data)
{
private_pkcs11_manager_t *this;
enumerator_t *enumerator;
lib_entry_t *entry;
char *module;
INIT(this,
.public = {
.create_token_enumerator = _create_token_enumerator,
.destroy = _destroy,
},
.libs = linked_list_create(),
.cb = cb,
.data = data,
);
enumerator = lib->settings->create_section_enumerator(lib->settings,
"%s.plugins.pkcs11.modules", lib->ns);
while (enumerator->enumerate(enumerator, &module))
{
INIT(entry,
.this = this,
);
entry->path = lib->settings->get_str(lib->settings,
"%s.plugins.pkcs11.modules.%s.path", NULL, lib->ns, module);
if (!entry->path)
{
DBG1(DBG_CFG, "PKCS11 module '%s' lacks library path", module);
free(entry);
continue;
}
entry->lib = pkcs11_library_create(module, entry->path,
lib->settings->get_bool(lib->settings,
"%s.plugins.pkcs11.modules.%s.os_locking",
FALSE, lib->ns, module));
if (!entry->lib)
{
free(entry);
continue;
}
this->libs->insert_last(this->libs, entry);
}
enumerator->destroy(enumerator);
enumerator = this->libs->create_enumerator(this->libs);
while (enumerator->enumerate(enumerator, &entry))
{
query_slots(entry);
lib->processor->queue_job(lib->processor,
(job_t*)callback_job_create_with_prio(dispatch_slot_events,
entry, NULL, cancel_events, JOB_PRIO_CRITICAL));
}
enumerator->destroy(enumerator);
return &this->public;
}