diff options
Diffstat (limited to 'p11-kit/modules.c')
-rw-r--r-- | p11-kit/modules.c | 1366 |
1 files changed, 1224 insertions, 142 deletions
diff --git a/p11-kit/modules.c b/p11-kit/modules.c index e62a43c..c6f1bee 100644 --- a/p11-kit/modules.c +++ b/p11-kit/modules.c @@ -35,16 +35,21 @@ #include "config.h" -#include "conf.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 "message.h" +#include "modules.h" #include "path.h" #include "pkcs11.h" #include "p11-kit.h" #include "private.h" +#include "virtual.h" #include <sys/stat.h> #include <sys/types.h> @@ -70,44 +75,90 @@ * 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 registered so that applications + * installed PKCS\#11 modules, each module must be configured so that applications * or libraries know that they should load it. * - * The functions here provide support for initializing registered modules. The - * p11_kit_initialize_registered() function should be used to load and initialize - * the registered modules. When done, the p11_kit_finalize_registered() function + * 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_registered_option() can be used to access other parts + * In addition p11_kit_config_option() can be used to access other parts * of the module configuration. * - * 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. The functions here automatically provide - * initialization reference counting to make this work. - * * If a consumer wishes to load an arbitrary PKCS\#11 module that's not - * registered, that module should be initialized with p11_kit_initialize_module() - * and finalized with p11_kit_finalize_module(). The module's own - * <code>C_Initialize</code> and <code>C_Finalize</code> methods should not - * be called directly. + * 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. This means that callers can load modules elsewhere, using - * dlopen() for example, and then still use these methods on them. + * 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 { - CK_FUNCTION_LIST_PTR funcs; + /* + * 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 actual function pointers retrieved from the module. This is + * not necessarily populated. For non dl modules, such as rpc + * modules, this will be NULL. + */ + CK_FUNCTION_LIST *funcs; + + /* The initialize args built from configuration */ CK_C_INITIALIZE_ARGS init_args; int ref_count; + int init_count; /* Registered modules */ char *name; p11_dict *config; + bool critical; - /* Loaded modules */ - dl_module_t dl_module; + /* + * 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; @@ -115,12 +166,18 @@ typedef struct _Module { p11_thread_id_t initialize_thread; } Module; +typedef struct { + p11_virtual virt; + Module *mod; +} Managed; + /* * 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 *managed; p11_dict *config; } gl = { NULL, NULL }; @@ -184,15 +241,19 @@ free_module_unlocked (void *data) assert (mod != NULL); - /* Module must be finalized */ - assert (!mod->initialize_called); - assert (mod->initialize_thread == 0); - /* Module must have no outstanding references */ assert (mod->ref_count == 0); - if (mod->dl_module) - p11_dl_close (mod->dl_module); + if (mod->init_count > 0) { + p11_debug_precond ("module unloaded without C_Finalize having been " + "called for each C_Initialize"); + } else { + assert (!mod->initialize_called); + assert (mod->initialize_thread == 0); + } + + if (mod->loaded_destroy) + mod->loaded_destroy (mod->loaded_module); p11_mutex_uninit (&mod->initialize_mutex); p11_dict_free (mod->config); @@ -215,28 +276,50 @@ alloc_module_unlocked (void) 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 void +module_setup_with_functions (Module *mod, + CK_FUNCTION_LIST *funcs) +{ + mod->funcs = funcs; + p11_virtual_init (&mod->virt, &p11_virtual_base, funcs, NULL); +} + 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); - assert (path); + assert (mod != NULL); + assert (path != NULL); - mod->dl_module = p11_dl_open (path); - if (mod->dl_module == 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; } - gfl = p11_dl_symbol (mod->dl_module, "C_GetFunctionList"); + /* 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", @@ -245,13 +328,19 @@ dlopen_and_get_function_list (Module *mod, const char *path) return CKR_GENERAL_ERROR; } - rv = gfl (&mod->funcs); + 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 (mod->funcs == &_p11_proxy_function_list) { + p11_message ("refusing to load the p11-kit-proxy.so module as a registered module"); + return CKR_FUNCTION_FAILED; + } + + module_setup_with_functions (mod, funcs); p11_debug ("opened module: %s", path); return CKR_OK; } @@ -273,6 +362,7 @@ load_module_from_file_unlocked (const char *path, Module **result) } /* Do we have a previous one like this, if so ignore load */ + assert (mod->funcs != NULL); prev = p11_dict_get (gl.modules, mod->funcs); if (prev != NULL) { @@ -360,8 +450,9 @@ is_module_enabled_unlocked (const char *name, } static CK_RV -take_config_and_load_module_unlocked (char **name, - p11_dict **config) +take_config_and_load_module_inlock (char **name, + p11_dict **config, + bool critical) { Module *mod, *prev; const char *module_filename; @@ -401,6 +492,7 @@ take_config_and_load_module_unlocked (char **name, *config = NULL; mod->name = *name; *name = NULL; + mod->critical = critical; rv = dlopen_and_get_function_list (mod, path); if (rv != CKR_OK) { @@ -415,6 +507,7 @@ take_config_and_load_module_unlocked (char **name, */ mod->init_args.pReserved = p11_dict_get (mod->config, "x-init-reserved"); + assert (mod->funcs != NULL); prev = p11_dict_get (gl.modules, mod->funcs); /* If same module was loaded previously, just take over config */ @@ -485,8 +578,7 @@ load_registered_modules_unlocked (void) /* 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_unlocked (&name, &config); + rv = take_config_and_load_module_inlock (&name, &config, critical); /* * These variables will be cleared if ownership is transeferred @@ -510,10 +602,11 @@ load_registered_modules_unlocked (void) } static CK_RV -initialize_module_unlocked_reentrant (Module *mod) +initialize_module_inlock_reentrant (Module *mod) { CK_RV rv = CKR_OK; p11_thread_id_t self; + assert (mod); self = p11_thread_id_self (); @@ -535,19 +628,12 @@ initialize_module_unlocked_reentrant (Module *mod) p11_mutex_lock (&mod->initialize_mutex); if (!mod->initialize_called) { - assert (mod->funcs); + p11_debug ("C_Initialize: calling"); - if (mod->funcs == &_p11_proxy_function_list) { - p11_message ("refusing to load the p11-kit-proxy.so module as a registered module"); - rv = CKR_FUNCTION_FAILED; - - } else { - p11_debug ("C_Initialize: calling"); + rv = mod->virt.funcs.C_Initialize (&mod->virt.funcs, + &mod->init_args); - rv = mod->funcs->C_Initialize (&mod->init_args); - - p11_debug ("C_Initialize: result: %lu", rv); - } + p11_debug ("C_Initialize: result: %lu", rv); /* Module was initialized and C_Finalize should be called */ if (rv == CKR_OK) @@ -561,10 +647,14 @@ initialize_module_unlocked_reentrant (Module *mod) p11_mutex_unlock (&mod->initialize_mutex); p11_lock (); - /* Don't claim reference if failed */ - if (rv != CKR_OK) - --mod->ref_count; + 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; } @@ -605,6 +695,11 @@ init_globals_unlocked (void) return_val_if_fail (gl.modules != NULL, CKR_HOST_MEMORY); } + if (!gl.managed) { + gl.managed = p11_dict_new (p11_dict_direct_hash, p11_dict_direct_equal, NULL, NULL); + return_val_if_fail (gl.managed != NULL, CKR_HOST_MEMORY); + } + if (once) return CKR_OK; @@ -631,12 +726,14 @@ free_modules_when_no_refs_unlocked (void) p11_dict_free (gl.modules); gl.modules = NULL; + p11_dict_free (gl.managed); + gl.managed = NULL; p11_dict_free (gl.config); gl.config = NULL; } static CK_RV -finalize_module_unlocked_reentrant (Module *mod) +finalize_module_inlock_reentrant (Module *mod) { assert (mod); @@ -647,7 +744,7 @@ finalize_module_unlocked_reentrant (Module *mod) if (mod->ref_count == 0) return CKR_ARGUMENTS_BAD; - if (--mod->ref_count > 0) + if (--mod->init_count > 0) return CKR_OK; /* @@ -655,24 +752,20 @@ finalize_module_unlocked_reentrant (Module *mod) * the ref count. This prevents module from being freed out * from ounder us. */ - ++mod->ref_count; - p11_mutex_lock (&mod->initialize_mutex); p11_unlock (); + p11_mutex_lock (&mod->initialize_mutex); if (mod->initialize_called) { - - assert (mod->funcs); - mod->funcs->C_Finalize (NULL); - + mod->virt.funcs.C_Finalize (&mod->virt.funcs, NULL); mod->initialize_called = false; } p11_mutex_unlock (&mod->initialize_mutex); p11_lock (); - /* Match the increment above */ - --mod->ref_count; + /* Match the ref increment in initialize_module_inlock_reentrant() */ + mod->ref_count--; free_modules_when_no_refs_unlocked (); return CKR_OK; @@ -693,14 +786,18 @@ find_module_for_name_unlocked (const char *name) return NULL; } -CK_RV -_p11_kit_initialize_registered_unlocked_reentrant (void) +static CK_RV +initialize_registered_inlock_reentrant (void) { Module *mod; p11_dictiter iter; - int critical; 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; @@ -708,25 +805,20 @@ _p11_kit_initialize_registered_unlocked_reentrant (void) rv = load_registered_modules_unlocked (); if (rv == CKR_OK) { p11_dict_iterate (gl.modules, &iter); - while (p11_dict_next (&iter, NULL, (void **)&mod)) { + while (rv == CKR_OK && p11_dict_next (&iter, NULL, (void **)&mod)) { - /* Skip all modules that aren't registered */ + /* 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_unlocked_reentrant (mod); - - /* - * Module failed to initialize. If this is a critical module, - * then this, should abort loading of others. - */ + rv = initialize_module_inlock_reentrant (mod); if (rv != CKR_OK) { - p11_message ("failed to initialize module: %s: %s", - mod->name, p11_kit_strerror (rv)); - - critical = _p11_conf_parse_boolean (p11_dict_get (mod->config, "critical"), false); - if (!critical) { - p11_debug ("ignoring failure, non-critical module: %s", mod->name); + 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; } } @@ -751,6 +843,8 @@ _p11_kit_initialize_registered_unlocked_reentrant (void) * If this function fails, then an error message will be available via the * p11_kit_message() function. * + * Deprecated: Since: 0.16: Use p11_kit_modules_load() instead. + * * Returns: CKR_OK if the initialization succeeded, or an error code. */ CK_RV @@ -768,7 +862,7 @@ p11_kit_initialize_registered (void) p11_message_clear (); /* WARNING: Reentrancy can occur here */ - rv = _p11_kit_initialize_registered_unlocked_reentrant (); + rv = initialize_registered_inlock_reentrant (); _p11_kit_default_message (rv); @@ -782,14 +876,21 @@ p11_kit_initialize_registered (void) return rv; } -CK_RV -_p11_kit_finalize_registered_unlocked_reentrant (void) +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; @@ -804,7 +905,7 @@ _p11_kit_finalize_registered_unlocked_reentrant (void) while (p11_dict_next (&iter, NULL, (void **)&mod)) { /* Skip all modules that aren't registered */ - if (mod->name) + if (mod->name && mod->init_count) to_finalize[count++] = mod; } @@ -812,7 +913,7 @@ _p11_kit_finalize_registered_unlocked_reentrant (void) for (i = 0; i < count; ++i) { /* WARNING: Reentrant calls can occur here */ - finalize_module_unlocked_reentrant (to_finalize[i]); + finalize_module_inlock_reentrant (to_finalize[i]); } free (to_finalize); @@ -837,6 +938,8 @@ _p11_kit_finalize_registered_unlocked_reentrant (void) * If this function fails, then an error message will be available via the * p11_kit_message() function. * + * Deprecated: Since 0.16: Use p11_kit_modules_release() instead. + * * Returns: CKR_OK if the finalization succeeded, or an error code. */ @@ -855,7 +958,7 @@ p11_kit_finalize_registered (void) p11_message_clear (); /* WARNING: Reentrant calls can occur here */ - rv = _p11_kit_finalize_registered_unlocked_reentrant (); + rv = finalize_registered_inlock_reentrant (); _p11_kit_default_message (rv); @@ -875,8 +978,14 @@ compar_priority (const void *one, const char *v1, *v2; int o1, o2; - m1 = p11_dict_get (gl.modules, f1); - m2 = p11_dict_get (gl.modules, f2); + m1 = p11_dict_get (gl.managed, f1); + if (m1 == NULL) + m1 = p11_dict_get (gl.modules, f1); + + m2 = p11_dict_get (gl.managed, f2); + if (m2 == NULL) + m2 = p11_dict_get (gl.modules, f2); + assert (m1 != NULL && m2 != NULL); v1 = p11_dict_get (m1->config, "priority"); @@ -910,16 +1019,21 @@ sort_modules_by_priority (CK_FUNCTION_LIST_PTR *modules, qsort (modules, count, sizeof (CK_FUNCTION_LIST_PTR), compar_priority); } -CK_FUNCTION_LIST_PTR_PTR -_p11_kit_registered_modules_unlocked (void) +static CK_FUNCTION_LIST ** +list_registered_modules_inlock (void) { - CK_FUNCTION_LIST_PTR_PTR result = NULL; + CK_FUNCTION_LIST **result = NULL; 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.modules) { - result = calloc (p11_dict_size (gl.modules) + 1, sizeof (CK_FUNCTION_LIST_PTR)); + result = calloc (p11_dict_size (gl.modules) + 1, sizeof (CK_FUNCTION_LIST *)); return_val_if_fail (result != NULL, NULL); p11_dict_iterate (gl.modules, &iter); @@ -936,7 +1050,7 @@ _p11_kit_registered_modules_unlocked (void) * having initialized. This is a corner case, but want to make * sure to cover it. */ - if (mod->ref_count && mod->name && + if (mod->ref_count && mod->name && mod->init_count && mod->funcs && is_module_enabled_unlocked (mod->name, mod->config)) { result[i++] = mod->funcs; } @@ -957,6 +1071,10 @@ _p11_kit_registered_modules_unlocked (void) * 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.16: Use p11_kit_modules_load() instead. + * * Returns: A list of all the registered modules. Use the free() function to * free the list. */ @@ -971,7 +1089,7 @@ p11_kit_registered_modules (void) p11_message_clear (); - result = _p11_kit_registered_modules_unlocked (); + result = list_registered_modules_inlock (); p11_unlock (); @@ -987,6 +1105,8 @@ p11_kit_registered_modules (void) * 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.16: 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. @@ -994,6 +1114,28 @@ p11_kit_registered_modules (void) 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; @@ -1005,9 +1147,13 @@ p11_kit_registered_module_to_name (CK_FUNCTION_LIST_PTR module) p11_message_clear (); - mod = module && gl.modules ? p11_dict_get (gl.modules, module) : NULL; - if (mod && mod->name) - name = strdup (mod->name); + if (gl.modules) { + mod = p11_dict_get (gl.modules, module); + if (mod == NULL) + mod = p11_dict_get (gl.managed, module); + if (mod && mod->name) + name = strdup (mod->name); + } p11_unlock (); @@ -1015,12 +1161,60 @@ p11_kit_registered_module_to_name (CK_FUNCTION_LIST_PTR module) } /** + * 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) +{ + 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, module); + } else { + flags |= P11_KIT_MODULE_UNMANAGED; + mod = p11_dict_get (gl.modules, module); + } + if (!mod || mod->critical) + flags |= P11_KIT_MODULE_CRITICAL; + } + + 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.16: 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. */ @@ -1034,17 +1228,83 @@ p11_kit_registered_name_to_module (const char *name) p11_lock (); + p11_message_clear (); + + if (gl.modules) { + mod = find_module_for_name_unlocked (name); + if (mod != NULL && mod->funcs && is_module_enabled_unlocked (name, mod->config)) + module = mod->funcs; + } + + 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 (); - if (gl.modules) { - mod = find_module_for_name_unlocked (name); - if (mod != NULL && is_module_enabled_unlocked (name, mod->config)) - module = mod->funcs; + for (i = 0; gl.modules && modules[i] != NULL; i++) { + mod = p11_dict_get (gl.modules, modules[i]); + if (mod == NULL) + mod = p11_dict_get (gl.managed, modules[i]); + if (mod && mod->name && strcmp (mod->name, name) == 0) { + ret = modules[i]; + break; + } } p11_unlock (); - return module; + return ret; +} + +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); } /** @@ -1056,6 +1316,8 @@ p11_kit_registered_name_to_module (const char *name) * <code>NULL</code> module argument is specified, then this will lookup * the configuration option in the global config file. * + * Deprecated: Since 0.16: 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. @@ -1065,7 +1327,7 @@ p11_kit_registered_option (CK_FUNCTION_LIST_PTR module, const char *field) { Module *mod = NULL; char *option = NULL; - p11_dict *config = NULL; + const char *value; return_val_if_fail (field != NULL, NULL); @@ -1075,24 +1337,611 @@ p11_kit_registered_option (CK_FUNCTION_LIST_PTR module, const char *field) p11_message_clear (); - if (module == NULL) { - config = gl.config; - - } else { + if (module == NULL) + mod = NULL; + else mod = gl.modules ? p11_dict_get (gl.modules, module) : NULL; - if (mod) - config = mod->config; + + 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 = p11_dict_get (gl.managed, module); + if (mod == NULL) { + mod = p11_dict_get (gl.modules, module); + if (mod == NULL) + goto cleanup; + } + } + + value = module_get_option_inlock (mod, option); + if (value) + ret = strdup (value); } - if (config && field) { - option = p11_dict_get (config, field); - if (option) - option = strdup (option); + +cleanup: + p11_unlock (); + return ret; +} + +static CK_RV +managed_C_Initialize (CK_X_FUNCTION_LIST *self, + CK_VOID_PTR init_args) +{ + Module *mod = ((Managed *)self)->mod; + CK_RV rv; + + p11_debug ("in"); + p11_lock (); + + rv = initialize_module_inlock_reentrant (mod); + + p11_unlock (); + p11_debug ("out: %lu", rv); + + return rv; +} + +static CK_RV +managed_C_Finalize (CK_X_FUNCTION_LIST *self, + CK_VOID_PTR reserved) +{ + Module *mod = ((Managed *)self)->mod; + CK_RV rv; + + p11_debug ("in"); + p11_lock (); + + rv = finalize_module_inlock_reentrant (mod); + + p11_unlock (); + p11_debug ("out: %lu", rv); + + return rv; +} + +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->mod = mod; + mod->ref_count++; + + return &managed->virt; +} + +static bool +lookup_managed_option (Module *mod, + const char *option, + bool def_value) +{ + const char *string; + bool supported; + bool value; + + /* Whether managed stuff is supported or not */ + supported = p11_virtual_can_wrap (); + + 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 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); + 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 */ + mod = p11_dict_get (gl.managed, module); + if (mod != NULL) { + if (!p11_dict_remove (gl.managed, module)) + assert_not_reached (); + p11_virtual_unwrap (module); + + /* If an unmanaged module then caller should have finalized */ + } else { + mod = p11_dict_get (gl.modules, 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; + p11_virtual *virt; + bool is_managed; + + assert (module != NULL); + + if (flags & P11_KIT_MODULE_UNMANAGED) + is_managed = false; + else + is_managed = lookup_managed_option (mod, "managed", true); + + if (is_managed) { + virt = managed_create_inlock (mod); + return_val_if_fail (virt != NULL, CKR_HOST_MEMORY); + destroyer = managed_free_inlock; + + *module = p11_virtual_wrap (virt, destroyer); + return_val_if_fail (*module != NULL, CKR_GENERAL_ERROR); + + if (!p11_dict_set (gl.managed, *module, mod)) + return_val_if_reached (CKR_HOST_MEMORY); + + } else if (mod->funcs) { + *module = mod->funcs; + + } else { + 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) + 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 funtion 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 (); - return option; + 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) { + 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); + } + } + + /* 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. + * + * 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; + + 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); } /** @@ -1123,14 +1972,17 @@ p11_kit_registered_option (CK_FUNCTION_LIST_PTR module, const char *field) * If this function fails, then an error message will be available via the * p11_kit_message() function. * + * Deprecated: Since 0.16: 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) { - Module *allocated = NULL; + CK_FUNCTION_LIST_PTR result; Module *mod; - CK_RV rv = CKR_OK; + int flags; + CK_RV rv; return_val_if_fail (module != NULL, CKR_ARGUMENTS_BAD); @@ -1143,34 +1995,134 @@ p11_kit_initialize_module (CK_FUNCTION_LIST_PTR module) p11_message_clear (); - rv = init_globals_unlocked (); - if (rv == CKR_OK) { + 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.modules, module); - if (mod == NULL) { - p11_debug ("allocating new module"); - allocated = mod = alloc_module_unlocked (); - if (mod == NULL) - rv = CKR_HOST_MEMORY; - else - mod->funcs = module; + assert (mod != NULL); + rv = initialize_module_inlock_reentrant (mod); + if (rv != CKR_OK) { + p11_message ("module initialization failed: %s", p11_kit_strerror (rv)); + p11_module_release_inlock_reentrant (module); } + } - /* If this was newly allocated, add it to the list */ - if (rv == CKR_OK && allocated) { - if (p11_dict_set (gl.modules, allocated->funcs, allocated)) - allocated = NULL; - else - rv = CKR_HOST_MEMORY; - } + 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.modules, module); + if (mod == NULL) { + p11_debug ("allocating new module"); + allocated = mod = alloc_module_unlocked (); + if (mod == NULL) + rv = CKR_HOST_MEMORY; + else + module_setup_with_functions (mod, module); + } + /* If this was newly allocated, add it to the list */ + if (rv == CKR_OK && allocated) { + if (p11_dict_set (gl.modules, allocated->funcs, allocated)) + allocated = NULL; + else + rv = CKR_HOST_MEMORY; + } + + if (rv == CKR_OK) { + /* 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: 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. + * + * 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; + 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_unlocked (module_path, &mod); if (rv == CKR_OK) { /* WARNING: Reentrancy can occur here */ - rv = initialize_module_unlocked_reentrant (mod); + rv = prepare_module_inlock_reentrant (mod, flags, &module); + if (rv != CKR_OK) + module = NULL; } - - free (allocated); } /* @@ -1181,12 +2133,11 @@ p11_kit_initialize_module (CK_FUNCTION_LIST_PTR module) if (rv != CKR_OK) free_modules_when_no_refs_unlocked (); - _p11_kit_default_message (rv); - p11_unlock (); - p11_debug ("out: %lu", rv); - return rv; + p11_debug ("out: %s", module ? "success" : "fail"); + return module; + } /** @@ -1198,7 +2149,7 @@ p11_kit_initialize_module (CK_FUNCTION_LIST_PTR module) * 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 + * 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. * @@ -1211,10 +2162,13 @@ p11_kit_initialize_module (CK_FUNCTION_LIST_PTR module) * If this function fails, then an error message will be available via the * p11_kit_message() function. * + * Deprecated: Since 0.16: 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_PTR module) +p11_kit_finalize_module (CK_FUNCTION_LIST *module) { Module *mod; CK_RV rv = CKR_OK; @@ -1236,7 +2190,7 @@ p11_kit_finalize_module (CK_FUNCTION_LIST_PTR module) rv = CKR_ARGUMENTS_BAD; } else { /* WARNING: Rentrancy can occur here */ - rv = finalize_module_unlocked_reentrant (mod); + rv = finalize_module_inlock_reentrant (mod); } _p11_kit_default_message (rv); @@ -1248,6 +2202,130 @@ p11_kit_finalize_module (CK_FUNCTION_LIST_PTR module) } /** + * 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 @@ -1278,6 +2356,8 @@ p11_kit_finalize_module (CK_FUNCTION_LIST_PTR module) * If this function fails, then an error message will be available via the * p11_kit_message() function. * + * Deprecated: Since 0.16: Use p11_kit_module_load() instead. + * * Returns: CKR_OK if the initialization was successful. */ CK_RV @@ -1306,12 +2386,14 @@ p11_kit_load_initialize_module (const char *module_path, if (rv == CKR_OK) { /* WARNING: Reentrancy can occur here */ - rv = initialize_module_unlocked_reentrant (mod); + rv = initialize_module_inlock_reentrant (mod); } } - if (rv == CKR_OK && module) + if (rv == CKR_OK && module) { + assert (mod->funcs != NULL); *module = mod->funcs; + } /* * If initialization failed, we may need to cleanup. |