/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* gck-module.c - the GObject PKCS#11 wrapper library
Copyright (C) 2008, Stefan Walter
The Gnome Keyring Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The Gnome Keyring Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the Gnome Library; see the file COPYING.LIB. If not,
see .
Author: Stef Walter
*/
#include "config.h"
#include "gck.h"
#include "gck-private.h"
#include "gck/gck-marshal.h"
#include
#define P11_KIT_FUTURE_UNSTABLE_API 1
#include
#include
/**
* GckModule:
*
* Holds a loaded PKCS#11 module. A PKCS#11 module is a shared library.
*
* You can load and initialize a PKCS#11 module with the
* [func@Module.initialize] call. If you already have a loaded and
* initialized module that you'd like to use with the various Gck functions,
* then you can use [ctor@Module.new].
*/
/**
* GckModuleInfo:
* @pkcs11_version_major: The major version of the module.
* @pkcs11_version_minor: The minor version of the module.
* @manufacturer_id: The module manufacturer.
* @flags: The module PKCS#11 flags.
* @library_description: The module description.
* @library_version_major: The major version of the library.
* @library_version_minor: The minor version of the library.
*
* Holds information about the PKCS#11 module.
*
* This structure corresponds to `CK_MODULE_INFO` in the PKCS#11 standard. The
* strings are %NULL terminated for easier use.
*
* Use gck_module_info_free() to release this structure when done with it.
*/
/*
* MT safe
*
* The only thing that can change after object initialization in
* a GckModule is the finalized flag, which can be set
* to 1 in dispose.
*/
enum {
PROP_0,
PROP_PATH,
PROP_FUNCTIONS
};
typedef struct {
gchar *path;
gboolean initialized;
CK_FUNCTION_LIST_PTR funcs;
CK_C_INITIALIZE_ARGS init_args;
/* Modified atomically */
gint finalized;
} GckModulePrivate;
G_DEFINE_TYPE_WITH_PRIVATE (GckModule, gck_module, G_TYPE_OBJECT);
/* ----------------------------------------------------------------------------
* OBJECT
*/
static void
gck_module_init (GckModule *self)
{
}
static void
gck_module_get_property (GObject *obj, guint prop_id, GValue *value,
GParamSpec *pspec)
{
GckModule *self = GCK_MODULE (obj);
switch (prop_id) {
case PROP_PATH:
g_value_set_string (value, gck_module_get_path (self));
break;
case PROP_FUNCTIONS:
g_value_set_pointer (value, gck_module_get_functions (self));
break;
}
}
static void
gck_module_set_property (GObject *obj, guint prop_id, const GValue *value,
GParamSpec *pspec)
{
GckModule *self = GCK_MODULE (obj);
GckModulePrivate *priv = gck_module_get_instance_private (self);
/* Only allowed during initialization */
switch (prop_id) {
case PROP_PATH:
g_return_if_fail (!priv->path);
priv->path = g_value_dup_string (value);
break;
case PROP_FUNCTIONS:
g_return_if_fail (!priv->funcs);
priv->funcs = g_value_get_pointer (value);
break;
}
}
static void
gck_module_dispose (GObject *obj)
{
GckModule *self = GCK_MODULE (obj);
GckModulePrivate *priv = gck_module_get_instance_private (self);
gboolean finalize = FALSE;
CK_RV rv;
if (priv->initialized && priv->funcs) {
if (g_atomic_int_compare_and_exchange (&priv->finalized, 0, 1))
finalize = TRUE;
}
/* Must be careful when accessing funcs */
if (finalize) {
rv = p11_kit_module_finalize (priv->funcs);
if (rv != CKR_OK) {
g_warning ("C_Finalize on module '%s' failed: %s",
priv->path, gck_message_from_rv (rv));
}
}
G_OBJECT_CLASS (gck_module_parent_class)->dispose (obj);
}
static void
gck_module_finalize (GObject *obj)
{
GckModule *self = GCK_MODULE (obj);
GckModulePrivate *priv = gck_module_get_instance_private (self);
if (priv->initialized && priv->funcs)
g_clear_pointer (&priv->funcs, p11_kit_module_release);
g_clear_pointer (&priv->path, g_free);
G_OBJECT_CLASS (gck_module_parent_class)->finalize (obj);
}
static void
gck_module_class_init (GckModuleClass *klass)
{
GObjectClass *gobject_class = (GObjectClass*)klass;
gck_module_parent_class = g_type_class_peek_parent (klass);
gobject_class->get_property = gck_module_get_property;
gobject_class->set_property = gck_module_set_property;
gobject_class->dispose = gck_module_dispose;
gobject_class->finalize = gck_module_finalize;
/**
* GckModule:path:
*
* The PKCS#11 module file path.
*
* This may be set to NULL if this object was created from an already
* initialized module via the gck_module_new() function.
*/
g_object_class_install_property (gobject_class, PROP_PATH,
g_param_spec_string ("path", "Module Path", "Path to the PKCS11 Module",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
/**
* GckModule:functions:
*
* The raw PKCS#11 function list for the module.
*
* This points to a CK_FUNCTION_LIST structure.
*/
g_object_class_install_property (gobject_class, PROP_FUNCTIONS,
g_param_spec_pointer ("functions", "Function List", "PKCS11 Function List",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
}
G_DEFINE_BOXED_TYPE (GckModuleInfo, gck_module_info,
gck_module_info_copy, gck_module_info_free)
/**
* gck_module_info_copy:
* @module_info: a module info
*
* Make a copy of the module info.
*
* Returns: (transfer full): a newly allocated copy module info
*/
GckModuleInfo *
gck_module_info_copy (GckModuleInfo *module_info)
{
if (module_info == NULL)
return NULL;
module_info = g_memdup2 (module_info, sizeof (GckModuleInfo));
module_info->manufacturer_id = g_strdup (module_info->manufacturer_id);
module_info->library_description = g_strdup (module_info->library_description);
return module_info;
}
/**
* gck_module_info_free:
* @module_info: The module info to free, or %NULL.
*
* Free a GckModuleInfo structure.
**/
void
gck_module_info_free (GckModuleInfo *module_info)
{
if (!module_info)
return;
g_clear_pointer (&module_info->library_description, g_free);
g_clear_pointer (&module_info->manufacturer_id, g_free);
g_clear_pointer (&module_info, g_free);
}
typedef struct {
GckArguments base;
gchar *path;
GckModule *result;
GError *error;
} Initialize;
static CK_RV
perform_initialize (Initialize *args)
{
CK_FUNCTION_LIST_PTR funcs;
GckModule *result;
GckModulePrivate *priv;
CK_RV rv;
funcs = p11_kit_module_load (args->path, P11_KIT_MODULE_CRITICAL);
if (funcs == NULL) {
g_set_error (&args->error, GCK_ERROR, (int)GCK_ERROR_MODULE_PROBLEM,
_("Error loading PKCS#11 module: %s"), p11_kit_message ());
return GCK_ERROR_MODULE_PROBLEM;
}
result = g_object_new (GCK_TYPE_MODULE,
"functions", funcs,
"path", args->path,
NULL);
priv = gck_module_get_instance_private (result);
/* Now initialize the module */
rv = p11_kit_module_initialize (funcs);
if (rv != CKR_OK) {
p11_kit_module_release (funcs);
g_set_error (&args->error, GCK_ERROR, rv,
_("Couldn’t initialize PKCS#11 module: %s"),
gck_message_from_rv (rv));
g_object_unref (result);
return rv;
}
priv->initialized = TRUE;
args->result = result;
return CKR_OK;
}
static void
free_initialize (Initialize *args)
{
g_free (args->path);
g_clear_error (&args->error);
g_clear_object (&args->result);
g_free (args);
}
/**
* gck_module_initialize:
* @path: The file system path to the PKCS#11 module to load.
* @cancellable: (nullable): optional cancellation object
* @error: A location to store an error resulting from a failed load.
*
* Load and initialize a PKCS#11 module represented by a GckModule object.
*
* Return value: (transfer full): The loaded PKCS#11 module or %NULL if failed.
**/
GckModule*
gck_module_initialize (const gchar *path,
GCancellable *cancellable,
GError **error)
{
Initialize args = { GCK_ARGUMENTS_INIT, 0, };
g_return_val_if_fail (path != NULL, NULL);
g_return_val_if_fail (!error || !*error, NULL);
args.path = g_strdup (path);
if (!_gck_call_sync (NULL, perform_initialize, NULL, &args, cancellable, error)) {
/* A custom error from perform_initialize */
if (args.error) {
g_clear_error (error);
g_propagate_error (error, args.error);
args.error = NULL;
}
}
g_free (args.path);
g_clear_error (&args.error);
return args.result;
}
/**
* gck_module_initialize_async:
* @path: the file system path to the PKCS#11 module to load
* @cancellable: (nullable): optional cancellation object
* @callback: a callback which will be called when the operation completes
* @user_data: data to pass to the callback
*
* Asynchronously load and initialize a PKCS#11 module represented by a
* [class@Module] object.
**/
void
gck_module_initialize_async (const gchar *path,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
Initialize *args;
GckCall *call;
g_return_if_fail (path != NULL);
call = _gck_call_async_prep (NULL, perform_initialize, NULL,
sizeof (*args), free_initialize);
args = _gck_call_get_arguments (call);
args->path = g_strdup (path);
_gck_call_async_ready_go (call, NULL, cancellable, callback, user_data);
}
/**
* gck_module_initialize_finish:
* @result: the asynchronous result
* @error: location to place an error on failure
*
* Finishes the asynchronous initialize operation.
*
* Returns: (transfer full) (nullable): The initialized module, or %NULL
*/
GckModule *
gck_module_initialize_finish (GAsyncResult *result,
GError **error)
{
GckModule *module = NULL;
Initialize *args;
args = _gck_call_async_result_arguments (result, Initialize);
if (_gck_call_basic_finish (result, error)) {
module = args->result;
args->result = NULL;
} else {
/* A custom error from perform_initialize */
if (args->error) {
g_clear_error (error);
g_propagate_error (error, args->error);
args->error = NULL;
}
}
return module;
}
/**
* gck_module_new: (skip)
* @funcs: Initialized PKCS#11 function list pointer
*
* Create a [class@Module] representing a PKCS#11 module. It is assumed that
* this the module is already initialized. In addition it will not be
* finalized when complete.
*
* Return value: The new PKCS#11 module.
**/
GckModule*
gck_module_new (CK_FUNCTION_LIST_PTR funcs)
{
g_return_val_if_fail (funcs != NULL, NULL);
return g_object_new (GCK_TYPE_MODULE, "functions", funcs, NULL);
}
GckModule*
_gck_module_new_initialized (CK_FUNCTION_LIST_PTR funcs)
{
GckModule *module = gck_module_new (funcs);
GckModulePrivate *priv = gck_module_get_instance_private (module);
priv->initialized = TRUE; /* As if we initialized it */
return module;
}
/**
* gck_module_equal:
* @module1: a first #GckModule
* @module2: a second #GckModule
*
* Checks equality of two modules. Two GckModule objects can point to the same
* underlying PKCS#11 module.
*
* Return value: %TRUE if module1 and module2 are equal.
* %FALSE if either is not a GckModule.
**/
gboolean
gck_module_equal (GckModule *module1, GckModule *module2)
{
GckModulePrivate *priv1 = gck_module_get_instance_private (module1);
GckModulePrivate *priv2 = gck_module_get_instance_private (module2);
if (module1 == module2)
return TRUE;
if (!GCK_IS_MODULE (module1) || !GCK_IS_MODULE (module2))
return FALSE;
return priv1->funcs == priv2->funcs;
}
/**
* gck_module_hash:
* @module: (type Gck.Module): a pointer to a #GckModule
*
* Create a hash value for the GckModule.
*
* This function is intended for easily hashing a [class@Module] to add to
* a [struct@GLib.HashTable] or similar data structure.
*
* Return value: An integer that can be used as a hash value, or 0 if invalid.
**/
guint
gck_module_hash (GckModule *module)
{
GckModulePrivate *priv = gck_module_get_instance_private (module);
g_return_val_if_fail (GCK_IS_MODULE (module), 0);
return g_direct_hash (priv->funcs);
}
GckModuleInfo*
_gck_module_info_from_pkcs11 (CK_INFO_PTR info)
{
GckModuleInfo *modinfo;
modinfo = g_new0 (GckModuleInfo, 1);
modinfo->flags = info->flags;
modinfo->library_description = gck_string_from_chars (info->libraryDescription,
sizeof (info->libraryDescription));
modinfo->manufacturer_id = gck_string_from_chars (info->manufacturerID,
sizeof (info->manufacturerID));
modinfo->library_version_major = info->libraryVersion.major;
modinfo->library_version_minor = info->libraryVersion.minor;
modinfo->pkcs11_version_major = info->cryptokiVersion.major;
modinfo->pkcs11_version_minor = info->cryptokiVersion.minor;
return modinfo;
}
void
_gck_module_info_to_pkcs11 (GckModuleInfo* module_info, CK_INFO_PTR info)
{
info->flags = module_info->flags;
if (!gck_string_to_chars (info->libraryDescription,
sizeof (info->libraryDescription),
module_info->library_description))
g_return_if_reached ();
if (!gck_string_to_chars (info->manufacturerID,
sizeof (info->manufacturerID),
module_info->manufacturer_id))
g_return_if_reached ();
info->libraryVersion.major = module_info->library_version_major;
info->libraryVersion.minor = module_info->library_version_minor;
info->cryptokiVersion.major = module_info->pkcs11_version_major;
info->cryptokiVersion.minor = module_info->pkcs11_version_minor;
}
/**
* gck_module_get_info:
* @self: The module to get info for.
*
* Get the info about a PKCS#11 module.
*
* Returns: (transfer full): the module info; release this with gck_module_info_free()
**/
GckModuleInfo*
gck_module_get_info (GckModule *self)
{
GckModulePrivate *priv = gck_module_get_instance_private (self);
CK_INFO info;
CK_RV rv;
g_return_val_if_fail (GCK_IS_MODULE (self), NULL);
g_return_val_if_fail (priv->funcs, NULL);
memset (&info, 0, sizeof (info));
rv = (priv->funcs->C_GetInfo (&info));
if (rv != CKR_OK) {
g_warning ("couldn't get module info: %s", gck_message_from_rv (rv));
return NULL;
}
return _gck_module_info_from_pkcs11 (&info);
}
/**
* gck_module_get_slots:
* @self: The module for which to get the slots.
* @token_present: Whether to limit only to slots with a token present.
*
* Get the GckSlot objects for a given module.
*
* Return value: (element-type Gck.Slot) (transfer full): The possibly empty
* list of slots.
*/
GList*
gck_module_get_slots (GckModule *self, gboolean token_present)
{
GckModulePrivate *priv = gck_module_get_instance_private (self);
CK_SLOT_ID_PTR slot_list;
CK_ULONG count, i;
GList *result;
CK_RV rv;
g_return_val_if_fail (GCK_IS_MODULE (self), NULL);
g_return_val_if_fail (priv->funcs, NULL);
rv = (priv->funcs->C_GetSlotList) (token_present ? CK_TRUE : CK_FALSE, NULL, &count);
if (rv != CKR_OK) {
g_warning ("couldn't get slot count: %s", gck_message_from_rv (rv));
return NULL;
}
if (!count)
return NULL;
slot_list = g_new (CK_SLOT_ID, count);
rv = (priv->funcs->C_GetSlotList) (token_present ? CK_TRUE : CK_FALSE, slot_list, &count);
if (rv != CKR_OK) {
g_warning ("couldn't get slot list: %s", gck_message_from_rv (rv));
g_free (slot_list);
return NULL;
}
result = NULL;
for (i = 0; i < count; ++i) {
result = g_list_prepend (result, g_object_new (GCK_TYPE_SLOT,
"handle", slot_list[i],
"module", self, NULL));
}
g_free (slot_list);
return g_list_reverse (result);
}
/**
* gck_module_get_path:
* @self: The module for which to get the path.
*
* Get the file path of this module. This may not be an absolute path, and
* usually reflects the path passed to [func@Module.initialize].
*
* Return value: The path, do not modify or free this value.
**/
const gchar*
gck_module_get_path (GckModule *self)
{
GckModulePrivate *priv = gck_module_get_instance_private (self);
g_return_val_if_fail (GCK_IS_MODULE (self), NULL);
return priv->path;
}
/**
* gck_module_get_functions: (skip)
* @self: The module for which to get the function list.
*
* Get the PKCS#11 function list for the module.
*
* Return value: The function list, do not modify this structure.
**/
CK_FUNCTION_LIST_PTR
gck_module_get_functions (GckModule *self)
{
GckModulePrivate *priv = gck_module_get_instance_private (self);
g_return_val_if_fail (GCK_IS_MODULE (self), NULL);
return priv->funcs;
}
/**
* gck_module_match:
* @self: the module to match
* @uri: the uri to match against the module
*
* Check whether the PKCS#11 URI matches the module
*
* Returns: whether the URI matches or not
*/
gboolean
gck_module_match (GckModule *self,
GckUriData *uri)
{
gboolean match = TRUE;
GckModuleInfo *info;
g_return_val_if_fail (GCK_IS_MODULE (self), FALSE);
g_return_val_if_fail (uri != NULL, FALSE);
if (uri->any_unrecognized)
match = FALSE;
if (match && uri->module_info) {
info = gck_module_get_info (self);
match = _gck_module_info_match (uri->module_info, info);
gck_module_info_free (info);
}
return match;
}