/* gck-misc.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 "egg/egg-secure-memory.h"
#include
#include
#include
EGG_SECURE_DEFINE_GLIB_GLOBALS ();
/**
* GCK_CHECK_VERSION:
* @major: the major version to check for
* @minor: the minor version to check for
* @micro: the micro version to check for
*
* Checks the version of the Gck library that is being compiled
* against.
*
* ```
* #if !GCK_CHECK_VERSION (3, 0, 0)
* #warning Old Gck version, disabling functionality
* #endif
* ```
*
* Returns: %TRUE if the version of the GLib header files
* is the same as or newer than the passed-in version.
*/
/**
* GCK_MAJOR_VERSION:
*
* The major version number of the Gck library.
*/
/**
* GCK_MINOR_VERSION:
*
* The minor version number of the Gck library.
*/
/**
* GCK_MICRO_VERSION:
*
* The micro version number of the Gck library.
*/
/**
* GCK_INVALID:
*
* Used as a terminator at the end of variable argument lists.
*/
/**
* GCK_VENDOR_CODE:
*
* Custom PKCS#11 errors that originate from the gck library, are
* based at this error code.
*/
/**
* GckError:
* @GCK_ERROR_MODULE_PROBLEM: a result code that signifies there was a problem
* loading a PKCS#11 module, usually a shared library
*
* Various error codes. All the `CKR_XXX` error codes from PKCS#11 are also
* relevant error codes.
*
* Note that errors are returned as [struct@GLib.Error] structures. The `code`
* member of the error then contains the raw PKCS#11 `CK_RV` result value.
*/
/**
* GCK_ERROR:
*
* The error domain for gck library errors.
*/
G_DEFINE_QUARK(GckError, gck_error)
/**
* gck_message_from_rv:
* @rv: The PKCS#11 return value to get a message for.
*
* Get a message for a PKCS#11 return value or error code. Do not
* pass `CKR_OK` or other non-errors to this function.
*
* Return value: The user readable message.
*/
const gchar*
gck_message_from_rv (gulong rv)
{
switch (rv) {
/* These are not really errors, or not current */
case CKR_OK:
case CKR_NO_EVENT:
case CKR_FUNCTION_NOT_PARALLEL:
case CKR_SESSION_PARALLEL_NOT_SUPPORTED:
g_return_val_if_reached ("");
default:
return p11_kit_strerror (rv);
}
}
const gchar *
_gck_stringize_rv (CK_RV rv)
{
switch(rv) {
#define X(x) case x: return #x;
X (CKR_OK)
X (CKR_CANCEL)
X (CKR_HOST_MEMORY)
X (CKR_SLOT_ID_INVALID)
X (CKR_GENERAL_ERROR)
X (CKR_FUNCTION_FAILED)
X (CKR_ARGUMENTS_BAD)
X (CKR_NO_EVENT)
X (CKR_NEED_TO_CREATE_THREADS)
X (CKR_CANT_LOCK)
X (CKR_ATTRIBUTE_READ_ONLY)
X (CKR_ATTRIBUTE_SENSITIVE)
X (CKR_ATTRIBUTE_TYPE_INVALID)
X (CKR_ATTRIBUTE_VALUE_INVALID)
X (CKR_DATA_INVALID)
X (CKR_DATA_LEN_RANGE)
X (CKR_DEVICE_ERROR)
X (CKR_DEVICE_MEMORY)
X (CKR_DEVICE_REMOVED)
X (CKR_ENCRYPTED_DATA_INVALID)
X (CKR_ENCRYPTED_DATA_LEN_RANGE)
X (CKR_FUNCTION_CANCELED)
X (CKR_FUNCTION_NOT_PARALLEL)
X (CKR_FUNCTION_NOT_SUPPORTED)
X (CKR_KEY_HANDLE_INVALID)
X (CKR_KEY_SIZE_RANGE)
X (CKR_KEY_TYPE_INCONSISTENT)
X (CKR_KEY_NOT_NEEDED)
X (CKR_KEY_CHANGED)
X (CKR_KEY_NEEDED)
X (CKR_KEY_INDIGESTIBLE)
X (CKR_KEY_FUNCTION_NOT_PERMITTED)
X (CKR_KEY_NOT_WRAPPABLE)
X (CKR_KEY_UNEXTRACTABLE)
X (CKR_MECHANISM_INVALID)
X (CKR_MECHANISM_PARAM_INVALID)
X (CKR_OBJECT_HANDLE_INVALID)
X (CKR_OPERATION_ACTIVE)
X (CKR_OPERATION_NOT_INITIALIZED)
X (CKR_PIN_INCORRECT)
X (CKR_PIN_INVALID)
X (CKR_PIN_LEN_RANGE)
X (CKR_PIN_EXPIRED)
X (CKR_PIN_LOCKED)
X (CKR_SESSION_CLOSED)
X (CKR_SESSION_COUNT)
X (CKR_SESSION_HANDLE_INVALID)
X (CKR_SESSION_PARALLEL_NOT_SUPPORTED)
X (CKR_SESSION_READ_ONLY)
X (CKR_SESSION_EXISTS)
X (CKR_SESSION_READ_ONLY_EXISTS)
X (CKR_SESSION_READ_WRITE_SO_EXISTS)
X (CKR_SIGNATURE_INVALID)
X (CKR_SIGNATURE_LEN_RANGE)
X (CKR_TEMPLATE_INCOMPLETE)
X (CKR_TEMPLATE_INCONSISTENT)
X (CKR_TOKEN_NOT_PRESENT)
X (CKR_TOKEN_NOT_RECOGNIZED)
X (CKR_TOKEN_WRITE_PROTECTED)
X (CKR_UNWRAPPING_KEY_HANDLE_INVALID)
X (CKR_UNWRAPPING_KEY_SIZE_RANGE)
X (CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT)
X (CKR_USER_ALREADY_LOGGED_IN)
X (CKR_USER_NOT_LOGGED_IN)
X (CKR_USER_PIN_NOT_INITIALIZED)
X (CKR_USER_TYPE_INVALID)
X (CKR_USER_ANOTHER_ALREADY_LOGGED_IN)
X (CKR_USER_TOO_MANY_TYPES)
X (CKR_WRAPPED_KEY_INVALID)
X (CKR_WRAPPED_KEY_LEN_RANGE)
X (CKR_WRAPPING_KEY_HANDLE_INVALID)
X (CKR_WRAPPING_KEY_SIZE_RANGE)
X (CKR_WRAPPING_KEY_TYPE_INCONSISTENT)
X (CKR_RANDOM_SEED_NOT_SUPPORTED)
X (CKR_RANDOM_NO_RNG)
X (CKR_DOMAIN_PARAMS_INVALID)
X (CKR_BUFFER_TOO_SMALL)
X (CKR_SAVED_STATE_INVALID)
X (CKR_INFORMATION_SENSITIVE)
X (CKR_STATE_UNSAVEABLE)
X (CKR_CRYPTOKI_NOT_INITIALIZED)
X (CKR_CRYPTOKI_ALREADY_INITIALIZED)
X (CKR_MUTEX_BAD)
X (CKR_MUTEX_NOT_LOCKED)
X (CKR_FUNCTION_REJECTED)
X (CKR_VENDOR_DEFINED)
#undef X
default:
return "CKR_??????";
}
}
CK_RV
_gck_rv_from_error (GError *error,
CK_RV catch_all_code)
{
g_return_val_if_fail (error != NULL, CKR_GENERAL_ERROR);
if (error->domain == GCK_ERROR)
return error->code;
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return CKR_FUNCTION_CANCELED;
return catch_all_code;
}
GType
gck_list_get_boxed_type (void)
{
static GType type = 0;
if (!type)
type = g_boxed_type_register_static ("GckList",
(GBoxedCopyFunc)gck_list_ref_copy,
(GBoxedFreeFunc)gck_list_unref_free);
return type;
}
/**
* gck_list_unref_free: (skip)
* @reflist: (element-type GObject.Object): list of Gobject reference counted pointers
*
* Free a list of GObject based pointers. All objects in the list
* will be unreffed and then the list itself will be freed.
**/
void
gck_list_unref_free (GList *reflist)
{
GList *l;
for (l = reflist; l; l = g_list_next (l)) {
g_return_if_fail (G_IS_OBJECT (l->data));
g_object_unref (l->data);
}
g_list_free (reflist);
}
/**
* gck_list_ref_copy: (skip)
* @reflist: (element-type GObject.Object): list of GObject reference counted
* objects
*
* Copy a list of GObject based pointers. All objects
* in the list will be reffed and the list will be copied.
*
* Return value: (transfer full) (element-type GObject.Object): the copied and
* reffed list, when done, free it with gck_list_unref_free ()
**/
GList *
gck_list_ref_copy (GList *reflist)
{
GList *l, *copy = g_list_copy (reflist);
for (l = copy; l; l = g_list_next (l)) {
g_return_val_if_fail (G_IS_OBJECT (l->data), NULL);
g_object_ref (l->data);
}
return copy;
}
/**
* gck_string_from_chars: (skip)
* @data: The character data to turn into a null terminated string.
* @max: The maximum length of the charater data.
*
* Create a string from a set of PKCS#11 characters. This is
* similar to [func@GLib.strndup], except for that it also strips trailing
* spaces. These space padded strings are often used in PKCS#11
* structures.
*
* If the space padded string is filled with null characters then
* this function will return %NULL.
*
* Return value: The null terminated string.
*/
gchar*
gck_string_from_chars (const guchar *data, gsize max)
{
gchar *string;
g_return_val_if_fail (data, NULL);
g_return_val_if_fail (max, NULL);
/* Means no value */
if (!data[0])
return NULL;
string = g_strndup ((gchar*)data, max);
g_strchomp (string);
return string;
}
/**
* gck_string_to_chars: (skip)
* @data: The character buffer to place string into.
* @max: The maximum length of the charater buffer.
* @string: The string to place in the buffer.
*
* Create a space padded PKCS#11 string from a null terminated string.
* The string must be shorter than the buffer or %FALSE will be
* returned.
*
* If a %NULL string is passed, then the space padded string will be
* set to zero characters.
*
* Return value: The null terminated string.
*/
gboolean
gck_string_to_chars (guchar *data, gsize max, const gchar *string)
{
gsize len;
g_return_val_if_fail (data, FALSE);
g_return_val_if_fail (max, FALSE);
if (!string) {
memset (data, 0, max);
return TRUE;
}
len = strlen (string);
if (len > max)
return FALSE;
memset (data, ' ', max);
memcpy (data, string, len);
return TRUE;
}
guint
_gck_ulong_hash (gconstpointer v)
{
const signed char *p = v;
guint32 i, h = *p;
for(i = 0; i < sizeof (gulong); ++i)
h = (h << 5) - h + *(p++);
return h;
}
gboolean
_gck_ulong_equal (gconstpointer v1, gconstpointer v2)
{
return memcmp(v1, v2, sizeof (gulong)) == 0;
}
/**
* gck_value_to_ulong:
* @value: (array length=length): memory to convert
* @length: length of memory
* @result: (out): A location to store the result
*
* Convert `CK_ULONG` type memory to a boolean.
*
* Returns: Whether the conversion was successful.
*/
gboolean
gck_value_to_ulong (const guchar *value,
gsize length,
gulong *result)
{
if (!value || length != sizeof (CK_ULONG))
return FALSE;
if (result)
memcpy(result, value, sizeof(CK_ULONG));
return TRUE;
}
/**
* gck_value_to_boolean:
* @value: (array length=length): memory to convert
* @length: length of memory
* @result: (out): A location to store the result
*
* Convert `CK_BBOOL` type memory to a boolean.
*
* Returns: Whether the conversion was successful.
*/
gboolean
gck_value_to_boolean (const guchar *value,
gsize length,
gboolean *result)
{
CK_BBOOL tempval = CK_FALSE;
if (!value || length != sizeof (CK_BBOOL))
return FALSE;
if (result) {
memcpy(&tempval, value, sizeof(CK_BBOOL));
*result = tempval ? TRUE : FALSE;
}
return TRUE;
}
static gboolean
match_info_string (const gchar *match, const gchar *string)
{
/* NULL matches anything */
if (match == NULL)
return TRUE;
if (string == NULL)
return FALSE;
return g_str_equal (match, string);
}
gboolean
_gck_module_info_match (GckModuleInfo *match, GckModuleInfo *info)
{
/* Matches two GckModuleInfo for use in PKCS#11 URI's */
g_return_val_if_fail (match, FALSE);
g_return_val_if_fail (info, FALSE);
return (match_info_string (match->library_description, info->library_description) &&
match_info_string (match->manufacturer_id, info->manufacturer_id));
}
gboolean
_gck_token_info_match (GckTokenInfo *match, GckTokenInfo *info)
{
/* Matches two GckTokenInfo for use in PKCS#11 URI's */
g_return_val_if_fail (match, FALSE);
g_return_val_if_fail (info, FALSE);
return (match_info_string (match->label, info->label) &&
match_info_string (match->manufacturer_id, info->manufacturer_id) &&
match_info_string (match->model, info->model) &&
match_info_string (match->serial_number, info->serial_number));
}