321 lines
8.4 KiB
C
321 lines
8.4 KiB
C
/*
|
|
* Copyright (C) 2011 Tobias Brunner
|
|
* Hochschule fuer Technik Rapperswil
|
|
*
|
|
* 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_plugin.h"
|
|
|
|
#include <library.h>
|
|
#include <utils/debug.h>
|
|
#include <collections/linked_list.h>
|
|
#include <threading/mutex.h>
|
|
#include <threading/rwlock.h>
|
|
|
|
#include "pkcs11_manager.h"
|
|
#include "pkcs11_creds.h"
|
|
#include "pkcs11_private_key.h"
|
|
#include "pkcs11_public_key.h"
|
|
#include "pkcs11_hasher.h"
|
|
#include "pkcs11_rng.h"
|
|
#include "pkcs11_dh.h"
|
|
|
|
typedef struct private_pkcs11_plugin_t private_pkcs11_plugin_t;
|
|
|
|
/**
|
|
* private data of pkcs11_plugin
|
|
*/
|
|
struct private_pkcs11_plugin_t {
|
|
|
|
/**
|
|
* public functions
|
|
*/
|
|
pkcs11_plugin_t public;
|
|
|
|
/**
|
|
* PKCS#11 library/slot manager
|
|
*/
|
|
pkcs11_manager_t *manager;
|
|
|
|
/**
|
|
* List of credential sets, pkcs11_creds_t
|
|
*/
|
|
linked_list_t *creds;
|
|
|
|
/**
|
|
* mutex to lock list
|
|
*/
|
|
mutex_t *mutex;
|
|
|
|
/**
|
|
* TRUE if events from tokens are to be handled
|
|
*/
|
|
bool handle_events;
|
|
|
|
/**
|
|
* Lock for the above flag
|
|
*/
|
|
rwlock_t *handle_events_lock;
|
|
};
|
|
|
|
/**
|
|
* Token event callback function
|
|
*/
|
|
static void token_event_cb(private_pkcs11_plugin_t *this, pkcs11_library_t *p11,
|
|
CK_SLOT_ID slot, bool add)
|
|
{
|
|
enumerator_t *enumerator;
|
|
pkcs11_creds_t *creds, *found = NULL;
|
|
|
|
this->handle_events_lock->read_lock(this->handle_events_lock);
|
|
if (add && this->handle_events)
|
|
{
|
|
if (lib->settings->get_bool(lib->settings,
|
|
"%s.plugins.pkcs11.modules.%s.load_certs",
|
|
TRUE, lib->ns, p11->get_name(p11)))
|
|
{
|
|
creds = pkcs11_creds_create(p11, slot);
|
|
if (creds)
|
|
{
|
|
this->mutex->lock(this->mutex);
|
|
this->creds->insert_last(this->creds, creds);
|
|
this->mutex->unlock(this->mutex);
|
|
lib->credmgr->add_set(lib->credmgr, &creds->set);
|
|
}
|
|
}
|
|
}
|
|
else if (this->handle_events)
|
|
{
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->creds->create_enumerator(this->creds);
|
|
while (enumerator->enumerate(enumerator, &creds))
|
|
{
|
|
if (creds->get_library(creds) == p11 &&
|
|
creds->get_slot(creds) == slot)
|
|
{
|
|
found = creds;
|
|
this->creds->remove_at(this->creds, enumerator);
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
|
|
if (found)
|
|
{
|
|
lib->credmgr->remove_set(lib->credmgr, &found->set);
|
|
found->destroy(found);
|
|
/* flush the cache after a token is gone */
|
|
lib->credmgr->flush_cache(lib->credmgr, CERT_X509);
|
|
}
|
|
}
|
|
this->handle_events_lock->unlock(this->handle_events_lock);
|
|
}
|
|
|
|
METHOD(plugin_t, get_name, char*,
|
|
private_pkcs11_plugin_t *this)
|
|
{
|
|
return "pkcs11";
|
|
}
|
|
|
|
/**
|
|
* Load/unload certificates from tokens.
|
|
*/
|
|
static bool handle_certs(private_pkcs11_plugin_t *this,
|
|
plugin_feature_t *feature, bool reg, void *data)
|
|
{
|
|
this->handle_events_lock->write_lock(this->handle_events_lock);
|
|
this->handle_events = reg;
|
|
this->handle_events_lock->unlock(this->handle_events_lock);
|
|
|
|
if (reg)
|
|
{
|
|
enumerator_t *enumerator;
|
|
pkcs11_library_t *p11;
|
|
CK_SLOT_ID slot;
|
|
|
|
enumerator = this->manager->create_token_enumerator(this->manager);
|
|
while (enumerator->enumerate(enumerator, &p11, &slot))
|
|
{
|
|
token_event_cb(this, p11, slot, TRUE);
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
lib->creds->add_builder(lib->creds, CRED_CERTIFICATE,
|
|
CERT_X509, FALSE, (void*)pkcs11_creds_load);
|
|
}
|
|
else
|
|
{
|
|
pkcs11_creds_t *creds;
|
|
|
|
while (this->creds->remove_last(this->creds, (void**)&creds) == SUCCESS)
|
|
{
|
|
lib->credmgr->remove_set(lib->credmgr, &creds->set);
|
|
creds->destroy(creds);
|
|
}
|
|
|
|
lib->creds->remove_builder(lib->creds, (void*)pkcs11_creds_load);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
METHOD(plugin_t, reload, bool,
|
|
private_pkcs11_plugin_t *this)
|
|
{
|
|
if (lib->settings->get_bool(lib->settings, "%s.plugins.pkcs11.reload_certs",
|
|
FALSE, lib->ns))
|
|
{
|
|
DBG1(DBG_CFG, "reloading certificates from PKCS#11 tokens");
|
|
handle_certs(this, NULL, FALSE, NULL);
|
|
handle_certs(this, NULL, TRUE, NULL);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
METHOD(plugin_t, get_features, int,
|
|
private_pkcs11_plugin_t *this, plugin_feature_t *features[])
|
|
{
|
|
static plugin_feature_t f_hash[] = {
|
|
PLUGIN_REGISTER(HASHER, pkcs11_hasher_create),
|
|
PLUGIN_PROVIDE(HASHER, HASH_MD2),
|
|
PLUGIN_PROVIDE(HASHER, HASH_MD5),
|
|
PLUGIN_PROVIDE(HASHER, HASH_SHA1),
|
|
PLUGIN_PROVIDE(HASHER, HASH_SHA256),
|
|
PLUGIN_PROVIDE(HASHER, HASH_SHA384),
|
|
PLUGIN_PROVIDE(HASHER, HASH_SHA512),
|
|
};
|
|
static plugin_feature_t f_dh[] = {
|
|
PLUGIN_REGISTER(DH, pkcs11_dh_create),
|
|
PLUGIN_PROVIDE(DH, MODP_2048_BIT),
|
|
PLUGIN_PROVIDE(DH, MODP_2048_224),
|
|
PLUGIN_PROVIDE(DH, MODP_2048_256),
|
|
PLUGIN_PROVIDE(DH, MODP_1536_BIT),
|
|
PLUGIN_PROVIDE(DH, MODP_3072_BIT),
|
|
PLUGIN_PROVIDE(DH, MODP_4096_BIT),
|
|
PLUGIN_PROVIDE(DH, MODP_6144_BIT),
|
|
PLUGIN_PROVIDE(DH, MODP_8192_BIT),
|
|
PLUGIN_PROVIDE(DH, MODP_1024_BIT),
|
|
PLUGIN_PROVIDE(DH, MODP_1024_160),
|
|
PLUGIN_PROVIDE(DH, MODP_768_BIT),
|
|
PLUGIN_PROVIDE(DH, MODP_CUSTOM),
|
|
};
|
|
static plugin_feature_t f_ecdh[] = {
|
|
PLUGIN_REGISTER(DH, pkcs11_dh_create),
|
|
PLUGIN_PROVIDE(DH, ECP_192_BIT),
|
|
PLUGIN_PROVIDE(DH, ECP_224_BIT),
|
|
PLUGIN_PROVIDE(DH, ECP_256_BIT),
|
|
PLUGIN_PROVIDE(DH, ECP_384_BIT),
|
|
PLUGIN_PROVIDE(DH, ECP_521_BIT),
|
|
};
|
|
static plugin_feature_t f_rng[] = {
|
|
PLUGIN_REGISTER(RNG, pkcs11_rng_create),
|
|
PLUGIN_PROVIDE(RNG, RNG_STRONG),
|
|
PLUGIN_PROVIDE(RNG, RNG_TRUE),
|
|
};
|
|
static plugin_feature_t f_privkey[] = {
|
|
PLUGIN_REGISTER(PRIVKEY, pkcs11_private_key_connect, FALSE),
|
|
PLUGIN_PROVIDE(PRIVKEY, KEY_ANY),
|
|
};
|
|
static plugin_feature_t f_pubkey[] = {
|
|
PLUGIN_REGISTER(PUBKEY, pkcs11_public_key_load, TRUE),
|
|
PLUGIN_PROVIDE(PUBKEY, KEY_RSA),
|
|
PLUGIN_PROVIDE(PUBKEY, KEY_ECDSA),
|
|
};
|
|
static plugin_feature_t f_manager[] = {
|
|
PLUGIN_CALLBACK((plugin_feature_callback_t)handle_certs, NULL),
|
|
PLUGIN_PROVIDE(CUSTOM, "pkcs11-certs"),
|
|
PLUGIN_DEPENDS(CERT_DECODE, CERT_X509),
|
|
};
|
|
static plugin_feature_t f[countof(f_hash) + countof(f_dh) + countof(f_rng) +
|
|
countof(f_ecdh) + countof(f_privkey) +
|
|
countof(f_pubkey) + countof(f_manager)] = {};
|
|
static int count = 0;
|
|
|
|
if (!count)
|
|
{ /* initialize only once */
|
|
bool use_ecc = lib->settings->get_bool(lib->settings,
|
|
"%s.plugins.pkcs11.use_ecc", FALSE, lib->ns);
|
|
plugin_features_add(f, f_manager, countof(f_manager), &count);
|
|
/* private key handling for EC keys is not disabled by use_ecc */
|
|
plugin_features_add(f, f_privkey, countof(f_privkey), &count);
|
|
if (lib->settings->get_bool(lib->settings,
|
|
"%s.plugins.pkcs11.use_pubkey", FALSE, lib->ns))
|
|
{
|
|
plugin_features_add(f, f_pubkey, countof(f_pubkey) - (use_ecc ? 0 : 1),
|
|
&count);
|
|
}
|
|
if (lib->settings->get_bool(lib->settings,
|
|
"%s.plugins.pkcs11.use_hasher", FALSE, lib->ns))
|
|
{
|
|
plugin_features_add(f, f_hash, countof(f_hash), &count);
|
|
}
|
|
if (lib->settings->get_bool(lib->settings,
|
|
"%s.plugins.pkcs11.use_rng", FALSE, lib->ns))
|
|
{
|
|
plugin_features_add(f, f_rng, countof(f_rng), &count);
|
|
}
|
|
if (lib->settings->get_bool(lib->settings,
|
|
"%s.plugins.pkcs11.use_dh", FALSE, lib->ns))
|
|
{
|
|
plugin_features_add(f, f_dh, countof(f_dh), &count);
|
|
if (use_ecc)
|
|
{
|
|
plugin_features_add(f, f_ecdh, countof(f_ecdh), &count);
|
|
}
|
|
}
|
|
}
|
|
*features = f;
|
|
return count;
|
|
}
|
|
|
|
METHOD(plugin_t, destroy, void,
|
|
private_pkcs11_plugin_t *this)
|
|
{
|
|
lib->set(lib, "pkcs11-manager", NULL);
|
|
this->manager->destroy(this->manager);
|
|
this->creds->destroy(this->creds);
|
|
this->mutex->destroy(this->mutex);
|
|
this->handle_events_lock->destroy(this->handle_events_lock);
|
|
free(this);
|
|
}
|
|
|
|
/*
|
|
* see header file
|
|
*/
|
|
plugin_t *pkcs11_plugin_create()
|
|
{
|
|
private_pkcs11_plugin_t *this;
|
|
|
|
INIT(this,
|
|
.public = {
|
|
.plugin = {
|
|
.get_name = _get_name,
|
|
.get_features = _get_features,
|
|
.reload = _reload,
|
|
.destroy = _destroy,
|
|
},
|
|
},
|
|
.creds = linked_list_create(),
|
|
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
|
|
.handle_events_lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
|
|
);
|
|
|
|
this->manager = pkcs11_manager_create((void*)token_event_cb, this);
|
|
lib->set(lib, "pkcs11-manager", this->manager);
|
|
|
|
return &this->public.plugin;
|
|
}
|