diff options
Diffstat (limited to 'p11-kit/modules.c')
-rw-r--r-- | p11-kit/modules.c | 1748 |
1 files changed, 1507 insertions, 241 deletions
diff --git a/p11-kit/modules.c b/p11-kit/modules.c index 19ba895..ef8cea6 100644 --- a/p11-kit/modules.c +++ b/p11-kit/modules.c @@ -35,16 +35,23 @@ #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 "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 "virtual.h" #include <sys/stat.h> #include <sys/types.h> @@ -70,44 +77,83 @@ * 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 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; @@ -121,6 +167,8 @@ typedef struct _Module { */ static struct _Shared { p11_dict *modules; + p11_dict *unmanaged_by_funcs; + p11_dict *managed_by_closure; p11_dict *config; } gl = { NULL, NULL }; @@ -184,15 +232,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 +267,44 @@ 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 CK_RV -dlopen_and_get_function_list (Module *mod, const char *path) +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); + assert (funcs != 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,65 +313,77 @@ 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 (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_unlocked (const char *path, Module **result) +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); - rv = dlopen_and_get_function_list (mod, path); + 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); + + 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.modules, mod->funcs); + prev = p11_dict_get (gl.unmanaged_by_funcs, funcs); + /* If same module was loaded previously, just take over config */ if (prev != NULL) { - p11_debug ("duplicate module %s, using previous", path); + if (!name || prev->name || prev->config) + p11_debug ("duplicate module %s, using previous", path); free_module_unlocked (mod); mod = prev; - } else if (!p11_dict_set (gl.modules, mod->funcs, mod)) { + /* 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); } - if (result) - *result= mod; + *result= mod; return CKR_OK; } -static char* -expand_module_path (const char *filename) -{ - char *path; - - if (!p11_path_absolute (filename)) { - p11_debug ("module path is relative, loading from: %s", P11_MODULE_PATH); - path = p11_path_build (P11_MODULE_PATH, filename, NULL); - } else { - path = strdup (filename); - } - - return path; -} - static int is_list_delimiter (char ch) { @@ -360,13 +440,12 @@ 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; - char *path; - char *key; + const char *filename; + Module *mod; CK_RV rv; assert (name); @@ -377,36 +456,22 @@ take_config_and_load_module_unlocked (char **name, if (!is_module_enabled_unlocked (*name, *config)) return CKR_OK; - module_filename = p11_dict_get (*config, "module"); - if (module_filename == NULL) { + filename = p11_dict_get (*config, "module"); + if (filename == NULL) { p11_debug ("no module path for module, skipping: %s", *name); return CKR_OK; } - path = expand_module_path (module_filename); - return_val_if_fail (path != NULL, CKR_HOST_MEMORY); - - key = strdup ("module"); - return_val_if_fail (key != NULL, CKR_HOST_MEMORY); - - /* The hash map will take ownership of the variable */ - if (!p11_dict_set (*config, key, path)) - return_val_if_reached (CKR_HOST_MEMORY); - - mod = alloc_module_unlocked (); - return_val_if_fail (mod != NULL, CKR_HOST_MEMORY); + rv = load_module_from_file_inlock (*name, filename, &mod); + if (rv != CKR_OK) + return CKR_OK; /* Take ownership of thes evariables */ mod->config = *config; *config = NULL; mod->name = *name; *name = NULL; - - rv = dlopen_and_get_function_list (mod, path); - if (rv != CKR_OK) { - free_module_unlocked (mod); - return rv; - } + mod->critical = critical; /* * We support setting of CK_C_INITIALIZE_ARGS.pReserved from @@ -415,27 +480,6 @@ take_config_and_load_module_unlocked (char **name, */ mod->init_args.pReserved = p11_dict_get (mod->config, "x-init-reserved"); - prev = p11_dict_get (gl.modules, mod->funcs); - - /* If same module was loaded previously, just take over config */ - if (prev && !prev->name && !prev->config) { - prev->name = mod->name; - mod->name = NULL; - prev->config = mod->config; - mod->config = NULL; - free_module_unlocked (mod); - - /* Ignore duplicate module */ - } else if (prev) { - p11_message ("duplicate configured module: %s: %s", mod->name, path); - free_module_unlocked (mod); - - /* Add this new module to our hash table */ - } else { - if (!p11_dict_set (gl.modules, mod->funcs, mod)) - return_val_if_reached (CKR_HOST_MEMORY); - } - return CKR_OK; } @@ -485,8 +529,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 +553,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 (); @@ -531,23 +575,16 @@ initialize_module_unlocked_reentrant (Module *mod) mod->initialize_thread = self; /* Change over to the module specific mutex */ - p11_mutex_lock (&mod->initialize_mutex); p11_unlock (); + p11_mutex_lock (&mod->initialize_mutex); if (!mod->initialize_called) { - assert (mod->funcs); - - 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; + p11_debug ("C_Initialize: calling"); - } else { - p11_debug ("C_Initialize: calling"); - - rv = mod->funcs->C_Initialize (&mod->init_args); + rv = mod->virt.funcs.C_Initialize (&mod->virt.funcs, + &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 +598,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; } @@ -583,13 +624,13 @@ reinitialize_after_fork (void) if (gl.modules) { p11_dict_iterate (gl.modules, &iter); - while (p11_dict_next (&iter, NULL, (void **)&mod)) + while (p11_dict_next (&iter, (void **)&mod, NULL)) mod->initialize_called = false; } p11_unlock (); - _p11_kit_proxy_after_fork (); + p11_proxy_after_fork (); } #endif /* OS_UNIX */ @@ -600,11 +641,26 @@ init_globals_unlocked (void) static bool once = false; if (!gl.modules) { - gl.modules = p11_dict_new (p11_dict_direct_hash, p11_dict_direct_equal, - NULL, free_module_unlocked); + 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; @@ -624,19 +680,26 @@ free_modules_when_no_refs_unlocked (void) /* Check if any modules have a ref count */ p11_dict_iterate (gl.modules, &iter); - while (p11_dict_next (&iter, NULL, (void **)&mod)) { + 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_unlocked_reentrant (Module *mod) +finalize_module_inlock_reentrant (Module *mod) { assert (mod); @@ -647,7 +710,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,78 +718,58 @@ 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; } -static Module* -find_module_for_name_unlocked (const char *name) +static CK_RV +initialize_registered_inlock_reentrant (void) { - Module *mod; p11_dictiter iter; - - assert (name); - - p11_dict_iterate (gl.modules, &iter); - while (p11_dict_next (&iter, NULL, (void **)&mod)) - if (mod->ref_count && mod->name && strcmp (name, mod->name) == 0) - return mod; - return NULL; -} - -CK_RV -_p11_kit_initialize_registered_unlocked_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; rv = load_registered_modules_unlocked (); if (rv == CKR_OK) { - p11_dict_iterate (gl.modules, &iter); - while (p11_dict_next (&iter, NULL, (void **)&mod)) { + 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 */ + /* 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; } } @@ -736,6 +779,27 @@ _p11_kit_initialize_registered_unlocked_reentrant (void) 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: * @@ -751,6 +815,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.19.0: Use p11_kit_modules_load() instead. + * * Returns: CKR_OK if the initialization succeeded, or an error code. */ CK_RV @@ -768,7 +834,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,29 +848,36 @@ 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; /* WARNING: This function must be reentrant */ - to_finalize = calloc (p11_dict_size (gl.modules), sizeof (Module *)); + 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.modules, &iter); + 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) + if (mod->name && mod->init_count) to_finalize[count++] = mod; } @@ -812,7 +885,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 +910,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.19.0: Use p11_kit_modules_release() instead. + * * Returns: CKR_OK if the finalization succeeded, or an error code. */ @@ -855,7 +930,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 +950,8 @@ 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 = module_for_functions_inlock (f1); + m2 = module_for_functions_inlock (f2); assert (m1 != NULL && m2 != NULL); v1 = p11_dict_get (m1->config, "priority"); @@ -910,20 +985,27 @@ 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; + CK_FUNCTION_LIST *funcs; Module *mod; p11_dictiter iter; int i = 0; - if (gl.modules) { - result = calloc (p11_dict_size (gl.modules) + 1, sizeof (CK_FUNCTION_LIST_PTR)); + /* + * 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.modules, &iter); - while (p11_dict_next (&iter, NULL, (void **)&mod)) { + 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 @@ -936,9 +1018,9 @@ _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 && is_module_enabled_unlocked (mod->name, mod->config)) { - result[i++] = mod->funcs; + result[i++] = funcs; } } @@ -957,6 +1039,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.19.0: Use p11_kit_modules_load() instead. + * * Returns: A list of all the registered modules. Use the free() function to * free the list. */ @@ -971,7 +1057,7 @@ p11_kit_registered_modules (void) p11_message_clear (); - result = _p11_kit_registered_modules_unlocked (); + result = list_registered_modules_inlock (); p11_unlock (); @@ -987,6 +1073,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.19.0: Use p11_kit_module_get_name() instead. + * * Returns: A newly allocated string containing the module name, or * <code>NULL</code> if no such registered module exists. Use free() to * free this string. @@ -994,6 +1082,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 +1115,11 @@ 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 = module_for_functions_inlock (module); + if (mod && mod->name) + name = strdup (mod->name); + } p11_unlock (); @@ -1015,12 +1127,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_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; + } + + p11_unlock (); + + return flags; +} + +/** * p11_kit_registered_name_to_module: * @name: name of a registered module * * Lookup a registered PKCS\#11 module by its name. This name is specified by * the registered module configuration. * + * Deprecated: Since 0.19.0: Use p11_kit_module_for_name() instead. + * * Returns: a pointer to a PKCS\#11 module, or <code>NULL</code> if this name was * not found. */ @@ -1028,19 +1188,28 @@ 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 (); + 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; + 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 (); @@ -1048,6 +1217,70 @@ p11_kit_registered_name_to_module (const char *name) } /** + * 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; +} + +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_registered_option: * @module: a pointer to a registered module * @field: the name of the option to lookup. @@ -1056,6 +1289,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.19.0: Use p11_kit_config_option() instead. + * * Returns: A newly allocated string containing the option value, or * <code>NULL</code> if the registered module or the option were not found. * Use free() to free the returned string. @@ -1065,7 +1300,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 +1310,824 @@ p11_kit_registered_option (CK_FUNCTION_LIST_PTR module, const char *field) p11_message_clear (); - if (module == NULL) { - config = gl.config; + 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; + bool 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) { + 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); + if (rv == CKR_OK) { + managed->sessions = sessions; + managed->initialized = true; } else { - mod = gl.modules ? p11_dict_get (gl.modules, module) : NULL; - if (mod) - config = mod->config; + p11_dict_free (sessions); } + } + + p11_unlock (); + p11_debug ("out: %lu", rv); - if (config && field) { - option = p11_dict_get (config, field); - if (option) - option = strdup (option); + 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) { + rv = CKR_CRYPTOKI_NOT_INITIALIZED; + + } 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 = false; + p11_dict_free (managed->sessions); + managed->sessions = NULL; + } + } p11_unlock (); + p11_debug ("out: %lu", rv); - return option; + 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); + free (stolen); + + return stolen ? CKR_OK : 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; + p11_virtual *virt; + bool is_managed; + bool with_log; + + assert (module != NULL); + + 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) + 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 (); + + 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 +2158,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.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) { - 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 +2181,132 @@ 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); - 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; - } + /* An unmanaged module should return the same pointer */ + assert (rv != CKR_OK || result == 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) { + mod = p11_dict_get (gl.unmanaged_by_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); } + } + + 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 (rv == CKR_OK && 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; + } + + 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 = 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 = initialize_module_unlocked_reentrant (mod); + rv = prepare_module_inlock_reentrant (mod, flags, &module); + if (rv != CKR_OK) + module = NULL; } - - free (allocated); } /* @@ -1181,12 +2317,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 +2333,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 +2346,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.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_PTR module) +p11_kit_finalize_module (CK_FUNCTION_LIST *module) { Module *mod; CK_RV rv = CKR_OK; @@ -1230,13 +2368,13 @@ p11_kit_finalize_module (CK_FUNCTION_LIST_PTR module) p11_message_clear (); - mod = gl.modules ? p11_dict_get (gl.modules, module) : NULL; + 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_unlocked_reentrant (mod); + rv = finalize_module_inlock_reentrant (mod); } _p11_kit_default_message (rv); @@ -1248,6 +2386,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 +2540,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.19.0: Use p11_kit_module_load() instead. + * * Returns: CKR_OK if the initialization was successful. */ CK_RV @@ -1302,16 +2566,18 @@ p11_kit_load_initialize_module (const char *module_path, rv = init_globals_unlocked (); if (rv == CKR_OK) { - rv = load_module_from_file_unlocked (module_path, &mod); + rv = load_module_from_file_inlock (NULL, module_path, &mod); 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) - *module = mod->funcs; + if (rv == CKR_OK && module) { + *module = unmanaged_for_module_inlock (mod); + assert (*module != NULL); + } /* * If initialization failed, we may need to cleanup. |