/* * Copyright (C) 2008 Stefan Walter * Copyright (C) 2011 Collabora Ltd. * * 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 */ #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 #include #include #include #include #include #include #include #include #include #include #include #include /** * 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 CK_FUNCTION_LIST * 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 */ 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); } 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; } 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; where = strstr (list, 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))) return false; where += strlen (string); return (*where == '\0' || is_list_delimiter (*where)); } static bool is_module_enabled_unlocked (const char *name, p11_dict *config) { 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)); else if (disable_in) enable = (progname == NULL || !is_string_in_list (disable_in, progname)); 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) { const char *filename = NULL; const char *remote = NULL; CK_RV rv = CKR_OK; Module *mod; assert (name); assert (*name); assert (config); assert (*config); if (!is_module_enabled_unlocked (*name, *config)) 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. */ mod->init_args.pReserved = p11_dict_get (*config, "x-init-reserved"); /* Take ownership of thes evariables */ 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 (void) { p11_dictiter iter; p11_dict *configs; void *key; char *name; p11_dict *config; int mode; CK_RV rv; bool critical; 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); rv = take_config_and_load_module_inlock (&name, &config, critical); /* * 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) { 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 (); 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)) 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), 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)) { 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 NULL terminated array of * CK_FUNCTION_LIST_PTR 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 * NULL 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 * NULL 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 * NULL 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 NULL 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 * NULL 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 * NULL 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), 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 + at)) 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) { if (!p11_virtual_can_wrap ()) { /* * This is because libffi dependency was not built. The libffi dependency * is highly recommended and building without it results in a large loss * of functionality. */ p11_message ("the '%s' option for module '%s' is not supported on this system", option, mod->name); } else { /* * 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 false; } 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, p11_virtual_can_wrap (), "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); return_val_if_fail (*module != NULL, 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 (); 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)) 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. * * 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"); 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 * C_Initialize function. * * For managed modules the C_Initialize 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 * CKR_CRYPTOKI_ALREADY_INITIALIZED 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 CKR_OK. * * 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 CK_C_INITIALIZE_ARGS argument. * Custom initialization arguments cannot be supported when multiple consumers * load the same module. * * Returns: CKR_OK 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) { 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 { 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 NULL. 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. * * Returns: a NULL terminated list of modules, or * NULL on failure */ CK_FUNCTION_LIST ** p11_kit_modules_load_and_initialize (int flags) { CK_FUNCTION_LIST **modules; CK_RV rv; 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 NULL terminated list of modules * * Finalize each module in the @modules list by calling its * C_Finalize function. Regardless of failures, all * @modules will have their C_Finalize function called. * * If a module returns a failure from its C_Finalize * method it will be returned. If multiple modules fail, the last failure * will be returned. * * For managed modules the C_Finalize 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 * CKR_CRYPTOKI_NOT_INITIALIZED 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: CKR_OK 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 C_Finalize 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 * CKR_CRYPTOKI_NOT_INITIALIZED 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 * C_Initialize method. This function will call * C_Initialize 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 C_Finalize method directly. * * This function does not accept a CK_C_INITIALIZE_ARGS 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); 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 C_Finalize method. This function will call * C_Finalize 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 C_Initialize * function. * * For managed modules the C_Initialize 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 * CKR_CRYPTOKI_ALREADY_INITIALIZED 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 CK_C_INITIALIZE_ARGS argument. * Custom initialization arguments cannot be supported when multiple consumers * load the same module. * * Returns: CKR_OK 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 C_Finalize * function. * * For managed modules the C_Finalize 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 * CKR_CRYPTOKI_NOT_INITIALIZED 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: CKR_OK 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 C_Finalize * 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 C_Initialize method. This function will call * C_Initialize 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 C_Finalize method directly. * * This function does not accept a CK_C_INITIALIZE_ARGS 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) { *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; }