/*
* Copyright (C) 2008 Stefan Walter
* Copyright (C) 2011 Collabora Ltd.
* Copyright (C) 2017 Red Hat, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
* * Redistributions in binary form must reproduce the
* above copyright notice, this list of conditions and
* the following disclaimer in the documentation and/or
* other materials provided with the distribution.
* * The names of contributors to this software may not be
* used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* Author: Stef Walter <stefw@collabora.co.uk>
*/
#include "config.h"
/* We use and define deprecated functions here */
#define P11_KIT_NO_DEPRECATIONS
#define P11_DEBUG_FLAG P11_DEBUG_LIB
#include "conf.h"
#include "debug.h"
#include "dict.h"
#include "library.h"
#include "log.h"
#include "message.h"
#include "modules.h"
#include "path.h"
#include "pkcs11.h"
#include "p11-kit.h"
#include "private.h"
#include "proxy.h"
#include "rpc.h"
#include "virtual.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/**
* SECTION:p11-kit
* @title: Modules
* @short_description: Module loading and initializing
*
* PKCS\#11 modules are used by crypto libraries and applications to access
* crypto objects (like keys and certificates) and to perform crypto operations.
*
* In order for applications to behave consistently with regard to the user's
* installed PKCS\#11 modules, each module must be configured so that applications
* or libraries know that they should load it.
*
* When multiple consumers of a module (such as libraries or applications) are
* in the same process, coordination of the initialization and finalization
* of PKCS\#11 modules is required. To do this modules are managed by p11-kit.
* This means that various unsafe methods are coordinated between callers. Unmanaged
* modules are simply the raw PKCS\#11 module pointers without p11-kit getting in the
* way. It is highly recommended that the default managed behavior is used.
*
* The functions here provide support for initializing configured modules. The
* p11_kit_modules_load() function should be used to load and initialize
* the configured modules. When done, the p11_kit_modules_release() function
* should be used to release those modules and associated resources.
*
* In addition p11_kit_config_option() can be used to access other parts
* of the module configuration.
*
* If a consumer wishes to load an arbitrary PKCS\#11 module that's not
* configured use p11_kit_module_load() to do so. And use p11_kit_module_release()
* to later release it.
*
* Modules are represented by a pointer to their <code>CK_FUNCTION_LIST</code>
* entry points.
*/
/**
* SECTION:p11-kit-deprecated
* @title: Deprecated
* @short_description: Deprecated functions
*
* These functions have been deprecated from p11-kit and are not recommended for
* general usage. In large part they were deprecated because they did not adequately
* insulate multiple callers of a PKCS\#11 module from another, and could not
* support the 'managed' mode needed to do this.
*/
/**
* P11_KIT_MODULE_UNMANAGED:
*
* Module is loaded in non 'managed' mode. This is not recommended,
* disables many features, and prevents coordination between multiple
* callers of the same module.
*/
/**
* P11_KIT_MODULE_CRITICAL:
*
* Flag to load a module in 'critical' mode. Failure to load a critical module
* will prevent all other modules from loading. A failure when loading a
* non-critical module skips that module.
*/
typedef struct _Module {
/*
* When using managed modules, this forms the base of the
* virtual stack into which all the other modules call. This is also
* the first field in this structure so we can cast between them.
*/
p11_virtual virt;
/* The initialize args built from configuration */
CK_C_INITIALIZE_ARGS init_args;
int ref_count;
int init_count;
/* Registered modules */
char *name;
char *filename;
p11_dict *config;
bool critical;
/*
* This is a pointer to the actual dl shared module, or perhaps
* the RPC client context.
*/
void *loaded_module;
p11_kit_destroyer loaded_destroy;
/* Initialization, mutex must be held */
p11_mutex_t initialize_mutex;
unsigned int initialize_called;
p11_thread_id_t initialize_thread;
} Module;
/*
* Shared data between threads, protected by the mutex, a structure so
* we can audit thread safety easier.
*/
static struct _Shared {
p11_dict *modules;
p11_dict *unmanaged_by_funcs;
p11_dict *managed_by_closure;
p11_dict *config;
} gl = { NULL, NULL };
/* These are global variables to be overridden in tests */
const char *p11_config_system_file = P11_SYSTEM_CONFIG_FILE;
const char *p11_config_user_file = P11_USER_CONFIG_FILE;
const char *p11_config_package_modules = P11_PACKAGE_CONFIG_MODULES;
const char *p11_config_system_modules = P11_SYSTEM_CONFIG_MODULES;
const char *p11_config_user_modules = P11_USER_CONFIG_MODULES;
/* -----------------------------------------------------------------------------
* P11-KIT FUNCTIONALITY
*/
/**
* p11_kit_override_system_files:
* @system_conf: the system configuration file (default: system_config_dir/pkcs11.conf)
* @user_conf: the user configuration file (default: ~/.config/pkcs11/pkcs11.conf)
* @package_modules: location of modules shipped by p11-kit (default: system_config/modules)
* @system_modules: location of system pkcs11 modules (default: system_config/modules)
* @user_modules: location of user modules (default: ~/.config/pkcs11/modules)
*
* Overrides the default system configuration files. The
* provided values should be accessible for the lifetime
* of p11-kit usage.
*
* When the value %NULL is provided for any of the locations,
* it will not be updated.
*
* This is function intended to be used in test suites and
* not production, and as such %P11_KIT_FUTURE_UNSTABLE_API
* must be defined before including p11-kit.h.
*
* Since: 0.23.10
*
*/
void
p11_kit_override_system_files (const char *system_conf,
const char *user_conf,
const char *package_modules,
const char *system_modules,
const char *user_modules)
{
if (system_conf)
p11_config_system_file = system_conf;
if (user_conf)
p11_config_user_file = user_conf;
if (package_modules)
p11_config_package_modules = package_modules;
if (system_modules)
p11_config_system_modules = system_modules;
if (user_modules)
p11_config_user_modules = user_modules;
}
static CK_RV
create_mutex (CK_VOID_PTR_PTR mut)
{
p11_mutex_t *pmutex;
return_val_if_fail (mut != NULL, CKR_ARGUMENTS_BAD);
pmutex = malloc (sizeof (p11_mutex_t));
return_val_if_fail (pmutex != NULL, CKR_HOST_MEMORY);
p11_mutex_init (pmutex);
*mut = pmutex;
return CKR_OK;
}
static CK_RV
destroy_mutex (CK_VOID_PTR mut)
{
p11_mutex_t *pmutex = mut;
return_val_if_fail (mut != NULL, CKR_MUTEX_BAD);
p11_mutex_uninit (pmutex);
free (pmutex);
return CKR_OK;
}
static CK_RV
lock_mutex (CK_VOID_PTR mut)
{
p11_mutex_t *pmutex = mut;
return_val_if_fail (mut != NULL, CKR_MUTEX_BAD);
p11_mutex_lock (pmutex);
return CKR_OK;
}
static CK_RV
unlock_mutex (CK_VOID_PTR mut)
{
p11_mutex_t *pmutex = mut;
return_val_if_fail (mut != NULL, CKR_MUTEX_BAD);
p11_mutex_unlock (pmutex);
return CKR_OK;
}
static void
free_module_unlocked (void *data)
{
Module *mod = data;
assert (mod != NULL);
/* Module must have no outstanding references */
assert (mod->ref_count == 0);
if (mod->init_count > 0) {
p11_debug_precond ("module unloaded without C_Finalize having been "
"called for each C_Initialize");
} else {
assert (mod->initialize_thread == 0);
}
p11_virtual_uninit (&mod->virt);
if (mod->loaded_destroy)
mod->loaded_destroy (mod->loaded_module);
p11_mutex_uninit (&mod->initialize_mutex);
p11_dict_free (mod->config);
free (mod->name);
free (mod->filename);
free (mod->init_args.pReserved);
free (mod);
}
static Module *
alloc_module_unlocked (void)
{
Module *mod;
mod = calloc (1, sizeof (Module));
return_val_if_fail (mod != NULL, NULL);
mod->init_args.CreateMutex = create_mutex;
mod->init_args.DestroyMutex = destroy_mutex;
mod->init_args.LockMutex = lock_mutex;
mod->init_args.UnlockMutex = unlock_mutex;
mod->init_args.flags = CKF_OS_LOCKING_OK;
p11_mutex_init (&mod->initialize_mutex);
/*
* The default for configured modules is non-critical, but for
* modules loaded explicitly, and not from config, we treat them
* as critical. So this gets overridden for configured modules
* later when the config is loaded.
*/
mod->critical = true;
return mod;
}
#ifdef __GNUC__
bool p11_proxy_module_check (CK_FUNCTION_LIST_PTR module) __attribute__((weak));
bool
p11_proxy_module_check (CK_FUNCTION_LIST_PTR module)
{
return false;
}
#endif
static CK_RV
dlopen_and_get_function_list (Module *mod,
const char *path,
CK_FUNCTION_LIST **funcs)
{
CK_C_GetFunctionList gfl;
dl_module_t dl;
char *error;
CK_RV rv;
assert (mod != NULL);
assert (path != NULL);
assert (funcs != NULL);
dl = p11_dl_open (path);
if (dl == NULL) {
error = p11_dl_error ();
p11_message ("couldn't load module: %s: %s", path, error);
free (error);
return CKR_GENERAL_ERROR;
}
/* When the Module goes away, dlclose the loaded module */
mod->loaded_destroy = (p11_kit_destroyer)p11_dl_close;
mod->loaded_module = dl;
gfl = p11_dl_symbol (dl, "C_GetFunctionList");
if (!gfl) {
error = p11_dl_error ();
p11_message ("couldn't find C_GetFunctionList entry point in module: %s: %s",
path, error);
free (error);
return CKR_GENERAL_ERROR;
}
rv = gfl (funcs);
if (rv != CKR_OK) {
p11_message ("call to C_GetFunctiontList failed in module: %s: %s",
path, p11_kit_strerror (rv));
return rv;
}
if (p11_proxy_module_check (*funcs)) {
p11_message ("refusing to load the p11-kit-proxy.so module as a registered module");
return CKR_FUNCTION_FAILED;
}
p11_virtual_init (&mod->virt, &p11_virtual_base, *funcs, NULL);
p11_debug ("opened module: %s", path);
return CKR_OK;
}
static CK_RV
load_module_from_file_inlock (const char *name,
const char *path,
Module **result)
{
CK_FUNCTION_LIST *funcs;
char *expand = NULL;
Module *mod;
Module *prev;
CK_RV rv;
assert (path != NULL);
assert (result != NULL);
mod = alloc_module_unlocked ();
return_val_if_fail (mod != NULL, CKR_HOST_MEMORY);
if (!p11_path_absolute (path)) {
p11_debug ("module path is relative, loading from: %s", P11_MODULE_PATH);
path = expand = p11_path_build (P11_MODULE_PATH, path, NULL);
return_val_if_fail (path != NULL, CKR_HOST_MEMORY);
}
p11_debug ("loading module %s%sfrom path: %s",
name ? name : "", name ? " " : "", path);
mod->filename = strdup (path);
rv = dlopen_and_get_function_list (mod, path, &funcs);
free (expand);
if (rv != CKR_OK) {
free_module_unlocked (mod);
return rv;
}
/* Do we have a previous one like this, if so ignore load */
prev = p11_dict_get (gl.unmanaged_by_funcs, funcs);
/* If same module was loaded previously, just take over config */
if (prev != NULL) {
if (!name || prev->name || prev->config)
p11_debug ("duplicate module %s, using previous", name);
free_module_unlocked (mod);
mod = prev;
/* This takes ownership of the module */
} else if (!p11_dict_set (gl.modules, mod, mod) ||
!p11_dict_set (gl.unmanaged_by_funcs, funcs, mod)) {
return_val_if_reached (CKR_HOST_MEMORY);
}
*result= mod;
return CKR_OK;
}
static CK_RV
setup_module_for_remote_inlock (const char *name,
const char *remote,
Module **result)
{
p11_rpc_transport *rpc;
Module *mod;
p11_debug ("remoting module %s using: %s", name, remote);
mod = alloc_module_unlocked ();
return_val_if_fail (mod != NULL, CKR_HOST_MEMORY);
rpc = p11_rpc_transport_new (&mod->virt, remote, name);
if (rpc == NULL) {
free_module_unlocked (mod);
return CKR_DEVICE_ERROR;
}
mod->filename = NULL;
mod->loaded_module = rpc;
mod->loaded_destroy = p11_rpc_transport_free;
/* This takes ownership of the module */
if (!p11_dict_set (gl.modules, mod, mod))
return_val_if_reached (CKR_HOST_MEMORY);
*result = mod;
return CKR_OK;
}
static int
is_list_delimiter (char ch)
{
return ch == ',' || isspace (ch);
}
static bool
is_string_in_list (const char *list,
const char *string)
{
const char *where;
const char *start = list;
while (*start != '\0') {
where = strstr (start, string);
if (where == NULL)
return false;
/* Has to be at beginning/end of string, and delimiter before/after */
if (where != list && !is_list_delimiter (*(where - 1))) {
start += strlen (string);
continue;
}
where += strlen (string);
if (*where == '\0' || is_list_delimiter (*where)) {
return true;
}
start = where;
}
return false;
}
static bool
is_module_enabled_unlocked (const char *name,
p11_dict *config,
int flags)
{
const char *progname;
const char *enable_in;
const char *disable_in;
bool enable = false;
enable_in = p11_dict_get (config, "enable-in");
disable_in = p11_dict_get (config, "disable-in");
/* Defaults to enabled if neither of these are set */
if (!enable_in && !disable_in)
return true;
progname = _p11_get_progname_unlocked ();
if (enable_in && disable_in)
p11_message ("module '%s' has both enable-in and disable-in options", name);
if (enable_in) {
enable = (progname != NULL &&
is_string_in_list (enable_in, progname)) ||
((flags & P11_KIT_MODULE_LOADED_FROM_PROXY) != 0 &&
is_string_in_list (enable_in, "p11-kit-proxy"));
} else if (disable_in) {
enable = (progname == NULL ||
!is_string_in_list (disable_in, progname)) &&
((flags & P11_KIT_MODULE_LOADED_FROM_PROXY) == 0 ||
!is_string_in_list (disable_in, "p11-kit-proxy"));
}
p11_debug ("%s module '%s' running in '%s'",
enable ? "enabled" : "disabled",
name,
progname ? progname : "(null)");
return enable;
}
static CK_RV
take_config_and_load_module_inlock (char **name,
p11_dict **config,
bool critical,
bool verbose)
{
const char *filename = NULL;
const char *remote = NULL;
char *init_reserved = NULL;
CK_RV rv = CKR_OK;
Module *mod;
assert (name);
assert (*name);
assert (config);
assert (*config);
if (!is_module_enabled_unlocked (*name, *config, 0))
goto out;
remote = p11_dict_get (*config, "remote");
if (remote == NULL) {
filename = p11_dict_get (*config, "module");
if (filename == NULL) {
p11_debug ("no module path for module, skipping: %s", *name);
goto out;
}
}
if (remote != NULL) {
rv = setup_module_for_remote_inlock (*name, remote, &mod);
if (rv != CKR_OK)
goto out;
} else {
rv = load_module_from_file_inlock (*name, filename, &mod);
if (rv != CKR_OK)
goto out;
}
/*
* We support setting of CK_C_INITIALIZE_ARGS.pReserved from
* 'x-init-reserved' setting in the config. This only works with specific
* PKCS#11 modules, and is non-standard use of that field.
*/
init_reserved = p11_dict_get (*config, "x-init-reserved");
if (init_reserved) {
if (verbose) {
init_reserved = strconcat (init_reserved, " verbose=yes", NULL);
} else {
init_reserved = strdup (init_reserved);
}
if (init_reserved == NULL) {
rv = CKR_HOST_MEMORY;
goto out;
}
}
mod->init_args.pReserved = init_reserved;
/* Take ownership of these variables */
p11_dict_free (mod->config);
mod->config = *config;
*config = NULL;
free (mod->name);
mod->name = *name;
*name = NULL;
mod->critical = critical;
out:
return rv;
}
static CK_RV
load_registered_modules_unlocked (int flags)
{
p11_dictiter iter;
p11_dict *configs;
void *key;
char *name;
p11_dict *config;
int mode;
CK_RV rv;
bool critical;
bool verbose;
if (gl.config)
return CKR_OK;
/* Load the global configuration files */
config = _p11_conf_load_globals (p11_config_system_file, p11_config_user_file, &mode);
if (config == NULL)
return CKR_GENERAL_ERROR;
assert (mode != CONF_USER_INVALID);
configs = _p11_conf_load_modules (mode,
p11_config_package_modules,
p11_config_system_modules,
p11_config_user_modules);
if (configs == NULL) {
rv = CKR_GENERAL_ERROR;
p11_dict_free (config);
return rv;
}
assert (gl.config == NULL);
gl.config = config;
/*
* Now go through each config and turn it into a module. As we iterate
* we steal the values of the config.
*/
p11_dict_iterate (configs, &iter);
while (p11_dict_next (&iter, &key, NULL)) {
if (!p11_dict_steal (configs, key, (void**)&name, (void**)&config))
assert_not_reached ();
/* Is this a critical module, should abort loading of others? */
critical = _p11_conf_parse_boolean (p11_dict_get (config, "critical"), false);
verbose = (flags & P11_KIT_MODULE_VERBOSE) != 0;
rv = take_config_and_load_module_inlock (&name, &config, critical, verbose);
/*
* These variables will be cleared if ownership is transeferred
* by the above function call.
*/
p11_dict_free (config);
if (critical && rv != CKR_OK) {
p11_message ("aborting initialization because module '%s' was marked as critical",
name);
p11_dict_free (configs);
free (name);
return rv;
}
free (name);
}
p11_dict_free (configs);
return CKR_OK;
}
static CK_RV
initialize_module_inlock_reentrant (Module *mod, CK_C_INITIALIZE_ARGS *init_args)
{
CK_RV rv = CKR_OK;
p11_thread_id_t self;
assert (mod);
self = p11_thread_id_self ();
if (mod->initialize_thread == self) {
p11_message ("p11-kit initialization called recursively");
return CKR_FUNCTION_FAILED;
}
/*
* Increase ref first, so module doesn't get freed out from
* underneath us when the mutex is unlocked below.
*/
++mod->ref_count;
mod->initialize_thread = self;
/* Change over to the module specific mutex */
p11_unlock ();
p11_mutex_lock (&mod->initialize_mutex);
if (mod->initialize_called != p11_forkid) {
p11_debug ("C_Initialize: calling");
/* The init_args argument takes precedence over mod->init_args */
if (init_args == NULL)
init_args = &mod->init_args;
rv = mod->virt.funcs.C_Initialize (&mod->virt.funcs,
init_args);
p11_debug ("C_Initialize: result: %lu", rv);
/* Module was initialized and C_Finalize should be called */
if (rv == CKR_OK)
mod->initialize_called = p11_forkid;
else
mod->initialize_called = 0;
/* Module was already initialized, we don't call C_Finalize */
if (rv == CKR_CRYPTOKI_ALREADY_INITIALIZED)
rv = CKR_OK;
/* Matches the init count in finalize_module_inlock_reentrant() */
if (rv == CKR_OK)
mod->init_count = 0;
}
p11_mutex_unlock (&mod->initialize_mutex);
p11_lock ();
if (rv == CKR_OK) {
/* Matches the ref count in finalize_module_inlock_reentrant() */
if (mod->init_count == 0)
mod->ref_count++;
mod->init_count++;
}
mod->ref_count--;
mod->initialize_thread = 0;
return rv;
}
static CK_RV
init_globals_unlocked (void)
{
static bool once = false;
if (!gl.modules) {
gl.modules = p11_dict_new (p11_dict_direct_hash,
p11_dict_direct_equal,
free_module_unlocked, NULL);
return_val_if_fail (gl.modules != NULL, CKR_HOST_MEMORY);
}
if (!gl.unmanaged_by_funcs) {
gl.unmanaged_by_funcs = p11_dict_new (p11_dict_direct_hash,
p11_dict_direct_equal,
NULL, NULL);
return_val_if_fail (gl.unmanaged_by_funcs != NULL, CKR_HOST_MEMORY);
}
if (!gl.managed_by_closure) {
gl.managed_by_closure = p11_dict_new (p11_dict_direct_hash,
p11_dict_direct_equal,
NULL, NULL);
return_val_if_fail (gl.managed_by_closure != NULL, CKR_HOST_MEMORY);
}
if (once)
return CKR_OK;
once = true;
return CKR_OK;
}
static void
free_modules_when_no_refs_unlocked (void)
{
if (gl.modules) {
Module *mod;
p11_dictiter iter;
/* Check if any modules have a ref count */
p11_dict_iterate (gl.modules, &iter);
while (p11_dict_next (&iter, (void **)&mod, NULL)) {
if (mod->ref_count)
return;
}
}
p11_dict_free (gl.unmanaged_by_funcs);
gl.unmanaged_by_funcs = NULL;
p11_dict_free (gl.managed_by_closure);
gl.managed_by_closure = NULL;
p11_dict_free (gl.modules);
gl.modules = NULL;
p11_dict_free (gl.config);
gl.config = NULL;
}
static CK_RV
finalize_module_inlock_reentrant (Module *mod)
{
assert (mod);
/*
* We leave module info around until all are finalized
* so we can encounter these zombie Module structures.
*/
if (mod->ref_count == 0)
return CKR_ARGUMENTS_BAD;
if (--mod->init_count > 0)
return CKR_OK;
/*
* Because of the mutex unlock below, we temporarily increase
* the ref count. This prevents module from being freed out
* from ounder us.
*/
p11_unlock ();
p11_mutex_lock (&mod->initialize_mutex);
if (mod->initialize_called == p11_forkid) {
mod->virt.funcs.C_Finalize (&mod->virt.funcs, NULL);
mod->initialize_called = 0;
}
p11_mutex_unlock (&mod->initialize_mutex);
p11_lock ();
/* Match the ref increment in initialize_module_inlock_reentrant() */
mod->ref_count--;
free_modules_when_no_refs_unlocked ();
return CKR_OK;
}
static CK_RV
initialize_registered_inlock_reentrant (void)
{
p11_dictiter iter;
Module *mod;
CK_RV rv;
/*
* This is only called by deprecated code. The caller expects all
* configured and enabled modules to be initialized.
*/
rv = init_globals_unlocked ();
if (rv != CKR_OK)
return rv;
rv = load_registered_modules_unlocked (0);
if (rv == CKR_OK) {
p11_dict_iterate (gl.unmanaged_by_funcs, &iter);
while (rv == CKR_OK && p11_dict_next (&iter, NULL, (void **)&mod)) {
/* Skip all modules that aren't registered or enabled */
if (mod->name == NULL || !is_module_enabled_unlocked (mod->name, mod->config, 0))
continue;
rv = initialize_module_inlock_reentrant (mod, NULL);
if (rv != CKR_OK) {
if (mod->critical) {
p11_message ("initialization of critical module '%s' failed: %s",
mod->name, p11_kit_strerror (rv));
} else {
p11_message ("skipping module '%s' whose initialization failed: %s",
mod->name, p11_kit_strerror (rv));
rv = CKR_OK;
}
}
}
}
return rv;
}
static Module *
module_for_functions_inlock (CK_FUNCTION_LIST *funcs)
{
if (p11_virtual_is_wrapper (funcs))
return p11_dict_get (gl.managed_by_closure, funcs);
else
return p11_dict_get (gl.unmanaged_by_funcs, funcs);
}
static CK_FUNCTION_LIST *
unmanaged_for_module_inlock (Module *mod)
{
CK_FUNCTION_LIST *funcs;
funcs = mod->virt.lower_module;
if (p11_dict_get (gl.unmanaged_by_funcs, funcs) == mod)
return funcs;
return NULL;
}
/**
* p11_kit_initialize_registered:
*
* Initialize all the registered PKCS\#11 modules.
*
* If this is the first time this function is called multiple times
* consecutively within a single process, then it merely increments an
* initialization reference count for each of these modules.
*
* Use p11_kit_finalize_registered() to finalize these registered modules once
* the caller is done with them.
*
* If this function fails, then an error message will be available via the
* p11_kit_message() function.
*
* Deprecated: Since: 0.19.0: Use p11_kit_modules_load() instead.
*
* Returns: CKR_OK if the initialization succeeded, or an error code.
*/
CK_RV
p11_kit_initialize_registered (void)
{
CK_RV rv;
p11_library_init_once ();
/* WARNING: This function must be reentrant */
p11_debug ("in");
p11_lock ();
p11_message_clear ();
/* WARNING: Reentrancy can occur here */
rv = initialize_registered_inlock_reentrant ();
_p11_kit_default_message (rv);
p11_unlock ();
/* Cleanup any partial initialization */
if (rv != CKR_OK)
p11_kit_finalize_registered ();
p11_debug ("out: %lu", rv);
return rv;
}
static CK_RV
finalize_registered_inlock_reentrant (void)
{
Module *mod;
p11_dictiter iter;
Module **to_finalize;
int i, count;
/*
* This is only called from deprecated code. The caller expects all
* modules initialized earlier to be finalized (once). If non-critical
* modules failed to initialize, then it is not possible to completely
* guarantee the internal state.
*/
if (!gl.modules)
return CKR_CRYPTOKI_NOT_INITIALIZED;
/* WARNING: This function must be reentrant */
to_finalize = calloc (p11_dict_size (gl.unmanaged_by_funcs) + 1, sizeof (Module *));
if (!to_finalize)
return CKR_HOST_MEMORY;
count = 0;
p11_dict_iterate (gl.unmanaged_by_funcs, &iter);
while (p11_dict_next (&iter, NULL, (void **)&mod)) {
/* Skip all modules that aren't registered */
if (mod->name && mod->init_count)
to_finalize[count++] = mod;
}
p11_debug ("finalizing %d modules", count);
for (i = 0; i < count; ++i) {
/* WARNING: Reentrant calls can occur here */
finalize_module_inlock_reentrant (to_finalize[i]);
}
free (to_finalize);
/* In case nothing loaded, free up internal memory */
if (count == 0)
free_modules_when_no_refs_unlocked ();
return CKR_OK;
}
/**
* p11_kit_finalize_registered:
*
* Finalize all the registered PKCS\#11 modules. These should have been
* initialized with p11_kit_initialize_registered().
*
* If p11_kit_initialize_registered() has been called more than once in this
* process, then this function must be called the same number of times before
* actual finalization will occur.
*
* If this function fails, then an error message will be available via the
* p11_kit_message() function.
*
* Deprecated: Since 0.19.0: Use p11_kit_modules_release() instead.
*
* Returns: CKR_OK if the finalization succeeded, or an error code.
*/
CK_RV
p11_kit_finalize_registered (void)
{
CK_RV rv;
p11_library_init_once ();
/* WARNING: This function must be reentrant */
p11_debug ("in");
p11_lock ();
p11_message_clear ();
/* WARNING: Reentrant calls can occur here */
rv = finalize_registered_inlock_reentrant ();
_p11_kit_default_message (rv);
p11_unlock ();
p11_debug ("out: %lu", rv);
return rv;
}
static int
compar_priority (const void *one,
const void *two)
{
CK_FUNCTION_LIST_PTR f1 = *((CK_FUNCTION_LIST_PTR *)one);
CK_FUNCTION_LIST_PTR f2 = *((CK_FUNCTION_LIST_PTR *)two);
Module *m1, *m2;
const char *v1, *v2;
int o1, o2;
m1 = module_for_functions_inlock (f1);
m2 = module_for_functions_inlock (f2);
assert (m1 != NULL && m2 != NULL);
v1 = p11_dict_get (m1->config, "priority");
v2 = p11_dict_get (m2->config, "priority");
o1 = atoi (v1 ? v1 : "0");
o2 = atoi (v2 ? v2 : "0");
/* Priority is in descending order, highest first */
if (o1 != o2)
return o1 > o2 ? -1 : 1;
/*
* Otherwise use the names alphabetically in ascending order. This
* is really just to provide consistency between various loads of
* the configuration.
*/
if (m1->name == m2->name)
return 0;
if (!m1->name)
return -1;
if (!m2->name)
return 1;
return strcmp (m1->name, m2->name);
}
static void
sort_modules_by_priority (CK_FUNCTION_LIST_PTR *modules,
int count)
{
qsort (modules, count, sizeof (CK_FUNCTION_LIST_PTR), compar_priority);
}
static CK_FUNCTION_LIST **
list_registered_modules_inlock (void)
{
CK_FUNCTION_LIST **result = NULL;
CK_FUNCTION_LIST *funcs;
Module *mod;
p11_dictiter iter;
int i = 0;
/*
* This is only called by deprecated code. The caller expects to get
* a list of all registered enabled modules that have been initialized.
*/
if (gl.unmanaged_by_funcs) {
result = calloc (p11_dict_size (gl.unmanaged_by_funcs) + 1,
sizeof (CK_FUNCTION_LIST *));
return_val_if_fail (result != NULL, NULL);
p11_dict_iterate (gl.unmanaged_by_funcs, &iter);
while (p11_dict_next (&iter, (void **)&funcs, (void **)&mod)) {
/*
* We don't include unreferenced modules. We don't include
* modules that have been initialized but aren't in the
* registry. These have a NULL name.
*
* In addition we check again that the module isn't disabled
* using enable-in or disable-in. This is because a caller
* can change the progname we recognize the process as after
* having initialized. This is a corner case, but want to make
* sure to cover it.
*/
if (mod->ref_count && mod->name && mod->init_count &&
is_module_enabled_unlocked (mod->name, mod->config, 0)) {
result[i++] = funcs;
}
}
sort_modules_by_priority (result, i);
}
return result;
}
/**
* p11_kit_registered_modules:
*
* Get a list of all the registered PKCS\#11 modules. This list will be valid
* once the p11_kit_initialize_registered() function has been called.
*
* The returned value is a <code>NULL</code> terminated array of
* <code>CK_FUNCTION_LIST_PTR</code> pointers.
*
* The returned modules are unmanaged.
*
* Deprecated: Since 0.19.0: Use p11_kit_modules_load() instead.
*
* Returns: A list of all the registered modules. Use the free() function to
* free the list.
*/
CK_FUNCTION_LIST_PTR_PTR
p11_kit_registered_modules (void)
{
CK_FUNCTION_LIST_PTR_PTR result;
p11_library_init_once ();
p11_lock ();
p11_message_clear ();
result = list_registered_modules_inlock ();
p11_unlock ();
return result;
}
/**
* p11_kit_registered_module_to_name:
* @module: pointer to a registered module
*
* Get the name of a registered PKCS\#11 module.
*
* You can use p11_kit_registered_modules() to get a list of all the registered
* modules. This name is specified by the registered module configuration.
*
* Deprecated: Since 0.19.0: Use p11_kit_module_get_name() instead.
*
* Returns: A newly allocated string containing the module name, or
* <code>NULL</code> if no such registered module exists. Use free() to
* free this string.
*/
char*
p11_kit_registered_module_to_name (CK_FUNCTION_LIST_PTR module)
{
return_val_if_fail (module != NULL, NULL);
return p11_kit_module_get_name (module);
}
/**
* p11_kit_module_get_name:
* @module: pointer to a loaded module
*
* Get the configured name of the PKCS\#11 module.
*
* Configured modules are loaded by p11_kit_modules_load(). The module
* passed to this function can be either managed or unmanaged. Non
* configured modules will return %NULL.
*
* Use free() to release the return value when you're done with it.
*
* Returns: a newly allocated string containing the module name, or
* <code>NULL</code> if the module is not a configured module
*/
char *
p11_kit_module_get_name (CK_FUNCTION_LIST *module)
{
Module *mod;
char *name = NULL;
return_val_if_fail (module != NULL, NULL);
p11_library_init_once ();
p11_lock ();
p11_message_clear ();
if (gl.modules) {
mod = module_for_functions_inlock (module);
if (mod && mod->name)
name = strdup (mod->name);
}
p11_unlock ();
return name;
}
/**
* p11_kit_module_get_filename:
* @module: pointer to a loaded module
*
* Get the configured name of the PKCS\#11 module.
*
* Configured modules are loaded by p11_kit_modules_load(). The module
* passed to this function can be either managed or unmanaged. Non
* configured modules will return %NULL.
*
* Use free() to release the return value when you're done with it.
*
* Returns: a newly allocated string containing the module name, or
* <code>NULL</code> if the module is not a configured module
*/
char *
p11_kit_module_get_filename (CK_FUNCTION_LIST *module)
{
Module *mod;
char *name = NULL;
return_val_if_fail (module != NULL, NULL);
p11_library_init_once ();
p11_lock ();
p11_message_clear ();
if (gl.modules) {
mod = module_for_functions_inlock (module);
if (mod && mod->filename)
name = strdup (mod->filename);
}
p11_unlock ();
return name;
}
static const char *
module_get_option_inlock (Module *mod,
const char *option)
{
p11_dict *config;
if (mod == NULL)
config = gl.config;
else
config = mod->config;
if (config == NULL)
return NULL;
return p11_dict_get (config, option);
}
/**
* p11_kit_module_get_flags:
* @module: the module
*
* Get the flags for this module.
*
* The %P11_KIT_MODULE_UNMANAGED flag will be set if the module is not
* managed by p11-kit. It is a raw PKCS\#11 module function list.
*
* The %P11_KIT_MODULE_CRITICAL flag will be set if the module is configured
* to be critical, and not be skipped over if it fails to initialize or
* load. This flag is also set for modules that are not configured, but have
* been loaded in another fashion.
*
* Returns: the flags for the module
*/
int
p11_kit_module_get_flags (CK_FUNCTION_LIST *module)
{
const char *trusted;
Module *mod;
int flags = 0;
return_val_if_fail (module != NULL, 0);
p11_library_init_once ();
p11_lock ();
p11_message_clear ();
if (gl.modules) {
if (p11_virtual_is_wrapper (module)) {
mod = p11_dict_get (gl.managed_by_closure, module);
} else {
flags |= P11_KIT_MODULE_UNMANAGED;
mod = p11_dict_get (gl.unmanaged_by_funcs, module);
}
if (!mod || mod->critical)
flags |= P11_KIT_MODULE_CRITICAL;
if (mod) {
trusted = module_get_option_inlock (mod, "trust-policy");
if (_p11_conf_parse_boolean (trusted, false))
flags |= P11_KIT_MODULE_TRUSTED;
}
}
p11_unlock ();
return flags;
}
/**
* p11_kit_registered_name_to_module:
* @name: name of a registered module
*
* Lookup a registered PKCS\#11 module by its name. This name is specified by
* the registered module configuration.
*
* Deprecated: Since 0.19.0: Use p11_kit_module_for_name() instead.
*
* Returns: a pointer to a PKCS\#11 module, or <code>NULL</code> if this name was
* not found.
*/
CK_FUNCTION_LIST_PTR
p11_kit_registered_name_to_module (const char *name)
{
CK_FUNCTION_LIST_PTR module = NULL;
CK_FUNCTION_LIST_PTR funcs;
p11_dictiter iter;
Module *mod;
return_val_if_fail (name != NULL, NULL);
p11_lock ();
p11_message_clear ();
if (gl.modules) {
assert (name);
p11_dict_iterate (gl.unmanaged_by_funcs, &iter);
while (p11_dict_next (&iter, (void **)&funcs, (void **)&mod)) {
if (mod->ref_count && mod->name && strcmp (name, mod->name) == 0) {
module = funcs;
break;
}
}
}
p11_unlock ();
return module;
}
/**
* p11_kit_module_for_name:
* @modules: a list of modules to look through
* @name: the name of the module to find
*
* Look through the list of @modules and return the module whose @name
* matches.
*
* Only configured modules have names. Configured modules are loaded by
* p11_kit_modules_load(). The module passed to this function can be either
* managed or unmanaged.
*
* The return value is not copied or duplicated in anyway. It is still
* 'owned' by the @modules list.
*
* Returns: the module which matches the name, or %NULL if no match.
*/
CK_FUNCTION_LIST *
p11_kit_module_for_name (CK_FUNCTION_LIST **modules,
const char *name)
{
CK_FUNCTION_LIST *ret = NULL;
Module *mod;
int i;
return_val_if_fail (name != NULL, NULL);
if (!modules)
return NULL;
p11_library_init_once ();
p11_lock ();
p11_message_clear ();
for (i = 0; gl.modules && modules[i] != NULL; i++) {
mod = module_for_functions_inlock (modules[i]);
if (mod && mod->name && strcmp (mod->name, name) == 0) {
ret = modules[i];
break;
}
}
p11_unlock ();
return ret;
}
/**
* p11_kit_registered_option:
* @module: a pointer to a registered module
* @field: the name of the option to lookup.
*
* Lookup a configured option for a registered PKCS\#11 module. If a
* <code>NULL</code> module argument is specified, then this will lookup
* the configuration option in the global config file.
*
* Deprecated: Since 0.19.0: Use p11_kit_config_option() instead.
*
* Returns: A newly allocated string containing the option value, or
* <code>NULL</code> if the registered module or the option were not found.
* Use free() to free the returned string.
*/
char*
p11_kit_registered_option (CK_FUNCTION_LIST_PTR module, const char *field)
{
Module *mod = NULL;
char *option = NULL;
const char *value;
return_val_if_fail (field != NULL, NULL);
p11_library_init_once ();
p11_lock ();
p11_message_clear ();
if (module == NULL)
mod = NULL;
else
mod = gl.unmanaged_by_funcs ? p11_dict_get (gl.unmanaged_by_funcs, module) : NULL;
value = module_get_option_inlock (mod, field);
if (value)
option = strdup (value);
p11_unlock ();
return option;
}
/**
* p11_kit_config_option:
* @module: the module to retrieve the option for, or %NULL for global options
* @option: the option to retrieve
*
* Retrieve the value for a configured option.
*
* If @module is %NULL, then the global option with the given name will
* be retrieved. Otherwise @module should point to a configured loaded module.
* If no such @option or configured @module exists, then %NULL will be returned.
*
* Use free() to release the returned value.
*
* Returns: the option value or %NULL
*/
char *
p11_kit_config_option (CK_FUNCTION_LIST *module,
const char *option)
{
Module *mod = NULL;
const char *value = NULL;
char *ret = NULL;
return_val_if_fail (option != NULL, NULL);
p11_library_init_once ();
p11_lock ();
p11_message_clear ();
if (gl.modules) {
if (module != NULL) {
mod = module_for_functions_inlock (module);
if (mod == NULL)
goto cleanup;
}
value = module_get_option_inlock (mod, option);
if (value)
ret = strdup (value);
}
cleanup:
p11_unlock ();
return ret;
}
typedef struct {
p11_virtual virt;
Module *mod;
unsigned int initialized;
p11_dict *sessions;
} Managed;
static CK_RV
managed_C_Initialize (CK_X_FUNCTION_LIST *self,
CK_VOID_PTR init_args)
{
Managed *managed = ((Managed *)self);
p11_dict *sessions;
CK_RV rv;
p11_debug ("in");
p11_lock ();
if (managed->initialized == p11_forkid) {
rv = CKR_CRYPTOKI_ALREADY_INITIALIZED;
} else {
sessions = p11_dict_new (p11_dict_ulongptr_hash,
p11_dict_ulongptr_equal,
free, free);
if (!sessions)
rv = CKR_HOST_MEMORY;
else
rv = initialize_module_inlock_reentrant (managed->mod, init_args);
if (rv == CKR_OK) {
if (managed->sessions)
p11_dict_free (managed->sessions);
managed->sessions = sessions;
managed->initialized = p11_forkid;
} else {
p11_dict_free (sessions);
}
}
p11_unlock ();
p11_debug ("out: %lu", rv);
return rv;
}
static CK_RV
managed_track_session_inlock (p11_dict *sessions,
CK_SLOT_ID slot_id,
CK_SESSION_HANDLE session)
{
void *key;
void *value;
key = memdup (&session, sizeof (CK_SESSION_HANDLE));
return_val_if_fail (key != NULL, CKR_HOST_MEMORY);
value = memdup (&slot_id, sizeof (CK_SESSION_HANDLE));
return_val_if_fail (value != NULL, CKR_HOST_MEMORY);
if (!p11_dict_set (sessions, key, value))
return_val_if_reached (CKR_HOST_MEMORY);
return CKR_OK;
}
static void
managed_untrack_session_inlock (p11_dict *sessions,
CK_SESSION_HANDLE session)
{
p11_dict_remove (sessions, &session);
}
static CK_SESSION_HANDLE *
managed_steal_sessions_inlock (p11_dict *sessions,
bool matching_slot_id,
CK_SLOT_ID slot_id,
int *count)
{
CK_SESSION_HANDLE *stolen;
CK_SESSION_HANDLE *key;
CK_SLOT_ID *value;
p11_dictiter iter;
int at, i;
assert (sessions != NULL);
assert (count != NULL);
stolen = calloc (p11_dict_size (sessions) + 1, sizeof (CK_SESSION_HANDLE));
return_val_if_fail (stolen != NULL, NULL);
at = 0;
p11_dict_iterate (sessions, &iter);
while (p11_dict_next (&iter, (void **)&key, (void **)&value)) {
if (!matching_slot_id || slot_id == *value)
stolen[at++] = *key;
}
/* Removed them all, clear the whole array */
if (at == p11_dict_size (sessions)) {
p11_dict_clear (sessions);
/* Only removed some, go through and remove those */
} else {
for (i = 0; i < at; i++) {
if (!p11_dict_remove (sessions, stolen + i))
assert_not_reached ();
}
}
*count = at;
return stolen;
}
static void
managed_close_sessions (CK_X_FUNCTION_LIST *funcs,
CK_SESSION_HANDLE *stolen,
int count)
{
CK_RV rv;
int i;
for (i = 0; i < count; i++) {
rv = funcs->C_CloseSession (funcs, stolen[i]);
if (rv != CKR_OK)
p11_message ("couldn't close session: %s", p11_kit_strerror (rv));
}
}
static CK_RV
managed_C_Finalize (CK_X_FUNCTION_LIST *self,
CK_VOID_PTR reserved)
{
Managed *managed = ((Managed *)self);
CK_SESSION_HANDLE *sessions;
int count;
CK_RV rv;
p11_debug ("in");
p11_lock ();
if (managed->initialized == 0) {
rv = CKR_CRYPTOKI_NOT_INITIALIZED;
} else if (managed->initialized != p11_forkid) {
/*
* In theory we should be returning CKR_CRYPTOKI_NOT_INITIALIZED here
* but enough callers are not completely aware of their forking.
* So we just clean up any state we have, rather than forcing callers
* to initialize just to finalize.
*/
p11_debug ("finalizing module in wrong process, skipping C_Finalize");
rv = CKR_OK;
} else {
sessions = managed_steal_sessions_inlock (managed->sessions, false, 0, &count);
if (sessions && count) {
/* WARNING: reentrancy can occur here */
p11_unlock ();
managed_close_sessions (&managed->mod->virt.funcs, sessions, count);
p11_lock ();
}
free (sessions);
/* WARNING: reentrancy can occur here */
rv = finalize_module_inlock_reentrant (managed->mod);
}
if (rv == CKR_OK) {
managed->initialized = 0;
p11_dict_free (managed->sessions);
managed->sessions = NULL;
}
p11_unlock ();
p11_debug ("out: %lu", rv);
return rv;
}
static CK_RV
managed_C_OpenSession (CK_X_FUNCTION_LIST *self,
CK_SLOT_ID slot_id,
CK_FLAGS flags,
CK_VOID_PTR application,
CK_NOTIFY notify,
CK_SESSION_HANDLE_PTR session)
{
Managed *managed = ((Managed *)self);
CK_RV rv;
return_val_if_fail (session != NULL, CKR_ARGUMENTS_BAD);
self = &managed->mod->virt.funcs;
rv = self->C_OpenSession (self, slot_id, flags, application, notify, session);
if (rv == CKR_OK) {
p11_lock ();
rv = managed_track_session_inlock (managed->sessions, slot_id, *session);
p11_unlock ();
}
return rv;
}
static CK_RV
managed_C_CloseSession (CK_X_FUNCTION_LIST *self,
CK_SESSION_HANDLE session)
{
Managed *managed = ((Managed *)self);
CK_RV rv;
self = &managed->mod->virt.funcs;
rv = self->C_CloseSession (self, session);
if (rv == CKR_OK) {
p11_lock ();
managed_untrack_session_inlock (managed->sessions, session);
p11_unlock ();
}
return rv;
}
static CK_RV
managed_C_CloseAllSessions (CK_X_FUNCTION_LIST *self,
CK_SLOT_ID slot_id)
{
Managed *managed = ((Managed *)self);
CK_SESSION_HANDLE *stolen;
int count;
p11_lock ();
stolen = managed_steal_sessions_inlock (managed->sessions, true, slot_id, &count);
p11_unlock ();
self = &managed->mod->virt.funcs;
managed_close_sessions (self, stolen, count);
if (stolen) {
free (stolen);
return CKR_OK;
} else {
return CKR_GENERAL_ERROR;
}
}
static void
managed_free_inlock (void *data)
{
Managed *managed = data;
managed->mod->ref_count--;
free (managed);
}
static p11_virtual *
managed_create_inlock (Module *mod)
{
Managed *managed;
managed = calloc (1, sizeof (Managed));
return_val_if_fail (managed != NULL, NULL);
p11_virtual_init (&managed->virt, &p11_virtual_stack,
&mod->virt, NULL);
managed->virt.funcs.C_Initialize = managed_C_Initialize;
managed->virt.funcs.C_Finalize = managed_C_Finalize;
managed->virt.funcs.C_CloseAllSessions = managed_C_CloseAllSessions;
managed->virt.funcs.C_CloseSession = managed_C_CloseSession;
managed->virt.funcs.C_OpenSession = managed_C_OpenSession;
managed->mod = mod;
mod->ref_count++;
return &managed->virt;
}
static bool
lookup_managed_option (Module *mod,
bool supported,
const char *option,
bool def_value)
{
const char *string;
bool value;
string = module_get_option_inlock (NULL, option);
if (!string)
string = module_get_option_inlock (mod, option);
if (!string) {
if (!supported)
return false;
return def_value;
}
value = _p11_conf_parse_boolean (string, def_value);
if (!supported && value != supported) {
/*
* This is because the module is running in unmanaged mode, so turn off the
*/
p11_message ("the '%s' option for module '%s' is only supported for managed modules",
option, mod->name);
}
return value;
}
static CK_RV
release_module_inlock_rentrant (CK_FUNCTION_LIST *module,
const char *caller_func)
{
Module *mod;
assert (module != NULL);
/* See if a managed module, and finalize if so */
if (p11_virtual_is_wrapper (module)) {
mod = p11_dict_get (gl.managed_by_closure, module);
if (mod != NULL) {
if (!p11_dict_remove (gl.managed_by_closure, module))
assert_not_reached ();
p11_virtual_unwrap (module);
}
/* If an unmanaged module then caller should have finalized */
} else {
mod = p11_dict_get (gl.unmanaged_by_funcs, module);
}
if (mod == NULL) {
p11_debug_precond ("invalid module pointer passed to %s", caller_func);
return CKR_ARGUMENTS_BAD;
}
/* Matches the ref in prepare_module_inlock_reentrant() */
mod->ref_count--;
return CKR_OK;
}
CK_RV
p11_modules_release_inlock_reentrant (CK_FUNCTION_LIST **modules)
{
CK_RV ret = CKR_OK;
CK_RV rv;
int i;
for (i = 0; modules[i] != NULL; i++) {
rv = release_module_inlock_rentrant (modules[i], __PRETTY_FUNCTION__);
if (rv != CKR_OK)
ret = rv;
}
free (modules);
/* In case nothing loaded, free up internal memory */
free_modules_when_no_refs_unlocked ();
return ret;
}
static CK_RV
prepare_module_inlock_reentrant (Module *mod,
int flags,
CK_FUNCTION_LIST **module)
{
p11_destroyer destroyer;
const char *trusted;
p11_virtual *virt;
bool is_managed;
bool with_log;
assert (module != NULL);
if (flags & P11_KIT_MODULE_TRUSTED) {
trusted = module_get_option_inlock (mod, "trust-policy");
if (!_p11_conf_parse_boolean (trusted, false))
return CKR_FUNCTION_NOT_SUPPORTED;
}
if (flags & P11_KIT_MODULE_UNMANAGED) {
is_managed = false;
with_log = false;
} else {
is_managed = lookup_managed_option (mod, true, "managed", true);
with_log = lookup_managed_option (mod, is_managed, "log-calls", false);
}
if (is_managed) {
virt = managed_create_inlock (mod);
return_val_if_fail (virt != NULL, CKR_HOST_MEMORY);
destroyer = managed_free_inlock;
/* Add the logger if configured */
if (p11_log_force || with_log) {
virt = p11_log_subclass (virt, destroyer);
destroyer = p11_log_release;
}
*module = p11_virtual_wrap (virt, destroyer);
if (*module == NULL)
return CKR_GENERAL_ERROR;
if (!p11_dict_set (gl.managed_by_closure, *module, mod))
return_val_if_reached (CKR_HOST_MEMORY);
} else {
*module = unmanaged_for_module_inlock (mod);
if (*module == NULL)
return CKR_FUNCTION_NOT_SUPPORTED;
}
/* Matches the deref in release_module_inlock_rentrant() */
mod->ref_count++;
return CKR_OK;
}
CK_RV
p11_modules_load_inlock_reentrant (int flags,
CK_FUNCTION_LIST ***results)
{
CK_FUNCTION_LIST **modules;
Module *mod;
p11_dictiter iter;
CK_RV rv;
int at;
rv = init_globals_unlocked ();
if (rv != CKR_OK)
return rv;
rv = load_registered_modules_unlocked (flags);
if (rv != CKR_OK)
return rv;
modules = calloc (p11_dict_size (gl.modules) + 1, sizeof (CK_FUNCTION_LIST *));
return_val_if_fail (modules != NULL, CKR_HOST_MEMORY);
at = 0;
rv = CKR_OK;
p11_dict_iterate (gl.modules, &iter);
while (p11_dict_next (&iter, NULL, (void **)&mod)) {
/*
* We don't include unreferenced modules. We don't include
* modules that have been initialized but aren't in the
* registry. These have a NULL name.
*
* In addition we check again that the module isn't disabled
* using enable-in or disable-in. This is because a caller
* can change the progname we recognize the process as after
* having initialized. This is a corner case, but want to make
* sure to cover it.
*/
if (!mod->name || !is_module_enabled_unlocked (mod->name, mod->config, flags))
continue;
rv = prepare_module_inlock_reentrant (mod, flags, modules + at);
if (rv == CKR_OK)
at++;
else if (rv == CKR_FUNCTION_NOT_SUPPORTED)
rv = CKR_OK;
else
break;
}
modules[at] = NULL;
if (rv != CKR_OK) {
p11_modules_release_inlock_reentrant (modules);
return rv;
}
sort_modules_by_priority (modules, at);
*results = modules;
return CKR_OK;
}
/**
* p11_kit_modules_load:
* @reserved: set to %NULL
* @flags: flags to use to load the module
*
* Load the configured PKCS\#11 modules.
*
* If @flags contains the %P11_KIT_MODULE_UNMANAGED flag, then the
* modules will be not be loaded in 'managed' mode regardless of its
* configuration. This is not recommended for general usage.
*
* If @flags contains the %P11_KIT_MODULE_CRITICAL flag then the
* modules will all be treated as 'critical', regardless of the module
* configuration. This means that a failure to load any module will
* cause this function to fail.
*
* For unmanaged modules there is no guarantee to the state of the
* modules. Other callers may be using the modules. Using unmanaged
* modules haphazardly is not recommended for this reason. Some
* modules (such as those configured with RPC) cannot be loaded in
* unmanaged mode, and will be skipped.
*
* If @flags contains the %P11_KIT_MODULE_TRUSTED flag then only the
* marked as trusted modules will be loaded.
*
* Use p11_kit_modules_release() to release the modules returned by
* this function.
*
* If this function fails, then an error message will be available via the
* p11_kit_message() function.
*
* Returns: a null terminated list of modules represented as PKCS\#11
* function lists, or %NULL on failure
*/
CK_FUNCTION_LIST **
p11_kit_modules_load (const char *reserved,
int flags)
{
CK_FUNCTION_LIST **modules;
CK_RV rv;
/* progname attribute not implemented yet */
return_val_if_fail (reserved == NULL, NULL);
p11_library_init_once ();
/* WARNING: This function must be reentrant */
p11_debug ("in");
/* mask out internal flags */
flags &= P11_KIT_MODULE_MASK;
p11_lock ();
p11_message_clear ();
/* WARNING: Reentrancy can occur here */
rv = p11_modules_load_inlock_reentrant (flags, &modules);
p11_unlock ();
if (rv != CKR_OK)
modules = NULL;
p11_debug ("out: %s", modules ? "success" : "fail");
return modules;
}
/**
* p11_kit_modules_initialize:
* @modules: a %NULL terminated list of modules
* @failure_callback: called with modules that fail to initialize
*
* Initialize all the modules in the @modules list by calling their
* <literal>C_Initialize</literal> function.
*
* For managed modules the <literal>C_Initialize</literal> function
* is overridden so that multiple callers can initialize the same
* modules. In addition for managed modules multiple callers can
* initialize from different threads, and still guarantee consistent
* thread-safe behavior.
*
* For unmanaged modules if multiple callers try to initialize
* a module, then one of the calls will return
* <literal>CKR_CRYPTOKI_ALREADY_INITIALIZED</literal> according to the
* PKCS\#11 specification. In addition there are no guarantees that
* thread-safe behavior will occur if multiple callers initialize from
* different threads.
*
* When a module fails to initialize it is removed from the @modules list.
* If the @failure_callback is not %NULL then it is called with the modules that
* fail to initialize. For example, you may pass p11_kit_module_release()
* as a @failure_callback if the @modules list was loaded wit p11_kit_modules_load().
*
* The return value will return the failure code of the last critical
* module that failed to initialize. Non-critical module failures do not affect
* the return value. If no critical modules failed to initialize then the
* return value will be <literal>CKR_OK</literal>.
*
* When modules are removed, the list will be %NULL terminated at the
* appropriate place so it can continue to be used as a modules list.
*
* This function does not accept a <code>CK_C_INITIALIZE_ARGS</code> argument.
* Custom initialization arguments cannot be supported when multiple consumers
* load the same module.
*
* Returns: <literal>CKR_OK</literal> or the failure code of the last critical
* module that failed to initialize.
*/
CK_RV
p11_kit_modules_initialize (CK_FUNCTION_LIST **modules,
p11_kit_destroyer failure_callback)
{
CK_RV ret = CKR_OK;
CK_RV rv;
bool critical;
char *name;
int i, out;
return_val_if_fail (modules != NULL, CKR_ARGUMENTS_BAD);
for (i = 0, out = 0; modules[i] != NULL; i++, out++) {
rv = modules[i]->C_Initialize (NULL);
if (rv != CKR_OK && rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) {
name = p11_kit_module_get_name (modules[i]);
if (name == NULL)
name = strdup ("(unknown)");
return_val_if_fail (name != NULL, CKR_HOST_MEMORY);
critical = (p11_kit_module_get_flags (modules[i]) & P11_KIT_MODULE_CRITICAL);
p11_message ("%s: module failed to initialize%s: %s",
name, critical ? "" : ", skipping", p11_kit_strerror (rv));
if (critical)
ret = rv;
if (failure_callback)
failure_callback (modules[i]);
out--;
free (name);
} else {
if (rv == CKR_CRYPTOKI_ALREADY_INITIALIZED) {
name = p11_kit_module_get_name (modules[i]);
p11_message ("%s: module was already initialized",
name ? name : "(unknown)");
free (name);
}
modules[out] = modules[i];
}
}
/* NULL terminate after above changes */
modules[out] = NULL;
return ret;
}
/**
* p11_kit_modules_load_and_initialize:
* @flags: flags to use to load the modules
*
* Load and initialize configured modules.
*
* If a critical module fails to load or initialize then the function will
* return <literal>NULL</literal>. Non-critical modules will be skipped
* and not included in the returned module list.
*
* Use p11_kit_modules_finalize_and_release() when you're done with the
* modules returned by this function.
*
* The @flags allowed by this function, as well as their meaning, are the
* same as with p11_kit_modules_load().
*
* Returns: a <literal>NULL</literal> terminated list of modules, or
* <literal>NULL</literal> on failure
*/
CK_FUNCTION_LIST **
p11_kit_modules_load_and_initialize (int flags)
{
CK_FUNCTION_LIST **modules;
CK_RV rv;
/* mask out internal flags */
flags &= P11_KIT_MODULE_MASK;
modules = p11_kit_modules_load (NULL, flags);
if (modules == NULL)
return NULL;
rv = p11_kit_modules_initialize (modules, (p11_destroyer)p11_kit_module_release);
if (rv != CKR_OK) {
p11_kit_modules_release (modules);
modules = NULL;
}
return modules;
}
/**
* p11_kit_modules_finalize:
* @modules: a <literal>NULL</literal> terminated list of modules
*
* Finalize each module in the @modules list by calling its
* <literal>C_Finalize</literal> function. Regardless of failures, all
* @modules will have their <literal>C_Finalize</literal> function called.
*
* If a module returns a failure from its <literal>C_Finalize</literal>
* method it will be returned. If multiple modules fail, the last failure
* will be returned.
*
* For managed modules the <literal>C_Finalize</literal> function
* is overridden so that multiple callers can finalize the same
* modules. In addition for managed modules multiple callers can
* finalize from different threads, and still guarantee consistent
* thread-safe behavior.
*
* For unmanaged modules if multiple callers try to finalize
* a module, then one of the calls will return
* <literal>CKR_CRYPTOKI_NOT_INITIALIZED</literal> according to the
* PKCS\#11 specification. In addition there are no guarantees that
* thread-safe behavior will occur if multiple callers finalize from
* different threads.
*
* Returns: <literal>CKR_OK</literal> or the failure code of the last
* module that failed to finalize
*/
CK_RV
p11_kit_modules_finalize (CK_FUNCTION_LIST **modules)
{
CK_RV ret = CKR_OK;
CK_RV rv;
char *name;
int i;
return_val_if_fail (modules != NULL, CKR_ARGUMENTS_BAD);
for (i = 0; modules[i] != NULL; i++) {
rv = modules[i]->C_Finalize (NULL);
if (rv != CKR_OK) {
name = p11_kit_module_get_name (modules[i]);
p11_message ("%s: module failed to finalize: %s",
name ? name : "(unknown)", p11_kit_strerror (rv));
free (name);
ret = rv;
}
}
return ret;
}
/**
* p11_kit_modules_release:
* @modules: the modules to release
*
* Release the a set of loaded PKCS\#11 modules.
*
* The modules may be either managed or unmanaged. The array containing
* the module pointers is also freed by this function.
*
* Managed modules will not be actually released until all
* callers using them have done so. If the modules were initialized, they
* should have been finalized first.
*/
void
p11_kit_modules_release (CK_FUNCTION_LIST **modules)
{
p11_library_init_once ();
return_if_fail (modules != NULL);
/* WARNING: This function must be reentrant */
p11_debug ("in");
p11_lock ();
p11_message_clear ();
p11_modules_release_inlock_reentrant (modules);
p11_unlock ();
p11_debug ("out");
}
/**
* p11_kit_modules_finalize_and_release:
* @modules: the modules to release
*
* Finalize and then release the a set of loaded PKCS\#11 modules.
*
* The modules may be either managed or unmanaged. The array containing
* the module pointers is also freed by this function.
*
* Modules are released even if their finalization returns an error code.
* Managed modules will not be actually finalized or released until all
* callers using them have done so.
*
* For managed modules the <literal>C_Finalize</literal> function
* is overridden so that multiple callers can finalize the same
* modules. In addition for managed modules multiple callers can
* finalize from different threads, and still guarantee consistent
* thread-safe behavior.
*
* For unmanaged modules if multiple callers try to finalize
* a module, then one of the calls will return
* <literal>CKR_CRYPTOKI_NOT_INITIALIZED</literal> according to the
* PKCS\#11 specification. In addition there are no guarantees that
* thread-safe behavior will occur if multiple callers initialize from
* different threads.
*/
void
p11_kit_modules_finalize_and_release (CK_FUNCTION_LIST **modules)
{
return_if_fail (modules != NULL);
p11_kit_modules_finalize (modules);
p11_kit_modules_release (modules);
}
/**
* p11_kit_initialize_module:
* @module: loaded module to initialize.
*
* Initialize an arbitrary PKCS\#11 module. Normally using the
* p11_kit_initialize_registered() is preferred.
*
* Using this function to initialize modules allows coordination between
* multiple users of the same module in a single process. It should be called
* on modules that have been loaded (with dlopen() for example) but not yet
* initialized. The caller should not yet have called the module's
* <code>C_Initialize</code> method. This function will call
* <code>C_Initialize</code> as necessary.
*
* Subsequent calls to this function for the same module will result in an
* initialization count being incremented for the module. It is safe (although
* usually unnecessary) to use this function on registered modules.
*
* The module must be finalized with p11_kit_finalize_module() instead of
* calling its <code>C_Finalize</code> method directly.
*
* This function does not accept a <code>CK_C_INITIALIZE_ARGS</code> argument.
* Custom initialization arguments cannot be supported when multiple consumers
* load the same module.
*
* If this function fails, then an error message will be available via the
* p11_kit_message() function.
*
* Deprecated: Since 0.19.0: Use p11_kit_module_initialize() instead.
*
* Returns: CKR_OK if the initialization was successful.
*/
CK_RV
p11_kit_initialize_module (CK_FUNCTION_LIST_PTR module)
{
CK_FUNCTION_LIST_PTR result;
Module *mod;
int flags;
CK_RV rv;
return_val_if_fail (module != NULL, CKR_ARGUMENTS_BAD);
p11_library_init_once ();
/* WARNING: This function must be reentrant for the same arguments */
p11_debug ("in");
p11_lock ();
p11_message_clear ();
flags = P11_KIT_MODULE_CRITICAL | P11_KIT_MODULE_UNMANAGED;
rv = p11_module_load_inlock_reentrant (module, flags, &result);
/* An unmanaged module should return the same pointer */
assert (rv != CKR_OK || result == module);
if (rv == CKR_OK) {
mod = p11_dict_get (gl.unmanaged_by_funcs, module);
assert (mod != NULL);
rv = initialize_module_inlock_reentrant (mod, NULL);
if (rv != CKR_OK) {
p11_message ("module initialization failed: %s", p11_kit_strerror (rv));
p11_module_release_inlock_reentrant (module);
}
}
p11_unlock ();
p11_debug ("out: %lu", rv);
return rv;
}
CK_RV
p11_module_load_inlock_reentrant (CK_FUNCTION_LIST *module,
int flags,
CK_FUNCTION_LIST **result)
{
Module *allocated = NULL;
Module *mod;
CK_RV rv = CKR_OK;
rv = init_globals_unlocked ();
if (rv == CKR_OK) {
mod = p11_dict_get (gl.unmanaged_by_funcs, module);
if (mod == NULL) {
p11_debug ("allocating new module");
allocated = mod = alloc_module_unlocked ();
return_val_if_fail (mod != NULL, CKR_HOST_MEMORY);
p11_virtual_init (&mod->virt, &p11_virtual_base, module, NULL);
}
/* If this was newly allocated, add it to the list */
if (allocated) {
if (!p11_dict_set (gl.modules, allocated, allocated) ||
!p11_dict_set (gl.unmanaged_by_funcs, module, allocated))
return_val_if_reached (CKR_HOST_MEMORY);
allocated = NULL;
}
/* WARNING: Reentrancy can occur here */
rv = prepare_module_inlock_reentrant (mod, flags, result);
free (allocated);
}
/*
* If initialization failed, we may need to cleanup.
* If we added this module above, then this will
* clean things up as expected.
*/
if (rv != CKR_OK)
free_modules_when_no_refs_unlocked ();
_p11_kit_default_message (rv);
return rv;
}
/**
* p11_kit_module_load:
* @module_path: relative or full file path of module library
* @flags: flags to use when loading the module
*
* Load an arbitrary PKCS\#11 module from a dynamic library file, and
* initialize it. Normally using the p11_kit_modules_load() function
* is preferred.
*
* A full file path or just (path/)filename relative to
* P11_MODULE_PATH are accepted.
*
* Using this function to load modules allows coordination between multiple
* callers of the same module in a single process. If @flags contains the
* %P11_KIT_MODULE_UNMANAGED flag, then the modules will be not be loaded
* in 'managed' mode and not be coordinated. This is not recommended
* for general usage.
*
* Subsequent calls to this function for the same module will result in an
* initialization count being incremented for the module. It is safe (although
* usually unnecessary) to use this function on registered modules.
*
* The module should be released with p11_kit_module_release().
*
* If this function fails, then an error message will be available via the
* p11_kit_message() function.
*
* Returns: the loaded module PKCS\#11 functions or %NULL on failure
*/
CK_FUNCTION_LIST *
p11_kit_module_load (const char *module_path,
int flags)
{
CK_FUNCTION_LIST *module = NULL;
CK_RV rv;
Module *mod;
return_val_if_fail (module_path != NULL, NULL);
p11_library_init_once ();
/* WARNING: This function must be reentrant for the same arguments */
p11_debug ("in: %s", module_path);
/* mask out internal flags */
flags &= P11_KIT_MODULE_MASK;
p11_lock ();
p11_message_clear ();
rv = init_globals_unlocked ();
if (rv == CKR_OK) {
rv = load_module_from_file_inlock (NULL, module_path, &mod);
if (rv == CKR_OK) {
/* WARNING: Reentrancy can occur here */
rv = prepare_module_inlock_reentrant (mod, flags, &module);
if (rv != CKR_OK)
module = NULL;
}
}
/*
* If initialization failed, we may need to cleanup.
* If we added this module above, then this will
* clean things up as expected.
*/
if (rv != CKR_OK)
free_modules_when_no_refs_unlocked ();
p11_unlock ();
p11_debug ("out: %s", module ? "success" : "fail");
return module;
}
/**
* p11_kit_finalize_module:
* @module: loaded module to finalize.
*
* Finalize an arbitrary PKCS\#11 module. The module must have been initialized
* using p11_kit_initialize_module(). In most cases callers will want to use
* p11_kit_finalize_registered() instead of this function.
*
* Using this function to finalize modules allows coordination between
* multiple users of the same module in a single process. The caller should not
* call the module's <code>C_Finalize</code> method. This function will call
* <code>C_Finalize</code> as necessary.
*
* If the module was initialized more than once, then this function will
* decrement an initialization count for the module. When the count reaches zero
* the module will be truly finalized. It is safe (although usually unnecessary)
* to use this function on registered modules if (and only if) they were
* initialized using p11_kit_initialize_module() for some reason.
*
* If this function fails, then an error message will be available via the
* p11_kit_message() function.
*
* Deprecated: Since 0.19.0: Use p11_kit_module_finalize() and
* p11_kit_module_release() instead.
*
* Returns: CKR_OK if the finalization was successful.
*/
CK_RV
p11_kit_finalize_module (CK_FUNCTION_LIST *module)
{
Module *mod;
CK_RV rv = CKR_OK;
return_val_if_fail (module != NULL, CKR_ARGUMENTS_BAD);
p11_library_init_once ();
/* WARNING: This function must be reentrant for the same arguments */
p11_debug ("in");
p11_lock ();
p11_message_clear ();
mod = gl.unmanaged_by_funcs ? p11_dict_get (gl.unmanaged_by_funcs, module) : NULL;
if (mod == NULL) {
p11_debug ("module not found");
rv = CKR_ARGUMENTS_BAD;
} else {
/* WARNING: Rentrancy can occur here */
rv = finalize_module_inlock_reentrant (mod);
}
_p11_kit_default_message (rv);
p11_unlock ();
p11_debug ("out: %lu", rv);
return rv;
}
/**
* p11_kit_module_initialize:
* @module: the module to initialize
*
* Initialize a PKCS\#11 module by calling its <literal>C_Initialize</literal>
* function.
*
* For managed modules the <literal>C_Initialize</literal> function
* is overridden so that multiple callers can initialize the same
* modules. In addition for managed modules multiple callers can
* initialize from different threads, and still guarantee consistent
* thread-safe behavior.
*
* For unmanaged modules if multiple callers try to initialize
* a module, then one of the calls will return
* <literal>CKR_CRYPTOKI_ALREADY_INITIALIZED</literal> according to the
* PKCS\#11 specification. In addition there are no guarantees that
* thread-safe behavior will occur if multiple callers initialize from
* different threads.
*
* This function does not accept a <code>CK_C_INITIALIZE_ARGS</code> argument.
* Custom initialization arguments cannot be supported when multiple consumers
* load the same module.
*
* Returns: <literal>CKR_OK</literal> or a failure code
*/
CK_RV
p11_kit_module_initialize (CK_FUNCTION_LIST *module)
{
char *name;
CK_RV rv;
return_val_if_fail (module != NULL, CKR_ARGUMENTS_BAD);
rv = module->C_Initialize (NULL);
if (rv != CKR_OK) {
name = p11_kit_module_get_name (module);
p11_message ("%s: module failed to initialize: %s",
name ? name : "(unknown)", p11_kit_strerror (rv));
free (name);
}
return rv;
}
/**
* p11_kit_module_finalize:
* @module: the module to finalize
*
* Finalize a PKCS\#11 module by calling its <literal>C_Finalize</literal>
* function.
*
* For managed modules the <literal>C_Finalize</literal> function
* is overridden so that multiple callers can finalize the same
* modules. In addition for managed modules multiple callers can
* finalize from different threads, and still guarantee consistent
* thread-safe behavior.
*
* For unmanaged modules if multiple callers try to finalize
* a module, then one of the calls will return
* <literal>CKR_CRYPTOKI_NOT_INITIALIZED</literal> according to the
* PKCS\#11 specification. In addition there are no guarantees that
* thread-safe behavior will occur if multiple callers finalize from
* different threads.
*
* Returns: <literal>CKR_OK</literal> or a failure code
*/
CK_RV
p11_kit_module_finalize (CK_FUNCTION_LIST *module)
{
char *name;
CK_RV rv;
return_val_if_fail (module != NULL, CKR_ARGUMENTS_BAD);
rv = module->C_Finalize (NULL);
if (rv != CKR_OK) {
name = p11_kit_module_get_name (module);
p11_message ("%s: module failed to finalize: %s",
name ? name : "(unknown)", p11_kit_strerror (rv));
free (name);
}
return rv;
}
/**
* p11_kit_module_release:
* @module: the module to release
*
* Release the a loaded PKCS\#11 modules.
*
* The module may be either managed or unmanaged. The <literal>C_Finalize</literal>
* function will be called if no other callers are using this module.
*/
void
p11_kit_module_release (CK_FUNCTION_LIST *module)
{
return_if_fail (module != NULL);
p11_library_init_once ();
/* WARNING: This function must be reentrant for the same arguments */
p11_debug ("in");
p11_lock ();
p11_message_clear ();
release_module_inlock_rentrant (module, __PRETTY_FUNCTION__);
p11_unlock ();
p11_debug ("out");
}
CK_RV
p11_module_release_inlock_reentrant (CK_FUNCTION_LIST *module)
{
return release_module_inlock_rentrant (module, __PRETTY_FUNCTION__);
}
/**
* p11_kit_load_initialize_module:
* @module_path: full file path of module library
* @module: location to place loaded module pointer
*
* Load an arbitrary PKCS\#11 module from a dynamic library file, and
* initialize it. Normally using the p11_kit_initialize_registered() function
* is preferred.
*
* Using this function to load and initialize modules allows coordination between
* multiple users of the same module in a single process. The caller should not
* call the module's <code>C_Initialize</code> method. This function will call
* <code>C_Initialize</code> as necessary.
*
* If a module has already been loaded, then use of this function is unnecesasry.
* Instead use the p11_kit_initialize_module() function to initialize it.
*
* Subsequent calls to this function for the same module will result in an
* initialization count being incremented for the module. It is safe (although
* usually unnecessary) to use this function on registered modules.
*
* The module must be finalized with p11_kit_finalize_module() instead of
* calling its <code>C_Finalize</code> method directly.
*
* This function does not accept a <code>CK_C_INITIALIZE_ARGS</code> argument.
* Custom initialization arguments cannot be supported when multiple consumers
* load the same module.
*
* If this function fails, then an error message will be available via the
* p11_kit_message() function.
*
* Deprecated: Since 0.19.0: Use p11_kit_module_load() instead.
*
* Returns: CKR_OK if the initialization was successful.
*/
CK_RV
p11_kit_load_initialize_module (const char *module_path,
CK_FUNCTION_LIST_PTR_PTR module)
{
Module *mod;
CK_RV rv = CKR_OK;
return_val_if_fail (module_path != NULL, CKR_ARGUMENTS_BAD);
return_val_if_fail (module != NULL, CKR_ARGUMENTS_BAD);
p11_library_init_once ();
/* WARNING: This function must be reentrant for the same arguments */
p11_debug ("in: %s", module_path);
p11_lock ();
p11_message_clear ();
rv = init_globals_unlocked ();
if (rv == CKR_OK) {
rv = load_module_from_file_inlock (NULL, module_path, &mod);
if (rv == CKR_OK) {
/* WARNING: Reentrancy can occur here */
rv = initialize_module_inlock_reentrant (mod, NULL);
}
}
if (rv == CKR_OK) {
*module = unmanaged_for_module_inlock (mod);
assert (*module != NULL);
}
/*
* If initialization failed, we may need to cleanup.
* If we added this module above, then this will
* clean things up as expected.
*/
if (rv != CKR_OK)
free_modules_when_no_refs_unlocked ();
_p11_kit_default_message (rv);
p11_unlock ();
p11_debug ("out: %lu", rv);
return rv;
}