diff options
Diffstat (limited to 'src/libstrongswan/plugins/pkcs11/pkcs11_manager.c')
-rw-r--r-- | src/libstrongswan/plugins/pkcs11/pkcs11_manager.c | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_manager.c b/src/libstrongswan/plugins/pkcs11/pkcs11_manager.c new file mode 100644 index 000000000..0c27600a6 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs11/pkcs11_manager.c @@ -0,0 +1,407 @@ +/* + * 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 <debug.h> +#include <utils/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; + /* event dispatcher job */ + callback_job_t *job; +} lib_entry_t; + +/** + * Destroy a lib_entry_t + */ +static void lib_entry_destroy(lib_entry_t *entry) +{ + if (entry->job) + { + entry->job->cancel(entry->job); + } + 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); + } + } +} + +/** + * Dispatch slot events + */ +static job_requeue_t dispatch_slot_events(lib_entry_t *entry) +{ + CK_SLOT_ID slot; + CK_RV rv; + bool old; + + old = thread_cancelability(TRUE); + rv = entry->lib->f->C_WaitForSlotEvent(0, &slot, NULL); + thread_cancelability(old); + if (rv == CKR_FUNCTION_NOT_SUPPORTED || rv == CKR_NO_EVENT) + { + DBG1(DBG_CFG, "module '%s' does not support hot-plugging, cancelled", + 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; +} + +/** + * End dispatching, unset job + */ +static void end_dispatch(lib_entry_t *entry) +{ + entry->job = NULL; +} + +/** + * 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, pkcs11_library_t **out, CK_SLOT_ID *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 = (void*)_enumerate_token, + .destroy = _destroy_token, + }, + .inner = this->libs->create_enumerator(this->libs), + ); + return &enumerator->public; +} + +/** + * Singleton instance + */ +static private_pkcs11_manager_t *singleton = NULL; + +METHOD(pkcs11_manager_t, destroy, void, + private_pkcs11_manager_t *this) +{ + this->libs->destroy_function(this->libs, (void*)lib_entry_destroy); + free(this); + singleton = NULL; +} + +/** + * 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, + "libstrongswan.plugins.pkcs11.modules"); + while (enumerator->enumerate(enumerator, &module)) + { + INIT(entry, + .this = this, + ); + + entry->path = lib->settings->get_str(lib->settings, + "libstrongswan.plugins.pkcs11.modules.%s.path", NULL, module); + if (!entry->path) + { + DBG1(DBG_CFG, "PKCS11 module '%s' misses library path", module); + free(entry); + continue; + } + entry->lib = pkcs11_library_create(module, entry->path); + if (!entry->lib) + { + free(entry); + continue; + } + this->libs->insert_last(this->libs, entry); + } + enumerator->destroy(enumerator); + + singleton = this; + + enumerator = this->libs->create_enumerator(this->libs); + while (enumerator->enumerate(enumerator, &entry)) + { + query_slots(entry); + entry->job = callback_job_create((void*)dispatch_slot_events, + entry, (void*)end_dispatch, NULL); + lib->processor->queue_job(lib->processor, (job_t*)entry->job); + } + enumerator->destroy(enumerator); + + return &this->public; +} + +/** + * See header + */ +pkcs11_manager_t *pkcs11_manager_get() +{ + return (pkcs11_manager_t*)singleton; +} |