diff options
author | Stef Walter <stef@memberwebs.com> | 2010-07-29 16:34:07 +0200 |
---|---|---|
committer | Stef Walter <stef@memberwebs.com> | 2010-07-29 16:34:07 +0200 |
commit | 8d80ac517dea7f35b5bb7b72fe425681d4ed5601 (patch) | |
tree | 6e6c0a6a9ca80f14c9ce5a83de6bbb8cc8a67109 | |
parent | 68cb40cc11a8188cd4f8e608ab882f19e6d51329 (diff) | |
download | gnome-keyring-8d80ac517dea7f35b5bb7b72fe425681d4ed5601.tar.gz |
[gck] In preparation for public release, rename library.
* Next steps will include cleaning up the API making it ready
for gobject introspection etc..
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | configure.in | 11 | ||||
-rw-r--r-- | gck/.gitignore | 2 | ||||
-rw-r--r-- | gck/Makefile.am | 66 | ||||
-rw-r--r-- | gck/gck-attributes.c | 1428 | ||||
-rw-r--r-- | gck/gck-call.c | 541 | ||||
-rw-r--r-- | gck/gck-marshal.list | 3 | ||||
-rw-r--r-- | gck/gck-misc.c | 437 | ||||
-rw-r--r-- | gck/gck-module.c | 1228 | ||||
-rw-r--r-- | gck/gck-object.c | 1615 | ||||
-rw-r--r-- | gck/gck-private.h | 162 | ||||
-rw-r--r-- | gck/gck-session.c | 2886 | ||||
-rw-r--r-- | gck/gck-slot.c | 1061 | ||||
-rw-r--r-- | gck/gck.h | 1297 | ||||
-rw-r--r-- | gck/gck.pc.in | 14 | ||||
-rw-r--r-- | gck/pkcs11.h | 30 | ||||
-rw-r--r-- | gck/tests/Makefile.am | 42 | ||||
-rw-r--r-- | gck/tests/gck-test-module.c | 1702 | ||||
-rw-r--r-- | gck/tests/gck-test.h | 46 | ||||
-rw-r--r-- | gck/tests/test-gck-attributes.c | 528 | ||||
-rw-r--r-- | gck/tests/test-gck-crypto.c | 595 | ||||
-rw-r--r-- | gck/tests/test-gck-mechanism.c | 60 | ||||
-rw-r--r-- | gck/tests/test-gck-module.c | 159 | ||||
-rw-r--r-- | gck/tests/test-gck-object.c | 463 | ||||
-rw-r--r-- | gck/tests/test-gck-session.c | 323 | ||||
-rw-r--r-- | gck/tests/test-gck-slot.c | 147 |
26 files changed, 14847 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am index ca70b917..88c56cea 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,6 +12,7 @@ endif SUBDIRS = \ . \ + gck \ gp11 \ egg \ gcr \ diff --git a/configure.in b/configure.in index e3a14cac..74739cd2 100644 --- a/configure.in +++ b/configure.in @@ -11,6 +11,10 @@ GP11_MAJOR=0 # Increment for major version number, breaks old apps. GP11_REVISION=0 # Increment for internal changes, nothing affected. GP11_AGE=0 # Increment for interface that doesn't break anything +GCK_MAJOR=0 # Increment for major version number, breaks old apps. +GCK_REVISION=0 # Increment for internal changes, nothing affected. +GCK_AGE=0 # Increment for interface that doesn't break anything + GCR_MAJOR=0 # Increment for major version number, breaks old apps. GCR_REVISION=0 # Increment for internal changes, nothing affected. GCR_AGE=0 # Increment for interface that doesn't break anything @@ -577,6 +581,10 @@ GP11_LT_RELEASE=$GP11_MAJOR:$GP11_REVISION:$GP11_AGE AC_SUBST(GP11_LT_RELEASE) AC_SUBST(GP11_MAJOR) +GCK_LT_RELEASE=$GCK_MAJOR:$GCK_REVISION:$GCK_AGE +AC_SUBST(GCK_LT_RELEASE) +AC_SUBST(GCK_MAJOR) + GCR_LT_RELEASE=$GCR_MAJOR:$GCR_REVISION:$GCR_AGE AC_SUBST(GCR_LT_RELEASE) AC_SUBST(GCR_MAJOR) @@ -624,6 +632,9 @@ docs/reference/gcr/Makefile docs/reference/gp11/Makefile egg/Makefile egg/tests/Makefile +gck/gck.pc +gck/Makefile +gck/tests/Makefile gcr/gcr.pc gcr/Makefile gcr/tests/Makefile diff --git a/gck/.gitignore b/gck/.gitignore new file mode 100644 index 00000000..4fbdec3a --- /dev/null +++ b/gck/.gitignore @@ -0,0 +1,2 @@ +/gck-*.pc +/gck.pc diff --git a/gck/Makefile.am b/gck/Makefile.am new file mode 100644 index 00000000..0173680c --- /dev/null +++ b/gck/Makefile.am @@ -0,0 +1,66 @@ +incdir = $(includedir)/gck + +inc_HEADERS = \ + gck.h + +INCLUDES = \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + $(GOBJECT_CFLAGS) \ + $(GTHREAD_CFLAGS) \ + $(GLIB_CFLAGS) + +BUILT_SOURCES = \ + gck-marshal.c gck-marshal.h + +lib_LTLIBRARIES = libgck.la + +libgck_la_SOURCES = \ + gck.h gck-private.h pkcs11.h \ + gck-attributes.c \ + gck-call.c \ + gck-misc.c \ + gck-module.c \ + gck-object.c \ + gck-session.c \ + gck-slot.c \ + $(BUILT_SOURCES) + +libgck_la_LDFLAGS = \ + -version-info $(GCK_LT_RELEASE) \ + -no-undefined -export-symbols-regex 'gck_*' + +libgck_la_LIBADD = \ + $(GOBJECT_LIBS) \ + $(GTHREAD_LIBS) \ + $(GIO_LIBS) \ + $(GLIB_LIBS) + +gck-marshal.h: gck-marshal.list $(GLIB_GENMARSHAL) + $(GLIB_GENMARSHAL) $< --header --prefix=_gck_marshal > $@ + +gck-marshal.c: gck-marshal.list $(GLIB_GENMARSHAL) + echo "#include \"gck-marshal.h\"" > $@ && \ + $(GLIB_GENMARSHAL) $< --body --prefix=_gck_marshal >> $@ + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = gck-$(GCK_MAJOR).pc + +EXTRA_DIST = \ + gck.pc.in \ + gck-marshal.list + +DISTCLEANFILES = \ + gck-$(GCK_MAJOR).pc + +gck-$(GCK_MAJOR).pc: gck.pc + cp gck.pc gck-$(GCK_MAJOR).pc + +if WITH_TESTS +TESTS_DIR = tests +else +TESTS_DIR = +endif + +SUBDIRS = . \ + $(TESTS_DIR) diff --git a/gck/gck-attributes.c b/gck/gck-attributes.c new file mode 100644 index 00000000..30250e89 --- /dev/null +++ b/gck/gck-attributes.c @@ -0,0 +1,1428 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* gck-attribute.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, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter <nielsen@memberwebs.com> +*/ + +#include "config.h" + +#include "gck.h" +#include "gck-private.h" + +#include <stdlib.h> +#include <string.h> + +/** + * SECTION:gck-attribute + * @title: GckAttribute + * @short_description: A PKCS11 attribute. + * + * This structure represents a PKCS11 CK_ATTRIBUTE. These attributes contain information + * about a PKCS11 object. Use gck_object_get() or gck_object_set() to set and retrieve + * attributes on an object. + */ + +/** + * GckAttribute: + * @type: The attribute type, such as CKA_LABEL. + * @value: The value of the attribute. May be NULL. + * @length: The length of the attribute. May be G_MAXULONG if the attribute is invalid. + * + * This structure represents a PKCS11 CK_ATTRIBUTE. + */ + +static void +attribute_init (GckAttribute *attr, gulong attr_type, + gconstpointer value, gsize length, + GckAllocator allocator) +{ + g_assert (sizeof (GckAttribute) == sizeof (CK_ATTRIBUTE)); + g_assert (allocator); + + memset (attr, 0, sizeof (GckAttribute)); + attr->type = attr_type; + attr->length = length; + if (value) { + attr->value = (allocator) (NULL, length ? length : 1); + g_assert (attr->value); + memcpy (attr->value, value, length); + } +} + +/** + * gck_attribute_init: + * @attr: An uninitialized attribute. + * @attr_type: The PKCS#11 attribute type to set on the attribute. + * @value: The raw value of the attribute. + * @length: The length of the raw value. + * + * Initialize a PKCS#11 attribute. This copies the value memory + * into an internal buffer. + * + * When done with the attribute you should use gck_attribute_clear() + * to free the internal memory. + **/ +void +gck_attribute_init (GckAttribute *attr, gulong attr_type, + gconstpointer value, gsize length) +{ + g_return_if_fail (attr); + attribute_init (attr, attr_type, value, length, g_realloc); +} + +/** + * gck_attribute_init_invalid: + * @attr: An uninitialized attribute. + * @attr_type: The PKCS#11 attribute type to set on the attribute. + * + * Initialize a PKCS#11 attribute to an 'invalid' or 'not found' + * state. Specifically this sets the value length to (CK_ULONG)-1 + * as specified in the PKCS#11 specification. + * + * When done with the attribute you should use gck_attribute_clear() + * to free the internal memory. + **/ +void +gck_attribute_init_invalid (GckAttribute *attr, gulong attr_type) +{ + g_return_if_fail (attr); + g_assert (sizeof (GckAttribute) == sizeof (CK_ATTRIBUTE)); + memset (attr, 0, sizeof (GckAttribute)); + attr->type = attr_type; + attr->length = (gulong)-1; +} + +/** + * gck_attribute_init_empty: + * @attr: An uninitialized attribute. + * @attr_type: The PKCS#11 attribute type to set on the attribute. + * + * Initialize a PKCS#11 attribute to an empty state. The attribute + * type will be set, but no data will be set. + * + * When done with the attribute you should use gck_attribute_clear() + * to free the internal memory. + **/ +void +gck_attribute_init_empty (GckAttribute *attr, gulong attr_type) +{ + g_return_if_fail (attr); + g_assert (sizeof (GckAttribute) == sizeof (CK_ATTRIBUTE)); + memset (attr, 0, sizeof (GckAttribute)); + attr->type = attr_type; + attr->length = 0; + attr->value = 0; +} + +static void +attribute_init_boolean (GckAttribute *attr, gulong attr_type, + gboolean value, GckAllocator allocator) +{ + CK_BBOOL bvalue = value ? CK_TRUE : CK_FALSE; + attribute_init (attr, attr_type, &bvalue, sizeof (bvalue), allocator); +} + +/** + * gck_attribute_init_boolean: + * @attr: An uninitialized attribute. + * @attr_type: The PKCS#11 attribute type to set on the attribute. + * @value: The boolean value of the attribute. + * + * Initialize a PKCS#11 attribute to boolean. This will result + * in a CK_BBOOL attribute from the PKCS#11 specs. + * + * When done with the attribute you should use gck_attribute_clear() + * to free the internal memory. + **/ +void +gck_attribute_init_boolean (GckAttribute *attr, gulong attr_type, + gboolean value) +{ + g_return_if_fail (attr); + attribute_init_boolean (attr, attr_type, value, g_realloc); +} + +static void +attribute_init_date (GckAttribute *attr, gulong attr_type, + const GDate *value, GckAllocator allocator) +{ + gchar buffer[9]; + CK_DATE date; + g_assert (value); + g_snprintf (buffer, sizeof (buffer), "%04d%02d%02d", + (int)g_date_get_year (value), + (int)g_date_get_month (value), + (int)g_date_get_day (value)); + memcpy (&date.year, buffer + 0, 4); + memcpy (&date.month, buffer + 4, 2); + memcpy (&date.day, buffer + 6, 2); + attribute_init (attr, attr_type, &date, sizeof (CK_DATE), allocator); +} + +/** + * gck_attribute_init_date: + * @attr: An uninitialized attribute. + * @attr_type: The PKCS#11 attribute type to set on the attribute. + * @value: The date value of the attribute. + * + * Initialize a PKCS#11 attribute to a date. This will result + * in a CK_DATE attribute from the PKCS#11 specs. + * + * When done with the attribute you should use gck_attribute_clear() + * to free the internal memory. + **/ +void +gck_attribute_init_date (GckAttribute *attr, gulong attr_type, + const GDate *value) +{ + g_return_if_fail (attr); + g_return_if_fail (value); + attribute_init_date (attr, attr_type, value, g_realloc); +} + +static void +attribute_init_ulong (GckAttribute *attr, gulong attr_type, + gulong value, GckAllocator allocator) +{ + CK_ULONG uvalue = value; + attribute_init (attr, attr_type, &uvalue, sizeof (uvalue), allocator); +} + +/** + * gck_attribute_init_ulong: + * @attr: An uninitialized attribute. + * @attr_type: The PKCS#11 attribute type to set on the attribute. + * @value: The ulong value of the attribute. + * + * Initialize a PKCS#11 attribute to a unsigned long. This will result + * in a CK_ULONG attribute from the PKCS#11 specs. + * + * When done with the attribute you should use gck_attribute_clear() + * to free the internal memory. + **/ +void +gck_attribute_init_ulong (GckAttribute *attr, gulong attr_type, + gulong value) +{ + g_return_if_fail (attr); + attribute_init_ulong (attr, attr_type, value, g_realloc); +} + +static void +attribute_init_string (GckAttribute *attr, gulong attr_type, + const gchar *value, GckAllocator allocator) +{ + gsize len = value ? strlen (value) : 0; + attribute_init (attr, attr_type, (gpointer)value, len, allocator); +} + +/** + * gck_attribute_init_string: + * @attr: An uninitialized attribute. + * @attr_type: The PKCS#11 attribute type to set on the attribute. + * @value: The null terminated string value of the attribute. + * + * Initialize a PKCS#11 attribute to a string. This will result + * in an attribute containing the text, but not the null terminator. + * The text in the attribute will be of the same encoding as you pass + * to this function. + * + * When done with the attribute you should use gck_attribute_clear() + * to free the internal memory. + **/ +void +gck_attribute_init_string (GckAttribute *attr, gulong attr_type, + const gchar *value) +{ + g_return_if_fail (attr); + attribute_init_string (attr, attr_type, value, g_realloc); +} + +/** + * gck_attribute_new: + * @attr_type: The PKCS#11 attribute type to set on the attribute. + * @value: The raw value of the attribute. + * @length: The length of the attribute. + * + * Create a new PKCS#11 attribute. The value will be copied + * into the new attribute. + * + * Return value: The new attribute. When done with the attribute use + * gck_attribute_free() to free it. + **/ +GckAttribute* +gck_attribute_new (gulong attr_type, gpointer value, gsize length) +{ + GckAttribute *attr = g_slice_new0 (GckAttribute); + attribute_init (attr, attr_type, value, length, g_realloc); + return attr; +} + +/** + * gck_attribute_new_invalid: + * @attr_type: The PKCS#11 attribute type to set on the attribute. + * + * Create a new PKCS#11 attribute as 'invalid' or 'not found' + * state. Specifically this sets the value length to (CK_ULONG)-1 + * as specified in the PKCS#11 specification. + * + * Return value: The new attribute. When done with the attribute use + * gck_attribute_free() to free it. + **/ +GckAttribute* +gck_attribute_new_invalid (gulong attr_type) +{ + GckAttribute *attr = g_slice_new0 (GckAttribute); + gck_attribute_init_invalid (attr, attr_type); + return attr; +} + +/** + * gck_attribute_new_empty: + * @attr_type: The PKCS#11 attribute type to set on the attribute. + * + * Create a new PKCS#11 attribute with empty data. + * + * Return value: The new attribute. When done with the attribute use + * gck_attribute_free() to free it. + */ +GckAttribute* +gck_attribute_new_empty (gulong attr_type) +{ + GckAttribute *attr = g_slice_new0 (GckAttribute); + gck_attribute_init_empty (attr, attr_type); + return attr; +} + +/** + * gck_attribute_new_boolean: + * @attr_type: The PKCS#11 attribute type to set on the attribute. + * @value: The boolean value of the attribute. + * + * Initialize a PKCS#11 attribute to boolean. This will result + * in a CK_BBOOL attribute from the PKCS#11 specs. + * + * Return value: The new attribute. When done with the attribute use + * gck_attribute_free() to free it. + **/ +GckAttribute* +gck_attribute_new_boolean (gulong attr_type, gboolean value) +{ + GckAttribute *attr = g_slice_new0 (GckAttribute); + attribute_init_boolean (attr, attr_type, value, g_realloc); + return attr; +} + +/** + * gck_attribute_new_date: + * @attr_type: The PKCS#11 attribute type to set on the attribute. + * @value: The date value of the attribute. + * + * Initialize a PKCS#11 attribute to a date. This will result + * in a CK_DATE attribute from the PKCS#11 specs. + * + * Return value: The new attribute. When done with the attribute use + * gck_attribute_free() to free it. + **/ +GckAttribute* +gck_attribute_new_date (gulong attr_type, const GDate *value) +{ + GckAttribute *attr = g_slice_new0 (GckAttribute); + attribute_init_date (attr, attr_type, value, g_realloc); + return attr; +} + +/** + * gck_attribute_new_ulong: + * @attr_type: The PKCS#11 attribute type to set on the attribute. + * @value: The ulong value of the attribute. + * + * Initialize a PKCS#11 attribute to a unsigned long. This will result + * in a CK_ULONG attribute from the PKCS#11 specs. + * + * Return value: The new attribute. When done with the attribute use + * gck_attribute_free() to free it. + **/ +GckAttribute* +gck_attribute_new_ulong (gulong attr_type, gulong value) +{ + GckAttribute *attr = g_slice_new0 (GckAttribute); + attribute_init_ulong (attr, attr_type, value, g_realloc); + return attr; +} + +/** + * gck_attribute_new_string: + * @attr_type: The PKCS#11 attribute type to set on the attribute. + * @value: The null terminated string value of the attribute. + * + * Initialize a PKCS#11 attribute to a string. This will result + * in an attribute containing the text, but not the null terminator. + * The text in the attribute will be of the same encoding as you pass + * to this function. + * + * Return value: The new attribute. When done with the attribute use + * gck_attribute_free() to free it. + **/ +GckAttribute* +gck_attribute_new_string (gulong attr_type, const gchar *value) +{ + GckAttribute *attr = g_slice_new0 (GckAttribute); + attribute_init_string (attr, attr_type, value, g_realloc); + return attr; +} + +/** + * gck_attribute_is_invalid: + * @attr: The attribute to check. + * + * Check if the PKCS#11 attribute represents 'invalid' or 'not found' + * according to the PKCS#11 spec. That is, having length + * of (CK_ULONG)-1. + * + * Return value: Whether the attribute represents invalid or not. + */ +gboolean +gck_attribute_is_invalid (GckAttribute *attr) +{ + g_return_val_if_fail (attr, TRUE); + return attr->length == (gulong)-1; +} + +/** + * gck_attribute_get_boolean: + * @attr: The attribute to retrieve value from. + * + * Get the CK_BBOOL of a PKCS#11 attribute. No conversion + * is performed. It is an error to pass an attribute to this + * function unless you're know it's supposed to contain a + * boolean value. + * + * Return value: The boolean value of the attribute. + */ +gboolean +gck_attribute_get_boolean (GckAttribute *attr) +{ + g_return_val_if_fail (attr, FALSE); + if (gck_attribute_is_invalid (attr)) + return FALSE; + g_return_val_if_fail (attr->length == sizeof (CK_BBOOL), FALSE); + g_return_val_if_fail (attr->value, FALSE); + return *((CK_BBOOL*)attr->value) == CK_TRUE ? TRUE : FALSE; +} + +/** + * gck_attribute_get_ulong: + * @attr: The attribute to retrieve value from. + * + * Get the CK_ULONG value of a PKCS#11 attribute. No + * conversion is performed. It is an error to pass an attribute + * to this function unless you're know it's supposed to contain + * a value of the right type. + * + * Return value: The ulong value of the attribute. + */ +gulong +gck_attribute_get_ulong (GckAttribute *attr) +{ + g_return_val_if_fail (attr, FALSE); + if (gck_attribute_is_invalid (attr)) + return 0; + g_return_val_if_fail (attr->length == sizeof (CK_ULONG), (gulong)-1); + g_return_val_if_fail (attr->value, (gulong)-1); + return *((CK_ULONG*)attr->value); +} + +/** + * gck_attribute_get_string: + * @attr: The attribute to retrieve value from. + * + * Get the string value of a PKCS#11 attribute. No + * conversion is performed. It is an error to pass an attribute + * to this function unless you're know it's supposed to contain + * a value of the right type. + * + * Return value: A null terminated string, to be freed with g_free(), + * or NULL if the value contained a NULL string. + */ +gchar* +gck_attribute_get_string (GckAttribute *attr) +{ + g_return_val_if_fail (attr, NULL); + + if (gck_attribute_is_invalid (attr)) + return NULL; + if (!attr->value) + return NULL; + + return g_strndup ((gchar*)attr->value, attr->length); +} + +/** + * gck_attribute_get_date: + * @attr: The attribute to retrieve value from. + * @value: The date value to fill in with the parsed date. + * + * Get the CK_DATE of a PKCS#11 attribute. No + * conversion is performed. It is an error to pass an attribute + * to this function unless you're know it's supposed to contain + * a value of the right type. + */ +void +gck_attribute_get_date (GckAttribute *attr, GDate *value) +{ + guint year, month, day; + gchar buffer[5]; + CK_DATE *date; + gchar *end; + + g_return_if_fail (attr); + + if (gck_attribute_is_invalid (attr)) { + g_date_clear (value, 1); + return; + } + + g_return_if_fail (attr->length == sizeof (CK_DATE)); + g_return_if_fail (attr->value); + date = (CK_DATE*)attr->value; + + memset (&buffer, 0, sizeof (buffer)); + memcpy (buffer, date->year, 4); + year = strtol (buffer, &end, 10); + g_return_if_fail (end != buffer && !*end); + + memset (&buffer, 0, sizeof (buffer)); + memcpy (buffer, date->month, 2); + month = strtol (buffer, &end, 10); + g_return_if_fail (end != buffer && !*end); + + memset (&buffer, 0, sizeof (buffer)); + memcpy (buffer, date->day, 2); + day = strtol (buffer, &end, 10); + g_return_if_fail (end != buffer && !*end); + + g_date_set_dmy (value, day, month, year); +} + +/** + * gck_attribute_dup: + * @attr: The attribute to duplicate. + * + * Duplicate the PKCS#11 attribute. All value memory is + * also copied. + * + * Return value: The duplicated attribute. Use gck_attribute_free() + * to free it. + */ +GckAttribute* +gck_attribute_dup (GckAttribute *attr) +{ + GckAttribute *copy; + + if (!attr) + return NULL; + + copy = g_slice_new0 (GckAttribute); + gck_attribute_init_copy (copy, attr); + return copy; +} + +static void +attribute_init_copy (GckAttribute *dest, const GckAttribute *src, GckAllocator allocator) +{ + g_assert (dest); + g_assert (src); + g_assert (allocator); + + /* + * TODO: Handle stupid, dumb, broken, special cases like + * CKA_WRAP_TEMPLATE and CKA_UNWRAP_TEMPLATE. + */ + + memcpy (dest, src, sizeof (GckAttribute)); + if (src->value && src->length) { + dest->value = (allocator) (NULL, src->length); + g_assert (dest->value); + memcpy (dest->value, src->value, src->length); + } +} + +/** + * gck_attribute_init_copy: + * @dest: An uninitialized attribute. + * @src: An attribute to copy. + * + * Initialize a PKCS#11 attribute as a copy of another attribute. + * This copies the value memory as well. + * + * When done with the copied attribute you should use + * gck_attribute_clear() to free the internal memory. + **/ +void +gck_attribute_init_copy (GckAttribute *dest, const GckAttribute *src) +{ + g_return_if_fail (dest); + g_return_if_fail (src); + attribute_init_copy (dest, src, g_realloc); +} + +static void +attribute_clear (GckAttribute *attr, GckAllocator allocator) +{ + g_assert (attr); + g_assert (allocator); + if (attr->value) + (allocator) (attr->value, 0); + attr->value = NULL; + attr->length = 0; +} + +/** + * gck_attribute_clear: + * @attr: Attribute to clear. + * + * Clear allocated memory held by a statically allocated attribute. + * These are usually initialized with gck_attribute_init() or a + * similar function. + * + * The type of the attribute will remain set. + **/ +void +gck_attribute_clear (GckAttribute *attr) +{ + g_return_if_fail (attr); + attribute_clear (attr, g_realloc); +} + +/** + * gck_attribute_free: + * @attr: Attribute to free. + * + * Free an attribute and its allocated memory. These is usually + * used with attributes that are allocated by gck_attribute_new() + * or a similar function. + **/ +void +gck_attribute_free (GckAttribute *attr) +{ + if (attr) { + attribute_clear (attr, g_realloc); + g_slice_free (GckAttribute, attr); + } +} + +/** + * SECTION:gck-attributes + * @title: GckAttributes + * @short_description: A set of PKCS11 attributes. + * + * A set of GckAttribute structures. These attributes contain information + * about a PKCS11 object. Use gck_object_get() or gck_object_set() to set and retrieve + * attributes on an object. + */ + +/** + * GckAttributes: + * + * A set of GckAttribute structures. + */ +struct _GckAttributes { + GArray *array; + GckAllocator allocator; + gboolean locked; + gint refs; +}; + +/** + * gck_BOOLEAN: + * + * The attribute data is a gboolean. Used with variable argument functions. + **/ + +/** + * gck_ULONG: + * + * The attribute data is a gulong. Used with variable argument functions. + **/ + +/** + * gck_STRING: + * + * The attribute data is a gchar. Used with variable argument functions. + **/ + +/** + * gck_DATE: + * + * The attribute data is a GDate. Used with variable argument functions. + **/ + +/** + * gck_DATE: + * + * Signifies that no more attributes follow. Used with variable argument functions. + **/ + +/** + * gck_ATTRIBUTES_TYPE: + * + * A boxed type that can be used to hold a GckAttributes object. + **/ + +/** + * GckAllocator: + * @data: Memory to allocate or deallocate. + * @length: New length of memory. + * + * An allocator used to allocate data for the attributes in this GckAttributes set. + * + * This is a function that acts like g_realloc. Specifically it frees when length is + * set to zero, it allocates when data is set to NULL, and it reallocates when both + * are valid. + * + * Returns: The allocated memory, or NULL when freeing. + **/ + +/** + * gck_attributes_get_boxed_type: + * + * Get the boxed type representing a GckAttributes array. + * + * Return value: The boxed type. + **/ +GType +gck_attributes_get_boxed_type (void) +{ + static GType type = 0; + if (!type) + type = g_boxed_type_register_static ("GckAttributes", + (GBoxedCopyFunc)gck_attributes_ref, + (GBoxedFreeFunc)gck_attributes_unref); + return type; +} + +/** + * gck_attributes_new: + * + * Create a new GckAttributes array. + * + * Return value: The new attributes array. When done with the array + * release it with gck_attributes_unref(). + **/ +GckAttributes* +gck_attributes_new (void) +{ + return gck_attributes_new_full (g_realloc); +} + +/** + * gck_attributes_new_full: + * @allocator: Memory allocator for attribute data, or NULL for default. + * + * Create a new GckAttributes array. + * + * Return value: The new attributes array. When done with the array + * release it with gck_attributes_unref(). + **/ +GckAttributes* +gck_attributes_new_full (GckAllocator allocator) +{ + GckAttributes *attrs; + + if (!allocator) + allocator = g_realloc; + + g_assert (sizeof (GckAttribute) == sizeof (CK_ATTRIBUTE)); + attrs = g_slice_new0 (GckAttributes); + attrs->array = g_array_new (0, 1, sizeof (GckAttribute)); + attrs->allocator = allocator; + attrs->refs = 1; + attrs->locked = FALSE; + return attrs; +} + +/** + * gck_attributes_new_empty: + * @attr_type: The first attribute type to add as empty. + * @...: The arguments should be values of attribute types, terminated with gck_INVALID. + * + * Creates an GckAttributes array with empty attributes. The arguments + * should be values of attribute types, terminated with gck_INVALID. + * + * Return value: The new attributes array. When done with the array + * release it with gck_attributes_unref(). + **/ +GckAttributes* +gck_attributes_new_empty (gulong attr_type, ...) +{ + GckAttributes *attrs = gck_attributes_new_full (g_realloc); + va_list va; + + va_start (va, attr_type); + + while (attr_type != GCK_INVALID) { + gck_attributes_add_empty (attrs, attr_type); + attr_type = va_arg (va, gulong); + } + + va_end (va); + + return attrs; +} + +static GckAttributes* +initialize_from_valist (GckAllocator allocator, gulong type, va_list va) +{ + GckAttributes *attrs; + gssize length; + gpointer value; + + g_assert (allocator); + + attrs = gck_attributes_new_full (allocator); + + /* No attributes */ + if (type == GCK_INVALID) + return attrs; + + do { + length = va_arg (va, gssize); + + /* All the different set types */ + switch (length) { + case GCK_BOOLEAN: + gck_attributes_add_boolean (attrs, type, va_arg (va, gboolean)); + break; + case GCK_ULONG: + gck_attributes_add_ulong (attrs, type, va_arg (va, gulong)); + break; + case GCK_STRING: + gck_attributes_add_string (attrs, type, va_arg (va, const gchar*)); + break; + case GCK_DATE: + gck_attributes_add_date (attrs, type, va_arg (va, const GDate*)); + break; + + /* Otherwise it should be data */ + default: + value = va_arg (va, gpointer); + + /* But not this long */ + if (length < 0 || length >= G_MAXSSIZE) + g_warning ("length passed to attributes varargs is invalid or too large: %d", (int)length); + else + gck_attributes_add_data (attrs, type, value, length); + break; + }; + + type = va_arg (va, gulong); + + } while (type != GCK_INVALID); + + return attrs; +} + +/** + * gck_attributes_newv: + * @attr_type: The first attribute type. + * @...: The arguments must be triples of: attribute type, data type, value + * + * Create a new GckAttributes array, containing a list of attributes. + * + * <para>The variable argument list should contain: + * <variablelist> + * <varlistentry> + * <term>a)</term> + * <listitem><para>The gulong attribute type (ie: CKA_LABEL). </para></listitem> + * </varlistentry> + * <varlistentry> + * <term>b)</term> + * <listitem><para>The attribute data type (one of gck_BOOLEAN, gck_ULONG, + * gck_STRING, gck_DATE) orthe raw attribute value length.</para></listitem> + * </varlistentry> + * <varlistentry> + * <term>c)</term> + * <listitem><para>The attribute value, either a gboolean, gulong, gchar*, GDate* or + * a pointer to a raw attribute value.</para></listitem> + * </varlistentry> + * </variablelist> + * The variable argument list should be terminated with gck_INVALID.</para> + * + * Return value: The new attributes array. When done with the array + * release it with gck_attributes_unref(). + **/ +GckAttributes* +gck_attributes_newv (gulong attr_type, ...) +{ + GckAttributes *attrs; + va_list va; + + va_start (va, attr_type); + attrs = initialize_from_valist (g_realloc, attr_type, va); + va_end (va); + + return attrs; +} + +/** + * gck_attributes_new_valist: + * @allocator: Memory allocator for attribute data, or NULL for default. + * @va: Variable argument containing attributes to add. + * + * Create a new GckAttributes array. + * + * The arguments must be triples of: attribute type, data type, value + * + * <para>The variable argument list should contain: + * <variablelist> + * <varlistentry> + * <term>a)</term> + * <listitem><para>The gulong attribute type (ie: CKA_LABEL). </para></listitem> + * </varlistentry> + * <varlistentry> + * <term>b)</term> + * <listitem><para>The attribute data type (one of gck_BOOLEAN, gck_ULONG, + * gck_STRING, gck_DATE) orthe raw attribute value length.</para></listitem> + * </varlistentry> + * <varlistentry> + * <term>c)</term> + * <listitem><para>The attribute value, either a gboolean, gulong, gchar*, GDate* or + * a pointer to a raw attribute value.</para></listitem> + * </varlistentry> + * </variablelist> + * The variable argument list should be terminated with gck_INVALID.</para> + * + * Return value: The new attributes array. When done with the array + * release it with gck_attributes_unref(). + **/ +GckAttributes* +gck_attributes_new_valist (GckAllocator allocator, va_list va) +{ + gulong type = va_arg (va, gulong); + + if (!allocator) + allocator = g_realloc; + + return initialize_from_valist (allocator, type, va); +} + +/** + * gck_attributes_at: + * @attrs: The attributes array. + * @index: The attribute index to retrieve. + * + * Get attribute at the specified index in the attribute array. + * + * Use gck_attributes_count() to determine how many attributes are + * in the array. + * + * Return value: The specified attribute. + **/ +GckAttribute* +gck_attributes_at (GckAttributes *attrs, guint index) +{ + g_return_val_if_fail (attrs && attrs->array, NULL); + g_return_val_if_fail (index < attrs->array->len, NULL); + g_return_val_if_fail (!attrs->locked, NULL); + return &g_array_index (attrs->array, GckAttribute, index); +} + +static GckAttribute* +attributes_push (GckAttributes *attrs) +{ + GckAttribute attr; + g_assert (!attrs->locked); + memset (&attr, 0, sizeof (attr)); + g_array_append_val (attrs->array, attr); + return &g_array_index (attrs->array, GckAttribute, attrs->array->len - 1); +} + +/** + * gck_attributes_add: + * @attrs: The attributes array to add to + * @attr: The attribute to add. + * + * Add the specified attribute to the array. + * + * The value stored in the attribute will be copied. + * + * Return value: The attribute that was added. + **/ +GckAttribute* +gck_attributes_add (GckAttributes *attrs, GckAttribute *attr) +{ + GckAttribute *added; + g_return_val_if_fail (attrs && attrs->array, NULL); + g_return_val_if_fail (!attrs->locked, NULL); + g_return_val_if_fail (attr, NULL); + added = attributes_push (attrs); + attribute_init_copy (added, attr, attrs->allocator); + return added; +} + +/** + * gck_attributes_add_data: + * @attrs: The attributes array to add to. + * @attr_type: The type of attribute to add. + * @value: The raw memory of the attribute value. + * @length: The length of the attribute value. + * + * Add an attribute with the specified type and value to the array. + * + * The value stored in the attribute will be copied. + * + * Return value: The attribute that was added. + **/ +GckAttribute* +gck_attributes_add_data (GckAttributes *attrs, gulong attr_type, + gconstpointer value, gsize length) +{ + GckAttribute *added; + g_return_val_if_fail (attrs, NULL); + g_return_val_if_fail (!attrs->locked, NULL); + added = attributes_push (attrs); + attribute_init (added, attr_type, value, length, attrs->allocator); + return added; +} + +/** + * gck_attributes_add_invalid: + * @attrs: The attributes array to add to. + * @attr_type: The type of attribute to add. + * + * Add an attribute with the specified type and an 'invalid' value to the array. + * + * Return value: The attribute that was added. + **/ +GckAttribute* +gck_attributes_add_invalid (GckAttributes *attrs, gulong attr_type) +{ + GckAttribute *added; + g_return_val_if_fail (attrs, NULL); + g_return_val_if_fail (!attrs->locked, NULL); + added = attributes_push (attrs); + gck_attribute_init_invalid (added, attr_type); + return added; +} + +/** + * gck_attributes_add_empty: + * @attrs: The attributes array to add. + * @attr_type: The type of attribute to add. + * + * Add an attribute with the specified type, with empty data. + * + * Return value: The attribute that was added. + **/ +GckAttribute* +gck_attributes_add_empty (GckAttributes *attrs, gulong attr_type) +{ + GckAttribute *added; + g_return_val_if_fail (attrs, NULL); + g_return_val_if_fail (!attrs->locked, NULL); + added = attributes_push (attrs); + gck_attribute_init_empty (added, attr_type); + return added; +} + +/** + * gck_attributes_add_boolean: + * @attrs: The attributes array to add to. + * @attr_type: The type of attribute to add. + * @value: The boolean value to add. + * + * Add an attribute with the specified type and value to the array. + * + * The value will be stored as a CK_BBOOL PKCS#11 style attribute. + * + * Return value: The attribute that was added. + **/ +GckAttribute* +gck_attributes_add_boolean (GckAttributes *attrs, gulong attr_type, gboolean value) +{ + GckAttribute *added; + g_return_val_if_fail (attrs, NULL); + g_return_val_if_fail (!attrs->locked, NULL); + added = attributes_push (attrs); + attribute_init_boolean (added, attr_type, value, attrs->allocator); + return added; +} + +/** + * gck_attributes_add_string: + * @attrs: The attributes array to add to. + * @attr_type: The type of attribute to add. + * @value: The null terminated string value to add. + * + * Add an attribute with the specified type and value to the array. + * + * The value will be copied into the attribute. + * + * Return value: The attribute that was added. + **/ +GckAttribute* +gck_attributes_add_string (GckAttributes *attrs, gulong attr_type, const gchar *value) +{ + GckAttribute *added; + g_return_val_if_fail (attrs, NULL); + g_return_val_if_fail (!attrs->locked, NULL); + added = attributes_push (attrs); + attribute_init_string (added, attr_type, value, attrs->allocator); + return added; +} + +/** + * gck_attributes_add_date: + * @attrs: The attributes array to add to. + * @attr_type: The type of attribute to add. + * @value: The GDate value to add. + * + * Add an attribute with the specified type and value to the array. + * + * The value will be stored as a CK_DATE PKCS#11 style attribute. + * + * Return value: The attribute that was added. + **/ +GckAttribute* +gck_attributes_add_date (GckAttributes *attrs, gulong attr_type, const GDate *value) +{ + GckAttribute *added; + g_return_val_if_fail (attrs, NULL); + g_return_val_if_fail (!attrs->locked, NULL); + added = attributes_push (attrs); + attribute_init_date (added, attr_type, value, attrs->allocator); + return added; +} + +/** + * gck_attributes_add_ulong: + * @attrs: The attributes array to add to. + * @attr_type: The type of attribute to add. + * @value: The gulong value to add. + * + * Add an attribute with the specified type and value to the array. + * + * The value will be stored as a CK_ULONG PKCS#11 style attribute. + * + * Return value: The attribute that was added. + **/ +GckAttribute* +gck_attributes_add_ulong (GckAttributes *attrs, gulong attr_type, gulong value) +{ + GckAttribute *added; + g_return_val_if_fail (attrs, NULL); + g_return_val_if_fail (!attrs->locked, NULL); + added = attributes_push (attrs); + attribute_init_ulong (added, attr_type, value, attrs->allocator); + return added; +} + +/** + * gck_attributes_count: + * @attrs: The attributes array to count. + * + * Get the number of attributes in this attribute array. + * + * Return value: The number of contained attributes. + **/ +gulong +gck_attributes_count (GckAttributes *attrs) +{ + g_return_val_if_fail (attrs, 0); + g_return_val_if_fail (!attrs->locked, 0); + return attrs->array->len; +} + +/** + * gck_attributes_find: + * @attrs: The attributes array to search. + * @attr_type: The type of attribute to find. + * + * Find an attribute with the specified type in the array. + * + * Return value: The first attribute found with the specified type, or NULL. + **/ +GckAttribute* +gck_attributes_find (GckAttributes *attrs, gulong attr_type) +{ + GckAttribute *attr; + guint i; + + g_return_val_if_fail (attrs && attrs->array, NULL); + g_return_val_if_fail (!attrs->locked, NULL); + + for (i = 0; i < attrs->array->len; ++i) { + attr = gck_attributes_at (attrs, i); + if (attr->type == attr_type) + return attr; + } + + return NULL; +} + +/** + * gck_attributes_find_boolean: + * @attrs: The attributes array to search. + * @attr_type: The type of attribute to find. + * @value: The resulting gboolean value. + * + * Find an attribute with the specified type in the array. + * + * The attribute (if found) must be of the right size to store + * a boolean value (ie: CK_BBOOL). If the attribute is marked invalid + * then it will be treated as not found. + * + * Return value: Whether a value was found or not. + **/ +gboolean +gck_attributes_find_boolean (GckAttributes *attrs, gulong attr_type, gboolean *value) +{ + GckAttribute *attr; + + g_return_val_if_fail (value, FALSE); + g_return_val_if_fail (!attrs->locked, FALSE); + + attr = gck_attributes_find (attrs, attr_type); + if (!attr || gck_attribute_is_invalid (attr)) + return FALSE; + *value = gck_attribute_get_boolean (attr); + return TRUE; +} + +/** + * gck_attributes_find_ulong: + * @attrs: The attributes array to search. + * @attr_type: The type of attribute to find. + * @value: The resulting gulong value. + * + * Find an attribute with the specified type in the array. + * + * The attribute (if found) must be of the right size to store + * a unsigned long value (ie: CK_ULONG). If the attribute is marked invalid + * then it will be treated as not found. + * + * Return value: Whether a value was found or not. + **/ +gboolean +gck_attributes_find_ulong (GckAttributes *attrs, gulong attr_type, gulong *value) +{ + GckAttribute *attr; + + g_return_val_if_fail (value, FALSE); + g_return_val_if_fail (!attrs->locked, FALSE); + + attr = gck_attributes_find (attrs, attr_type); + if (!attr || gck_attribute_is_invalid (attr)) + return FALSE; + *value = gck_attribute_get_ulong (attr); + return TRUE; +} + +/** + * gck_attributes_find_string: + * @attrs: The attributes array to search. + * @attr_type: The type of attribute to find. + * @value: The resulting string value. + * + * Find an attribute with the specified type in the array. + * + * If the attribute is marked invalid then it will be treated as not found. + * The resulting string will be null-terminated, and must be freed by the caller + * using g_free(). + * + * Return value: Whether a value was found or not. + **/ +gboolean +gck_attributes_find_string (GckAttributes *attrs, gulong attr_type, gchar **value) +{ + GckAttribute *attr; + + g_return_val_if_fail (value, FALSE); + g_return_val_if_fail (!attrs->locked, FALSE); + + attr = gck_attributes_find (attrs, attr_type); + if (!attr || gck_attribute_is_invalid (attr)) + return FALSE; + *value = gck_attribute_get_string (attr); + return TRUE; +} + +/** + * gck_attributes_find_date: + * @attrs: The attributes array to search. + * @attr_type: The type of attribute to find. + * @value: The resulting GDate value. + * + * Find an attribute with the specified type in the array. + * + * The attribute (if found) must be of the right size to store + * a date value (ie: CK_DATE). If the attribute is marked invalid + * then it will be treated as not found. + * + * Return value: Whether a value was found or not. + **/ +gboolean +gck_attributes_find_date (GckAttributes *attrs, gulong attr_type, GDate *value) +{ + GckAttribute *attr; + + g_return_val_if_fail (value, FALSE); + g_return_val_if_fail (!attrs->locked, FALSE); + + attr = gck_attributes_find (attrs, attr_type); + if (!attr || gck_attribute_is_invalid (attr)) + return FALSE; + gck_attribute_get_date (attr, value); + return TRUE; +} + +/** + * gck_attributes_ref: + * @attrs: An attribute array + * + * Reference this attributes array. + * + * Returns: The attributes. + **/ +GckAttributes* +gck_attributes_ref (GckAttributes *attrs) +{ + g_return_val_if_fail (attrs, NULL); + g_atomic_int_inc (&attrs->refs); + return attrs; +} + +/** + * gck_attributes_unref: + * @attrs: An attribute array + * + * Unreference this attribute array. + * + * When all outstanding references are NULL, the array will be freed. + */ +void +gck_attributes_unref (GckAttributes *attrs) +{ + guint i; + + if (!attrs) + return; + + if (g_atomic_int_dec_and_test (&attrs->refs)) { + g_return_if_fail (attrs->array); + g_return_if_fail (!attrs->locked); + for (i = 0; i < attrs->array->len; ++i) + attribute_clear (gck_attributes_at (attrs, i), attrs->allocator); + g_array_free (attrs->array, TRUE); + attrs->array = NULL; + g_slice_free (GckAttributes, attrs); + } +} + +/* ------------------------------------------------------------------------------------------- + * INTERNAL + * + * The idea is that while we're processing a GckAttributes array (via PKCS#11 + * C_GetAtributeValue for example) the calling application shouldn't access those + * attributes at all, except to ref or unref them. + * + * We try to help debug this with our 'locked' states. The various processing + * functions that accept GckAttributes lock the attributes while handing + * them off to be processed (perhaps in a different thread). We check this locked + * flag in all public functions accessing GckAttributes. + * + * The reason we don't use thread safe or atomic primitives here, is because: + * a) The attributes are 'locked' by the same thread that prepares the call. + * b) This is a debugging feature, and should not be relied on for correctness. + */ + +void +_gck_attributes_lock (GckAttributes *attrs) +{ + g_assert (attrs); + g_assert (!attrs->locked); + attrs->locked = TRUE; +} + +void +_gck_attributes_unlock (GckAttributes *attrs) +{ + g_assert (attrs); + g_assert (attrs->locked); + attrs->locked = FALSE; +} + +CK_ATTRIBUTE_PTR +_gck_attributes_prepare_in (GckAttributes *attrs, CK_ULONG_PTR n_attrs) +{ + GckAttribute *attr; + guint i; + + g_assert (attrs); + g_assert (n_attrs); + g_assert (attrs->locked); + + /* Prepare the attributes to receive their length */ + + for (i = 0; i < attrs->array->len; ++i) { + attr = &g_array_index (attrs->array, GckAttribute, i); + attribute_clear (attr, attrs->allocator); + } + + *n_attrs = attrs->array->len; + return (CK_ATTRIBUTE_PTR)attrs->array->data; +} + +CK_ATTRIBUTE_PTR +_gck_attributes_commit_in (GckAttributes *attrs, CK_ULONG_PTR n_attrs) +{ + GckAttribute *attr; + guint i; + + g_assert (attrs); + g_assert (n_attrs); + g_assert (attrs->locked); + + /* Allocate each attribute with the length that was set */ + + for (i = 0; i < attrs->array->len; ++i) { + attr = &g_array_index (attrs->array, GckAttribute, i); + g_assert (!attr->value); + if (attr->length != 0 && attr->length != (gulong)-1) { + attr->value = (attrs->allocator) (NULL, attr->length); + g_assert (attr->value); + } + } + + *n_attrs = attrs->array->len; + return (CK_ATTRIBUTE_PTR)attrs->array->data; +} + +CK_ATTRIBUTE_PTR +_gck_attributes_commit_out (GckAttributes *attrs, CK_ULONG_PTR n_attrs) +{ + g_assert (attrs); + g_assert (n_attrs); + g_assert (attrs->locked); + + *n_attrs = attrs->array->len; + return (CK_ATTRIBUTE_PTR)attrs->array->data; +} diff --git a/gck/gck-call.c b/gck/gck-call.c new file mode 100644 index 00000000..939d81bb --- /dev/null +++ b/gck/gck-call.c @@ -0,0 +1,541 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* gck-call.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, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter <nielsen@memberwebs.com> +*/ + +#include "config.h" + +#include "gck-private.h" + +#include <string.h> + +typedef struct _GckCallSource GckCallSource; + +static gpointer _gck_call_parent_class = NULL; + +struct _GckCall { + GObject parent; + GckModule *module; + + /* For making the call */ + GckPerformFunc perform; + GckCompleteFunc complete; + GckArguments *args; + GCancellable *cancellable; + GDestroyNotify destroy; + CK_RV rv; + + /* For result callback only */ + gpointer object; + GAsyncReadyCallback callback; + gpointer user_data; +}; + +struct _GckCallClass { + GObjectClass parent; + GThreadPool *thread_pool; + GAsyncQueue *completed_queue; + guint completed_id; +}; + +struct _GckCallSource { + GSource source; + GckCallClass *klass; +}; + +/* ---------------------------------------------------------------------------- + * HELPER FUNCTIONS + */ + +static CK_RV +perform_call (GckPerformFunc func, GCancellable *cancellable, GckArguments *args) +{ + CK_RV rv; + + /* Double check a few things */ + g_assert (func); + g_assert (args); + + if (cancellable) { + if (g_cancellable_is_cancelled (cancellable)) { + return CKR_FUNCTION_CANCELED; + } + + /* Push for the notify callback */ + g_object_ref (cancellable); + g_cancellable_push_current (cancellable); + } + + rv = (func) (args); + + if (cancellable) { + g_cancellable_pop_current (cancellable); + g_object_unref (cancellable); + } + + return rv; +} + +static gboolean +complete_call (GckCompleteFunc func, GckArguments *args, CK_RV result) +{ + /* Double check a few things */ + g_assert (args); + + /* If no complete function, then just ignore */ + if (!func) + return TRUE; + + return (func) (args, result); +} + + +static void +process_async_call (gpointer data, GckCallClass *klass) +{ + GckCall *call = GCK_CALL (data); + + g_assert (GCK_IS_CALL (call)); + + call->rv = perform_call (call->perform, call->cancellable, call->args); + + g_async_queue_push (klass->completed_queue, call); + + /* Wakeup main thread if on a separate thread */ + g_main_context_wakeup (NULL); +} + +static void +process_result (GckCall *call, gpointer unused) +{ + gboolean stop = FALSE; + + /* Double check a few things */ + g_assert (GCK_IS_CALL (call)); + + if (call->cancellable) { + /* Don't call the callback when cancelled */ + if (g_cancellable_is_cancelled (call->cancellable)) { + call->rv = CKR_FUNCTION_CANCELED; + stop = TRUE; + } + } + + /* + * Hmmm, does the function want to actually be done? + * If not, then queue this call again. + */ + if (!stop && !complete_call (call->complete, call->args, call->rv)) { + g_object_ref (call); + g_thread_pool_push (GCK_CALL_GET_CLASS (call)->thread_pool, call, NULL); + + /* All done, finish processing */ + } else if (call->callback) { + g_assert (G_IS_OBJECT (call->object)); + (call->callback) (G_OBJECT (call->object), G_ASYNC_RESULT (call), + call->user_data); + } +} + +static gboolean +process_completed (GckCallClass *klass) +{ + gpointer call; + + g_assert (klass->completed_queue); + + call = g_async_queue_try_pop (klass->completed_queue); + if (call) { + process_result (call, NULL); + g_object_unref (call); + return TRUE; + } + + return FALSE; +} + +static gboolean +completed_prepare(GSource* base, gint *timeout) +{ + GckCallSource *source = (GckCallSource*)base; + gboolean have; + + g_assert (source->klass->completed_queue); + have = g_async_queue_length (source->klass->completed_queue) > 0; + *timeout = have ? 0 : -1; + return have; +} + +static gboolean +completed_check(GSource* base) +{ + GckCallSource *source = (GckCallSource*)base; + g_assert (source->klass->completed_queue); + return g_async_queue_length (source->klass->completed_queue) > 0; +} + +static gboolean +completed_dispatch(GSource* base, GSourceFunc callback, gpointer user_data) +{ + GckCallSource *source = (GckCallSource*)base; + process_completed (source->klass); + return TRUE; +} + +static void +completed_finalize(GSource* base) +{ + +} + +static GSourceFuncs completed_functions = { + completed_prepare, + completed_check, + completed_dispatch, + completed_finalize +}; + +/* ---------------------------------------------------------------------------- + * OBJECT + */ + +static void +_gck_call_init (GckCall *call) +{ + call->rv = CKR_OK; +} + +static void +_gck_call_finalize (GObject *obj) +{ + GckCall *call = GCK_CALL (obj); + + if (call->module) + g_object_unref (call->module); + call->module = NULL; + + if (call->object) + g_object_unref (call->object); + call->object = NULL; + + if (call->cancellable) + g_object_unref (call->cancellable); + call->cancellable = NULL; + + if (call->destroy) + (call->destroy) (call->args); + call->destroy = NULL; + call->args = NULL; + + G_OBJECT_CLASS (_gck_call_parent_class)->finalize (obj); +} + +static gpointer +_gck_call_get_user_data (GAsyncResult *async_result) +{ + g_return_val_if_fail (GCK_IS_CALL (async_result), NULL); + return GCK_CALL (async_result)->user_data; +} + +static GObject* +_gck_call_get_source_object (GAsyncResult *async_result) +{ + g_return_val_if_fail (GCK_IS_CALL (async_result), NULL); + return GCK_CALL (async_result)->object; +} + +static void +_gck_call_implement_async_result (GAsyncResultIface *iface) +{ + iface->get_user_data = _gck_call_get_user_data; + iface->get_source_object = _gck_call_get_source_object; +} + +static void +_gck_call_class_init (GckCallClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*)klass; + + _gck_call_parent_class = g_type_class_peek_parent (klass); + gobject_class->finalize = _gck_call_finalize; +} + +static void +_gck_call_base_init (GckCallClass *klass) +{ + GckCallSource *source; + GMainContext *context; + GError *err = NULL; + + klass->thread_pool = g_thread_pool_new ((GFunc)process_async_call, klass, 16, FALSE, &err); + if (!klass->thread_pool) { + g_critical ("couldn't create thread pool: %s", + err && err->message ? err->message : ""); + return; + } + + klass->completed_queue = g_async_queue_new_full (g_object_unref); + g_assert (klass->completed_queue); + + context = g_main_context_default (); + g_assert (context); + + /* Add our idle handler which processes other tasks */ + source = (GckCallSource*)g_source_new (&completed_functions, sizeof (GckCallSource)); + source->klass = klass; + klass->completed_id = g_source_attach ((GSource*)source, context); + g_source_set_callback ((GSource*)source, NULL, NULL, NULL); + g_source_unref ((GSource*)source); +} + +static void +_gck_call_base_finalize (GckCallClass *klass) +{ + GMainContext *context; + GSource *src; + + if (klass->thread_pool) { + g_assert (g_thread_pool_unprocessed (klass->thread_pool) == 0); + g_thread_pool_free (klass->thread_pool, FALSE, TRUE); + klass->thread_pool = NULL; + } + + if (klass->completed_id) { + context = g_main_context_default (); + g_return_if_fail (context); + + src = g_main_context_find_source_by_id (context, klass->completed_id); + g_assert (src); + g_source_destroy (src); + klass->completed_id = 0; + } + + if (klass->completed_queue) { + g_assert (g_async_queue_length (klass->completed_queue)); + g_async_queue_unref (klass->completed_queue); + klass->completed_queue = NULL; + } +} + +GType +_gck_call_get_type (void) +{ + static volatile gsize type_id__volatile = 0; + + if (g_once_init_enter (&type_id__volatile)) { + + static const GTypeInfo type_info = { + sizeof (GckCallClass), + (GBaseInitFunc)_gck_call_base_init, + (GBaseFinalizeFunc)_gck_call_base_finalize, + (GClassInitFunc)_gck_call_class_init, + (GClassFinalizeFunc)NULL, + NULL, // class_data + sizeof (GckCall), + 0, // n_preallocs + (GInstanceInitFunc)_gck_call_init, + }; + + static const GInterfaceInfo interface_info = { + (GInterfaceInitFunc)_gck_call_implement_async_result + }; + + GType type_id = g_type_register_static (G_TYPE_OBJECT, "_GckCall", &type_info, 0); + g_type_add_interface_static (type_id, G_TYPE_ASYNC_RESULT, &interface_info); + + g_once_init_leave (&type_id__volatile, type_id); + } + + return type_id__volatile; +} + +/* ---------------------------------------------------------------------------- + * PUBLIC + */ + +void +_gck_call_uninitialize (void) +{ + +} + +gboolean +_gck_call_sync (gpointer object, gpointer perform, gpointer complete, + gpointer data, GCancellable *cancellable, GError **err) +{ + GckArguments *args = (GckArguments*)data; + GckModule *module = NULL; + CK_RV rv; + + g_assert (G_IS_OBJECT (object)); + g_assert (perform); + g_assert (args); + + g_object_get (object, "module", &module, "handle", &args->handle, NULL); + g_assert (GCK_IS_MODULE (module)); + + /* We now hold a reference to module until below */ + args->pkcs11 = gck_module_get_functions (module); + g_assert (args->pkcs11); + + do { + rv = perform_call (perform, cancellable, args); + if (rv == CKR_FUNCTION_CANCELED) + break; + + } while (!complete_call (complete, args, rv)); + + g_object_unref (module); + + if (rv == CKR_OK) + return TRUE; + + g_set_error (err, GCK_ERROR, rv, "%s", gck_message_from_rv (rv)); + return FALSE; +} + +gpointer +_gck_call_async_prep (gpointer object, gpointer cb_object, gpointer perform, + gpointer complete, gsize args_size, gpointer destroy) +{ + GckArguments *args; + GckCall *call; + + g_assert (!object || G_IS_OBJECT (object)); + g_assert (perform); + + if (!destroy) + destroy = g_free; + + if (args_size == 0) + args_size = sizeof (GckArguments); + g_assert (args_size >= sizeof (GckArguments)); + + args = g_malloc0 (args_size); + call = g_object_new (GCK_TYPE_CALL, NULL); + call->destroy = (GDestroyNotify)destroy; + call->perform = (GckPerformFunc)perform; + call->complete = (GckCompleteFunc)complete; + call->object = cb_object; + g_object_ref (cb_object); + + /* Hook the two together */ + call->args = args; + call->args->call = call; + + /* Setup call object if available */ + if (object != NULL) + _gck_call_async_object (call, object); + + return args; +} + +void +_gck_call_async_object (GckCall *call, gpointer object) +{ + g_assert (GCK_IS_CALL (call)); + g_assert (call->args); + + if (call->module) + g_object_unref (call->module); + call->module = NULL; + + g_object_get (object, "module", &call->module, "handle", &call->args->handle, NULL); + g_assert (GCK_IS_MODULE (call->module)); + call->args->pkcs11 = gck_module_get_functions (call->module); + + /* We now hold a reference on module until finalize */ +} + +GckCall* +_gck_call_async_ready (gpointer data, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data) +{ + GckArguments *args = (GckArguments*)data; + g_assert (GCK_IS_CALL (args->call)); + + args->call->cancellable = cancellable; + if (cancellable) { + g_assert (G_IS_CANCELLABLE (cancellable)); + g_object_ref (cancellable); + } + + args->call->callback = callback; + args->call->user_data = user_data; + + return args->call; +} + +void +_gck_call_async_go (GckCall *call) +{ + g_assert (GCK_IS_CALL (call)); + g_assert (call->args->pkcs11); + + /* To keep things balanced, process at one completed event */ + process_completed(GCK_CALL_GET_CLASS (call)); + + g_assert (GCK_CALL_GET_CLASS (call)->thread_pool); + g_thread_pool_push (GCK_CALL_GET_CLASS (call)->thread_pool, call, NULL); +} + +void +_gck_call_async_ready_go (gpointer data, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data) +{ + GckCall *call = _gck_call_async_ready (data, cancellable, callback, user_data); + _gck_call_async_go (call); +} + +gboolean +_gck_call_basic_finish (GAsyncResult *result, GError **err) +{ + CK_RV rv; + + g_return_val_if_fail (GCK_IS_CALL (result), FALSE); + + rv = GCK_CALL (result)->rv; + if (rv == CKR_OK) + return TRUE; + + g_set_error (err, GCK_ERROR, rv, "%s", gck_message_from_rv (rv)); + return FALSE; +} + +void +_gck_call_async_short (GckCall *call, CK_RV rv) +{ + g_assert (GCK_IS_CALL (call)); + + call->rv = rv; + + /* Already complete, so just push it for processing in main loop */ + g_assert (GCK_CALL_GET_CLASS (call)->completed_queue); + g_async_queue_push (GCK_CALL_GET_CLASS (call)->completed_queue, call); + g_main_context_wakeup (NULL); +} + +gpointer +_gck_call_get_arguments (GckCall *call) +{ + g_assert (GCK_IS_CALL (call)); + return call->args; +} diff --git a/gck/gck-marshal.list b/gck/gck-marshal.list new file mode 100644 index 00000000..d9553afa --- /dev/null +++ b/gck/gck-marshal.list @@ -0,0 +1,3 @@ +BOOLEAN:STRING,POINTER +BOOLEAN:OBJECT,STRING,POINTER +BOOLEAN:ULONG diff --git a/gck/gck-misc.c b/gck/gck-misc.c new file mode 100644 index 00000000..ae8c66d5 --- /dev/null +++ b/gck/gck-misc.c @@ -0,0 +1,437 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* 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, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter <nielsen@memberwebs.com> +*/ + +#include "config.h" + +#include "gck.h" +#include "gck-private.h" + +#include <glib/gi18n.h> + +/** + * SECTION:gck-error + * @title: Errors + * @short_description: Gck Errors and error codes. + * + * Errors are returned as GError structures. The code member of GError + * contains the raw PKCS11 CK_RV result value. + */ + +/** + * GCK_VENDOR_CODE: + * + * Custom PKCS11 errors that originate from the gck library, are + * based at this error code. + */ + +/** + * CKR_GCK_MODULE_PROBLEM: + * + * A result code that signifies there was a problem loading a PKCS11 + * module, usually a shared library. + * + * More details can be found in the error string. + */ + +/** + * GCK_ERROR: + * + * The error domain for gck library errors. + */ +GQuark +gck_get_error_quark (void) +{ + static GQuark domain = 0; + static volatile gsize quark_inited = 0; + + if (g_once_init_enter (&quark_inited)) { + domain = g_quark_from_static_string ("gck-error"); + g_once_init_leave (&quark_inited, 1); + } + + return domain; +} + +/** + * 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 such non errors to this function. + * + * Return value: The user readable message. + **/ +const gchar* +gck_message_from_rv (CK_RV 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 (""); + + case CKR_CANCEL: + case CKR_FUNCTION_CANCELED: + return _("The operation was cancelled"); + + case CKR_HOST_MEMORY: + return _("Insufficient memory available"); + case CKR_SLOT_ID_INVALID: + return _("The specified slot ID is not valid"); + case CKR_GENERAL_ERROR: + return _("Internal error"); + case CKR_FUNCTION_FAILED: + return _("The operation failed"); + case CKR_ARGUMENTS_BAD: + return _("Invalid arguments"); + case CKR_NEED_TO_CREATE_THREADS: + return _("The module cannot create needed threads"); + case CKR_CANT_LOCK: + return _("The module cannot lock data properly"); + case CKR_ATTRIBUTE_READ_ONLY: + return _("The field is read-only"); + case CKR_ATTRIBUTE_SENSITIVE: + return _("The field is sensitive and cannot be revealed"); + case CKR_ATTRIBUTE_TYPE_INVALID: + return _("The field is invalid or does not exist"); + case CKR_ATTRIBUTE_VALUE_INVALID: + return _("Invalid value for field"); + case CKR_DATA_INVALID: + return _("The data is not valid or unrecognized"); + case CKR_DATA_LEN_RANGE: + return _("The data is too long"); + case CKR_DEVICE_ERROR: + return _("An error occurred on the device"); + case CKR_DEVICE_MEMORY: + return _("Insufficient memory available on device"); + case CKR_DEVICE_REMOVED: + return _("The device was removed or unplugged"); + case CKR_ENCRYPTED_DATA_INVALID: + return _("The encrypted data is not valid or unrecognized"); + case CKR_ENCRYPTED_DATA_LEN_RANGE: + return _("The encrypted data is too long"); + case CKR_FUNCTION_NOT_SUPPORTED: + return _("This operation is not supported"); + case CKR_KEY_HANDLE_INVALID: + return _("The key is missing or invalid"); + case CKR_KEY_SIZE_RANGE: + return _("The key is the wrong size"); + case CKR_KEY_TYPE_INCONSISTENT: + return _("The key is of the wrong type"); + case CKR_KEY_NOT_NEEDED: + return _("No key is needed"); + case CKR_KEY_CHANGED: + return _("The key is different than before"); + case CKR_KEY_NEEDED: + return _("A key is needed"); + case CKR_KEY_INDIGESTIBLE: + return _("Cannot include the key in digest"); + case CKR_KEY_FUNCTION_NOT_PERMITTED: + return _("This operation cannot be done with this key"); + case CKR_KEY_NOT_WRAPPABLE: + return _("The key cannot be wrapped"); + case CKR_KEY_UNEXTRACTABLE: + return _("Cannot export this key"); + case CKR_MECHANISM_INVALID: + return _("The crypto mechanism is invalid or unrecognized"); + case CKR_MECHANISM_PARAM_INVALID: + return _("The crypto mechanism has an invalid argument"); + case CKR_OBJECT_HANDLE_INVALID: + return _("The object is missing or invalid"); + case CKR_OPERATION_ACTIVE: + return _("Another operation is already taking place"); + case CKR_OPERATION_NOT_INITIALIZED: + return _("No operation is taking place"); + case CKR_PIN_INCORRECT: + return _("The password or PIN is incorrect"); + case CKR_PIN_INVALID: + return _("The password or PIN is invalid"); + case CKR_PIN_LEN_RANGE: + return _("The password or PIN is of an invalid length"); + case CKR_PIN_EXPIRED: + return _("The password or PIN has expired"); + case CKR_PIN_LOCKED: + return _("The password or PIN is locked"); + case CKR_SESSION_CLOSED: + return _("The session is closed"); + case CKR_SESSION_COUNT: + return _("Too many sessions are active"); + case CKR_SESSION_HANDLE_INVALID: + return _("The session is invalid"); + case CKR_SESSION_READ_ONLY: + return _("The session is read-only"); + case CKR_SESSION_EXISTS: + return _("An open session exists"); + case CKR_SESSION_READ_ONLY_EXISTS: + return _("A read-only session exists"); + case CKR_SESSION_READ_WRITE_SO_EXISTS: + return _("An administrator session exists"); + case CKR_SIGNATURE_INVALID: + return _("The signature is bad or corrupted"); + case CKR_SIGNATURE_LEN_RANGE: + return _("The signature is unrecognized or corrupted"); + case CKR_TEMPLATE_INCOMPLETE: + return _("Certain required fields are missing"); + case CKR_TEMPLATE_INCONSISTENT: + return _("Certain fields have invalid values"); + case CKR_TOKEN_NOT_PRESENT: + return _("The device is not present or unplugged"); + case CKR_TOKEN_NOT_RECOGNIZED: + return _("The device is invalid or unrecognizable"); + case CKR_TOKEN_WRITE_PROTECTED: + return _("The device is write protected"); + case CKR_UNWRAPPING_KEY_HANDLE_INVALID: + return _("Cannot import because the key is invalid"); + case CKR_UNWRAPPING_KEY_SIZE_RANGE: + return _("Cannot import because the key is of the wrong size"); + case CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT: + return _("Cannot import because the key is of the wrong type"); + case CKR_USER_ALREADY_LOGGED_IN: + return _("You are already logged in"); + case CKR_USER_NOT_LOGGED_IN: + return _("No user has logged in"); + case CKR_USER_PIN_NOT_INITIALIZED: + return _("The user's password or PIN is not set"); + case CKR_USER_TYPE_INVALID: + return _("The user is of an invalid type"); + case CKR_USER_ANOTHER_ALREADY_LOGGED_IN: + return _("Another user is already logged in"); + case CKR_USER_TOO_MANY_TYPES: + return _("Too many users of different types logged in"); + case CKR_WRAPPED_KEY_INVALID: + return _("Cannot import an invalid key"); + case CKR_WRAPPED_KEY_LEN_RANGE: + return _("Cannot import a key of the wrong size"); + case CKR_WRAPPING_KEY_HANDLE_INVALID: + return _("Cannot export because the key is invalid"); + case CKR_WRAPPING_KEY_SIZE_RANGE: + return _("Cannot export because the key is of the wrong size"); + case CKR_WRAPPING_KEY_TYPE_INCONSISTENT: + return _("Cannot export because the key is of the wrong type"); + case CKR_RANDOM_SEED_NOT_SUPPORTED: + return _("Unable to initialize the random number generator"); + case CKR_RANDOM_NO_RNG: + return _("No random number generator available"); + case CKR_DOMAIN_PARAMS_INVALID: + return _("The crypto mechanism has an invalid parameter"); + case CKR_BUFFER_TOO_SMALL: + return _("Not enough space to store the result"); + case CKR_SAVED_STATE_INVALID: + return _("The saved state is invalid"); + case CKR_INFORMATION_SENSITIVE: + return _("The information is sensitive and cannot be revealed"); + case CKR_STATE_UNSAVEABLE: + return _("The state cannot be saved"); + case CKR_CRYPTOKI_NOT_INITIALIZED: + return _("The module has not been initialized"); + case CKR_CRYPTOKI_ALREADY_INITIALIZED: + return _("The module has already been initialized"); + case CKR_MUTEX_BAD: + return _("Cannot lock data"); + case CKR_MUTEX_NOT_LOCKED: + return _("The data cannot be locked"); + case CKR_FUNCTION_REJECTED: + return _("The signature request was rejected by the user"); + + default: + g_message ("unknown error: %lu", (gulong)rv); + return _("Unknown error"); + } +} + +/** + * SECTION:gck-misc + * @title: Miscellaneous Functions + * @short_description: Other miscellaneous functions. + * + * A few supporting functions that come in handy when dealing with the gck + * library or PKCS11 in general. + */ + +/** + * gck_list_unref_free: + * @reflist: 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: + * @reflist: 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: 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: + * @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 g_strndup, except for that it also strips trailing + * spaces. These space padded strings are often used in PKCS#11 + * structures. + * + * 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); + + string = g_strndup ((gchar*)data, max); + g_strchomp (string); + return string; +} + +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 *((const gulong*)v1) == *((const gulong*)v2); +} + +static GQuark mechanism_quark = 0; + +static void +free_refs (gpointer data) +{ + gint *refs = data; + g_assert (refs); + g_assert (*refs == 0); + g_slice_free (gint, data); +} + +GckMechanism* +gck_mechanism_new (gulong type) +{ + return gck_mechanism_new_with_param (type, NULL, 0); +} + +GckMechanism* +gck_mechanism_new_with_param (gulong type, gconstpointer parameter, + gulong n_parameter) +{ + static volatile gsize inited_quark = 0; + GckMechanism *mech; + gint *refs; + + /* Initialize first time around */ + if (g_once_init_enter (&inited_quark)) { + mechanism_quark = g_quark_from_static_string ("GckMechanism::refs"); + g_once_init_leave (&inited_quark, 1); + } + + mech = g_slice_new (GckMechanism); + mech->type = type; + mech->parameter = g_memdup (parameter, n_parameter); + mech->n_parameter = n_parameter; + + refs = g_slice_new (gint); + *refs = 1; + g_dataset_id_set_data_full (mech, mechanism_quark, refs, free_refs); + + return mech; +} + +GckMechanism* +gck_mechanism_ref (GckMechanism* mech) +{ + gint *refs; + + g_return_val_if_fail (mech, NULL); + + refs = g_dataset_id_get_data (mech, mechanism_quark); + if (refs == NULL) { + g_warning ("Encountered invalid GckMechanism struct. Either it was unreffed or " + "possibly allocated on the stack. Always use gck_mechanism_new () and friends."); + return NULL; + } + + g_atomic_int_add (refs, 1); + return mech; +} + +void +gck_mechanism_unref (GckMechanism* mech) +{ + gint *refs; + + if (!mech) + return; + + refs = g_dataset_id_get_data (mech, mechanism_quark); + if (refs == NULL) { + g_warning ("Encountered invalid GckMechanism struct. Either it was unreffed or " + "possibly allocated on the stack. Always use gck_mechanism_new () and friends."); + return; + } + + if (g_atomic_int_dec_and_test (refs)) { + g_free (mech->parameter); + g_dataset_id_remove_data (mech, mechanism_quark); + g_slice_free (GckMechanism, mech); + } +} diff --git a/gck/gck-module.c b/gck/gck-module.c new file mode 100644 index 00000000..096a0442 --- /dev/null +++ b/gck/gck-module.c @@ -0,0 +1,1228 @@ +/* -*- 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, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter <nielsen@memberwebs.com> +*/ + +#include "config.h" + +#include "gck.h" +#include "gck-private.h" +#include "gck-marshal.h" + +#include <string.h> + +/** + * SECTION:gck-module + * @title: GckModule + * @short_description: A loaded and initialized PKCS#11 module. + * + * A GckModule object 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 gck_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 gck_module_new(). + */ + +/** + * GckModule: + * + * Holds a loaded and initialized PKCS#11 module. + */ + +/** + * 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, + PROP_POOL_SESSIONS, + PROP_AUTO_AUTHENTICATE +}; + +enum { + AUTHENTICATE_SLOT, + AUTHENTICATE_OBJECT, + LAST_SIGNAL +}; + +typedef struct _GckModuleData { + GModule *module; + gchar *path; + gboolean initialized; + CK_FUNCTION_LIST_PTR funcs; + CK_C_INITIALIZE_ARGS init_args; +} GckModuleData; + +typedef struct _GckModulePrivate { + GckModuleData data; + GStaticMutex mutex; + gboolean finalized; + GHashTable *open_sessions; + gint auto_authenticate; +} GckModulePrivate; + +#define gck_module_GET_DATA(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), GCK_TYPE_MODULE, GckModuleData)) + +G_DEFINE_TYPE (GckModule, gck_module, G_TYPE_OBJECT); + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef struct _SessionPool { + CK_SLOT_ID slot; + CK_FUNCTION_LIST_PTR funcs; + GArray *ro_sessions; /* array of CK_SESSION_HANDLE */ + GArray *rw_sessions; /* array of CK_SESSION_HANDLE */ +} SessionPool; + +/* ---------------------------------------------------------------------------- + * HELPERS + */ + +static CK_RV +create_mutex (void **mutex) +{ + if (!mutex) + return CKR_ARGUMENTS_BAD; + + if (!g_thread_supported ()) { + g_warning ("cannot create pkcs11 mutex, threading has not been initialized"); + return CKR_GENERAL_ERROR; + } + + *mutex = g_mutex_new (); + g_return_val_if_fail (*mutex, CKR_GENERAL_ERROR); + return CKR_OK; +} + +static CK_RV +destroy_mutex (void *mutex) +{ + if (!mutex) + return CKR_MUTEX_BAD; + g_mutex_free ((GMutex*)mutex); + return CKR_OK; +} + +static CK_RV +lock_mutex (void *mutex) +{ + if (!mutex) + return CKR_MUTEX_BAD; + g_mutex_lock ((GMutex*)mutex); + return CKR_OK; +} + +static CK_RV +unlock_mutex (void *mutex) +{ + if (!mutex) + return CKR_MUTEX_BAD; + g_mutex_unlock ((GMutex*)mutex); + return CKR_OK; +} + +static void +close_session (CK_FUNCTION_LIST_PTR funcs, CK_SESSION_HANDLE handle) +{ + CK_RV rv; + + g_return_if_fail (funcs); + + rv = (funcs->C_CloseSession) (handle); + if (rv != CKR_OK) { + g_warning ("couldn't close session properly: %s", + gck_message_from_rv (rv)); + } +} + +/* ---------------------------------------------------------------------------- + * INTERNAL + */ + +static GckModulePrivate* +lock_private (gpointer obj) +{ + GckModulePrivate *pv; + GckModule *self; + + g_assert (GCK_IS_MODULE (obj)); + self = GCK_MODULE (obj); + + g_object_ref (self); + + pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_MODULE, GckModulePrivate); + g_static_mutex_lock (&pv->mutex); + + return pv; +} + +static void +unlock_private (gpointer obj, GckModulePrivate *pv) +{ + GckModule *self; + + g_assert (pv); + g_assert (GCK_IS_MODULE (obj)); + + self = GCK_MODULE (obj); + + g_assert (G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_MODULE, GckModulePrivate) == pv); + + g_static_mutex_unlock (&pv->mutex); + g_object_unref (self); +} + +static void +free_session_pool (gpointer p) +{ + SessionPool *pool = p; + guint i; + + if (pool->ro_sessions) { + for(i = 0; i < pool->ro_sessions->len; ++i) + close_session (pool->funcs, g_array_index (pool->ro_sessions, CK_SESSION_HANDLE, i)); + g_array_free (pool->ro_sessions, TRUE); + } + + if (pool->rw_sessions) { + for(i = 0; i < pool->rw_sessions->len; ++i) + close_session (pool->funcs, g_array_index (pool->rw_sessions, CK_SESSION_HANDLE, i)); + g_array_free (pool->rw_sessions, TRUE); + } + + g_free (pool); +} + +static gboolean +push_session_table (GckModulePrivate *pv, CK_SLOT_ID slot, gulong flags, CK_SESSION_HANDLE handle) +{ + SessionPool *pool; + GArray *array; + + g_assert (handle); + + if (pv->open_sessions == NULL) + return FALSE; + + pool = g_hash_table_lookup (pv->open_sessions, &slot); + if (!pool) { + pool = g_new0 (SessionPool, 1); + pool->funcs = pv->data.funcs; + g_hash_table_insert (pv->open_sessions, g_memdup (&slot, sizeof (slot)), pool); + } + + if (flags & CKF_RW_SESSION) { + if (!pool->rw_sessions) + pool->rw_sessions = g_array_new (FALSE, TRUE, sizeof (CK_SESSION_HANDLE)); + array = pool->rw_sessions; + } else { + if (!pool->ro_sessions) + pool->ro_sessions = g_array_new (FALSE, TRUE, sizeof (CK_SESSION_HANDLE)); + array = pool->ro_sessions; + } + + g_array_append_val (array, handle); + return TRUE; +} + +static CK_SESSION_HANDLE +pop_session_table (GckModulePrivate *pv, CK_SLOT_ID slot, gulong flags) +{ + CK_SESSION_HANDLE result = 0; + SessionPool *pool; + GArray **array; + + g_return_val_if_fail (pv, 0); + + if (!pv->open_sessions) + return 0; + + pool = g_hash_table_lookup (pv->open_sessions, &slot); + if (pool == NULL) + return 0; + + if (flags & CKF_RW_SESSION) + array = &pool->rw_sessions; + else + array = &pool->ro_sessions; + + if (*array == NULL) + return 0; + + g_assert ((*array)->len > 0); + result = g_array_index (*array, CK_SESSION_HANDLE, (*array)->len - 1); + g_assert (result != 0); + g_array_remove_index_fast (*array, (*array)->len - 1); + + if (!(*array)->len) { + g_array_free (*array, TRUE); + *array = NULL; + if (!pool->rw_sessions && !pool->ro_sessions) + g_hash_table_remove (pv->open_sessions, &slot); + } + + return result; +} + +static void +destroy_session_table (GckModulePrivate *pv) +{ + if (pv->open_sessions) + g_hash_table_unref (pv->open_sessions); + pv->open_sessions = NULL; +} + +static void +create_session_table (GckModulePrivate *pv) +{ + if (!pv->open_sessions) + pv->open_sessions = g_hash_table_new_full (_gck_ulong_hash, _gck_ulong_equal, g_free, free_session_pool); +} + +CK_SESSION_HANDLE +_gck_module_pooled_session_handle (GckModule *self, CK_SLOT_ID slot, gulong flags) +{ + GckModulePrivate *pv = lock_private (self); + CK_SESSION_HANDLE handle; + + g_return_val_if_fail (GCK_IS_MODULE (self), 0); + + { + handle = pop_session_table (pv, slot, flags); + } + + unlock_private (self, pv); + + return handle; +} + +gboolean +_gck_module_pool_session_handle (GckSession *session, CK_SESSION_HANDLE handle, GckModule *self) +{ + GckModuleData *data = gck_module_GET_DATA (self); + GckModulePrivate *pv; + CK_SESSION_INFO info; + gboolean handled = FALSE; + CK_RV rv; + + g_return_val_if_fail (GCK_IS_SESSION (session), FALSE); + g_return_val_if_fail (GCK_IS_MODULE (self), FALSE); + + /* Get the session info so we know where to categorize this */ + rv = (data->funcs->C_GetSessionInfo) (handle, &info); + + if (rv == CKR_OK) { + + pv = lock_private (self); + + { + /* Keep this one around for later use */ + handled = push_session_table (pv, info.slotID, info.flags, handle); + } + + unlock_private (self, pv); + + } else { + + /* An already closed session, we don't want to bother with */ + if (rv == CKR_SESSION_CLOSED || rv == CKR_SESSION_HANDLE_INVALID) + handled = TRUE; + } + + return handled; +} + +gboolean +_gck_module_fire_authenticate_slot (GckModule *self, GckSlot *slot, gchar *label, gchar **password) +{ + GckTokenInfo *info; + gchar *allocated = NULL; + gboolean ret; + + g_assert (GCK_IS_MODULE (self)); + + info = gck_slot_get_token_info (slot); + if (info != NULL) { + + /* + * We'll have tried to login at least once at this point, + * with NULL password. This means that CKF_PROTECTED_AUTHENTICATION_PATH + * tokens have had their chance and we don't need to prompt for it. + */ + + if (info->flags & CKF_PROTECTED_AUTHENTICATION_PATH) + return FALSE; + + if (label == NULL) + label = allocated = g_strdup (info->label); + + gck_token_info_free (info); + } + + g_signal_emit (self, signals[AUTHENTICATE_SLOT], 0, slot, label, password, &ret); + g_free (allocated); + return ret; +} + +gboolean +_gck_module_fire_authenticate_object (GckModule *self, GckObject *object, + gchar *label, gchar **password) +{ + GckTokenInfo *info; + GckSlot *slot; + gboolean ret; + + g_assert (GCK_IS_MODULE (self)); + g_assert (GCK_IS_OBJECT (object)); + g_assert (password); + + slot = gck_object_get_slot (object); + info = gck_slot_get_token_info (slot); + g_object_unref (slot); + + if (info != NULL) { + if (info->flags & CKF_PROTECTED_AUTHENTICATION_PATH) { + gck_token_info_free (info); + *password = NULL; + return TRUE; + } + + gck_token_info_free (info); + } + + g_signal_emit (self, signals[AUTHENTICATE_OBJECT], 0, object, label, password, &ret); + return ret; +} + +/* ---------------------------------------------------------------------------- + * OBJECT + */ + +static gboolean +gck_module_real_authenticate_slot (GckModule *module, GckSlot *self, gchar *label, gchar **password) +{ + return FALSE; +} + +static gboolean +gck_module_real_authenticate_object (GckModule *module, GckObject *object, gchar *label, gchar **password) +{ + return FALSE; +} + +static void +gck_module_init (GckModule *self) +{ + GckModulePrivate *pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_MODULE, GckModulePrivate); + g_static_mutex_init (&pv->mutex); +} + +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; + case PROP_AUTO_AUTHENTICATE: + g_value_set_int (value, gck_module_get_auto_authenticate (self)); + break; + case PROP_POOL_SESSIONS: + g_value_set_boolean (value, gck_module_get_pool_sessions (self)); + break; + } +} + +static void +gck_module_set_property (GObject *obj, guint prop_id, const GValue *value, + GParamSpec *pspec) +{ + GckModule *self = GCK_MODULE (obj); + GckModuleData *data = gck_module_GET_DATA (obj); + + /* Only allowed during initialization */ + switch (prop_id) { + case PROP_PATH: + g_return_if_fail (!data->path); + data->path = g_value_dup_string (value); + break; + case PROP_FUNCTIONS: + g_return_if_fail (!data->funcs); + data->funcs = g_value_get_pointer (value); + break; + case PROP_AUTO_AUTHENTICATE: + gck_module_set_auto_authenticate (self, g_value_get_int (value)); + break; + case PROP_POOL_SESSIONS: + gck_module_set_pool_sessions (self, g_value_get_boolean (value)); + break; + } +} + +static void +gck_module_dispose (GObject *obj) +{ + GckModuleData *data = gck_module_GET_DATA (obj); + GckModulePrivate *pv = lock_private (obj); + gboolean finalize = FALSE; + CK_RV rv; + + { + destroy_session_table (pv); + + if (!pv->finalized && data->initialized && data->funcs) { + finalize = TRUE; + pv->finalized = TRUE; + } + } + + unlock_private (obj, pv); + + /* Must be careful when accessing funcs */ + if (finalize) { + rv = (data->funcs->C_Finalize) (NULL); + if (rv != CKR_OK) { + g_warning ("C_Finalize on module '%s' failed: %s", + data->path, gck_message_from_rv (rv)); + } + } + + G_OBJECT_CLASS (gck_module_parent_class)->dispose (obj); +} + +static void +gck_module_finalize (GObject *obj) +{ + GckModulePrivate *pv = G_TYPE_INSTANCE_GET_PRIVATE (obj, GCK_TYPE_MODULE, GckModulePrivate); + GckModuleData *data = gck_module_GET_DATA (obj); + + g_assert (!pv->open_sessions); + + data->funcs = NULL; + + if (data->module) { + if (!g_module_close (data->module)) + g_warning ("failed to close the pkcs11 module: %s", + g_module_error ()); + data->module = NULL; + } + + g_free (data->path); + data->path = NULL; + + g_static_mutex_free (&pv->mutex); + + 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; + + klass->authenticate_object = gck_module_real_authenticate_object; + klass->authenticate_slot = gck_module_real_authenticate_slot; + + /** + * 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)); + + /** + * 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)); + + /** + * GckModule:auto-authenticate: + * + * Whether or not to automatically authenticate token objects that need + * a C_Login call before they can be used. + * + * The #GckModule::authenticate-object signal will be fired when an + * object needs to be authenticated. + */ + g_object_class_install_property (gobject_class, PROP_AUTO_AUTHENTICATE, + g_param_spec_int ("auto-authenticate", "Auto Authenticate", "Auto Login to Token when necessary", + 0, G_MAXINT, 0, G_PARAM_READWRITE)); + + /** + * GckModule:pool-sessions: + * + * Whether or not to pool PKCS#11 sessions. When this is set, sessions + * will be pooled and reused if their flags match when gck_slot_open_session() + * is called. + */ + g_object_class_install_property (gobject_class, PROP_POOL_SESSIONS, + g_param_spec_boolean ("pool-sessions", "Pool Sessions", "Pool sessions?", + FALSE, G_PARAM_READWRITE)); + + /** + * GckModule::authenticate-slot: + * @module: The module + * @slot: The slot to be authenticated. + * @string: A displayable label which describes the object. + * @password: A gchar** where a password should be returned. + * + * This signal is emitted when a password is needed to authenticate a PKCS#11 + * slot. If the module prompts for passwords itself, then this signal will + * not be emitted. + * + * Returns: FALSE if the user cancelled, TRUE if we should proceed. + */ + signals[AUTHENTICATE_SLOT] = g_signal_new ("authenticate-slot", GCK_TYPE_MODULE, + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GckModuleClass, authenticate_slot), + g_signal_accumulator_true_handled, NULL, _gck_marshal_BOOLEAN__OBJECT_STRING_POINTER, + G_TYPE_BOOLEAN, 3, GCK_TYPE_SLOT, G_TYPE_STRING, G_TYPE_POINTER); + + /** + * GckModule::authenticate-object: + * @module: The module. + * @object: The object to be authenticated. + * @label: A displayable label which describes the object. + * @password: A gchar** where a password should be returned. + * + * This signal is emitted when a password is needed to authenticate a PKCS#11 + * object like a key. If the module prompts for passwords itself, then this signal will + * not be emitted. + * + * Returns: FALSE if the user cancelled, TRUE if we should proceed. + */ + signals[AUTHENTICATE_OBJECT] = g_signal_new ("authenticate-object", GCK_TYPE_MODULE, + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GckModuleClass, authenticate_object), + g_signal_accumulator_true_handled, NULL, _gck_marshal_BOOLEAN__OBJECT_STRING_POINTER, + G_TYPE_BOOLEAN, 3, GCK_TYPE_OBJECT, G_TYPE_STRING, G_TYPE_POINTER); + + g_type_class_add_private (gobject_class, sizeof (GckModulePrivate)); +} + +/* ---------------------------------------------------------------------------- + * PUBLIC + */ + +/** + * 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_free (module_info->library_description); + g_free (module_info->manufacturer_id); + g_free (module_info); +} + +/** + * gck_module_initialize: + * @path: The file system path to the PKCS#11 module to load. + * @reserved: Extra arguments for the PKCS#11 module, should usually be NULL. + * @err: 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: The loaded PKCS#11 module or NULL if failed. + **/ +GckModule* +gck_module_initialize (const gchar *path, gpointer reserved, GError **err) +{ + CK_C_GetFunctionList get_function_list; + CK_FUNCTION_LIST_PTR funcs; + GckModuleData *data; + GModule *module; + GckModule *mod; + CK_RV rv; + + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (!err || !*err, NULL); + + /* Load the actual module */ + module = g_module_open (path, 0); + if (!module) { + g_set_error (err, GCK_ERROR, (int)CKR_GCK_MODULE_PROBLEM, + "Error loading pkcs11 module: %s", g_module_error ()); + return NULL; + } + + /* Get the entry point */ + if (!g_module_symbol (module, "C_GetFunctionList", (void**)&get_function_list)) { + g_set_error (err, GCK_ERROR, (int)CKR_GCK_MODULE_PROBLEM, + "Invalid pkcs11 module: %s", g_module_error ()); + g_module_close (module); + return NULL; + } + + /* Get the function list */ + rv = (get_function_list) (&funcs); + if (rv != CKR_OK) { + g_set_error (err, GCK_ERROR, rv, "Couldn't get pkcs11 function list: %s", + gck_message_from_rv (rv)); + g_module_close (module); + return NULL; + } + + mod = g_object_new (GCK_TYPE_MODULE, "functions", funcs, "path", path, NULL); + data = gck_module_GET_DATA (mod); + data->module = module; + + memset (&data->init_args, 0, sizeof (data->init_args)); + data->init_args.flags = CKF_OS_LOCKING_OK; + data->init_args.CreateMutex = create_mutex; + data->init_args.DestroyMutex = destroy_mutex; + data->init_args.LockMutex = lock_mutex; + data->init_args.UnlockMutex = unlock_mutex; + data->init_args.pReserved = reserved; + + /* Now initialize the module */ + rv = (data->funcs->C_Initialize) (&data->init_args); + if (rv != CKR_OK) { + g_set_error (err, GCK_ERROR, rv, "Couldn't initialize module: %s", + gck_message_from_rv (rv)); + g_object_unref (mod); + return NULL; + } + + data->initialized = TRUE; + return mod; +} + +/** + * gck_module_new: + * @funcs: Initialized PKCS#11 function list pointer + * + * Create a GckModule 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); + return g_object_new (GCK_TYPE_MODULE, "functions", funcs, NULL); +} + +/** + * gck_module_equal: + * @module1: A pointer to the first GckModule + * @module2: A pointer to the 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 (gconstpointer module1, gconstpointer module2) +{ + GckModuleData *data1, *data2; + + if (module1 == module2) + return TRUE; + if (!GCK_IS_MODULE (module1) || !GCK_IS_MODULE (module2)) + return FALSE; + + data1 = gck_module_GET_DATA (module1); + data2 = gck_module_GET_DATA (module2); + + return data1->funcs == data2->funcs; +} + +/** + * gck_module_hash: + * @module: A pointer to a GckModule + * + * Create a hash value for the GckModule. + * + * This function is intended for easily hashing a GckModule to add to + * a GHashTable or similar data structure. + * + * Return value: An integer that can be used as a hash value, or 0 if invalid. + **/ +guint +gck_module_hash (gconstpointer module) +{ + GckModuleData *data; + + g_return_val_if_fail (GCK_IS_MODULE (module), 0); + + data = gck_module_GET_DATA (module); + + return g_direct_hash (data->funcs); +} + +/** + * gck_module_get_info: + * @self: The module to get info for. + * + * Get the info about a PKCS#11 module. + * + * Return value: The module info. Release this with gck_module_info_free(). + **/ +GckModuleInfo* +gck_module_get_info (GckModule *self) +{ + GckModuleData *data = gck_module_GET_DATA (self); + GckModuleInfo *modinfo; + CK_INFO info; + CK_RV rv; + + g_return_val_if_fail (GCK_IS_MODULE (self), NULL); + g_return_val_if_fail (data->funcs, NULL); + + memset (&info, 0, sizeof (info)); + rv = (data->funcs->C_GetInfo (&info)); + if (rv != CKR_OK) { + g_warning ("couldn't get module info: %s", gck_message_from_rv (rv)); + return NULL; + } + + 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; +} + +/** + * 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: The possibly empty list of slots. Release this with gck_list_unref_free(). + */ +GList* +gck_module_get_slots (GckModule *self, gboolean token_present) +{ + GckModuleData *data = gck_module_GET_DATA (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 (data->funcs, NULL); + + rv = (data->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 = (data->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 gck_module_initialize(). + * + * Return value: The path, do not modify or free this value. + **/ +const gchar* +gck_module_get_path (GckModule *self) +{ + GckModuleData *data = gck_module_GET_DATA (self); + g_return_val_if_fail (GCK_IS_MODULE (self), NULL); + return data->path; +} + +/** + * gck_module_get_functions: + * @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) +{ + GckModuleData *data = gck_module_GET_DATA (self); + g_return_val_if_fail (GCK_IS_MODULE (self), NULL); + return data->funcs; +} + + +/** + * gck_module_get_pool_sessions: + * @self: The module to get setting from. + * + * Get the reuse sessions setting. When this is set, sessions + * will be pooled and reused if their flags match when + * gck_slot_open_session() is called. + * + * Return value: Whether reusing sessions or not. + **/ +gboolean +gck_module_get_pool_sessions (GckModule *self) +{ + GckModulePrivate *pv = lock_private (self); + gboolean ret; + + g_return_val_if_fail (pv, FALSE); + + { + ret = pv->open_sessions != NULL; + } + + unlock_private (self, pv); + + return ret; +} + +/** + * gck_module_set_pool_sessions: + * @self: The module to set the setting on. + * @pool: Whether to reuse sessions or not. + * + * When this is set, sessions will be pooled and reused + * if their flags match when gck_slot_open_session() is called. + **/ +void +gck_module_set_pool_sessions (GckModule *self, gboolean pool) +{ + GckModulePrivate *pv = lock_private (self); + + g_return_if_fail (pv); + + { + if (pool) + create_session_table (pv); + else + destroy_session_table (pv); + } + + unlock_private (self, pv); + g_object_notify (G_OBJECT (self), "pool-sessions"); +} + +/** + * gck_module_get_auto_authenticate: + * @self: The module to get setting from. + * + * Get the auto login setting. When this is set, this slot + * will emit the 'authenticate-slot' signal when a session + * requires authentication, and the 'authenticate-object' + * signal when an object requires authintication. + * + * Return value: Whether auto login or not. + **/ +gint +gck_module_get_auto_authenticate (GckModule *self) +{ + GckModulePrivate *pv = lock_private (self); + gint ret; + + g_return_val_if_fail (pv, FALSE); + + { + ret = pv->auto_authenticate; + } + + unlock_private (self, pv); + + return ret; +} + +/** + * gck_module_set_auto_authenticate: + * @self: The module to set the setting on. + * @auto_authenticate: Whether auto login or not. + * + * When this is set, this slot + * will emit the 'authenticate-slot' signal when a session + * requires authentication, and the 'authenticate-object' + * signal when an object requires authintication. + **/ +void +gck_module_set_auto_authenticate (GckModule *self, gint auto_authenticate) +{ + GckModulePrivate *pv = lock_private (self); + + /* HACK: Get needed fix around API freeze. */ + if (auto_authenticate == 1) + auto_authenticate = GCK_AUTHENTICATE_TOKENS | GCK_AUTHENTICATE_OBJECTS; + + g_return_if_fail (pv); + + { + pv->auto_authenticate = auto_authenticate; + } + + unlock_private (self, pv); + g_object_notify (G_OBJECT (self), "auto-authenticate"); +} + +/** + * gck_module_enumerate_objects: + * @self: The module to enumerate objects. + * @func: Function to call for each object. + * @user_data: Data to pass to the function. + * @...: The arguments must be triples of: attribute type, data type, value. + * + * Call a function for every matching object on the module. This call may + * block for an indefinite period. + * + * + * <para>The variable argument list should contain: + * <variablelist> + * <varlistentry> + * <term>a)</term> + * <listitem><para>The gulong attribute type (ie: CKA_LABEL). </para></listitem> + * </varlistentry> + * <varlistentry> + * <term>b)</term> + * <listitem><para>The attribute data type (one of GCK_BOOLEAN, GCK_ULONG, + * GCK_STRING, GCK_DATE) orthe raw attribute value length.</para></listitem> + * </varlistentry> + * <varlistentry> + * <term>c)</term> + * <listitem><para>The attribute value, either a gboolean, gulong, gchar*, GDate* or + * a pointer to a raw attribute value.</para></listitem> + * </varlistentry> + * </variablelist> + * The variable argument list should be terminated with GCK_INVALID.</para> + * + * This function will open a session per slot. It's recommended that you + * set the 'reuse-sessions' property on each slot if you'll be calling + * it a lot. + * + * You can access the session in which the object was found, by using the + * gck_object_get_session() function on the resulting objects. + * + * This function skips tokens that are not initialize, and makes a best effort to + * find objects on valid tokens. + * + * The function can return FALSE to stop the enumeration. + * + * Return value: If FALSE then an error prevented all matching objects from being enumerated. + **/ +gboolean +gck_module_enumerate_objects (GckModule *self, GckObjectForeachFunc func, + gpointer user_data, ...) +{ + GckAttributes *attrs; + GError *error = NULL; + va_list va; + + va_start (va, user_data); + attrs = gck_attributes_new_valist (g_realloc, va); + va_end (va); + + gck_module_enumerate_objects_full (self, attrs, NULL, func, user_data, &error); + gck_attributes_unref (attrs); + + if (error != NULL) { + g_warning ("enumerating objects failed: %s", error->message); + g_clear_error (&error); + return FALSE; + } + + return TRUE; +} + +/** + * gck_module_enumerate_objects_full: + * @self: The module to enumerate objects. + * @attrs: Attributes that the objects must have, or empty for all objects. + * @cancellable: Optional cancellation object, or NULL. + * @func: Function to call for each object. + * @user_data: Data to pass to the function. + * @error: Location to return error information. + * + * Call a function for every matching object on the module. This call may + * block for an indefinite period. + * + * This function will open a session per slot. It's recommended that you + * set the 'reuse-sessions' property on each slot if you'll be calling + * it a lot. + * + * You can access the session in which the object was found, by using the + * gck_object_get_session() function on the resulting objects. + * + * The function can return FALSE to stop the enumeration. + * + * Return value: If FALSE then an error prevented all matching objects from being enumerated. + **/ +gboolean +gck_module_enumerate_objects_full (GckModule *self, GckAttributes *attrs, + GCancellable *cancellable, GckObjectForeachFunc func, + gpointer user_data, GError **err) +{ + gboolean stop = FALSE; + gboolean ret = TRUE; + GList *objects, *o; + GList *slots, *l; + GError *error = NULL; + GckSession *session; + + g_return_val_if_fail (GCK_IS_MODULE (self), FALSE); + g_return_val_if_fail (attrs, FALSE); + g_return_val_if_fail (func, FALSE); + + gck_attributes_ref (attrs); + slots = gck_module_get_slots (self, TRUE); + + for (l = slots; ret && !stop && l; l = g_list_next (l)) { + + /* TODO: We really should allow the caller to specify the flags, at least read-write */ + session = gck_slot_open_session (l->data, CKF_RW_SESSION | CKF_SERIAL_SESSION, &error); + if (!session) { + g_return_val_if_fail (error != NULL, FALSE); + + /* Ignore these errors when enumerating */ + if (g_error_matches (error, GCK_ERROR, CKR_USER_PIN_NOT_INITIALIZED)) { + g_clear_error (&error); + + } else { + ret = FALSE; + g_propagate_error (err, error); + error = NULL; + } + continue; + } + + objects = gck_session_find_objects_full (session, attrs, cancellable, &error); + if (error) { + ret = FALSE; + g_object_unref (session); + g_propagate_error (err, error); + error = NULL; + continue; + } + + for (o = objects; !stop && o; o = g_list_next (o)) { + gck_object_set_session (o->data, session); + if (!(func)(o->data, user_data)) { + stop = TRUE; + break; + } + } + + g_object_unref (session); + gck_list_unref_free (objects); + } + + gck_list_unref_free (slots); + gck_attributes_unref (attrs); + + return ret; +} + +/** + * GckObjectForeachFunc: + * @object: The enumerated object. + * @user_data: Data passed to enumerate function. + * + * This function is passed to gck_module_enumerate_objects() or a similar function. + * It is called once for each object matched. + * + * The GckSession through which the object is accessible can be retrieved by calling + * gck_object_get_session() on object. + * + * Returns: TRUE to continue enumerating, FALSE to stop. + */ diff --git a/gck/gck-object.c b/gck/gck-object.c new file mode 100644 index 00000000..bc3e8ade --- /dev/null +++ b/gck/gck-object.c @@ -0,0 +1,1615 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* gck-object.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, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter <nielsen@memberwebs.com> +*/ + +#include "config.h" + +#include "gck.h" +#include "gck-private.h" + +#include <string.h> + +/** + * SECTION:gck-object + * @title: GckObject + * @short_description: Represents a PKCS11 object such as a key or certificate. + * + * A GckObject holds a handle to a PKCS11 object such as a key or certificate. Token objects + * are stored on the token persistently. Others are transient and are called session objects. + */ + +/** + * GckObject: + * + * Represents a PKCS11 object handle such as a key or certifiacte. + */ + +/* + * MT safe -- Nothing in GckObjectData changes between + * init and finalize. All GckObjectPrivate access between init + * and finalize is locked. + */ + +enum { + PROP_0, + PROP_MODULE, + PROP_SLOT, + PROP_HANDLE, + PROP_SESSION +}; + +typedef struct _GckObjectData { + GckModule *module; + GckSlot *slot; + CK_OBJECT_HANDLE handle; +} GckObjectData; + +typedef struct _GckObjectPrivate { + GckObjectData data; + GStaticMutex mutex; + GckSession *session; +} GckObjectPrivate; + +#define GCK_OBJECT_GET_DATA(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), GCK_TYPE_OBJECT, GckObjectData)) + +G_DEFINE_TYPE (GckObject, gck_object, G_TYPE_OBJECT); + +/* ---------------------------------------------------------------------------- + * INTERNAL + */ + +static void +run_call_with_session (GckCall *call, GckSession *session) +{ + g_assert (GCK_IS_CALL (call)); + g_assert (GCK_IS_SESSION (session)); + + /* Hold onto this session for the length of the call */ + g_object_set_data_full (G_OBJECT (call), "call-opened-session", + g_object_ref (session), g_object_unref); + + _gck_call_async_object (call, session); + _gck_call_async_go (call); +} + +static void +opened_session (GObject *obj, GAsyncResult *result, gpointer user_data) +{ + GckSession *session; + GError *err = NULL; + GckCall *call; + + g_assert (GCK_IS_CALL (user_data)); + call = GCK_CALL (user_data); + + session = gck_slot_open_session_finish (GCK_SLOT (obj), result, &err); + + /* Transtfer the error to the outer call and finish */ + if (!session) { + _gck_call_async_short (user_data, err->code); + g_error_free (err); + return; + } + + run_call_with_session (GCK_CALL (user_data), session); + g_object_unref (session); +} + +static void +require_session_async (GckObject *self, GckCall *call, + gulong flags, GCancellable *cancellable) +{ + GckObjectData *data = GCK_OBJECT_GET_DATA (self); + GckSession *session; + + g_assert (GCK_IS_OBJECT (self)); + + session = gck_object_get_session (self); + if (session) { + run_call_with_session (call, session); + g_object_unref (session); + } else { + gck_slot_open_session_async (data->slot, flags, NULL, NULL, + cancellable, opened_session, call); + } + +} + +static GckSession* +require_session_sync (GckObject *self, gulong flags, GError **err) +{ + GckObjectData *data = GCK_OBJECT_GET_DATA (self); + GckSession *session; + + g_assert (GCK_IS_OBJECT (self)); + + session = gck_object_get_session (self); + if (session) + return session; + + return gck_slot_open_session (data->slot, flags, err); +} + +/* ---------------------------------------------------------------------------- + * OBJECT + */ + +static void +gck_object_init (GckObject *self) +{ + GckObjectPrivate *pv = (G_TYPE_INSTANCE_GET_PRIVATE(self, GCK_TYPE_OBJECT, GckObjectPrivate)); + g_static_mutex_init (&pv->mutex); +} + +static void +gck_object_get_property (GObject *obj, guint prop_id, GValue *value, + GParamSpec *pspec) +{ + GckObject *self = GCK_OBJECT (obj); + + switch (prop_id) { + case PROP_MODULE: + g_value_take_object (value, gck_object_get_module (self)); + break; + case PROP_SLOT: + g_value_take_object (value, gck_object_get_slot (self)); + break; + case PROP_SESSION: + g_value_take_object (value, gck_object_get_session (self)); + break; + case PROP_HANDLE: + g_value_set_ulong (value, gck_object_get_handle (self)); + break; + } +} + +static void +gck_object_set_property (GObject *obj, guint prop_id, const GValue *value, + GParamSpec *pspec) +{ + GckObjectData *data = GCK_OBJECT_GET_DATA (obj); + GckObject *self = GCK_OBJECT (obj); + + /* The sets to data below are only allowed during construction */ + + switch (prop_id) { + case PROP_MODULE: + g_return_if_fail (!data->module); + data->module = g_value_get_object (value); + g_return_if_fail (data->module); + g_object_ref (data->module); + break; + case PROP_SLOT: + g_return_if_fail (!data->slot); + data->slot = g_value_get_object (value); + g_return_if_fail (data->slot); + g_object_ref (data->slot); + break; + case PROP_SESSION: + gck_object_set_session (self, g_value_get_object (value)); + break; + case PROP_HANDLE: + g_return_if_fail (!data->handle); + data->handle = g_value_get_ulong (value); + break; + } +} + +static void +gck_object_finalize (GObject *obj) +{ + GckObjectPrivate *pv = (G_TYPE_INSTANCE_GET_PRIVATE(obj, GCK_TYPE_OBJECT, GckObjectPrivate)); + GckObjectData *data = GCK_OBJECT_GET_DATA (obj); + + if (data->slot) + g_object_unref (data->slot); + data->slot = NULL; + + if (data->module) + g_object_unref (data->module); + data->module = NULL; + + if (pv->session) + g_object_unref (pv->session); + pv->session = NULL; + + data->handle = 0; + + g_static_mutex_free (&pv->mutex); + + G_OBJECT_CLASS (gck_object_parent_class)->finalize (obj); +} + + +static void +gck_object_class_init (GckObjectClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*)klass; + gck_object_parent_class = g_type_class_peek_parent (klass); + + gobject_class->get_property = gck_object_get_property; + gobject_class->set_property = gck_object_set_property; + gobject_class->finalize = gck_object_finalize; + + /** + * GckObject:module: + * + * The GckModule that this object belongs to. + */ + g_object_class_install_property (gobject_class, PROP_MODULE, + g_param_spec_object ("module", "Module", "PKCS11 Module", + GCK_TYPE_MODULE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /** + * GckObject:slot: + * + * The GckSlot that this object belongs to. + * + * If this is a token object then it will be stored on the token in this slot. + * If this is a session object, then it belongs to a session opened on this slot. + */ + g_object_class_install_property (gobject_class, PROP_SLOT, + g_param_spec_object ("slot", "slot", "PKCS11 Slot", + GCK_TYPE_SLOT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /** + * GckObject:handle: + * + * The raw PKCS11 handle for this object. + */ + g_object_class_install_property (gobject_class, PROP_HANDLE, + g_param_spec_ulong ("handle", "Object Handle", "PKCS11 Object Handle", + 0, G_MAXULONG, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /** + * GckObject:session: + * + * The PKCS11 session to make calls on when this object needs to + * perform operations on itself. + * + * If this is NULL then a new session is opened for each operation, + * such as gck_object_get(), gck_object_set() or gck_object_destroy(). + */ + g_object_class_install_property (gobject_class, PROP_SESSION, + g_param_spec_object ("session", "session", "PKCS11 Session to make calls on", + GCK_TYPE_SESSION, G_PARAM_READWRITE)); + + g_type_class_add_private (klass, sizeof (GckObjectPrivate)); +} + +/* ---------------------------------------------------------------------------- + * PUBLIC + */ + +/** + * gck_object_from_handle: + * @slot: The slot on which this object is present. + * @handle: The raw handle of the object. + * + * Initialize a GckObject from a raw PKCS#11 handle. Normally you would use + * gck_session_create_object() or gck_session_find_objects() to access objects. + * + * Return value: The new GckObject. You should use g_object_unref() when done with this object. + **/ +GckObject* +gck_object_from_handle (GckSlot *slot, CK_OBJECT_HANDLE handle) +{ + GckModule *module = NULL; + GckObject *object; + + g_return_val_if_fail (GCK_IS_SLOT (slot), NULL); + + module = gck_slot_get_module (slot); + object = g_object_new (GCK_TYPE_OBJECT, "module", module, "handle", handle, "slot", slot, NULL); + g_object_unref (module); + + return object; +} + +/** + * gck_objects_from_handle_array: + * @slot: The slot on which these objects are present. + * @handles: The raw object handles. + * @n_handles: The number of raw object handles. + * + * Initialize a list of GckObject from raw PKCS#11 handles. The handles argument must contain + * contiguous CK_OBJECT_HANDLE handles in an array. + * + * Return value: The list of GckObject. You should use gck_list_unref_free() when done with + * this list. + **/ +GList* +gck_objects_from_handle_array (GckSlot *slot, CK_OBJECT_HANDLE_PTR handles, CK_ULONG n_handles) +{ + GList *results = NULL; + CK_ULONG i; + + g_return_val_if_fail (GCK_IS_SLOT (slot), NULL); + g_return_val_if_fail (handles || !n_handles, NULL); + + for (i = 0; i < n_handles; ++i) + results = g_list_prepend (results, gck_object_from_handle (slot, handles[i])); + return g_list_reverse (results); +} + +/** + * gck_object_equal: + * @object1: A pointer to the first GckObject + * @object2: A pointer to the second GckObject + * + * Checks equality of two objects. Two GckObject objects can point to the same + * underlying PKCS#11 object. + * + * Return value: TRUE if object1 and object2 are equal. FALSE if either is not a GckObject. + **/ +gboolean +gck_object_equal (gconstpointer object1, gconstpointer object2) +{ + GckObjectData *data1, *data2; + + if (object1 == object2) + return TRUE; + if (!GCK_IS_OBJECT (object1) || !GCK_IS_OBJECT (object2)) + return FALSE; + + data1 = GCK_OBJECT_GET_DATA (object1); + data2 = GCK_OBJECT_GET_DATA (object2); + + return data1->handle == data2->handle && + gck_slot_equal (data1->slot, data2->slot); +} + +/** + * gck_object_hash: + * @object: A pointer to a GckObject + * + * Create a hash value for the GckObject. + * + * This function is intended for easily hashing a GckObject to add to + * a GHashTable or similar data structure. + * + * Return value: An integer that can be used as a hash value, or 0 if invalid. + **/ +guint +gck_object_hash (gconstpointer object) +{ + GckObjectData *data; + + g_return_val_if_fail (GCK_IS_OBJECT (object), 0); + + data = GCK_OBJECT_GET_DATA (object); + + return _gck_ulong_hash (&data->handle) ^ + gck_slot_hash (data->slot); +} + + +/** + * gck_object_get_handle: + * @self: The object. + * + * Get the raw PKCS#11 handle of a GckObject. + * + * Return value: The raw object handle. + **/ +CK_OBJECT_HANDLE +gck_object_get_handle (GckObject *self) +{ + GckObjectData *data = GCK_OBJECT_GET_DATA (self); + g_return_val_if_fail (GCK_IS_OBJECT (self), (CK_OBJECT_HANDLE)-1); + return data->handle; +} + +/** + * gck_object_get_module: + * @self: The object. + * + * Get the PKCS#11 module to which this object belongs. + * + * Return value: The module, which should be unreffed after use. + **/ +GckModule* +gck_object_get_module (GckObject *self) +{ + GckObjectData *data = GCK_OBJECT_GET_DATA (self); + g_return_val_if_fail (GCK_IS_OBJECT (self), NULL); + g_return_val_if_fail (GCK_IS_MODULE (data->module), NULL); + return g_object_ref (data->module); +} + +/** + * gck_object_get_slot: + * @self: The object. + * + * Get the PKCS#11 slot to which this object belongs. + * + * Return value: The slot, which should be unreffed after use. + **/ +GckSlot* +gck_object_get_slot (GckObject *self) +{ + GckObjectData *data = GCK_OBJECT_GET_DATA (self); + g_return_val_if_fail (GCK_IS_OBJECT (self), NULL); + g_return_val_if_fail (GCK_IS_SLOT (data->slot), NULL); + return g_object_ref (data->slot); +} + +/** + * gck_object_get_session: + * @self: The object + * + * Get the PKCS#11 session assigned to make calls on when operating + * on this object. + * + * This will only return a session if it was set explitly on this + * object. By default an object will open and close sessions + * appropriate for its calls. + * + * Return value: The assigned session, which must be unreffed after use. + **/ +GckSession* +gck_object_get_session (GckObject *self) +{ + GckObjectPrivate *pv = (G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_OBJECT, GckObjectPrivate)); + GckSession *session; + + g_return_val_if_fail (GCK_IS_OBJECT (self), NULL); + + g_static_mutex_lock (&pv->mutex); + + { + session = pv->session ? g_object_ref (pv->session) : NULL; + } + + g_static_mutex_unlock (&pv->mutex); + + return session; +} + +/** + * gck_object_set_session: + * @self: The object + * @session: The assigned session + * + * Set the PKCS#11 session assigned to make calls on when operating + * on this object. + * + * It isn't always necessary to assign a session to an object. + * By default an object will open and close sessions appropriate for + * its calls. + * + * If you assign a read-only session, then calls on this object + * that modify the state of the object will probably fail. + **/ +void +gck_object_set_session (GckObject *self, GckSession *session) +{ + GckObjectPrivate *pv = (G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_OBJECT, GckObjectPrivate)); + + g_return_if_fail (GCK_IS_OBJECT (self)); + + g_static_mutex_lock (&pv->mutex); + + { + if (session) + g_object_ref (session); + if (pv->session) + g_object_unref (pv->session); + pv->session = session; + } + + g_static_mutex_unlock (&pv->mutex); +} + +/* -------------------------------------------------------------------------------------- + * DESTROY + */ + +typedef struct _Destroy { + GckArguments base; + CK_OBJECT_HANDLE object; +} Destroy; + +static CK_RV +perform_destroy (Destroy *args) +{ + g_assert (args); + return (args->base.pkcs11->C_DestroyObject) (args->base.handle, args->object); +} + +/** + * gck_object_destroy: + * @self: The object to destroy. + * @err: A location to return an error. + * + * Destroy a PKCS#11 object, deleting it from storage or the session. + * This call may block for an indefinite period. + * + * Return value: Whether the call was successful or not. + **/ +gboolean +gck_object_destroy (GckObject *self, GError **err) +{ + g_return_val_if_fail (GCK_IS_OBJECT (self), FALSE); + g_return_val_if_fail (!err || !*err, FALSE); + return gck_object_destroy_full (self, NULL, err); +} + +/** + * gck_object_destroy_full: + * @self: The object to destroy. + * @cancellable: Optional cancellable object, or NULL to ignore. + * @err: A location to return an error. + * + * Destroy a PKCS#11 object, deleting it from storage or the session. + * This call may block for an indefinite period. + * + * Return value: Whether the call was successful or not. + **/ +gboolean +gck_object_destroy_full (GckObject *self, GCancellable *cancellable, GError **err) +{ + GckObjectData *data = GCK_OBJECT_GET_DATA (self); + Destroy args = { GCK_ARGUMENTS_INIT, 0 }; + GckSession *session; + gboolean ret = FALSE; + + g_return_val_if_fail (GCK_IS_OBJECT (self), FALSE); + g_return_val_if_fail (GCK_IS_SLOT (data->slot), FALSE); + g_return_val_if_fail (!err || !*err, FALSE); + + args.object = data->handle; + + session = require_session_sync (self, CKF_RW_SESSION, err); + if (session) + ret = _gck_call_sync (session, perform_destroy, NULL, &args, cancellable, err); + g_object_unref (session); + return ret; +} + +/** + * gck_object_destroy_async: + * @self: The object to destroy. + * @cancellable: Optional cancellable object, or NULL to ignore. + * @callback: Callback which is called when operation completes. + * @user_data: Data to pass to the callback. + * + * Destroy a PKCS#11 object, deleting it from storage or the session. + * This call will return immediately and complete asynchronously. + **/ +void +gck_object_destroy_async (GckObject *self, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data) +{ + GckObjectData *data = GCK_OBJECT_GET_DATA (self); + Destroy* args; + GckCall *call; + + g_return_if_fail (GCK_IS_OBJECT (self)); + g_return_if_fail (GCK_IS_SLOT (data->slot)); + + args = _gck_call_async_prep (data->slot, self, perform_destroy, NULL, sizeof (*args), NULL); + args->object = data->handle; + + call = _gck_call_async_ready (args, cancellable, callback, user_data); + require_session_async (self, call, CKF_RW_SESSION, cancellable); +} + +/** + * gck_object_destroy_finish: + * @self: The object being destroyed. + * @result: The result of the destory operation passed to the callback. + * @err: A location to store an error. + * + * Get the status of the operation to destroy a PKCS#11 object, begun with + * gck_object_destroy_async(). + * + * Return value: Whether the object was destroyed successfully or not. + */ +gboolean +gck_object_destroy_finish (GckObject *self, GAsyncResult *result, GError **err) +{ + g_return_val_if_fail (GCK_IS_OBJECT (self), FALSE); + g_return_val_if_fail (GCK_IS_CALL (result), FALSE); + return _gck_call_basic_finish (result, err); +} + +/* -------------------------------------------------------------------------------------- + * SET ATTRIBUTES + */ + +typedef struct _SetAttributes { + GckArguments base; + GckAttributes *attrs; + CK_OBJECT_HANDLE object; +} SetAttributes; + +static CK_RV +perform_set_attributes (SetAttributes *args) +{ + CK_ATTRIBUTE_PTR attrs; + CK_ULONG n_attrs; + + g_assert (args); + attrs = _gck_attributes_commit_out (args->attrs, &n_attrs); + + return (args->base.pkcs11->C_SetAttributeValue) (args->base.handle, args->object, + attrs, n_attrs); +} + +static void +free_set_attributes (SetAttributes *args) +{ + g_assert (args); + gck_attributes_unref (args->attrs); + g_free (args); +} + +/** + * gck_object_set: + * @self: The object to set attributes on. + * @err: A location to return an error. + * @...: The attributes to set. + * + * Set PKCS#11 attributes on an object. + * This call may block for an indefinite period. + * + * The arguments must be triples of: attribute type, data type, value + * + * <para>The variable argument list should contain: + * <variablelist> + * <varlistentry> + * <term>a)</term> + * <listitem><para>The gulong attribute type (ie: CKA_LABEL). </para></listitem> + * </varlistentry> + * <varlistentry> + * <term>b)</term> + * <listitem><para>The attribute data type (one of GCK_BOOLEAN, GCK_ULONG, + * GCK_STRING, GCK_DATE) orthe raw attribute value length.</para></listitem> + * </varlistentry> + * <varlistentry> + * <term>c)</term> + * <listitem><para>The attribute value, either a gboolean, gulong, gchar*, GDate* or + * a pointer to a raw attribute value.</para></listitem> + * </varlistentry> + * </variablelist> + * The variable argument list should be terminated with GCK_INVALID.</para> + * + * Return value: Whether the call was successful or not. + **/ +gboolean +gck_object_set (GckObject *self, GError **err, ...) +{ + GckAttributes *attrs; + va_list va; + CK_RV rv; + + g_return_val_if_fail (GCK_IS_OBJECT (self), FALSE); + g_return_val_if_fail (!err || !*err, FALSE); + + va_start (va, err); + attrs = gck_attributes_new_valist (g_realloc, va); + va_end (va); + + rv = gck_object_set_full (self, attrs, NULL, err); + + gck_attributes_unref (attrs); + return rv; +} + +/** + * gck_object_set_full: + * @self: The object to set attributes on. + * @attrs: The attributes to set on the object. + * @cancellable: Optional cancellable object, or NULL to ignore. + * @err: A location to return an error. + * + * Set PKCS#11 attributes on an object. This call may block for an indefinite period. + * + * Return value: Whether the call was successful or not. + **/ +gboolean +gck_object_set_full (GckObject *self, GckAttributes *attrs, + GCancellable *cancellable, GError **err) +{ + GckObjectData *data = GCK_OBJECT_GET_DATA (self); + SetAttributes args; + GckSession *session; + gboolean ret = FALSE; + + g_return_val_if_fail (GCK_IS_OBJECT (self), FALSE); + g_return_val_if_fail (attrs, FALSE); + g_return_val_if_fail (!err || !*err, FALSE); + + _gck_attributes_lock (attrs); + + memset (&args, 0, sizeof (args)); + args.attrs = attrs; + args.object = data->handle; + + session = require_session_sync (self, CKF_RW_SESSION, err); + if (session) + ret = _gck_call_sync (session, perform_set_attributes, NULL, &args, cancellable, err); + + _gck_attributes_unlock (attrs); + g_object_unref (session); + return ret; +} + +/** + * gck_object_set_async: + * @self: The object to set attributes on. + * @attrs: The attributes to set on the object. + * @cancellable: Optional cancellable object, or NULL to ignore. + * @callback: Callback which is called when operation completes. + * @user_data: Data to pass to the callback. + * + * Set PKCS#11 attributes on an object. This call will return + * immediately and completes asynchronously. + **/ +void +gck_object_set_async (GckObject *self, GckAttributes *attrs, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data) +{ + GckObjectData *data = GCK_OBJECT_GET_DATA (self); + SetAttributes *args; + GckCall *call; + + g_return_if_fail (GCK_IS_OBJECT (self)); + g_return_if_fail (attrs); + + args = _gck_call_async_prep (data->slot, self, perform_set_attributes, + NULL, sizeof (*args), free_set_attributes); + + _gck_attributes_lock (attrs); + args->attrs = gck_attributes_ref (attrs); + args->object = data->handle; + + call = _gck_call_async_ready (args, cancellable, callback, user_data); + require_session_async (self, call, CKF_RW_SESSION, cancellable); +} + +/** + * gck_object_set_finish: + * @self: The object to set attributes on. + * @result: The result of the destory operation passed to the callback. + * @err: A location to store an error. + * + * Get the status of the operation to set attributes on a PKCS#11 object, + * begun with gck_object_set_async(). + * + * Return value: Whether the attributes were successfully set on the object or not. + */ +gboolean +gck_object_set_finish (GckObject *self, GAsyncResult *result, GError **err) +{ + SetAttributes *args; + + g_return_val_if_fail (GCK_IS_OBJECT (self), FALSE); + g_return_val_if_fail (GCK_IS_CALL (result), FALSE); + g_return_val_if_fail (!err || !*err, FALSE); + + /* Unlock the attributes we were using */ + args = _gck_call_arguments (result, SetAttributes); + g_assert (args->attrs); + _gck_attributes_unlock (args->attrs); + + return _gck_call_basic_finish (result, err); +} + +/* ------------------------------------------------------------------------------------ + * GET ATTRIBUTES + */ + +typedef struct _GetAttributes { + GckArguments base; + CK_OBJECT_HANDLE object; + GckAttributes *attrs; +} GetAttributes; + +/* + * Certain failure return values only apply to individual attributes + * being retrieved. These are ignored, since the attribute should + * already have -1 set as the length. + */ +static gboolean +is_ok_get_attributes_rv (CK_RV rv) +{ + switch (rv) { + case CKR_OK: + case CKR_ATTRIBUTE_SENSITIVE: + case CKR_ATTRIBUTE_TYPE_INVALID: + return TRUE; + default: + return FALSE; + } +} + +static CK_RV +perform_get_attributes (GetAttributes *args) +{ + CK_ATTRIBUTE_PTR attrs; + CK_ULONG n_attrs; + CK_RV rv; + + g_assert (args); + g_assert (args->attrs); + + /* Prepare all the attributes */ + attrs = _gck_attributes_prepare_in (args->attrs, &n_attrs); + + /* Get the size of each value */ + rv = (args->base.pkcs11->C_GetAttributeValue) (args->base.handle, args->object, + attrs, n_attrs); + if (!is_ok_get_attributes_rv (rv)) + return rv; + + /* Allocate memory for each value */ + attrs = _gck_attributes_commit_in (args->attrs, &n_attrs); + + /* Now get the actual values */ + rv = (args->base.pkcs11->C_GetAttributeValue) (args->base.handle, args->object, + attrs, n_attrs); + + if (is_ok_get_attributes_rv (rv)) + rv = CKR_OK; + + return rv; +} + +static void +free_get_attributes (GetAttributes *args) +{ + g_assert (args); + g_assert (args->attrs); + gck_attributes_unref (args->attrs); + g_free (args); +} + + +/** + * gck_object_get: + * @self: The object to get attributes from. + * @err: A location to store an error. + * @...: The attribute types to get. + * + * Get the specified attributes from the object. This call may + * block for an indefinite period. + * + * Return value: The resulting PKCS#11 attributes, or NULL if an error occurred. + * The result must be unreffed when you're finished with it. + **/ +GckAttributes* +gck_object_get (GckObject *self, GError **err, ...) +{ + GckAttributes *attrs; + va_list va; + gulong type; + + g_return_val_if_fail (GCK_IS_OBJECT (self), NULL); + g_return_val_if_fail (!err || !*err, NULL); + + attrs = gck_attributes_new (); + va_start (va, err); + for (;;) { + type = va_arg (va, gulong); + if (type == GCK_INVALID) + break; + gck_attributes_add_invalid (attrs, type); + } + va_end (va); + + if (!gck_object_get_full (self, attrs, NULL, err)) { + gck_attributes_unref (attrs); + return NULL; + } + + return attrs; +} + +/** + * gck_object_get_full: + * @self: The object to get attributes from. + * @attrs: The attributes to get, with the types filled in. + * @cancellable: Optional cancellation object, or NULL. + * @err: A location to store an error. + * + * Get the specified attributes from the object. This call may + * block for an indefinite period. + * + * No extra references are added to the returned attributes pointer. + * During this call you may not access the attributes in any way. + * + * Return value: A pointer to the filled in attributes if successful, + * or NULL if not. + **/ +GckAttributes* +gck_object_get_full (GckObject *self, GckAttributes *attrs, + GCancellable *cancellable, GError **err) +{ + GckObjectData *data = GCK_OBJECT_GET_DATA (self); + GetAttributes args; + GckSession *session; + gboolean ret; + + g_return_val_if_fail (GCK_IS_OBJECT (self), NULL); + g_return_val_if_fail (attrs, NULL); + g_return_val_if_fail (!err || !*err, NULL); + + session = require_session_sync (self, 0, err); + if (!session) + return NULL; + + _gck_attributes_lock (attrs); + + memset (&args, 0, sizeof (args)); + args.attrs = attrs; + args.object = data->handle; + + ret = _gck_call_sync (session, perform_get_attributes, NULL, &args, cancellable, err); + _gck_attributes_unlock (attrs); + g_object_unref (session); + + return ret ? attrs : NULL; +} + +/** + * gck_object_get_async: + * @self: The object to get attributes from. + * @attrs: The attributes to get, initialized with their types. + * @cancellable: Optional cancellation object, or NULL. + * @callback: A callback which is called when the operation completes. + * @user_data: Data to be passed to the callback. + * + * Get the specified attributes from the object. The attributes will be cleared + * of their current values, and new attributes will be stored. The attributes + * should not be accessed in any way except for referencing and unreferencing + * them until gck_object_get_finish() is called. + * + * This call returns immediately and completes asynchronously. + **/ +void +gck_object_get_async (GckObject *self, GckAttributes *attrs, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data) +{ + GckObjectData *data = GCK_OBJECT_GET_DATA (self); + GetAttributes *args; + GckCall *call; + + g_return_if_fail (GCK_IS_OBJECT (self)); + g_return_if_fail (attrs); + + args = _gck_call_async_prep (data->slot, self, perform_get_attributes, + NULL, sizeof (*args), free_get_attributes); + + _gck_attributes_lock (attrs); + args->attrs = gck_attributes_ref (attrs); + args->object = data->handle; + + call = _gck_call_async_ready (args, cancellable, callback, user_data); + require_session_async (self, call, 0, cancellable); +} + +/** + * gck_object_get_finish: + * @self: The object to get attributes from. + * @result: The result passed to the callback. + * @err: A location to store an error. + * + * Get the result of a get operation and return specified attributes from + * the object. + * + * No extra references are added to the returned attributes pointer. + * + * Return value: The filled in attributes structure if successful or + * NULL if not successful. + **/ +GckAttributes* +gck_object_get_finish (GckObject *self, GAsyncResult *result, GError **err) +{ + GetAttributes *args; + + g_return_val_if_fail (GCK_IS_OBJECT (self), NULL); + g_return_val_if_fail (GCK_IS_CALL (result), NULL); + g_return_val_if_fail (!err || !*err, NULL); + + args = _gck_call_arguments (result, GetAttributes); + _gck_attributes_unlock (args->attrs); + + if (!_gck_call_basic_finish (result, err)) + return NULL; + + return args->attrs; +} + +/* --------------------------------------------------------------------------------- + * GET ATTRIBUTE DATA + */ + +typedef struct _GetAttributeData { + GckArguments base; + CK_OBJECT_HANDLE object; + CK_ATTRIBUTE_TYPE type; + GckAllocator allocator; + guchar *result; + gsize n_result; +} GetAttributeData; + +static CK_RV +perform_get_attribute_data (GetAttributeData *args) +{ + CK_ATTRIBUTE attr; + CK_RV rv; + + g_assert (args); + g_assert (args->allocator); + + attr.type = args->type; + attr.ulValueLen = 0; + attr.pValue = 0; + + /* Get the size of the value */ + rv = (args->base.pkcs11->C_GetAttributeValue) (args->base.handle, args->object, + &attr, 1); + if (rv != CKR_OK) + return rv; + + /* Allocate memory for the value */ + args->result = (args->allocator) (NULL, attr.ulValueLen + 1); + g_assert (args->result); + attr.pValue = args->result; + + /* Now get the actual value */ + rv = (args->base.pkcs11->C_GetAttributeValue) (args->base.handle, args->object, + &attr, 1); + + if (rv == CKR_OK) { + args->n_result = attr.ulValueLen; + args->result[args->n_result] = 0; + } + + return rv; +} + +static void +free_get_attribute_data (GetAttributeData *args) +{ + g_assert (args); + g_free (args->result); + g_free (args); +} + +/** + * gck_object_get_data: + * @self: The object to get attribute data from. + * @attr_type: The attribute to get data for. + * @n_data: The length of the resulting data. + * @err: A location to store an error. + * + * Get the data for the specified attribute from the object. For convenience + * the returned data has a null terminator. + * + * This call may block for an indefinite period. + * + * Return value: The resulting PKCS#11 attribute data, or NULL if an error occurred. + **/ +gpointer +gck_object_get_data (GckObject *self, gulong attr_type, gsize *n_data, GError **err) +{ + g_return_val_if_fail (GCK_IS_OBJECT (self), NULL); + g_return_val_if_fail (n_data, NULL); + g_return_val_if_fail (!err || !*err, NULL); + + return gck_object_get_data_full (self, attr_type, g_realloc, NULL, n_data, err); +} + +/** + * gck_object_get_data_full: + * @self: The object to get attribute data from. + * @attr_type: The attribute to get data for. + * @allocator: An allocator with which to allocate memory for the data, or NULL for default. + * @cancellable: Optional cancellation object, or NULL. + * @n_data: The length of the resulting data. + * @err: A location to store an error. + * + * Get the data for the specified attribute from the object. For convenience + * the returned data has an extra null terminator, not included in the returned length. + * + * This call may block for an indefinite period. + * + * Return value: The resulting PKCS#11 attribute data, or NULL if an error occurred. + **/ +gpointer +gck_object_get_data_full (GckObject *self, gulong attr_type, GckAllocator allocator, + GCancellable *cancellable, gsize *n_data, GError **err) +{ + GckObjectData *data = GCK_OBJECT_GET_DATA (self); + GetAttributeData args; + GckSession *session; + gboolean ret; + + g_return_val_if_fail (GCK_IS_OBJECT (self), NULL); + g_return_val_if_fail (n_data, NULL); + g_return_val_if_fail (!err || !*err, NULL); + + if (!allocator) + allocator = g_realloc; + + session = require_session_sync (self, 0, err); + if (!session) + return NULL; + + memset (&args, 0, sizeof (args)); + args.allocator = allocator; + args.object = data->handle; + args.type = attr_type; + + ret = _gck_call_sync (session, perform_get_attribute_data, NULL, &args, cancellable, err); + g_object_unref (session); + + /* Free any value if failed */ + if (!ret) { + if (args.result) + (allocator) (args.result, 0); + return NULL; + } + + *n_data = args.n_result; + return args.result; +} + +/** + * gck_object_get_data_async: + * @self: The object to get attribute data from. + * @attr_type: The attribute to get data for. + * @allocator: An allocator with which to allocate memory for the data, or NULL for default. + * @cancellable: Optional cancellation object, or NULL. + * @callback: Called when the operation completes. + * @user_data: Data to be passed to the callback. + * + * Get the data for the specified attribute from the object. + * + * This call will return immediately and complete asynchronously. + **/ +void +gck_object_get_data_async (GckObject *self, gulong attr_type, GckAllocator allocator, + GCancellable *cancellable, GAsyncReadyCallback callback, + gpointer user_data) +{ + GckObjectData *data = GCK_OBJECT_GET_DATA (self); + GetAttributeData *args; + GckCall *call; + + g_return_if_fail (GCK_IS_OBJECT (self)); + + if (!allocator) + allocator = g_realloc; + + args = _gck_call_async_prep (data->slot, self, perform_get_attribute_data, + NULL, sizeof (*args), free_get_attribute_data); + + args->allocator = allocator; + args->object = data->handle; + args->type = attr_type; + + call = _gck_call_async_ready (args, cancellable, callback, user_data); + require_session_async (self, call, 0, cancellable); +} + +/** + * gck_object_get_data_finish: + * @self: The object to get an attribute from. + * @result: The result passed to the callback. + * @n_data: The length of the resulting data. + * @err: A location to store an error. + * + * Get the result of an operation to get attribute data from + * an object. For convenience the returned data has an extra null terminator, + * not included in the returned length. + * + * + * Return value: The PKCS#11 attribute data or NULL if an error occurred. + **/ +gpointer +gck_object_get_data_finish (GckObject *self, GAsyncResult *result, + gsize *n_data, GError **err) +{ + GetAttributeData *args; + guchar *data; + + g_return_val_if_fail (GCK_IS_OBJECT (self), NULL); + g_return_val_if_fail (GCK_IS_CALL (result), NULL); + g_return_val_if_fail (n_data, NULL); + g_return_val_if_fail (!err || !*err, NULL); + + if (!_gck_call_basic_finish (result, err)) + return NULL; + + args = _gck_call_arguments (result, GetAttributeData); + + *n_data = args->n_result; + data = args->result; + args->result = NULL; + + return data; +} + +/* --------------------------------------------------------------------------------------- + * SET TEMPLATE + */ + +typedef struct _set_template_args { + GckArguments base; + CK_OBJECT_HANDLE object; + CK_ATTRIBUTE_TYPE type; + GckAttributes *attrs; +} set_template_args; + +static CK_RV +perform_set_template (set_template_args *args) +{ + CK_ATTRIBUTE attr; + CK_ULONG n_attrs; + + g_assert (args); + + attr.type = args->type; + attr.pValue = _gck_attributes_commit_out (args->attrs, &n_attrs); + attr.ulValueLen = n_attrs * sizeof (CK_ATTRIBUTE); + + return (args->base.pkcs11->C_SetAttributeValue) (args->base.handle, args->object, &attr, 1); +} + +static void +free_set_template (set_template_args *args) +{ + g_assert (args); + gck_attributes_unref (args->attrs); + g_free (args); +} + +/** + * gck_object_set_template: + * @self: The object to set an attribute template on. + * @attr_type: The attribute template type. + * @attrs: The attribute template. + * @err: A location to store an error. + * + * Set an attribute template on the object. The attr_type must be for + * an attribute which contains a template. + * + * This call may block for an indefinite period. + * + * Return value: TRUE if the operation succeeded. + **/ +gboolean +gck_object_set_template (GckObject *self, gulong attr_type, GckAttributes *attrs, + GError **err) +{ + g_return_val_if_fail (GCK_IS_OBJECT (self), FALSE); + g_return_val_if_fail (!err || !*err, FALSE); + return gck_object_set_template_full (self, attr_type, attrs, NULL, err); +} + +/** + * gck_object_set_template_full: + * @self: The object to set an attribute template on. + * @attr_type: The attribute template type. + * @attrs: The attribute template. + * @cancellable: Optional cancellation object, or NULL. + * @err: A location to store an error. + * + * Set an attribute template on the object. The attr_type must be for + * an attribute which contains a template. + * + * This call may block for an indefinite period. + * + * Return value: TRUE if the operation succeeded. + **/ +gboolean +gck_object_set_template_full (GckObject *self, gulong attr_type, GckAttributes *attrs, + GCancellable *cancellable, GError **err) +{ + GckObjectData *data = GCK_OBJECT_GET_DATA (self); + set_template_args args; + GckSession *session; + gboolean ret = FALSE; + + g_return_val_if_fail (GCK_IS_OBJECT (self), FALSE); + g_return_val_if_fail (attrs, FALSE); + g_return_val_if_fail (!err || !*err, FALSE); + + _gck_attributes_lock (attrs); + + memset (&args, 0, sizeof (args)); + args.attrs = attrs; + args.type = attr_type; + args.object = data->handle; + + session = require_session_sync (self, CKF_RW_SESSION, err); + if (session) + ret = _gck_call_sync (session, perform_set_template, NULL, &args, cancellable, err); + + _gck_attributes_unlock (attrs); + g_object_unref (session); + return ret; +} + +/** + * gck_object_set_template_async: + * @self: The object to set an attribute template on. + * @attr_type: The attribute template type. + * @attrs: The attribute template. + * @cancellable: Optional cancellation object, or NULL. + * @callback: Called when the operation completes. + * @user_data: Data to be passed to the callback. + * + * Set an attribute template on the object. The attr_type must be for + * an attribute which contains a template. + * + * This call will return immediately and complete asynchronously. + **/ +void +gck_object_set_template_async (GckObject *self, gulong attr_type, GckAttributes *attrs, + GCancellable *cancellable, GAsyncReadyCallback callback, + gpointer user_data) +{ + GckObjectData *data = GCK_OBJECT_GET_DATA (self); + set_template_args *args; + GckCall *call; + + g_return_if_fail (GCK_IS_OBJECT (self)); + g_return_if_fail (attrs); + + args = _gck_call_async_prep (data->slot, self, perform_set_template, + NULL, sizeof (*args), free_set_template); + + _gck_attributes_lock (attrs); + args->attrs = gck_attributes_ref (attrs); + args->type = attr_type; + args->object = data->handle; + + call = _gck_call_async_ready (args, cancellable, callback, user_data); + require_session_async (self, call, CKF_RW_SESSION, cancellable); +} + +/** + * gck_object_set_template_finish: + * @self: The object to set an attribute template on. + * @result: The result passed to the callback. + * @err: A location to store an error. + * + * Get the result of an operation to set attribute template on + * an object. + * + * Return value: TRUE if the operation succeeded. + **/ +gboolean +gck_object_set_template_finish (GckObject *self, GAsyncResult *result, GError **err) +{ + set_template_args *args; + + g_return_val_if_fail (GCK_IS_OBJECT (self), FALSE); + g_return_val_if_fail (GCK_IS_CALL (result), FALSE); + g_return_val_if_fail (!err || !*err, FALSE); + + /* Unlock the attributes we were using */ + args = _gck_call_arguments (result, set_template_args); + g_assert (args->attrs); + _gck_attributes_unlock (args->attrs); + + return _gck_call_basic_finish (result, err); +} + +/* --------------------------------------------------------------------------------------- + * GET TEMPLATE + */ + +typedef struct _get_template_args { + GckArguments base; + CK_OBJECT_HANDLE object; + CK_ATTRIBUTE_TYPE type; + GckAttributes *attrs; +} get_template_args; + +static CK_RV +perform_get_template (get_template_args *args) +{ + CK_ATTRIBUTE attr; + CK_ULONG n_attrs, i; + CK_RV rv; + + g_assert (args); + g_assert (!args->attrs); + + args->attrs = gck_attributes_new (); + attr.type = args->type; + attr.ulValueLen = 0; + attr.pValue = 0; + + /* Get the length of the entire template */ + rv = (args->base.pkcs11->C_GetAttributeValue) (args->base.handle, args->object, &attr, 1); + if (rv != CKR_OK) + return rv; + + /* Number of attributes, rounded down */ + n_attrs = (attr.ulValueLen / sizeof (CK_ATTRIBUTE)); + for (i = 0; i < n_attrs; ++i) + gck_attributes_add_empty (args->attrs, 0); + + /* Prepare all the attributes */ + _gck_attributes_lock (args->attrs); + attr.pValue = _gck_attributes_prepare_in (args->attrs, &n_attrs); + + /* Get the size of each value */ + rv = (args->base.pkcs11->C_GetAttributeValue) (args->base.handle, args->object, &attr, 1); + if (rv != CKR_OK) + return rv; + + /* Allocate memory for each value */ + attr.pValue = _gck_attributes_commit_in (args->attrs, &n_attrs); + + /* Now get the actual values */ + return (args->base.pkcs11->C_GetAttributeValue) (args->base.handle, args->object, &attr, 1); +} + +static void +free_get_template (get_template_args *args) +{ + g_assert (args); + gck_attributes_unref (args->attrs); + g_free (args); +} + +/** + * gck_object_get_template: + * @self: The object to get an attribute template from. + * @attr_type: The attribute template type. + * @err: A location to store an error. + * + * Get an attribute template from the object. The attr_type must be for + * an attribute which returns a template. + * + * This call may block for an indefinite period. + * + * Return value: The resulting PKCS#11 attribute template, or NULL if an error occurred. + **/ +GckAttributes* +gck_object_get_template (GckObject *self, gulong attr_type, GError **err) +{ + g_return_val_if_fail (GCK_IS_OBJECT (self), NULL); + g_return_val_if_fail (!err || !*err, NULL); + + return gck_object_get_template_full (self, attr_type, NULL, err); +} + +/** + * gck_object_get_template_full: + * @self: The object to get an attribute template from. + * @attr_type: The template attribute type. + * @cancellable: Optional cancellation object, or NULL. + * @err: A location to store an error. + * + * Get an attribute template from the object. The attr_type must be for + * an attribute which returns a template. + * + * This call may block for an indefinite period. + * + * Return value: The resulting PKCS#11 attribute template, or NULL if an error occurred. + **/ +GckAttributes* +gck_object_get_template_full (GckObject *self, gulong attr_type, + GCancellable *cancellable, GError **err) +{ + GckObjectData *data = GCK_OBJECT_GET_DATA (self); + get_template_args args; + GckSession *session; + gboolean ret; + + g_return_val_if_fail (GCK_IS_OBJECT (self), NULL); + g_return_val_if_fail (!err || !*err, NULL); + + session = require_session_sync (self, 0, err); + if (!session) + return NULL; + + memset (&args, 0, sizeof (args)); + args.object = data->handle; + args.type = attr_type; + + ret = _gck_call_sync (session, perform_get_template, NULL, &args, cancellable, err); + g_object_unref (session); + + _gck_attributes_unlock (args.attrs); + + /* Free any value if failed */ + if (!ret) { + gck_attributes_unref (args.attrs); + args.attrs = NULL; + } + + return args.attrs; +} + +/** + * gck_object_get_template_async: + * @self: The object to get an attribute template from. + * @attr_type: The template attribute type. + * @cancellable: Optional cancellation object, or NULL. + * @callback: Called when the operation completes. + * @user_data: Data to be passed to the callback. + * + * Get an attribute template from the object. The attr_type must be for + * an attribute which returns a template. + * + * This call will return immediately and complete asynchronously. + **/ +void +gck_object_get_template_async (GckObject *self, gulong attr_type, + GCancellable *cancellable, GAsyncReadyCallback callback, + gpointer user_data) +{ + GckObjectData *data = GCK_OBJECT_GET_DATA (self); + get_template_args *args; + GckCall *call; + + g_return_if_fail (GCK_IS_OBJECT (self)); + + args = _gck_call_async_prep (data->slot, self, perform_get_template, + NULL, sizeof (*args), free_get_template); + + args->object = data->handle; + args->type = attr_type; + + call = _gck_call_async_ready (args, cancellable, callback, user_data); + require_session_async (self, call, 0, cancellable); +} + +/** + * gck_object_get_template_finish: + * @self: The object to get an attribute from. + * @result: The result passed to the callback. + * @err: A location to store an error. + * + * Get the result of an operation to get attribute template from + * an object. + * + * Return value: The resulting PKCS#11 attribute template, or NULL if an error occurred. + **/ +GckAttributes* +gck_object_get_template_finish (GckObject *self, GAsyncResult *result, + GError **err) +{ + get_template_args *args; + + g_return_val_if_fail (GCK_IS_OBJECT (self), NULL); + g_return_val_if_fail (GCK_IS_CALL (result), NULL); + g_return_val_if_fail (!err || !*err, NULL); + + if (!_gck_call_basic_finish (result, err)) + return NULL; + + args = _gck_call_arguments (result, get_template_args); + _gck_attributes_unlock (args->attrs); + return gck_attributes_ref (args->attrs); +} diff --git a/gck/gck-private.h b/gck/gck-private.h new file mode 100644 index 00000000..f30f21c3 --- /dev/null +++ b/gck/gck-private.h @@ -0,0 +1,162 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* gck-private.h - 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, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter <nielsen@memberwebs.com> +*/ + +#ifndef GCK_PRIVATE_H_ +#define GCK_PRIVATE_H_ + +#include "gck.h" + +#include <glib.h> +#include <glib-object.h> +#include <gio/gio.h> + +G_BEGIN_DECLS + +/* --------------------------------------------------------------------------- + * ATTRIBUTE INTERNALS + */ + +void _gck_attributes_lock (GckAttributes *attrs); + +void _gck_attributes_unlock (GckAttributes *attrs); + +CK_ATTRIBUTE_PTR _gck_attributes_prepare_in (GckAttributes *attrs, + CK_ULONG_PTR n_attrs); + +CK_ATTRIBUTE_PTR _gck_attributes_commit_in (GckAttributes *attrs, + CK_ULONG_PTR n_attrs); + +CK_ATTRIBUTE_PTR _gck_attributes_commit_out (GckAttributes *attrs, + CK_ULONG_PTR n_attrs); + +/* ---------------------------------------------------------------------------- + * MISC + */ + +guint _gck_ulong_hash (gconstpointer v); + +gboolean _gck_ulong_equal (gconstpointer v1, + gconstpointer v2); + +/* ---------------------------------------------------------------------------- + * MODULE + */ + +gboolean _gck_module_fire_authenticate_slot (GckModule *module, + GckSlot *slot, + gchar *label, + gchar **password); + +gboolean _gck_module_fire_authenticate_object (GckModule *module, + GckObject *object, + gchar *label, + gchar **password); + +gboolean _gck_module_pool_session_handle (GckSession *session, + CK_SESSION_HANDLE handle, + GckModule *self); + +CK_SESSION_HANDLE _gck_module_pooled_session_handle (GckModule *module, + CK_SLOT_ID slot, + gulong flags); + +/* ---------------------------------------------------------------------------- + * SLOT + */ + +GckObject* _gck_slot_object_from_handle (GckSlot *slot, + CK_OBJECT_HANDLE handle); + +/* ---------------------------------------------------------------------------- + * CALL + */ + +typedef CK_RV (*GckPerformFunc) (gpointer call_data); +typedef gboolean (*GckCompleteFunc) (gpointer call_data, CK_RV result); + +typedef struct _GckCall GckCall; + +typedef struct _GckArguments { + GckCall *call; + + /* For the call function to use */ + CK_FUNCTION_LIST_PTR pkcs11; + CK_ULONG handle; + +} GckArguments; + +#define GCK_ARGUMENTS_INIT { NULL, NULL, 0 } + +#define GCK_TYPE_CALL (_gck_call_get_type()) +#define GCK_CALL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GCK_TYPE_CALL, GckCall)) +#define GCK_CALL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GCK_TYPE_CALL, GckCall)) +#define GCK_IS_CALL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GCK_TYPE_CALL)) +#define GCK_IS_CALL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GCK_TYPE_CALL)) +#define GCK_CALL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GCK_TYPE_CALL, GckCallClass)) + +typedef struct _GckCallClass GckCallClass; + +GType _gck_call_get_type (void) G_GNUC_CONST; + +#define _gck_call_arguments(call, type) (type*)(_gck_call_get_arguments (GCK_CALL (call))) + +gpointer _gck_call_get_arguments (GckCall *call); + +void _gck_call_uninitialize (void); + +gboolean _gck_call_sync (gpointer object, + gpointer perform, + gpointer complete, + gpointer args, + GCancellable *cancellable, + GError **err); + +gpointer _gck_call_async_prep (gpointer object, + gpointer cb_object, + gpointer perform, + gpointer complete, + gsize args_size, + gpointer destroy_func); + +GckCall* _gck_call_async_ready (gpointer args, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +void _gck_call_async_go (GckCall *call); + +void _gck_call_async_ready_go (gpointer args, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +void _gck_call_async_short (GckCall *call, + CK_RV rv); + +gboolean _gck_call_basic_finish (GAsyncResult *result, + GError **err); + +void _gck_call_async_object (GckCall *call, + gpointer object); + +#endif /* GCK_PRIVATE_H_ */ diff --git a/gck/gck-session.c b/gck/gck-session.c new file mode 100644 index 00000000..dc34f2f4 --- /dev/null +++ b/gck/gck-session.c @@ -0,0 +1,2886 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* gck-session.h - 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, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter <nielsen@memberwebs.com> +*/ + +#include "config.h" + +#include "gck.h" +#include "gck-marshal.h" +#include "gck-private.h" + +#include <string.h> + +#include <glib/gi18n.h> + +/** + * SECTION:gck-session + * @title: GckSession + * @short_description: Represents an open PKCS11 session. + * + * Before performing any PKCS11 operations, a session must be opened. This is + * analogous to an open database handle, or a file handle. + */ + +/** + * GckSession: + * + * Represents an open PKCS11 session. + */ + +/** + * GckMechanism: + * @type: The mechanism type + * @parameter: Mechanism specific data. + * @n_parameter: Length of mechanism specific data. + * + * Represents a mechanism used with crypto operations. + */ + +enum { + DISCARD_HANDLE, + LAST_SIGNAL +}; + +enum { + PROP_0, + PROP_MODULE, + PROP_HANDLE, + PROP_SLOT +}; + +typedef struct _GckSessionData { + GckSlot *slot; + GckModule *module; + CK_SESSION_HANDLE handle; +} GckSessionData; + +typedef struct _GckSessionPrivate { + + /* Remain same from init to finalize */ + GckSessionData data; + + /* Modified atomically */ + gint discarded; + gint auto_login; + +} GckSessionPrivate; + +#define GCK_SESSION_GET_DATA(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), GCK_TYPE_SESSION, GckSessionData)) + +G_DEFINE_TYPE (GckSession, gck_session, G_TYPE_OBJECT); + +static guint signals[LAST_SIGNAL] = { 0 }; + +/* ---------------------------------------------------------------------------- + * HELPERS + */ + +static GckSessionPrivate* +lock_private (gpointer obj) +{ + GckSessionPrivate *pv; + GckSession *self; + + g_return_val_if_fail (GCK_IS_SESSION (obj), NULL); + self = GCK_SESSION (obj); + + g_object_ref (self); + + pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_SESSION, GckSessionPrivate); + /* g_static_mutex_lock (&pv->mutex); */ + + return pv; +} + +static void +unlock_private (gpointer obj, GckSessionPrivate *pv) +{ + GckSession *self; + + g_assert (pv); + g_assert (GCK_IS_SESSION (obj)); + + self = GCK_SESSION (obj); + + g_assert (G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_SESSION, GckSessionPrivate) == pv); + + /* g_static_mutex_unlock (&pv->mutex); */ + g_object_unref (self); +} + +/* ---------------------------------------------------------------------------- + * OBJECT + */ + +static gboolean +gck_session_real_discard_handle (GckSession *self, CK_OBJECT_HANDLE handle) +{ + GckSessionData *data = GCK_SESSION_GET_DATA (self); + CK_FUNCTION_LIST_PTR funcs; + CK_RV rv; + + /* The default functionality, close the handle */ + + g_return_val_if_fail (data->module, FALSE); + g_object_ref (data->module); + + funcs = gck_module_get_functions (data->module); + g_return_val_if_fail (funcs, FALSE); + + rv = (funcs->C_CloseSession) (handle); + if (rv != CKR_OK) { + g_warning ("couldn't close session properly: %s", + gck_message_from_rv (rv)); + } + + g_object_unref (data->module); + return TRUE; +} + +static void +gck_session_init (GckSession *self) +{ + +} + +static void +gck_session_get_property (GObject *obj, guint prop_id, GValue *value, + GParamSpec *pspec) +{ + GckSession *self = GCK_SESSION (obj); + + switch (prop_id) { + case PROP_MODULE: + g_value_take_object (value, gck_session_get_module (self)); + break; + case PROP_HANDLE: + g_value_set_ulong (value, gck_session_get_handle (self)); + break; + case PROP_SLOT: + g_value_take_object (value, gck_session_get_slot (self)); + break; + } +} + +static void +gck_session_set_property (GObject *obj, guint prop_id, const GValue *value, + GParamSpec *pspec) +{ + GckSessionData *data = GCK_SESSION_GET_DATA (obj); + + /* Only valid calls are from constructor */ + + switch (prop_id) { + case PROP_MODULE: + g_return_if_fail (!data->module); + data->module = g_value_dup_object (value); + g_return_if_fail (data->module); + break; + case PROP_HANDLE: + g_return_if_fail (!data->handle); + data->handle = g_value_get_ulong (value); + break; + case PROP_SLOT: + g_return_if_fail (!data->slot); + data->slot = g_value_dup_object (value); + g_return_if_fail (data->slot); + break; + } +} + +static void +gck_session_dispose (GObject *obj) +{ + GckSessionPrivate *pv; + GckSession *self = GCK_SESSION (obj); + gboolean handled; + gint discarded; + + g_return_if_fail (GCK_IS_SESSION (self)); + + pv = lock_private (obj); + + { + discarded = g_atomic_int_get (&pv->discarded); + if (!discarded && g_atomic_int_compare_and_exchange (&pv->discarded, discarded, 1)) { + + /* + * Let the world know that we're discarding the session + * handle. This allows session reuse to work. + */ + + g_signal_emit_by_name (self, "discard-handle", pv->data.handle, &handled); + g_return_if_fail (handled); + } + + } + + unlock_private (obj, pv); + + G_OBJECT_CLASS (gck_session_parent_class)->dispose (obj); +} + +static void +gck_session_finalize (GObject *obj) +{ + GckSessionPrivate *pv = G_TYPE_INSTANCE_GET_PRIVATE (obj, GCK_TYPE_SESSION, GckSessionPrivate); + GckSessionData *data = GCK_SESSION_GET_DATA (obj); + + g_assert (pv->discarded != 0); + + if (data->slot) + g_object_unref (data->slot); + data->slot = NULL; + + if (data->module) + g_object_unref (data->module); + data->module = NULL; + + G_OBJECT_CLASS (gck_session_parent_class)->finalize (obj); +} + +static void +gck_session_class_init (GckSessionClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*)klass; + gck_session_parent_class = g_type_class_peek_parent (klass); + + gobject_class->get_property = gck_session_get_property; + gobject_class->set_property = gck_session_set_property; + gobject_class->dispose = gck_session_dispose; + gobject_class->finalize = gck_session_finalize; + + klass->discard_handle = gck_session_real_discard_handle; + + /** + * GckSession:module: + * + * The GckModule that this session is opened on. + */ + g_object_class_install_property (gobject_class, PROP_MODULE, + g_param_spec_object ("module", "Module", "PKCS11 Module", + GCK_TYPE_MODULE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /** + * GckSession:handle: + * + * The raw CK_SESSION_HANDLE handle of this session. + */ + g_object_class_install_property (gobject_class, PROP_HANDLE, + g_param_spec_ulong ("handle", "Session Handle", "PKCS11 Session Handle", + 0, G_MAXULONG, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /** + * GckSession:slot: + * + * The GckSlot this session is opened on. + */ + g_object_class_install_property (gobject_class, PROP_SLOT, + g_param_spec_object ("slot", "Slot that this session uses", "PKCS11 Slot", + GCK_TYPE_SLOT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /** + * GckSession::discard-handle: + * @session: The session. + * @handle: The handle being discarded. + * + * When a GckSession is being disposed of it emits this signal to allow + * a session pool to pick up the handle and keep it around. + * + * If no signal handler claims the handle, then it is closed. This is used by + * gck_module_set_pool_sessions() to implement the module session pool. + * + * Returns: Whether or not this handle was claimed. + */ + signals[DISCARD_HANDLE] = g_signal_new ("discard-handle", GCK_TYPE_SESSION, + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GckSessionClass, discard_handle), + g_signal_accumulator_true_handled, NULL, + _gck_marshal_BOOLEAN__ULONG, G_TYPE_BOOLEAN, 1, G_TYPE_ULONG); + + g_type_class_add_private (klass, sizeof (GckSessionPrivate)); +} + +/* ---------------------------------------------------------------------------- + * PUBLIC + */ + +/** + * GckSessionInfo: + * @slot_id: The handle of the PKCS11 slot that this session is opened on. + * @state: The user login state of the session. + * @flags: Various PKCS11 flags. + * @device_error: The last device error that occurred from an operation on this session. + * + * Information about the session. This is analogous to a CK_SESSION_INFO structure. + * + * When done with this structure, release it using gck_session_info_free(). + */ + +/** + * gck_session_info_free: + * @session_info: Session info to free. + * + * Free the GckSessionInfo structure and all associated memory. + **/ +void +gck_session_info_free (GckSessionInfo *session_info) +{ + if (!session_info) + return; + g_free (session_info); +} + +/** + * gck_session_from_handle: + * @slot: The slot which the session belongs to. + * @handle: The raw PKCS#11 handle of the session. + * + * Initialize a GckSession object from a raw PKCS#11 session handle. + * Usually one would use the gck_slot_open_session() function to + * create a session. + * + * Return value: The new GckSession object. + **/ +GckSession* +gck_session_from_handle (GckSlot *slot, CK_SESSION_HANDLE handle) +{ + GckModule *module; + GckSession *session; + + g_return_val_if_fail (GCK_IS_SLOT (slot), NULL); + + module = gck_slot_get_module (slot); + session = g_object_new (GCK_TYPE_SESSION, "module", module, + "handle", handle, "slot", slot, NULL); + g_object_unref (module); + + return session; +} + +/** + * gck_session_get_handle: + * @self: The session object. + * + * Get the raw PKCS#11 session handle from a GckSession object. + * + * Return value: The raw session handle. + **/ +CK_SESSION_HANDLE +gck_session_get_handle (GckSession *self) +{ + GckSessionData *data = GCK_SESSION_GET_DATA (self); + g_return_val_if_fail (GCK_IS_SESSION (self), (CK_SESSION_HANDLE)-1); + return data->handle; +} + +/** + * gck_session_get_module: + * @self: The session object. + * + * Get the PKCS#11 module to which this session belongs. + * + * Return value: The module, which should be unreffed after use. + **/ +GckModule* +gck_session_get_module (GckSession *self) +{ + GckSessionData *data = GCK_SESSION_GET_DATA (self); + g_return_val_if_fail (GCK_IS_SESSION (self), NULL); + g_return_val_if_fail (GCK_IS_MODULE (data->module), NULL); + return g_object_ref (data->module); +} + +/** + * gck_session_get_slot: + * @self: The session object. + * + * Get the PKCS#11 slot to which this session belongs. + * + * Return value: The slot, which should be unreffed after use. + **/ +GckSlot* +gck_session_get_slot (GckSession *self) +{ + GckSessionData *data = GCK_SESSION_GET_DATA (self); + g_return_val_if_fail (GCK_IS_SESSION (self), NULL); + g_return_val_if_fail (GCK_IS_SLOT (data->slot), NULL); + return g_object_ref (data->slot); +} + +/** + * gck_session_get_info: + * @self: The session object. + * + * Get information about the session. + * + * Return value: The session info. Use the gck_session_info_free() to release + * when done. + **/ +GckSessionInfo* +gck_session_get_info (GckSession *self) +{ + GckSessionData *data = GCK_SESSION_GET_DATA (self); + GckSessionInfo *sessioninfo; + CK_FUNCTION_LIST_PTR funcs; + CK_SESSION_INFO info; + CK_RV rv; + + g_return_val_if_fail (GCK_IS_SESSION (self), NULL); + g_return_val_if_fail (GCK_IS_MODULE (data->module), NULL); + + g_object_ref (data->module); + + funcs = gck_module_get_functions (data->module); + g_return_val_if_fail (funcs, NULL); + + memset (&info, 0, sizeof (info)); + rv = (funcs->C_GetSessionInfo) (data->handle, &info); + + g_object_unref (data->module); + + if (rv != CKR_OK) { + g_warning ("couldn't get session info: %s", gck_message_from_rv (rv)); + return NULL; + } + + sessioninfo = g_new0 (GckSessionInfo, 1); + sessioninfo->flags = info.flags; + sessioninfo->slot_id = info.slotID; + sessioninfo->state = info.state; + sessioninfo->device_error = info.ulDeviceError; + + return sessioninfo; +} + +/* --------------------------------------------------------------------------------------------- + * INIT PIN + */ + +typedef struct _InitPin { + GckArguments base; + guchar *pin; + gsize n_pin; +} InitPin; + + +static void +free_init_pin (InitPin *args) +{ + g_free (args->pin); + g_free (args); +} + +static CK_RV +perform_init_pin (InitPin *args) +{ + return (args->base.pkcs11->C_InitPIN) (args->base.handle, (CK_BYTE_PTR)args->pin, + args->n_pin); +} + +/** + * gck_session_init_pin: + * @self: Initialize PIN for this session's slot. + * @pin: The user's PIN, or NULL for protected authentication path. + * @n_pin: The length of the PIN. + * @err: A location to return an error. + * + * Initialize the user's pin on this slot that this session is opened on. + * According to the PKCS#11 standards, the session must be logged in with + * the CKU_SO user type. + * + * This call may block for an indefinite period. + * + * Return value: Whether successful or not. + **/ +gboolean +gck_session_init_pin (GckSession *self, const guchar *pin, gsize n_pin, + GError **err) +{ + return gck_session_init_pin_full (self, pin, n_pin, NULL, err); +} + +/** + * gck_session_init_pin_full: + * @self: Initialize PIN for this session's slot. + * @pin: The user's PIN, or NULL for protected authentication path. + * @n_pin: The length of the PIN. + * @cancellable: Optional cancellation object, or NULL. + * @err: A location to return an error. + * + * Initialize the user's pin on this slot that this session is opened on. + * According to the PKCS#11 standards, the session must be logged in with + * the CKU_SO user type. + * + * This call may block for an indefinite period. + * + * Return value: Whether successful or not. + **/ +gboolean +gck_session_init_pin_full (GckSession *self, const guchar *pin, gsize n_pin, + GCancellable *cancellable, GError **err) +{ + InitPin args = { GCK_ARGUMENTS_INIT, (guchar*)pin, n_pin }; + return _gck_call_sync (self, perform_init_pin, NULL, &args, cancellable, err); + +} + +/** + * gck_session_init_pin_async: + * @self: Initialize PIN for this session's slot. + * @pin: The user's PIN, or NULL for protected authentication path. + * @n_pin: The length of the PIN. + * @cancellable: Optional cancellation object, or NULL. + * @callback: Called when the operation completes. + * @user_data: Data to pass to the callback. + * + * Initialize the user's pin on this slot that this session is opened on. + * According to the PKCS#11 standards, the session must be logged in with + * the CKU_SO user type. + * + * This call will return immediately and completes asynchronously. + **/ +void +gck_session_init_pin_async (GckSession *self, const guchar *pin, gsize n_pin, + GCancellable *cancellable, GAsyncReadyCallback callback, + gpointer user_data) +{ + InitPin* args = _gck_call_async_prep (self, self, perform_init_pin, NULL, sizeof (*args), free_init_pin); + + args->pin = pin && n_pin ? g_memdup (pin, n_pin) : NULL; + args->n_pin = n_pin; + + _gck_call_async_ready_go (args, cancellable, callback, user_data); +} + +/** + * gck_session_init_pin_finish: + * @self: The session. + * @result: The result passed to the callback. + * @err: A location to return an error. + * + * Get the result of initializing a user's PIN. + * + * Return value: Whether the operation was successful or not. + **/ +gboolean +gck_session_init_pin_finish (GckSession *self, GAsyncResult *result, GError **err) +{ + return _gck_call_basic_finish (result, err); +} + + +/* --------------------------------------------------------------------------------------------- + * SET PIN + */ + +typedef struct _SetPin { + GckArguments base; + guchar *old_pin; + gsize n_old_pin; + guchar *new_pin; + gsize n_new_pin; +} SetPin; + +static void +free_set_pin (SetPin *args) +{ + g_free (args->old_pin); + g_free (args->new_pin); + g_free (args); +} + +static CK_RV +perform_set_pin (SetPin *args) +{ + return (args->base.pkcs11->C_SetPIN) (args->base.handle, (CK_BYTE_PTR)args->old_pin, + args->n_old_pin, args->new_pin, args->n_new_pin); +} + +/** + * gck_session_set_pin: + * @self: Change the PIN for this session's slot. + * @old_pin: The user's old PIN, or NULL for protected authentication path. + * @n_old_pin: The length of the PIN. + * @new_pin: The user's new PIN, or NULL for protected authentication path. + * @n_new_pin: The length of the PIN. + * @err: A location to return an error. + * + * Change the user's pin on this slot that this session is opened on. + * + * This call may block for an indefinite period. + * + * Return value: Whether successful or not. + **/ +gboolean +gck_session_set_pin (GckSession *self, const guchar *old_pin, gsize n_old_pin, + const guchar *new_pin, gsize n_new_pin, GError **err) +{ + return gck_session_set_pin_full (self, old_pin, n_old_pin, new_pin, n_new_pin, NULL, err); +} + +/** + * gck_session_set_pin_full: + * @self: Change the PIN for this session's slot. + * @old_pin: The user's old PIN, or NULL for protected authentication path. + * @n_old_pin: The length of the PIN. + * @new_pin: The user's new PIN, or NULL for protected authentication path. + * @n_new_pin: The length of the PIN. + * @cancellable: Optional cancellation object, or NULL. + * @err: A location to return an error. + * + * Change the user's pin on this slot that this session is opened on. + * + * This call may block for an indefinite period. + * + * Return value: Whether successful or not. + **/ +gboolean +gck_session_set_pin_full (GckSession *self, const guchar *old_pin, gsize n_old_pin, + const guchar *new_pin, gsize n_new_pin, GCancellable *cancellable, + GError **err) +{ + SetPin args = { GCK_ARGUMENTS_INIT, (guchar*)old_pin, n_old_pin, (guchar*)new_pin, n_new_pin }; + return _gck_call_sync (self, perform_set_pin, NULL, &args, cancellable, err); +} + +/** + * gck_session_set_pin_async: + * @self: Change the PIN for this session's slot. + * @old_pin: The user's old PIN, or NULL for protected authentication path. + * @n_old_pin: The length of the PIN. + * @new_pin: The user's new PIN, or NULL for protected authentication path. + * @n_new_pin: The length of the PIN. + * @cancellable: Optional cancellation object, or NULL. + * @callback: Called when the operation completes. + * @user_data: Data to pass to the callback. + * + * Change the user's pin on this slot that this session is opened on. + * + * This call will return immediately and completes asynchronously. + **/ +void +gck_session_set_pin_async (GckSession *self, const guchar *old_pin, gsize n_old_pin, + const guchar *new_pin, gsize n_new_pin, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data) +{ + SetPin* args = _gck_call_async_prep (self, self, perform_set_pin, NULL, sizeof (*args), free_set_pin); + + args->old_pin = old_pin && n_old_pin ? g_memdup (old_pin, n_old_pin) : NULL; + args->n_old_pin = n_old_pin; + args->new_pin = new_pin && n_new_pin ? g_memdup (new_pin, n_new_pin) : NULL; + args->n_new_pin = n_new_pin; + + _gck_call_async_ready_go (args, cancellable, callback, user_data); +} + +/** + * gck_session_set_pin_finish: + * @self: The session. + * @result: The result passed to the callback. + * @err: A location to return an error. + * + * Get the result of changing a user's PIN. + * + * Return value: Whether the operation was successful or not. + **/ +gboolean +gck_session_set_pin_finish (GckSession *self, GAsyncResult *result, GError **err) +{ + return _gck_call_basic_finish (result, err); +} + + +/* --------------------------------------------------------------------------------------------- + * LOGIN + */ + +typedef struct _Login { + GckArguments base; + gulong user_type; + guchar *pin; + gsize n_pin; +} Login; + +static void +free_login (Login *args) +{ + g_free (args->pin); + g_free (args); +} + +static CK_RV +perform_login (Login *args) +{ + return (args->base.pkcs11->C_Login) (args->base.handle, args->user_type, + (CK_BYTE_PTR)args->pin, args->n_pin); +} + +/** + * gck_session_login: + * @self: Log in to this session. + * @user_type: The type of login user. + * @pin: The user's PIN, or NULL for protected authentication path. + * @n_pin: The length of the PIN. + * @err: A location to return an error. + * + * Login the user on the session. This call may block + * for an indefinite period. + * + * Return value: Whether successful or not. + **/ +gboolean +gck_session_login (GckSession *self, gulong user_type, const guchar *pin, + gsize n_pin, GError **err) +{ + return gck_session_login_full (self, user_type, pin, n_pin, NULL, err); +} + +/** + * gck_session_login_full: + * @self: Log in to this session. + * @user_type: The type of login user. + * @pin: The user's PIN, or NULL for protected authentication path. + * @n_pin: The length of the PIN. + * @cancellable: Optional cancellation object, or NULL. + * @err: A location to return an error. + * + * Login the user on the session. This call may block for + * an indefinite period. + * + * Return value: Whether successful or not. + **/ +gboolean +gck_session_login_full (GckSession *self, gulong user_type, const guchar *pin, + gsize n_pin, GCancellable *cancellable, GError **err) +{ + Login args = { GCK_ARGUMENTS_INIT, user_type, (guchar*)pin, n_pin }; + return _gck_call_sync (self, perform_login, NULL, &args, cancellable, err); + +} + +/** + * gck_session_login_async: + * @self: Log in to this session. + * @user_type: The type of login user. + * @pin: The user's PIN, or NULL for protected authentication path. + * @n_pin: The length of the PIN. + * @cancellable: Optional cancellation object, or NULL. + * @callback: Called when the operation completes. + * @user_data: Data to pass to the callback. + * + * Login the user on the session. This call will return + * immediately and completes asynchronously. + **/ +void +gck_session_login_async (GckSession *self, gulong user_type, const guchar *pin, + gsize n_pin, GCancellable *cancellable, GAsyncReadyCallback callback, + gpointer user_data) +{ + Login* args = _gck_call_async_prep (self, self, perform_login, NULL, sizeof (*args), free_login); + + args->user_type = user_type; + args->pin = pin && n_pin ? g_memdup (pin, n_pin) : NULL; + args->n_pin = n_pin; + + _gck_call_async_ready_go (args, cancellable, callback, user_data); + +} + +/** + * gck_session_login_finish: + * @self: The session logged into. + * @result: The result passed to the callback. + * @err: A location to return an error. + * + * Get the result of a login operation. + * + * Return value: Whether the operation was successful or not. + **/ +gboolean +gck_session_login_finish (GckSession *self, GAsyncResult *result, GError **err) +{ + return _gck_call_basic_finish (result, err); +} + + + + +/* LOGOUT */ + +static CK_RV +perform_logout (GckArguments *args) +{ + return (args->pkcs11->C_Logout) (args->handle); +} + +/** + * gck_session_logout: + * @self: Logout of this session. + * @err: A location to return an error. + * + * Log out of the session. This call may block for an indefinite period. + * + * Return value: Whether the logout was successful or not. + **/ +gboolean +gck_session_logout (GckSession *self, GError **err) +{ + return gck_session_logout_full (self, NULL, err); +} + +/** + * gck_session_logout_full: + * @self: Logout of this session. + * @cancellable: Optional cancellation object, or NULL. + * @err: A location to return an error. + * + * Log out of the session. This call may block for an indefinite period. + * + * Return value: Whether the logout was successful or not. + **/ +gboolean +gck_session_logout_full (GckSession *self, GCancellable *cancellable, GError **err) +{ + GckArguments args = GCK_ARGUMENTS_INIT; + return _gck_call_sync (self, perform_logout, NULL, &args, cancellable, err); +} + +/** + * gck_session_logout_async: + * @self: Logout of this session. + * @cancellable: Optional cancellation object, or NULL. + * @callback: Called when the operation completes. + * @user_data: Data to pass to the callback. + * + * Log out of the session. This call returns immediately and completes + * asynchronously. + **/ +void +gck_session_logout_async (GckSession *self, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data) +{ + GckArguments *args = _gck_call_async_prep (self, self, perform_logout, NULL, 0, NULL); + _gck_call_async_ready_go (args, cancellable, callback, user_data); +} + +/** + * gck_session_logout_finish: + * @self: Logout of this session. + * @result: The result passed to the callback. + * @err: A location to return an error. + * + * Get the result of logging out of a session. + * + * Return value: Whether the logout was successful or not. + **/ +gboolean +gck_session_logout_finish (GckSession *self, GAsyncResult *result, GError **err) +{ + return _gck_call_basic_finish (result, err); +} + + + + +/* CREATE OBJECT */ + +typedef struct _CreateObject { + GckArguments base; + GckAttributes *attrs; + CK_OBJECT_HANDLE object; +} CreateObject; + +static void +free_create_object (CreateObject *args) +{ + gck_attributes_unref (args->attrs); + g_free (args); +} + +static CK_RV +perform_create_object (CreateObject *args) +{ + CK_ATTRIBUTE_PTR attrs; + CK_ULONG n_attrs; + + attrs = _gck_attributes_commit_out (args->attrs, &n_attrs); + + return (args->base.pkcs11->C_CreateObject) (args->base.handle, + attrs, n_attrs, + &args->object); +} + +/** + * gck_session_create_object: + * @self: The session to create the object on. + * @err: A location to store an error. + * @...: The attributes to create the new object with. + * + * Create a new PKCS#11 object. This call may block + * for an indefinite period. + * + * The arguments must be triples of: attribute type, data type, value + * + * <para>The variable argument list should contain: + * <variablelist> + * <varlistentry> + * <term>a)</term> + * <listitem><para>The gulong attribute type (ie: CKA_LABEL). </para></listitem> + * </varlistentry> + * <varlistentry> + * <term>b)</term> + * <listitem><para>The attribute data type (one of GCK_BOOLEAN, GCK_ULONG, + * GCK_STRING, GCK_DATE) orthe raw attribute value length.</para></listitem> + * </varlistentry> + * <varlistentry> + * <term>c)</term> + * <listitem><para>The attribute value, either a gboolean, gulong, gchar*, GDate* or + * a pointer to a raw attribute value.</para></listitem> + * </varlistentry> + * </variablelist> + * + * The variable argument list should be terminated with GCK_INVALID.</para> + * + * Return value: The newly created object, or NULL if an error occurred. + **/ +GckObject* +gck_session_create_object (GckSession *self, GError **err, ...) +{ + GckAttributes *attrs; + GckObject *object; + va_list va; + + va_start (va, err); + attrs = gck_attributes_new_valist (g_realloc, va); + va_end (va); + + object = gck_session_create_object_full (self, attrs, NULL, err); + gck_attributes_unref (attrs); + return object; +} + +/** + * gck_session_create_object_full: + * @self: The session to create the object on. + * @attrs: The attributes to create the object with. + * @cancellable: Optional cancellation object, or NULL. + * @err: A location to return an error, or NULL. + * + * Create a new PKCS#11 object. This call may block for an + * indefinite period. + * + * Return value: The newly created object or NULL if an error occurred. + **/ +GckObject* +gck_session_create_object_full (GckSession *self, GckAttributes *attrs, + GCancellable *cancellable, GError **err) +{ + GckSessionData *data = GCK_SESSION_GET_DATA (self); + CreateObject args = { GCK_ARGUMENTS_INIT, attrs, 0 }; + gboolean ret; + + g_return_val_if_fail (GCK_IS_SESSION (self), NULL); + g_return_val_if_fail (attrs, NULL); + + _gck_attributes_lock (attrs); + ret = _gck_call_sync (self, perform_create_object, NULL, &args, cancellable, err); + _gck_attributes_unlock (attrs); + + if (!ret) + return NULL; + + return gck_object_from_handle (data->slot, args.object); +} + +/** + * gck_session_create_object_async: + * @self: The session to create the object on. + * @attrs: The attributes to create the object with. + * @cancellable: Optional cancellation object or NULL. + * @callback: Called when the operation completes. + * @user_data: Data to pass to the callback. + * + * Create a new PKCS#11 object. This call will return immediately + * and complete asynchronously. + **/ +void +gck_session_create_object_async (GckSession *self, GckAttributes *attrs, + GCancellable *cancellable, GAsyncReadyCallback callback, + gpointer user_data) +{ + CreateObject *args = _gck_call_async_prep (self, self, perform_create_object, + NULL, sizeof (*args), free_create_object); + + g_return_if_fail (attrs); + + args->attrs = gck_attributes_ref (attrs); + _gck_attributes_lock (attrs); + + _gck_call_async_ready_go (args, cancellable, callback, user_data); +} + +/** + * gck_session_create_object_finish: + * @self: The session to create the object on. + * @result: The result passed to the callback. + * @err: A location to return an error, or NULL. + * + * Get the result of creating a new PKCS#11 object. + * + * Return value: The newly created object or NULL if an error occurred. + **/ +GckObject* +gck_session_create_object_finish (GckSession *self, GAsyncResult *result, GError **err) +{ + GckSessionData *data = GCK_SESSION_GET_DATA (self); + CreateObject *args; + + args = _gck_call_arguments (result, CreateObject); + _gck_attributes_unlock (args->attrs); + + if (!_gck_call_basic_finish (result, err)) + return NULL; + return gck_object_from_handle (data->slot, args->object); +} + + + +/* FIND OBJECTS */ + +typedef struct _FindObjects { + GckArguments base; + GckAttributes *attrs; + CK_OBJECT_HANDLE_PTR objects; + CK_ULONG n_objects; +} FindObjects; + +static void +free_find_objects (FindObjects *args) +{ + gck_attributes_unref (args->attrs); + g_free (args->objects); +} + +static CK_RV +perform_find_objects (FindObjects *args) +{ + CK_OBJECT_HANDLE_PTR batch; + CK_ULONG n_batch, n_found; + CK_ATTRIBUTE_PTR attrs; + CK_ULONG n_attrs; + GArray *array; + CK_RV rv; + + attrs = _gck_attributes_commit_out (args->attrs, &n_attrs); + + rv = (args->base.pkcs11->C_FindObjectsInit) (args->base.handle, + attrs, n_attrs); + if (rv != CKR_OK) + return rv; + + batch = NULL; + n_found = n_batch = 4; + array = g_array_new (0, 1, sizeof (CK_OBJECT_HANDLE)); + + do { + /* + * Reallocate and double in size: + * - First time. + * - Each time we found as many as batch + */ + + if (n_found == n_batch) { + n_batch *= 2; + batch = g_realloc (batch, sizeof (CK_OBJECT_HANDLE) * n_batch); + } + + rv = (args->base.pkcs11->C_FindObjects) (args->base.handle, + batch, n_batch, &n_found); + if (rv != CKR_OK) + break; + + g_array_append_vals (array, batch, n_found); + + } while (n_found > 0); + + g_free (batch); + + if (rv == CKR_OK) { + args->n_objects = array->len; + args->objects = (CK_OBJECT_HANDLE_PTR)g_array_free (array, FALSE); + rv = (args->base.pkcs11->C_FindObjectsFinal) (args->base.handle); + } else { + args->objects = NULL; + args->n_objects = 0; + g_array_free (array, TRUE); + } + + return rv; +} + +static GList* +objlist_from_handles (GckSession *self, CK_OBJECT_HANDLE_PTR objects, + CK_ULONG n_objects) +{ + GckSessionData *data = GCK_SESSION_GET_DATA (self); + GList *results = NULL; + + while (n_objects > 0) { + results = g_list_prepend (results, + gck_object_from_handle (data->slot, objects[--n_objects])); + } + + return g_list_reverse (results); +} + +/** + * gck_session_find_objects: + * @self: The session to find objects on. + * @err: A location to return an error or NULL. + * @...: The attributes to match. + * + * Find objects matching the passed attributes. This call may + * block for an indefinite period. + * + * The arguments must be triples of: attribute type, data type, value + * + * <para>The variable argument list should contain: + * <variablelist> + * <varlistentry> + * <term>a)</term> + * <listitem><para>The gulong attribute type (ie: CKA_LABEL). </para></listitem> + * </varlistentry> + * <varlistentry> + * <term>b)</term> + * <listitem><para>The attribute data type (one of GCK_BOOLEAN, GCK_ULONG, + * GCK_STRING, GCK_DATE) orthe raw attribute value length.</para></listitem> + * </varlistentry> + * <varlistentry> + * <term>c)</term> + * <listitem><para>The attribute value, either a gboolean, gulong, gchar*, GDate* or + * a pointer to a raw attribute value.</para></listitem> + * </varlistentry> + * </variablelist> + * The variable argument list should be terminated with GCK_INVALID.</para> + * + * Return value: A list of the matching objects, which may be empty. + **/ +GList* +gck_session_find_objects (GckSession *self, GError **err, ...) +{ + GckAttributes *attrs; + GList *results; + va_list va; + + va_start (va, err); + attrs = gck_attributes_new_valist (g_realloc, va); + va_end (va); + + results = gck_session_find_objects_full (self, attrs, NULL, err); + gck_attributes_unref (attrs); + return results; +} + +/** + * gck_session_find_objects_full: + * @self: The session to find objects on. + * @attrs: The attributes to match. + * @cancellable: Optional cancellation object or NULL. + * @err: A location to return an error or NULL. + * + * Find the objects matching the passed attributes. This call may + * block for an indefinite period. + * + * Return value: A list of the matching objects, which may be empty. + **/ +GList* +gck_session_find_objects_full (GckSession *self, GckAttributes *attrs, + GCancellable *cancellable, GError **err) +{ + FindObjects args = { GCK_ARGUMENTS_INIT, attrs, NULL, 0 }; + GList *results = NULL; + + g_return_val_if_fail (attrs, NULL); + _gck_attributes_lock (attrs); + + if (_gck_call_sync (self, perform_find_objects, NULL, &args, cancellable, err)) + results = objlist_from_handles (self, args.objects, args.n_objects); + + g_free (args.objects); + _gck_attributes_unlock (attrs); + return results; +} + +/** + * gck_session_find_objects_async: + * @self: The session to find objects on. + * @attrs: The attributes to match. + * @cancellable: Optional cancellation object or NULL. + * @callback: Called when the operation completes. + * @user_data: Data to pass to the callback. + * + * Find the objects matching the passed attributes. This call will + * return immediately and complete asynchronously. + **/ +void +gck_session_find_objects_async (GckSession *self, GckAttributes *attrs, + GCancellable *cancellable, GAsyncReadyCallback callback, + gpointer user_data) +{ + FindObjects *args = _gck_call_async_prep (self, self, perform_find_objects, + NULL, sizeof (*args), free_find_objects); + args->attrs = gck_attributes_ref (attrs); + _gck_attributes_lock (attrs); + _gck_call_async_ready_go (args, cancellable, callback, user_data); +} + +/** + * gck_session_find_objects_finish: + * @self: The session to find objects on. + * @result: The attributes to match. + * @err: A location to return an error. + * + * Get the result of a find operation. + * + * Return value: A list of the matching objects, which may be empty. + **/ +GList* +gck_session_find_objects_finish (GckSession *self, GAsyncResult *result, GError **err) +{ + FindObjects *args; + + args = _gck_call_arguments (result, FindObjects); + _gck_attributes_unlock (args->attrs); + + if (!_gck_call_basic_finish (result, err)) + return NULL; + return objlist_from_handles (self, args->objects, args->n_objects); +} + +/* ----------------------------------------------------------------------------- + * KEY PAIR GENERATION + */ + +typedef struct _GenerateKeyPair { + GckArguments base; + GckMechanism *mechanism; + GckAttributes *public_attrs; + GckAttributes *private_attrs; + CK_OBJECT_HANDLE public_key; + CK_OBJECT_HANDLE private_key; +} GenerateKeyPair; + +static void +free_generate_key_pair (GenerateKeyPair *args) +{ + gck_mechanism_unref (args->mechanism); + gck_attributes_unref (args->public_attrs); + gck_attributes_unref (args->private_attrs); + g_free (args); +} + +static CK_RV +perform_generate_key_pair (GenerateKeyPair *args) +{ + CK_ATTRIBUTE_PTR pub_attrs, priv_attrs; + CK_ULONG n_pub_attrs, n_priv_attrs; + + g_assert (sizeof (CK_MECHANISM) == sizeof (GckMechanism)); + + pub_attrs = _gck_attributes_commit_out (args->public_attrs, &n_pub_attrs); + priv_attrs = _gck_attributes_commit_out (args->private_attrs, &n_priv_attrs); + + return (args->base.pkcs11->C_GenerateKeyPair) (args->base.handle, + (CK_MECHANISM_PTR)args->mechanism, + pub_attrs, n_pub_attrs, + priv_attrs, n_priv_attrs, + &args->public_key, + &args->private_key); +} + +/** + * gck_session_generate_key_pair_full: + * @self: The session to use. + * @mechanism: The mechanism to use for key generation. + * @public_attrs: Additional attributes for the generated public key. + * @private_attrs: Additional attributes for the generated private key. + * @public_key: A location to return the resulting public key. + * @private_key: A location to return the resulting private key. + * @cancellable: Optional cancellation object, or NULL. + * @err: A location to return an error, or NULL. + * + * Generate a new key pair of public and private keys. This call may block for an + * indefinite period. + * + * Return value: TRUE if the operation succeeded. + **/ +gboolean +gck_session_generate_key_pair_full (GckSession *self, GckMechanism *mechanism, + GckAttributes *public_attrs, GckAttributes *private_attrs, + GckObject **public_key, GckObject **private_key, + GCancellable *cancellable, GError **err) +{ + GckSessionData *data = GCK_SESSION_GET_DATA (self); + GenerateKeyPair args = { GCK_ARGUMENTS_INIT, mechanism, public_attrs, private_attrs, 0, 0 }; + gboolean ret; + + g_return_val_if_fail (GCK_IS_SESSION (self), FALSE); + g_return_val_if_fail (mechanism, FALSE); + g_return_val_if_fail (public_attrs, FALSE); + g_return_val_if_fail (private_attrs, FALSE); + g_return_val_if_fail (public_key, FALSE); + g_return_val_if_fail (private_key, FALSE); + + _gck_attributes_lock (public_attrs); + if (public_attrs != private_attrs) + _gck_attributes_lock (private_attrs); + ret = _gck_call_sync (self, perform_generate_key_pair, NULL, &args, cancellable, err); + if (public_attrs != private_attrs) + _gck_attributes_unlock (private_attrs); + _gck_attributes_unlock (public_attrs); + + if (!ret) + return FALSE; + + *public_key = gck_object_from_handle (data->slot, args.public_key); + *private_key = gck_object_from_handle (data->slot, args.private_key); + return TRUE; +} + +/** + * gck_session_generate_key_pair_async: + * @self: The session to use. + * @mechanism: The mechanism to use for key generation. + * @public_attrs: Additional attributes for the generated public key. + * @private_attrs: Additional attributes for the generated private key. + * @cancellable: Optional cancellation object or NULL. + * @callback: Called when the operation completes. + * @user_data: Data to pass to the callback. + * + * Generate a new key pair of public and private keys. This call will + * return immediately and complete asynchronously. + **/ +void +gck_session_generate_key_pair_async (GckSession *self, GckMechanism *mechanism, + GckAttributes *public_attrs, GckAttributes *private_attrs, + GCancellable *cancellable, GAsyncReadyCallback callback, + gpointer user_data) +{ + GenerateKeyPair *args = _gck_call_async_prep (self, self, perform_generate_key_pair, + NULL, sizeof (*args), free_generate_key_pair); + + g_return_if_fail (GCK_IS_SESSION (self)); + g_return_if_fail (mechanism); + g_return_if_fail (public_attrs); + g_return_if_fail (private_attrs); + + args->public_attrs = gck_attributes_ref (public_attrs); + _gck_attributes_lock (public_attrs); + args->private_attrs = gck_attributes_ref (private_attrs); + if (public_attrs != private_attrs) + _gck_attributes_lock (private_attrs); + args->mechanism = gck_mechanism_ref (mechanism); + + _gck_call_async_ready_go (args, cancellable, callback, user_data); +} + +/** + * gck_session_generate_key_pair_finish: + * @self: The session to use. + * @result: The async result passed to the callback. + * @public_key: A location to return the resulting public key. + * @private_key: A location to return the resulting private key. + * @err: A location to return an error. + * + * Get the result of a generate key pair operation. + * + * Return value: TRUE if the operation succeeded. + **/ +gboolean +gck_session_generate_key_pair_finish (GckSession *self, GAsyncResult *result, + GckObject **public_key, GckObject **private_key, + GError **err) +{ + GckSessionData *data = GCK_SESSION_GET_DATA (self); + GenerateKeyPair *args; + + g_return_val_if_fail (GCK_IS_SESSION (self), FALSE); + g_return_val_if_fail (public_key, FALSE); + g_return_val_if_fail (private_key, FALSE); + + args = _gck_call_arguments (result, GenerateKeyPair); + _gck_attributes_unlock (args->public_attrs); + if (args->public_attrs != args->private_attrs) + _gck_attributes_unlock (args->private_attrs); + + if (!_gck_call_basic_finish (result, err)) + return FALSE; + + *public_key = gck_object_from_handle (data->slot, args->public_key); + *private_key = gck_object_from_handle (data->slot, args->private_key); + return TRUE; +} + +/* ----------------------------------------------------------------------------- + * KEY WRAPPING + */ + +typedef struct _WrapKey { + GckArguments base; + GckMechanism *mechanism; + CK_OBJECT_HANDLE wrapper; + CK_OBJECT_HANDLE wrapped; + gpointer result; + gulong n_result; +} WrapKey; + +static void +free_wrap_key (WrapKey *args) +{ + gck_mechanism_unref (args->mechanism); + g_free (args->result); + g_free (args); +} + +static CK_RV +perform_wrap_key (WrapKey *args) +{ + CK_RV rv; + + g_assert (sizeof (CK_MECHANISM) == sizeof (GckMechanism)); + + /* Get the length of the result */ + rv = (args->base.pkcs11->C_WrapKey) (args->base.handle, + (CK_MECHANISM_PTR)args->mechanism, + args->wrapper, args->wrapped, + NULL, &args->n_result); + if (rv != CKR_OK) + return rv; + + /* And try again with a real buffer */ + args->result = g_malloc0 (args->n_result); + return (args->base.pkcs11->C_WrapKey) (args->base.handle, + (CK_MECHANISM_PTR)args->mechanism, + args->wrapper, args->wrapped, + args->result, &args->n_result); +} + +/** + * gck_session_wrap_key: + * @self: The session to use. + * @wrapper: The key to use for wrapping. + * @mechanism: The mechanism type to use for wrapping. + * @wrapped: The key to wrap. + * @n_result: A location in which to return the length of the wrapped data. + * @err: A location to return an error, or NULL. + * + * Wrap a key into a byte stream. This call may block for an + * indefinite period. + * + * Return value: The wrapped data or NULL if the operation failed. + **/ +gpointer +gck_session_wrap_key (GckSession *self, GckObject *key, gulong mech_type, + GckObject *wrapped, gsize *n_result, GError **err) +{ + GckMechanism mech = { mech_type, NULL, 0 }; + return gck_session_wrap_key_full (self, key, &mech, wrapped, n_result, NULL, err); +} + +/** + * gck_session_wrap_key_full: + * @self: The session to use. + * @wrapper: The key to use for wrapping. + * @mechanism: The mechanism to use for wrapping. + * @wrapped: The key to wrap. + * @n_result: A location in which to return the length of the wrapped data. + * @cancellable: Optional cancellation object, or NULL. + * @err: A location to return an error, or NULL. + * + * Wrap a key into a byte stream. This call may block for an + * indefinite period. + * + * Return value: The wrapped data or NULL if the operation failed. + **/ +gpointer +gck_session_wrap_key_full (GckSession *self, GckObject *wrapper, GckMechanism *mechanism, + GckObject *wrapped, gsize *n_result, GCancellable *cancellable, + GError **err) +{ + WrapKey args = { GCK_ARGUMENTS_INIT, mechanism, 0, 0, NULL, 0 }; + gboolean ret; + + g_return_val_if_fail (GCK_IS_SESSION (self), FALSE); + g_return_val_if_fail (mechanism, FALSE); + g_return_val_if_fail (GCK_IS_OBJECT (wrapped), FALSE); + g_return_val_if_fail (GCK_IS_OBJECT (wrapper), FALSE); + g_return_val_if_fail (n_result, FALSE); + + g_object_get (wrapper, "handle", &args.wrapper, NULL); + g_return_val_if_fail (args.wrapper != 0, NULL); + g_object_get (wrapped, "handle", &args.wrapped, NULL); + g_return_val_if_fail (args.wrapped != 0, NULL); + + ret = _gck_call_sync (self, perform_wrap_key, NULL, &args, cancellable, err); + + if (!ret) + return FALSE; + + *n_result = args.n_result; + return args.result; +} + +/** + * gck_session_wrap_key_async: + * @self: The session to use. + * @wrapper: The key to use for wrapping. + * @mechanism: The mechanism to use for wrapping. + * @wrapped: The key to wrap. + * @cancellable: Optional cancellation object or NULL. + * @callback: Called when the operation completes. + * @user_data: Data to pass to the callback. + * + * Wrap a key into a byte stream. This call will + * return immediately and complete asynchronously. + **/ +void +gck_session_wrap_key_async (GckSession *self, GckObject *key, GckMechanism *mechanism, + GckObject *wrapped, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data) +{ + WrapKey *args = _gck_call_async_prep (self, self, perform_wrap_key, + NULL, sizeof (*args), free_wrap_key); + + g_return_if_fail (GCK_IS_SESSION (self)); + g_return_if_fail (mechanism); + g_return_if_fail (GCK_IS_OBJECT (wrapped)); + g_return_if_fail (GCK_IS_OBJECT (key)); + + args->mechanism = gck_mechanism_ref (mechanism); + g_object_get (key, "handle", &args->wrapper, NULL); + g_return_if_fail (args->wrapper != 0); + g_object_get (wrapped, "handle", &args->wrapped, NULL); + g_return_if_fail (args->wrapped != 0); + + _gck_call_async_ready_go (args, cancellable, callback, user_data); +} + +/** + * gck_session_wrap_key_finish: + * @self: The session to use. + * @result: The async result passed to the callback. + * @n_result: A location in which to return the length of the wrapped data. + * @err: A location to return an error. + * + * Get the result of a wrap key operation. + * + * Return value: The wrapped data or NULL if the operation failed. + **/ +gpointer +gck_session_wrap_key_finish (GckSession *self, GAsyncResult *result, + gsize *n_result, GError **err) +{ + WrapKey *args; + gpointer ret; + + g_return_val_if_fail (GCK_IS_SESSION (self), NULL); + g_return_val_if_fail (n_result, NULL); + + args = _gck_call_arguments (result, WrapKey); + + if (!_gck_call_basic_finish (result, err)) + return NULL; + + *n_result = args->n_result; + args->n_result = 0; + ret = args->result; + args->result = NULL; + + return ret; +} + +/* ----------------------------------------------------------------------------- + * KEY UNWRAPPING + */ + +typedef struct _UnwrapKey { + GckArguments base; + GckMechanism *mechanism; + GckAttributes *attrs; + CK_OBJECT_HANDLE wrapper; + gconstpointer input; + gulong n_input; + CK_OBJECT_HANDLE unwrapped; +} UnwrapKey; + +static void +free_unwrap_key (UnwrapKey *args) +{ + gck_mechanism_unref (args->mechanism); + gck_attributes_unref (args->attrs); + g_free (args); +} + +static CK_RV +perform_unwrap_key (UnwrapKey *args) +{ + CK_ATTRIBUTE_PTR attrs; + CK_ULONG n_attrs; + + g_assert (sizeof (CK_MECHANISM) == sizeof (GckMechanism)); + + attrs = _gck_attributes_commit_out (args->attrs, &n_attrs); + + return (args->base.pkcs11->C_UnwrapKey) (args->base.handle, + (CK_MECHANISM_PTR)args->mechanism, + args->wrapper, (CK_BYTE_PTR)args->input, + args->n_input, attrs, n_attrs, + &args->unwrapped); +} + +/** + * gck_session_unwrap_key: + * @self: The session to use. + * @wrapper: The key to use for unwrapping. + * @mech_type: The mechanism type to use for derivation. + * @input: The wrapped data as a byte stream. + * @n_input: The length of the wrapped data. + * @err: A location to return an error, or NULL. + * @...: Additional attributes for the unwrapped key. + * + * Unwrap a key from a byte stream. This call may block for an + * indefinite period. + * + * The arguments must be triples of: attribute type, data type, value + * + * <para>The variable argument list should contain: + * <variablelist> + * <varlistentry> + * <term>a)</term> + * <listitem><para>The gulong attribute type (ie: CKA_LABEL). </para></listitem> + * </varlistentry> + * <varlistentry> + * <term>b)</term> + * <listitem><para>The attribute data type (one of GCK_BOOLEAN, GCK_ULONG, + * GCK_STRING, GCK_DATE) orthe raw attribute value length.</para></listitem> + * </varlistentry> + * <varlistentry> + * <term>c)</term> + * <listitem><para>The attribute value, either a gboolean, gulong, gchar*, GDate* or + * a pointer to a raw attribute value.</para></listitem> + * </varlistentry> + * </variablelist> + * The variable argument list should be terminated with GCK_INVALID.</para> + * + * Return value: The new unwrapped key or NULL if the operation failed. + **/ +GckObject* +gck_session_unwrap_key (GckSession *self, GckObject *key, gulong mech_type, + gconstpointer input, gsize n_input, GError **err, ...) +{ + GckMechanism mech = { mech_type, NULL, 0 }; + GckAttributes *attrs; + GckObject *object; + va_list va; + + va_start (va, err); + attrs = gck_attributes_new_valist (g_realloc, va); + va_end (va); + + object = gck_session_unwrap_key_full (self, key, &mech, input, n_input, attrs, NULL, err); + gck_attributes_unref (attrs); + return object; +} + +/** + * gck_session_unwrap_key_full: + * @self: The session to use. + * @wrapper: The key to use for unwrapping. + * @mechanism: The mechanism to use for unwrapping. + * @input: The wrapped data as a byte stream. + * @n_input: The length of the wrapped data. + * @attrs: Additional attributes for the unwrapped key. + * @cancellable: Optional cancellation object, or NULL. + * @err: A location to return an error, or NULL. + * + * Unwrap a key from a byte stream. This call may block for an + * indefinite period. + * + * Return value: The new unwrapped key or NULL if the operation failed. + **/ +GckObject* +gck_session_unwrap_key_full (GckSession *self, GckObject *wrapper, GckMechanism *mechanism, + gconstpointer input, gsize n_input, GckAttributes *attrs, + GCancellable *cancellable, GError **err) +{ + GckSessionData *data = GCK_SESSION_GET_DATA (self); + UnwrapKey args = { GCK_ARGUMENTS_INIT, mechanism, attrs, 0, input, n_input, 0 }; + gboolean ret; + + g_return_val_if_fail (GCK_IS_SESSION (self), FALSE); + g_return_val_if_fail (GCK_IS_OBJECT (wrapper), FALSE); + g_return_val_if_fail (mechanism, FALSE); + g_return_val_if_fail (attrs, FALSE); + + g_object_get (wrapper, "handle", &args.wrapper, NULL); + g_return_val_if_fail (args.wrapper != 0, NULL); + + _gck_attributes_lock (attrs); + ret = _gck_call_sync (self, perform_unwrap_key, NULL, &args, cancellable, err); + _gck_attributes_unlock (attrs); + + if (!ret) + return NULL; + + return gck_object_from_handle (data->slot, args.unwrapped); +} + +/** + * gck_session_unwrap_key_async: + * @self: The session to use. + * @wrapper: The key to use for unwrapping. + * @mechanism: The mechanism to use for unwrapping. + * @input: The wrapped data as a byte stream. + * @n_input: The length of the wrapped data. + * @attrs: Additional attributes for the unwrapped key. + * @cancellable: Optional cancellation object or NULL. + * @callback: Called when the operation completes. + * @user_data: Data to pass to the callback. + * + * Unwrap a key from a byte stream. This call will + * return immediately and complete asynchronously. + **/ +void +gck_session_unwrap_key_async (GckSession *self, GckObject *wrapper, GckMechanism *mechanism, + gconstpointer input, gsize n_input, GckAttributes *attrs, + GCancellable *cancellable, GAsyncReadyCallback callback, + gpointer user_data) +{ + UnwrapKey *args = _gck_call_async_prep (self, self, perform_unwrap_key, + NULL, sizeof (*args), free_unwrap_key); + + g_return_if_fail (GCK_IS_SESSION (self)); + g_return_if_fail (GCK_IS_OBJECT (wrapper)); + g_return_if_fail (attrs); + + g_object_get (wrapper, "handle", &args->wrapper, NULL); + g_return_if_fail (args->wrapper != 0); + + args->mechanism = gck_mechanism_ref (mechanism); + args->attrs = gck_attributes_ref (attrs); + args->input = input; + args->n_input = n_input; + _gck_attributes_lock (attrs); + + _gck_call_async_ready_go (args, cancellable, callback, user_data); +} + +/** + * gck_session_wrap_key_finish: + * @self: The session to use. + * @result: The async result passed to the callback. + * @err: A location to return an error. + * + * Get the result of a unwrap key operation. + * + * Return value: The new unwrapped key or NULL if the operation failed. + **/ +GckObject* +gck_session_unwrap_key_finish (GckSession *self, GAsyncResult *result, GError **err) +{ + GckSessionData *data = GCK_SESSION_GET_DATA (self); + UnwrapKey *args; + + g_return_val_if_fail (GCK_IS_SESSION (self), NULL); + + args = _gck_call_arguments (result, UnwrapKey); + _gck_attributes_unlock (args->attrs); + + if (!_gck_call_basic_finish (result, err)) + return NULL; + return gck_object_from_handle (data->slot, args->unwrapped); +} + +/* ----------------------------------------------------------------------------- + * KEY DERIVATION + */ + +typedef struct _DeriveKey { + GckArguments base; + GckMechanism *mechanism; + GckAttributes *attrs; + CK_OBJECT_HANDLE key; + CK_OBJECT_HANDLE derived; +} DeriveKey; + +static void +free_derive_key (DeriveKey *args) +{ + gck_mechanism_unref (args->mechanism); + gck_attributes_unref (args->attrs); + g_free (args); +} + +static CK_RV +perform_derive_key (DeriveKey *args) +{ + CK_ATTRIBUTE_PTR attrs; + CK_ULONG n_attrs; + + g_assert (sizeof (CK_MECHANISM) == sizeof (GckMechanism)); + + attrs = _gck_attributes_commit_out (args->attrs, &n_attrs); + + return (args->base.pkcs11->C_DeriveKey) (args->base.handle, + (CK_MECHANISM_PTR)args->mechanism, + args->key, attrs, n_attrs, + &args->derived); +} + +/** + * gck_session_derive_key_full: + * @self: The session to use. + * @base: The key to derive from. + * @mech_type: The mechanism type to use for derivation. + * @err: A location to return an error, or NULL. + * @...: Additional attributes for the derived key. + * + * Derive a key from another key. This call may block for an + * indefinite period. + * + * The arguments must be triples of: attribute type, data type, value + * + * <para>The variable argument list should contain: + * <variablelist> + * <varlistentry> + * <term>a)</term> + * <listitem><para>The gulong attribute type (ie: CKA_LABEL). </para></listitem> + * </varlistentry> + * <varlistentry> + * <term>b)</term> + * <listitem><para>The attribute data type (one of GCK_BOOLEAN, GCK_ULONG, + * GCK_STRING, GCK_DATE) orthe raw attribute value length.</para></listitem> + * </varlistentry> + * <varlistentry> + * <term>c)</term> + * <listitem><para>The attribute value, either a gboolean, gulong, gchar*, GDate* or + * a pointer to a raw attribute value.</para></listitem> + * </varlistentry> + * </variablelist> + * The variable argument list should be terminated with GCK_INVALID.</para> + * + * Return value: The new derived key or NULL if the operation failed. + **/ +GckObject* +gck_session_derive_key (GckSession *self, GckObject *key, gulong mech_type, + GError **err, ...) +{ + GckMechanism mech = { mech_type, NULL, 0 }; + GckAttributes *attrs; + GckObject *object; + va_list va; + + va_start (va, err); + attrs = gck_attributes_new_valist (g_realloc, va); + va_end (va); + + object = gck_session_derive_key_full (self, key, &mech, attrs, NULL, err); + gck_attributes_unref (attrs); + return object; +} + +/** + * gck_session_derive_key_full: + * @self: The session to use. + * @base: The key to derive from. + * @mechanism: The mechanism to use for derivation. + * @attrs: Additional attributes for the derived key. + * @cancellable: Optional cancellation object, or NULL. + * @err: A location to return an error, or NULL. + * + * Derive a key from another key. This call may block for an + * indefinite period. + * + * Return value: The new derived key or NULL if the operation failed. + **/ +GckObject* +gck_session_derive_key_full (GckSession *self, GckObject *base, GckMechanism *mechanism, + GckAttributes *attrs, GCancellable *cancellable, GError **err) +{ + GckSessionData *data = GCK_SESSION_GET_DATA (self); + DeriveKey args = { GCK_ARGUMENTS_INIT, mechanism, attrs, 0, 0 }; + gboolean ret; + + g_return_val_if_fail (GCK_IS_SESSION (self), FALSE); + g_return_val_if_fail (GCK_IS_OBJECT (base), FALSE); + g_return_val_if_fail (mechanism, FALSE); + g_return_val_if_fail (attrs, FALSE); + + g_object_get (base, "handle", &args.key, NULL); + g_return_val_if_fail (args.key != 0, NULL); + + _gck_attributes_lock (attrs); + ret = _gck_call_sync (self, perform_derive_key, NULL, &args, cancellable, err); + _gck_attributes_unlock (attrs); + + if (!ret) + return NULL; + + return gck_object_from_handle (data->slot, args.derived); +} + +/** + * gck_session_derive_key_async: + * @self: The session to use. + * @base: The key to derive from. + * @mechanism: The mechanism to use for derivation. + * @attrs: Additional attributes for the derived key. + * @cancellable: Optional cancellation object or NULL. + * @callback: Called when the operation completes. + * @user_data: Data to pass to the callback. + * + * Derive a key from another key. This call will + * return immediately and complete asynchronously. + **/ +void +gck_session_derive_key_async (GckSession *self, GckObject *base, GckMechanism *mechanism, + GckAttributes *attrs, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data) +{ + DeriveKey *args = _gck_call_async_prep (self, self, perform_derive_key, + NULL, sizeof (*args), free_derive_key); + + g_return_if_fail (GCK_IS_SESSION (self)); + g_return_if_fail (GCK_IS_OBJECT (base)); + g_return_if_fail (attrs); + + g_object_get (base, "handle", &args->key, NULL); + g_return_if_fail (args->key != 0); + + args->mechanism = gck_mechanism_ref (mechanism); + args->attrs = gck_attributes_ref (attrs); + _gck_attributes_lock (attrs); + + _gck_call_async_ready_go (args, cancellable, callback, user_data); +} + +/** + * gck_session_wrap_key_finish: + * @self: The session to use. + * @result: The async result passed to the callback. + * @err: A location to return an error. + * + * Get the result of a derive key operation. + * + * Return value: The new derived key or NULL if the operation failed. + **/ +GckObject* +gck_session_derive_key_finish (GckSession *self, GAsyncResult *result, GError **err) +{ + GckSessionData *data = GCK_SESSION_GET_DATA (self); + DeriveKey *args; + + g_return_val_if_fail (GCK_IS_SESSION (self), NULL); + + args = _gck_call_arguments (result, DeriveKey); + _gck_attributes_unlock (args->attrs); + + if (!_gck_call_basic_finish (result, err)) + return NULL; + + return gck_object_from_handle (data->slot, args->derived); +} + +/* -------------------------------------------------------------------------------------------------- + * AUTHENTICATE + */ + +typedef enum _AuthenticateState { + AUTHENTICATE_NONE, + AUTHENTICATE_CAN, + AUTHENTICATE_WANT, + AUTHENTICATE_PERFORM +} AuthenticateState; + +typedef struct _Authenticate { + AuthenticateState state; + gboolean protected_auth; + GckModule *module; + GckObject *object; + gchar *label; + gchar *password; +} Authenticate; + +static CK_RV +authenticate_perform (Authenticate *args, GckArguments *base) +{ + CK_ATTRIBUTE attributes[2]; + CK_OBJECT_HANDLE handle; + CK_ULONG pin_len; + CK_BBOOL bvalue; + CK_RV rv; + + g_assert (args); + g_assert (base); + + switch (args->state) { + + /* + * Cannot authenticate for whatever reason, perhaps not + * enabled, or failed incomprehensibly etc. + * + */ + case AUTHENTICATE_NONE: + return CKR_OK; + + /* + * Can authenticate but haven't seen if we should, yet + * check out the object in question. + */ + case AUTHENTICATE_CAN: + + handle = gck_object_get_handle (args->object); + + attributes[0].type = CKA_LABEL; + attributes[0].pValue = NULL; + attributes[0].ulValueLen = 0; + attributes[1].type = CKA_ALWAYS_AUTHENTICATE; + attributes[1].pValue = &bvalue; + attributes[1].ulValueLen = sizeof (bvalue); + + rv = (base->pkcs11->C_GetAttributeValue) (base->handle, handle, attributes, 2); + if (rv == CKR_ATTRIBUTE_TYPE_INVALID) + bvalue = CK_FALSE; + else if (rv != CKR_OK) + return rv; + + /* No authentication needed, on this object */ + if (bvalue != CK_TRUE) { + args->state = AUTHENTICATE_NONE; + return CKR_OK; + } + + /* Protected authentication path, just go to perform */ + if (args->protected_auth) { + args->state = AUTHENTICATE_PERFORM; + return authenticate_perform (args, base); + } + + /* Get the label for a prompt */ + g_assert (!args->label); + if (attributes[0].ulValueLen) { + attributes[0].pValue = g_malloc0 (attributes[0].ulValueLen + 1); + rv = (base->pkcs11->C_GetAttributeValue) (base->handle, handle, attributes, 2); + if (rv == CKR_OK) { + g_assert (!args->label); + args->label = attributes[0].pValue; + args->label[attributes[0].ulValueLen] = 0; + } else { + g_free (attributes[0].pValue); + } + } + + /* Need a password */ + args->state = AUTHENTICATE_WANT; + return CKR_USER_NOT_LOGGED_IN; + + /* + * This state should be handled in verify_authenticate. + */ + case AUTHENTICATE_WANT: + g_assert (FALSE); + return CKR_GENERAL_ERROR; + + /* + * Do the actual login authentication. + */ + case AUTHENTICATE_PERFORM: + pin_len = args->password ? strlen (args->password) : 0; + rv = (base->pkcs11->C_Login) (base->handle, CKU_CONTEXT_SPECIFIC, + (CK_UTF8CHAR_PTR)args->password, pin_len); + if (rv == CKR_PIN_INCORRECT && !args->protected_auth) + args->state = AUTHENTICATE_WANT; + else + args->state = AUTHENTICATE_NONE; + return rv; + + default: + g_assert_not_reached (); + return CKR_GENERAL_ERROR; + } +} + +static gboolean +authenticate_complete (Authenticate *auth, GckArguments *base, CK_RV result) +{ + g_assert (auth); + g_assert (base); + + /* We're done here if not in this state */ + if (auth->state == AUTHENTICATE_WANT) { + + g_assert (GCK_IS_MODULE (auth->module)); + g_assert (GCK_IS_OBJECT (auth->object)); + + g_free (auth->password); + auth->password = NULL; + + if (_gck_module_fire_authenticate_object (auth->module, auth->object, auth->label, &auth->password)) { + auth->state = AUTHENTICATE_PERFORM; + return FALSE; /* Want to continue processing this call */ + } + } + + /* Free up various memory */ + if (auth->module) + g_object_unref (auth->module); + if (auth->object) + g_object_unref (auth->object); + g_free (auth->label); + g_free (auth->password); + + /* The call is complete */ + return TRUE; +} + +static void +authenticate_init (Authenticate *auth, GckSlot *slot, GckObject *object) +{ + GckModule *module; + + g_assert (GCK_IS_SLOT (slot)); + g_assert (GCK_IS_OBJECT (object)); + + module = gck_slot_get_module (slot); + if (gck_module_get_auto_authenticate (module) & GCK_AUTHENTICATE_OBJECTS) { + auth->state = AUTHENTICATE_CAN; + auth->protected_auth = gck_slot_has_flags (slot, CKF_PROTECTED_AUTHENTICATION_PATH); + auth->module = module; + auth->object = g_object_ref (object); + } else { + auth->state = AUTHENTICATE_NONE; + g_object_unref (module); + } +} + +/* -------------------------------------------------------------------------------------------------- + * COMMON CRYPTO ROUTINES + */ + +typedef struct _Crypt { + GckArguments base; + + /* Authenticator */ + Authenticate auth; + + /* Functions to call */ + CK_C_EncryptInit init_func; + CK_C_Encrypt complete_func; + + /* Input */ + CK_OBJECT_HANDLE key; + GckMechanism *mech; + guchar *input; + CK_ULONG n_input; + + /* Output */ + guchar *result; + CK_ULONG n_result; + +} Crypt; + +static CK_RV +perform_crypt (Crypt *args) +{ + CK_RV rv; + + g_assert (args); + g_assert (args->init_func); + g_assert (args->complete_func); + g_assert (!args->result); + g_assert (!args->n_result); + + /* Initialize the crypt operation */ + rv = (args->init_func) (args->base.handle, (CK_MECHANISM_PTR)args->mech, args->key); + if (rv != CKR_OK) + return rv; + + rv = authenticate_perform (&args->auth, &args->base); + if (rv != CKR_OK) + return rv; + + /* Get the length of the result */ + rv = (args->complete_func) (args->base.handle, args->input, args->n_input, NULL, &args->n_result); + if (rv != CKR_OK) + return rv; + + /* And try again with a real buffer */ + args->result = g_malloc0 (args->n_result); + return (args->complete_func) (args->base.handle, args->input, args->n_input, args->result, &args->n_result); +} + +static gboolean +complete_crypt (Crypt *args, CK_RV result) +{ + if (!authenticate_complete (&args->auth, &args->base, result)) + return FALSE; + + /* Call is complete */ + return TRUE; +} + +static void +free_crypt (Crypt *args) +{ + g_free (args->input); + gck_mechanism_unref (args->mech); + g_free (args->result); + g_free (args); +} + +static guchar* +crypt_sync (GckSession *self, GckObject *key, GckMechanism *mechanism, const guchar *input, + gsize n_input, gsize *n_result, GCancellable *cancellable, GError **err, + CK_C_EncryptInit init_func, CK_C_Encrypt complete_func) +{ + Crypt args; + GckSlot *slot; + + g_return_val_if_fail (GCK_IS_OBJECT (key), NULL); + g_return_val_if_fail (mechanism, NULL); + g_return_val_if_fail (init_func, NULL); + g_return_val_if_fail (complete_func, NULL); + + memset (&args, 0, sizeof (args)); + g_object_get (key, "handle", &args.key, NULL); + g_return_val_if_fail (args.key != 0, NULL); + + args.mech = mechanism; + + /* No need to copy in this case */ + args.input = (guchar*)input; + args.n_input = n_input; + + args.init_func = init_func; + args.complete_func = complete_func; + + slot = gck_session_get_slot (self); + authenticate_init (&args.auth, slot, key); + g_object_unref (slot); + + if (!_gck_call_sync (self, perform_crypt, complete_crypt, &args, cancellable, err)) { + g_free (args.result); + return NULL; + } + + *n_result = args.n_result; + return args.result; +} + +static void +crypt_async (GckSession *self, GckObject *key, GckMechanism *mechanism, const guchar *input, + gsize n_input, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data, + CK_C_EncryptInit init_func, CK_C_Encrypt complete_func) +{ + Crypt *args = _gck_call_async_prep (self, self, perform_crypt, complete_crypt, sizeof (*args), free_crypt); + GckSlot *slot; + + g_return_if_fail (GCK_IS_OBJECT (key)); + g_return_if_fail (mechanism); + g_return_if_fail (init_func); + g_return_if_fail (complete_func); + + g_object_get (key, "handle", &args->key, NULL); + g_return_if_fail (args->key != 0); + + args->mech = gck_mechanism_ref (mechanism); + + args->input = input && n_input ? g_memdup (input, n_input) : NULL; + args->n_input = n_input; + + args->init_func = init_func; + args->complete_func = complete_func; + + slot = gck_session_get_slot (self); + authenticate_init (&args->auth, slot, key); + g_object_unref (slot); + + _gck_call_async_ready_go (args, cancellable, callback, user_data); +} + +static guchar* +crypt_finish (GckSession *self, GAsyncResult *result, gsize *n_result, GError **err) +{ + Crypt *args; + guchar *res; + + if (!_gck_call_basic_finish (result, err)) + return NULL; + args = _gck_call_arguments (result, Crypt); + + /* Steal the values from the results */ + res = args->result; + args->result = NULL; + *n_result = args->n_result; + args->n_result = 0; + + return res; +} + +/* -------------------------------------------------------------------------------------------------- + * ENCRYPT + */ + +/** + * gck_session_encrypt: + * @self: The session. + * @key: The key to encrypt with. + * @mech_type: The mechanism type to use for encryption. + * @input: The data to encrypt. + * @n_input: The length of the data to encrypt. + * @n_result: A location to store the length of the result data. + * @err: A location to place error information. + * + * Encrypt data in a mechanism specific manner. This call may + * block for an indefinite period. + * + * Returns: The data that was encrypted, or NULL if an error occured. + */ +guchar* +gck_session_encrypt (GckSession *self, GckObject *key, gulong mech_type, const guchar *input, + gsize n_input, gsize *n_result, GError **err) +{ + GckMechanism mechanism = { mech_type, NULL, 0 }; + return gck_session_encrypt_full (self, key, &mechanism, input, n_input, n_result, NULL, err); +} + +/** + * gck_session_encrypt_full: + * @self: The session. + * @key: The key to encrypt with. + * @mechanism: The mechanism type and parameters to use for encryption. + * @input: The data to encrypt. + * @n_input: The length of the data to encrypt. + * @n_result: A location to store the length of the result data. + * @cancellable: A GCancellable which can be used to cancel the operation. + * @err: A location to place error information. + * + * Encrypt data in a mechanism specific manner. This call may + * block for an indefinite period. + * + * Returns: The data that was encrypted, or NULL if an error occured. + */ +guchar* +gck_session_encrypt_full (GckSession *self, GckObject *key, GckMechanism *mechanism, + const guchar *input, gsize n_input, gsize *n_result, + GCancellable *cancellable, GError **err) +{ + GckModule *module = NULL; + CK_FUNCTION_LIST_PTR funcs; + guchar *ret; + + g_object_get (self, "module", &module, NULL); + g_return_val_if_fail (module != NULL, NULL); + + funcs = gck_module_get_functions (module); + g_return_val_if_fail (module != NULL, NULL); + + ret = crypt_sync (self, key, mechanism, input, n_input, n_result, cancellable, err, + funcs->C_EncryptInit, funcs->C_Encrypt); + + g_object_unref (module); + return ret; +} + +/** + * gck_session_encrypt_async: + * @self: The session. + * @key: The key to encrypt with. + * @mechanism: The mechanism type and parameters to use for encryption. + * @input: The data to encrypt. + * @n_input: The length of the data to encrypt. + * @cancellable: A GCancellable which can be used to cancel the operation. + * @callback: Called when the operation completes. + * @user_data: A pointer to pass to the callback. + * + * Encrypt data in a mechanism specific manner. This call will + * return immediately and complete asynchronously. + **/ +void +gck_session_encrypt_async (GckSession *self, GckObject *key, GckMechanism *mechanism, + const guchar *input, gsize n_input, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data) +{ + GckModule *module = NULL; + CK_FUNCTION_LIST_PTR funcs; + + g_object_get (self, "module", &module, NULL); + g_return_if_fail (module != NULL); + + funcs = gck_module_get_functions (module); + g_return_if_fail (module != NULL); + + crypt_async (self, key, mechanism, input, n_input, cancellable, callback, user_data, + funcs->C_EncryptInit, funcs->C_Encrypt); + + g_object_unref (module); +} + +/** + * gck_session_encrypt_finish: + * @self: The session. + * @result: The result object passed to the callback. + * @n_result: A location to store the length of the result data. + * @err: A location to place error information. + * + * Get the result of an encryption operation. + * + * Returns: The data that was encrypted, or NULL if an error occurred. + */ +guchar* +gck_session_encrypt_finish (GckSession *self, GAsyncResult *result, gsize *n_result, + GError **err) +{ + return crypt_finish (self, result, n_result, err); +} + +/* -------------------------------------------------------------------------------------------------- + * DECRYPT + */ + +/** + * gck_session_decrypt: + * @self: The session. + * @key: The key to decrypt with. + * @mech_type: The mechanism type to use for decryption. + * @input: The data to decrypt. + * @n_input: The length of the data to decrypt. + * @n_result: A location to store the length of the result data. + * @err: A location to place an error. + * + * Decrypt data in a mechanism specific manner. This call may + * block for an indefinite period. + * + * Returns: The data that was decrypted, or NULL if an error occured. + */ +guchar* +gck_session_decrypt (GckSession *self, GckObject *key, gulong mech_type, const guchar *input, + gsize n_input, gsize *n_result, GError **err) +{ + GckMechanism mechanism = { mech_type, NULL, 0 }; + return gck_session_decrypt_full (self, key, &mechanism, input, n_input, n_result, NULL, err); +} + +/** + * gck_session_decrypt_full: + * @self: The session. + * @key: The key to decrypt with. + * @mechanism: The mechanism type and parameters to use for decryption. + * @input: The data to decrypt. + * @n_input: The length of the data to decrypt. + * @n_result: A location to store the length of the result data. + * @cancellable: A GCancellable which can be used to cancel the operation. + * @err: A location to place error information. + * + * Decrypt data in a mechanism specific manner. This call may + * block for an indefinite period. + * + * Returns: The data that was decrypted, or NULL if an error occured. + */ +guchar* +gck_session_decrypt_full (GckSession *self, GckObject *key, GckMechanism *mechanism, + const guchar *input, gsize n_input, gsize *n_result, + GCancellable *cancellable, GError **err) +{ + GckModule *module = NULL; + CK_FUNCTION_LIST_PTR funcs; + guchar *ret; + + g_object_get (self, "module", &module, NULL); + g_return_val_if_fail (module != NULL, NULL); + + funcs = gck_module_get_functions (module); + g_return_val_if_fail (module != NULL, NULL); + + ret = crypt_sync (self, key, mechanism, input, n_input, n_result, cancellable, err, + funcs->C_DecryptInit, funcs->C_Decrypt); + g_object_unref (module); + return ret; +} + +/** + * gck_session_decrypt_async: + * @self: The session. + * @key: The key to decrypt with. + * @mechanism: The mechanism type and parameters to use for decryption. + * @input: The data to decrypt. + * @n_input: The length of the data to decrypt. + * @cancellable: A GCancellable which can be used to cancel the operation. + * @callback: Called when the operation completes. + * @user_data: A pointer to pass to the callback. + * + * Decrypt data in a mechanism specific manner. This call will + * return immediately and complete asynchronously. + */ +void +gck_session_decrypt_async (GckSession *self, GckObject *key, GckMechanism *mechanism, + const guchar *input, gsize n_input, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data) +{ + GckModule *module = NULL; + CK_FUNCTION_LIST_PTR funcs; + + g_object_get (self, "module", &module, NULL); + g_return_if_fail (module != NULL); + + funcs = gck_module_get_functions (module); + g_return_if_fail (module != NULL); + + crypt_async (self, key, mechanism, input, n_input, cancellable, callback, user_data, + funcs->C_DecryptInit, funcs->C_Decrypt); + g_object_unref (module); +} + +/** + * gck_session_decrypt_finish: + * @self: The session. + * @result: The result object passed to the callback. + * @n_result: A location to store the length of the result data. + * @err: A location to place error information. + * + * Get the result of an decryption operation. + * + * Returns: The data that was decrypted, or NULL if an error occurred. + */ +guchar* +gck_session_decrypt_finish (GckSession *self, GAsyncResult *result, + gsize *n_result, GError **err) +{ + return crypt_finish (self, result, n_result, err); +} + +/* -------------------------------------------------------------------------------------------------- + * SIGN + */ + +/** + * gck_session_sign: + * @self: The session. + * @key: The key to sign with. + * @mech_type: The mechanism type to use for signing. + * @input: The data to sign. + * @n_input: The length of the data to sign. + * @n_result: A location to store the length of the result data. + * @err: A location to place an error. + * + * Sign data in a mechanism specific manner. This call may + * block for an indefinite period. + * + * Returns: The data that was signed, or NULL if an error occured. + */ +guchar* +gck_session_sign (GckSession *self, GckObject *key, gulong mech_type, const guchar *input, + gsize n_input, gsize *n_result, GError **err) +{ + GckMechanism mechanism = { mech_type, NULL, 0 }; + return gck_session_sign_full (self, key, &mechanism, input, n_input, n_result, NULL, err); +} + +/** + * gck_session_sign_full: + * @self: The session. + * @key: The key to sign with. + * @mechanism: The mechanism type and parameters to use for signing. + * @input: The data to sign. + * @n_input: The length of the data to sign. + * @n_result: A location to store the length of the result data. + * @cancellable: A GCancellable which can be used to cancel the operation. + * @err: A location to place error information. + * + * Sign data in a mechanism specific manner. This call may + * block for an indefinite period. + * + * Returns: The data that was signed, or NULL if an error occured. + */ +guchar* +gck_session_sign_full (GckSession *self, GckObject *key, GckMechanism *mechanism, + const guchar *input, gsize n_input, gsize *n_result, + GCancellable *cancellable, GError **err) +{ + GckModule *module = NULL; + CK_FUNCTION_LIST_PTR funcs; + guchar *ret; + + g_object_get (self, "module", &module, NULL); + g_return_val_if_fail (module != NULL, NULL); + + funcs = gck_module_get_functions (module); + g_return_val_if_fail (module != NULL, NULL); + + ret = crypt_sync (self, key, mechanism, input, n_input, n_result, cancellable, err, + funcs->C_SignInit, funcs->C_Sign); + g_object_unref (module); + return ret; +} + +/** + * gck_session_sign_async: + * @self: The session. + * @key: The key to sign with. + * @mechanism: The mechanism type and parameters to use for signing. + * @input: The data to sign. + * @n_input: The length of the data to sign. + * @cancellable: A GCancellable which can be used to cancel the operation. + * @callback: Called when the operation completes. + * @user_data: A pointer to pass to the callback. + * + * Sign data in a mechanism specific manner. This call will + * return immediately and complete asynchronously. + */ +void +gck_session_sign_async (GckSession *self, GckObject *key, GckMechanism *mechanism, + const guchar *input, gsize n_input, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data) +{ + GckModule *module = NULL; + CK_FUNCTION_LIST_PTR funcs; + + g_object_get (self, "module", &module, NULL); + g_return_if_fail (module != NULL); + + funcs = gck_module_get_functions (module); + g_return_if_fail (module != NULL); + + crypt_async (self, key, mechanism, input, n_input, cancellable, callback, user_data, + funcs->C_SignInit, funcs->C_Sign); + g_object_unref (module); +} + +/** + * gck_session_sign_finish: + * @self: The session. + * @result: The result object passed to the callback. + * @n_result: A location to store the length of the result data. + * @err: A location to place error information. + * + * Get the result of an signing operation. + * + * Returns: The data that was signed, or NULL if an error occurred. + */ +guchar* +gck_session_sign_finish (GckSession *self, GAsyncResult *result, + gsize *n_result, GError **err) +{ + return crypt_finish (self, result, n_result, err); +} + +/* -------------------------------------------------------------------------------------------------- + * VERIFY + */ + +typedef struct _Verify { + GckArguments base; + + /* Authenticator */ + Authenticate auth; + + /* Input */ + CK_OBJECT_HANDLE key; + GckMechanism *mech; + guchar *input; + CK_ULONG n_input; + guchar *signature; + CK_ULONG n_signature; + +} Verify; + +static CK_RV +perform_verify (Verify *args) +{ + CK_RV rv; + + /* Initialize the crypt operation */ + rv = (args->base.pkcs11->C_VerifyInit) (args->base.handle, (CK_MECHANISM_PTR)args->mech, args->key); + if (rv != CKR_OK) + return rv; + + rv = authenticate_perform (&args->auth, &args->base); + if (rv != CKR_OK) + return rv; + + /* Do the actual verify */ + return (args->base.pkcs11->C_Verify) (args->base.handle, args->input, args->n_input, + args->signature, args->n_signature); +} + +static gboolean +complete_verify (Verify *args, CK_RV result) +{ + if (!authenticate_complete (&args->auth, &args->base, result)) + return FALSE; + + /* Call is complete */ + return TRUE; +} + +static void +free_verify (Verify *args) +{ + g_free (args->input); + g_free (args->signature); + gck_mechanism_unref (args->mech); + g_free (args); +} + +/** + * gck_session_verify: + * @self: The session. + * @key: The key to verify with. + * @mech_type: The mechanism type to use for verifying. + * @input: The data to verify. + * @n_input: The length of the data to verify. + * @signature: The signature. + * @n_signature: The length of the signature. + * @err: A location to place an error. + * + * Verify data in a mechanism specific manner. This call may + * block for an indefinite period. + * + * Returns: TRUE if the data verified correctly, otherwise a failure or error occurred. + */ +gboolean +gck_session_verify (GckSession *self, GckObject *key, gulong mech_type, const guchar *input, + gsize n_input, const guchar *signature, gsize n_signature, GError **err) +{ + GckMechanism mechanism = { mech_type, NULL, 0 }; + return gck_session_verify_full (self, key, &mechanism, input, n_input, + signature, n_signature, NULL, err); +} + +/** + * gck_session_verify_full: + * @self: The session. + * @key: The key to verify with. + * @mechanism: The mechanism type and parameters to use for signing. + * @input: The data to verify. + * @n_input: The length of the data to verify. + * @signature: The signature. + * @n_signature: The length of the signature. + * @cancellable: A GCancellable which can be used to cancel the operation. + * @err: A location to place an error. + * + * Verify data in a mechanism specific manner. This call may + * block for an indefinite period. + * + * Returns: TRUE if the data verified correctly, otherwise a failure or error occurred. + */ +gboolean +gck_session_verify_full (GckSession *self, GckObject *key, GckMechanism *mechanism, + const guchar *input, gsize n_input, const guchar *signature, + gsize n_signature, GCancellable *cancellable, GError **err) +{ + Verify args; + GckSlot *slot; + + g_return_val_if_fail (GCK_IS_OBJECT (key), FALSE); + g_return_val_if_fail (mechanism, FALSE); + + memset (&args, 0, sizeof (args)); + g_object_get (key, "handle", &args.key, NULL); + g_return_val_if_fail (args.key != 0, FALSE); + + args.mech = mechanism; + + /* No need to copy in this case */ + args.input = (guchar*)input; + args.n_input = n_input; + args.signature = (guchar*)signature; + args.n_signature = n_signature; + + slot = gck_session_get_slot (self); + authenticate_init (&args.auth, slot, key); + g_object_unref (slot); + + return _gck_call_sync (self, perform_verify, complete_verify, &args, cancellable, err); +} + +/** + * gck_session_verify_async: + * @self: The session. + * @key: The key to verify with. + * @mechanism: The mechanism type and parameters to use for signing. + * @input: The data to verify. + * @n_input: The length of the data to verify. + * @signature: The signature. + * @n_signature: The length of the signature. + * @cancellable: A GCancellable which can be used to cancel the operation. + * @callback: Called when the operation completes. + * @user_data: A pointer to pass to the callback. + * + * Verify data in a mechanism specific manner. This call returns + * immediately and completes asynchronously. + */ +void +gck_session_verify_async (GckSession *self, GckObject *key, GckMechanism *mechanism, + const guchar *input, gsize n_input, const guchar *signature, + gsize n_signature, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data) +{ + Verify *args = _gck_call_async_prep (self, self, perform_verify, complete_verify, sizeof (*args), free_verify); + GckSlot *slot; + + g_return_if_fail (GCK_IS_OBJECT (key)); + g_return_if_fail (mechanism); + + g_object_get (key, "handle", &args->key, NULL); + g_return_if_fail (args->key != 0); + + args->mech = gck_mechanism_ref (mechanism); + + args->input = input && n_input ? g_memdup (input, n_input) : NULL; + args->n_input = n_input; + args->signature = signature && n_signature ? g_memdup (signature, n_signature) : NULL; + args->n_signature = n_signature; + + slot = gck_session_get_slot (self); + authenticate_init (&args->auth, slot, key); + g_object_unref (slot); + + _gck_call_async_ready_go (args, cancellable, callback, user_data); +} + +/** + * gck_session_verify_finish: + * @self: The session. + * @result: The result object passed to the callback. + * @err: A location to place error information. + * + * Get the result of an verify operation. + * + * Returns: TRUE if the data verified correctly, otherwise a failure or error occurred. + */ +gboolean +gck_session_verify_finish (GckSession *self, GAsyncResult *result, GError **err) +{ + return _gck_call_basic_finish (result, err); +} diff --git a/gck/gck-slot.c b/gck/gck-slot.c new file mode 100644 index 00000000..e4092bf0 --- /dev/null +++ b/gck/gck-slot.c @@ -0,0 +1,1061 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* gck-slot.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, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter <nielsen@memberwebs.com> +*/ + +#include "config.h" + +#include "gck.h" +#include "gck-private.h" + +#include <string.h> + +/** + * SECTION:gck-slot + * @title: GckSlot + * @short_description: Represents a PKCS11 slot that can contain a token. + * + * A PKCS11 slot can contain a token. As an example, a slot might be a card reader, and the token + * the card. If the PKCS11 module is not a hardware driver, often the slot and token are equivalent. + */ + +/** + * GckSlot: + * + * Represents a PKCS11 slot. + */ + +enum { + PROP_0, + PROP_MODULE, + PROP_HANDLE +}; + +typedef struct _GckSlotData { + GckModule *module; + CK_SLOT_ID handle; +} GckSlotData; + +typedef struct _GckSlotPrivate { + GckSlotData data; +} GckSlotPrivate; + +G_DEFINE_TYPE (GckSlot, gck_slot, G_TYPE_OBJECT); + +#define GCK_SLOT_GET_DATA(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), GCK_TYPE_SLOT, GckSlotData)) + +#ifndef HAVE_TIMEGM + +time_t +timegm(struct tm *t) +{ + time_t tl, tb; + struct tm *tg; + + tl = mktime (t); + if (tl == -1) + { + t->tm_hour--; + tl = mktime (t); + if (tl == -1) + return -1; + tl += 3600; + } + tg = gmtime (&tl); + tg->tm_isdst = 0; + tb = mktime (tg); + if (tb == -1) + { + tg->tm_hour--; + tb = mktime (tg); + if (tb == -1) + return -1; + tb += 3600; + } + return (tl - (tb - tl)); +} + +#endif + +/* ---------------------------------------------------------------------------- + * HELPERS + */ + +static GckSession* +make_session_object (GckSlot *self, gulong flags, CK_SESSION_HANDLE handle) +{ + GckSession *session; + GckModule *module; + + g_return_val_if_fail (handle != 0, NULL); + + module = gck_slot_get_module (self); + + session = gck_session_from_handle (self, handle); + g_return_val_if_fail (session != NULL, NULL); + + /* Session keeps a reference to module so this is safe */ + g_signal_connect (session, "discard-handle", + G_CALLBACK (_gck_module_pool_session_handle), module); + + g_object_unref (module); + + return session; +} + +/* ---------------------------------------------------------------------------- + * OBJECT + */ + +static void +gck_slot_init (GckSlot *self) +{ + +} + +static void +gck_slot_get_property (GObject *obj, guint prop_id, GValue *value, + GParamSpec *pspec) +{ + GckSlot *self = GCK_SLOT (obj); + + switch (prop_id) { + case PROP_MODULE: + g_value_take_object (value, gck_slot_get_module (self)); + break; + case PROP_HANDLE: + g_value_set_ulong (value, gck_slot_get_handle (self)); + break; + } +} + +static void +gck_slot_set_property (GObject *obj, guint prop_id, const GValue *value, + GParamSpec *pspec) +{ + GckSlotData *data = GCK_SLOT_GET_DATA (obj); + + /* All writes to data members below, happen only during construct phase */ + + switch (prop_id) { + case PROP_MODULE: + g_assert (!data->module); + data->module = g_value_get_object (value); + g_assert (data->module); + g_object_ref (data->module); + break; + case PROP_HANDLE: + g_assert (!data->handle); + data->handle = g_value_get_ulong (value); + break; + } +} + +static void +gck_slot_dispose (GObject *obj) +{ + G_OBJECT_CLASS (gck_slot_parent_class)->dispose (obj); +} + +static void +gck_slot_finalize (GObject *obj) +{ + GckSlotData *data = GCK_SLOT_GET_DATA (obj); + + data->handle = 0; + + if (data->module) + g_object_unref (data->module); + data->module = NULL; + + G_OBJECT_CLASS (gck_slot_parent_class)->finalize (obj); +} + + +static void +gck_slot_class_init (GckSlotClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*)klass; + gck_slot_parent_class = g_type_class_peek_parent (klass); + + gobject_class->get_property = gck_slot_get_property; + gobject_class->set_property = gck_slot_set_property; + gobject_class->dispose = gck_slot_dispose; + gobject_class->finalize = gck_slot_finalize; + + /** + * GckSlot:module: + * + * The PKCS11 object that this slot is a part of. + */ + g_object_class_install_property (gobject_class, PROP_MODULE, + g_param_spec_object ("module", "Module", "PKCS11 Module", + GCK_TYPE_MODULE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /** + * GckSlot:handle: + * + * The raw CK_SLOT_ID handle of this slot. + */ + g_object_class_install_property (gobject_class, PROP_HANDLE, + g_param_spec_ulong ("handle", "Handle", "PKCS11 Slot ID", + 0, G_MAXULONG, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_type_class_add_private (gobject_class, sizeof (GckSlotPrivate)); +} + +/* ---------------------------------------------------------------------------- + * PUBLIC + */ + +/** + * GckSlotInfo: + * @slot_description: Description of the slot. + * @manufacturer_id: The manufacturer of this slot. + * @flags: Various PKCS11 flags that apply to this slot. + * @hardware_version_major: The major version of the hardware. + * @hardware_version_minor: The minor version of the hardware. + * @firmware_version_major: The major version of the firmware. + * @firmware_version_minor: The minor version of the firmware. + * + * Represents information about a PKCS11 slot. + * + * This is analogous to a CK_SLOT_INFO structure, but the + * strings are far more usable. + * + * When you're done with this structure it should be released with + * gck_slot_info_free(). + */ + +/** + * gck_slot_info_free: + * @slot_info: The slot info to free, or NULL. + * + * Free the GckSlotInfo and associated resources. + **/ +void +gck_slot_info_free (GckSlotInfo *slot_info) +{ + if (!slot_info) + return; + g_free (slot_info->slot_description); + g_free (slot_info->manufacturer_id); + g_free (slot_info); +} + +/** + * GckTokenInfo: + * @label: The displayable token label. + * @manufacturer_id: The manufacturer of this slot. + * @model: The token model number as a string. + * @serial_number: The token serial number as a string. + * @flags: Various PKCS11 flags that apply to this token. + * @max_session_count: The maximum number of sessions allowed on this token. + * @session_count: The number of sessions open on this token. + * @max_rw_session_count: The maximum number of read/write sessions allowed on this token. + * @rw_session_count: The number of sessions open on this token. + * @max_pin_len: The maximum length of a PIN for locking this token. + * @min_pin_len: The minimum length of a PIN for locking this token. + * @total_public_memory: The total amount of memory on this token for storing public objects. + * @free_public_memory: The available amount of memory on this token for storing public objects. + * @total_private_memory: The total amount of memory on this token for storing private objects. + * @free_private_memory: The available amount of memory on this token for storing private objects. + * @hardware_version_major: The major version of the hardware. + * @hardware_version_minor: The minor version of the hardware. + * @firmware_version_major: The major version of the firmware. + * @firmware_version_minor: The minor version of the firmware. + * @utc_time: If the token has a hardware clock, this is set to the number of seconds since the epoch. + * + * Represents information about a PKCS11 token. + * + * This is analogous to a CK_TOKEN_INFO structure, but the + * strings are far more usable. + * + * When you're done with this structure it should be released with + * gck_token_info_free(). + */ + +/** + * gck_token_info_free: + * @token_info: The token info to free, or NULL. + * + * Free the GckTokenInfo and associated resources. + **/ +void +gck_token_info_free (GckTokenInfo *token_info) +{ + if (!token_info) + return; + g_free (token_info->label); + g_free (token_info->manufacturer_id); + g_free (token_info->model); + g_free (token_info->serial_number); + g_free (token_info); +} + +/** + * GckMechanismInfo: + * @min_key_size: The minimum key size that can be used with this mechanism. + * @max_key_size: The maximum key size that can be used with this mechanism. + * @flags: Various PKCS11 flags that apply to this mechanism. + * + * Represents information about a PKCS11 mechanism. + * + * This is analogous to a CK_MECHANISM_INFO structure. + * + * When you're done with this structure it should be released with + * gck_mechanism_info_free(). + */ + +/** + * gck_mechanism_info_free: + * @mech_info: The mechanism info to free, or NULL. + * + * Free the GckMechanismInfo and associated resources. + **/ +void +gck_mechanism_info_free (GckMechanismInfo *mech_info) +{ + if (!mech_info) + return; + g_free (mech_info); +} + +/** + * GckMechanisms: + * + * A set of GckMechanismInfo structures. + */ + +/** + * gck_mechanisms_length: + * @a: A GckMechanisms set. + * + * Get the number of GckMechanismInfo in the set. + * + * Returns: The number in the set. + */ + +/** + * gck_mechanisms_at: + * @a: A GckMechanisms set. + * @i: The index of a GckMechanismInfo. + * + * Get a specific GckMechanismInfo in a the set. + * + * Returns: The GckMechanismInfo. + */ + +/** + * gck_mechanisms_free: + * @a: A GckMechanism set. + * + * Free a GckMechanisms set. + */ + +/** + * gck_mechanisms_check: + * @mechanisms: A list of mechanisms, perhaps retrieved from gck_slot_get_mechanisms(). + * @...: A list of mechanism types followed by GCK_INVALID. + * + * Check whether all the mechanism types are in the list. + * + * The arguments should be a list of CKM_XXX mechanism types. The last argument + * should be GCK_INVALID. + * + * Return value: Whether the mechanism is in the list or not. + **/ +gboolean +gck_mechanisms_check (GckMechanisms *mechanisms, ...) +{ + gboolean found = TRUE; + va_list va; + gulong mech; + gsize i; + + g_return_val_if_fail (mechanisms, FALSE); + + va_start (va, mechanisms); + for (;;) { + mech = va_arg (va, gulong); + if (mech == GCK_INVALID) + break; + + found = FALSE; + for (i = 0; i < gck_mechanisms_length (mechanisms); ++i) { + if (gck_mechanisms_at (mechanisms, i) == mech) { + found = TRUE; + break; + } + } + + if (found == FALSE) + break; + + } + va_end (va); + + return found; +} + +/** + * gck_slot_equal: + * @slot1: A pointer to the first GckSlot + * @slot2: A pointer to the second GckSlot + * + * Checks equality of two slots. Two GckSlot objects can point to the same + * underlying PKCS#11 slot. + * + * Return value: TRUE if slot1 and slot2 are equal. FALSE if either is not a GckSlot. + **/ +gboolean +gck_slot_equal (gconstpointer slot1, gconstpointer slot2) +{ + GckSlotData *data1, *data2; + + if (slot1 == slot2) + return TRUE; + if (!GCK_IS_SLOT (slot1) || !GCK_IS_SLOT (slot2)) + return FALSE; + + data1 = GCK_SLOT_GET_DATA (slot1); + data2 = GCK_SLOT_GET_DATA (slot2); + + return data1->handle == data2->handle && + gck_module_equal (data1->module, data2->module); +} + +/** + * gck_slot_hash: + * @slot: A pointer to a GckSlot + * + * Create a hash value for the GckSlot. + * + * This function is intended for easily hashing a GckSlot to add to + * a GHashTable or similar data structure. + * + * Return value: An integer that can be used as a hash value, or 0 if invalid. + **/ +guint +gck_slot_hash (gconstpointer slot) +{ + GckSlotData *data; + + g_return_val_if_fail (GCK_IS_SLOT (slot), 0); + + data = GCK_SLOT_GET_DATA (slot); + + return _gck_ulong_hash (&data->handle) ^ + gck_module_hash (data->module); +} + +/** + * gck_slot_get_handle: + * @self: The slot to get the handle of. + * + * Get the raw PKCS#11 handle of a slot. + * + * Return value: The raw handle. + **/ +CK_SLOT_ID +gck_slot_get_handle (GckSlot *self) +{ + GckSlotData *data = GCK_SLOT_GET_DATA (self); + g_return_val_if_fail (GCK_IS_SLOT (self), (CK_SLOT_ID)-1); + return data->handle; +} + +/** + * gck_slot_get_module: + * @self: The slot to get the module for. + * + * Get the module that this slot is on. + * + * Return value: The module, you must unreference this after you're done with it. + */ +GckModule* +gck_slot_get_module (GckSlot *self) +{ + GckSlotData *data = GCK_SLOT_GET_DATA (self); + g_return_val_if_fail (GCK_IS_SLOT (self), NULL); + g_return_val_if_fail (GCK_IS_MODULE (data->module), NULL); + return g_object_ref (data->module); +} + +/** + * gck_slot_get_info: + * @self: The slot to get info for. + * + * Get the information for this slot. + * + * Return value: The slot information. When done, use gck_slot_info_free() + * to release it. + **/ +GckSlotInfo* +gck_slot_get_info (GckSlot *self) +{ + CK_SLOT_ID handle = (CK_SLOT_ID)-1; + GckModule *module = NULL; + CK_FUNCTION_LIST_PTR funcs; + GckSlotInfo *slotinfo; + CK_SLOT_INFO info; + CK_RV rv; + + g_return_val_if_fail (GCK_IS_SLOT (self), NULL); + + g_object_get (self, "module", &module, "handle", &handle, NULL); + g_return_val_if_fail (GCK_IS_MODULE (module), NULL); + + funcs = gck_module_get_functions (module); + g_return_val_if_fail (funcs, NULL); + + memset (&info, 0, sizeof (info)); + rv = (funcs->C_GetSlotInfo) (handle, &info); + + g_object_unref (module); + + if (rv != CKR_OK) { + g_warning ("couldn't get slot info: %s", gck_message_from_rv (rv)); + return NULL; + } + + slotinfo = g_new0 (GckSlotInfo, 1); + slotinfo->slot_description = gck_string_from_chars (info.slotDescription, + sizeof (info.slotDescription)); + slotinfo->manufacturer_id = gck_string_from_chars (info.manufacturerID, + sizeof (info.manufacturerID)); + slotinfo->flags = info.flags; + slotinfo->hardware_version_major = info.hardwareVersion.major; + slotinfo->hardware_version_minor = info.hardwareVersion.minor; + slotinfo->firmware_version_major = info.firmwareVersion.major; + slotinfo->firmware_version_minor = info.firmwareVersion.minor; + + return slotinfo; +} + +/** + * gck_slot_get_token_info: + * @self: The slot to get info for. + * + * Get the token information for this slot. + * + * Return value: The token information. When done, use gck_token_info_free() + * to release it. + **/ +GckTokenInfo* +gck_slot_get_token_info (GckSlot *self) +{ + CK_SLOT_ID handle = (CK_SLOT_ID)-1; + CK_FUNCTION_LIST_PTR funcs; + GckModule *module = NULL; + GckTokenInfo *tokeninfo; + CK_TOKEN_INFO info; + gchar *string; + struct tm tm; + CK_RV rv; + + g_return_val_if_fail (GCK_IS_SLOT (self), NULL); + + g_object_get (self, "module", &module, "handle", &handle, NULL); + g_return_val_if_fail (GCK_IS_MODULE (module), NULL); + + funcs = gck_module_get_functions (module); + g_return_val_if_fail (funcs, NULL); + + memset (&info, 0, sizeof (info)); + rv = (funcs->C_GetTokenInfo) (handle, &info); + + g_object_unref (module); + + if (rv != CKR_OK) { + g_warning ("couldn't get slot info: %s", gck_message_from_rv (rv)); + return NULL; + } + + tokeninfo = g_new0 (GckTokenInfo, 1); + tokeninfo->label = gck_string_from_chars (info.label, sizeof (info.label)); + tokeninfo->model = gck_string_from_chars (info.model, sizeof (info.model)); + tokeninfo->manufacturer_id = gck_string_from_chars (info.manufacturerID, + sizeof (info.manufacturerID)); + tokeninfo->serial_number = gck_string_from_chars (info.serialNumber, + sizeof (info.serialNumber)); + tokeninfo->flags = info.flags; + tokeninfo->max_session_count = info.ulMaxSessionCount; + tokeninfo->session_count = info.ulSessionCount; + tokeninfo->max_rw_session_count = info.ulMaxRwSessionCount; + tokeninfo->rw_session_count = info.ulRwSessionCount; + tokeninfo->max_pin_len = info.ulMaxPinLen; + tokeninfo->min_pin_len = info.ulMinPinLen; + tokeninfo->total_public_memory = info.ulTotalPublicMemory; + tokeninfo->total_private_memory = info.ulTotalPrivateMemory; + tokeninfo->free_private_memory = info.ulFreePrivateMemory; + tokeninfo->free_public_memory = info.ulFreePublicMemory; + tokeninfo->hardware_version_major = info.hardwareVersion.major; + tokeninfo->hardware_version_minor = info.hardwareVersion.minor; + tokeninfo->firmware_version_major = info.firmwareVersion.major; + tokeninfo->firmware_version_minor = info.firmwareVersion.minor; + + /* Parse the time into seconds since epoch */ + if (info.flags & CKF_CLOCK_ON_TOKEN) { + string = g_strndup ((gchar*)info.utcTime, MIN (14, sizeof (info.utcTime))); + if (!strptime (string, "%Y%m%d%H%M%S", &tm)) + tokeninfo->utc_time = -1; + else + tokeninfo->utc_time = timegm (&tm); + g_free (string); + } else { + tokeninfo->utc_time = -1; + } + + return tokeninfo; +} + +/** + * gck_slot_get_mechanisms: + * @self: The slot to get mechanisms for. + * + * Get the available mechanisms for this slot. + * + * Return value: A list of the mechanisms for this slot. Use + * gck_mechanisms_free() when done with this. + **/ +GckMechanisms* +gck_slot_get_mechanisms (GckSlot *self) +{ + CK_SLOT_ID handle = (CK_SLOT_ID)-1; + CK_FUNCTION_LIST_PTR funcs; + GckModule *module = NULL; + CK_MECHANISM_TYPE_PTR mech_list = NULL; + CK_ULONG count, i; + GckMechanisms *result; + CK_RV rv; + + g_return_val_if_fail (GCK_IS_SLOT (self), NULL); + + g_object_get (self, "module", &module, "handle", &handle, NULL); + g_return_val_if_fail (GCK_IS_MODULE (module), NULL); + + funcs = gck_module_get_functions (module); + g_return_val_if_fail (funcs, NULL); + + rv = (funcs->C_GetMechanismList) (handle, NULL, &count); + if (rv != CKR_OK) { + g_warning ("couldn't get mechanism count: %s", gck_message_from_rv (rv)); + count = 0; + } else { + mech_list = g_new (CK_MECHANISM_TYPE, count); + rv = (funcs->C_GetMechanismList) (handle, mech_list, &count); + if (rv != CKR_OK) { + g_warning ("couldn't get mechanism list: %s", gck_message_from_rv (rv)); + g_free (mech_list); + count = 0; + } + } + + g_object_unref (module); + + if (!count) + return NULL; + + result = g_array_new (FALSE, TRUE, sizeof (CK_MECHANISM_TYPE)); + for (i = 0; i < count; ++i) + g_array_append_val (result, mech_list[i]); + + g_free (mech_list); + return result; + +} + +/** + * gck_slot_get_mechanism_info: + * @self: The slot to get mechanism info from. + * @mech_type: The mechanisms type to get info for. + * + * Get information for the specified mechanism. + * + * Return value: The mechanism information, or NULL if failed. Use + * gck_mechanism_info_free() when done with it. + **/ +GckMechanismInfo* +gck_slot_get_mechanism_info (GckSlot *self, gulong mech_type) +{ + CK_SLOT_ID handle = (CK_SLOT_ID)-1; + CK_FUNCTION_LIST_PTR funcs; + GckMechanismInfo *mechinfo; + GckModule *module = NULL; + CK_MECHANISM_INFO info; + struct tm; + CK_RV rv; + + g_return_val_if_fail (GCK_IS_SLOT (self), NULL); + + g_object_get (self, "module", &module, "handle", &handle, NULL); + g_return_val_if_fail (GCK_IS_MODULE (module), NULL); + + funcs = gck_module_get_functions (module); + g_return_val_if_fail (funcs, NULL); + + memset (&info, 0, sizeof (info)); + rv = (funcs->C_GetMechanismInfo) (handle, mech_type, &info); + + g_object_unref (module); + + if (rv != CKR_OK) { + g_warning ("couldn't get mechanism info: %s", gck_message_from_rv (rv)); + return NULL; + } + + mechinfo = g_new0 (GckMechanismInfo, 1); + mechinfo->flags = info.flags; + mechinfo->max_key_size = info.ulMaxKeySize; + mechinfo->min_key_size = info.ulMinKeySize; + + return mechinfo; +} + +/** + * gck_slot_has_flags: + * @self: The GckSlot object. + * @flags: The flags to check. + * + * Check if the PKCS11 slot has the given flags. + * + * Returns: Whether one or more flags exist. + */ +gboolean +gck_slot_has_flags (GckSlot *self, gulong flags) +{ + CK_FUNCTION_LIST_PTR funcs; + GckModule *module = NULL; + CK_TOKEN_INFO info; + CK_SLOT_ID handle; + CK_RV rv; + + g_return_val_if_fail (GCK_IS_SLOT (self), FALSE); + + g_object_get (self, "module", &module, "handle", &handle, NULL); + g_return_val_if_fail (GCK_IS_MODULE (module), FALSE); + + funcs = gck_module_get_functions (module); + g_return_val_if_fail (funcs, FALSE); + + memset (&info, 0, sizeof (info)); + rv = (funcs->C_GetTokenInfo) (handle, &info); + + g_object_unref (module); + + if (rv != CKR_OK) { + g_warning ("couldn't get slot info: %s", gck_message_from_rv (rv)); + return FALSE; + } + + return (info.flags & flags) != 0; +} + +#if UNIMPLEMENTED + +typedef struct InitToken { + GckArguments base; + const guchar *pin; + gsize length; + const gchar *label; +} InitToken; + +static CK_RV +perform_init_token (InitToken *args) +{ + return (args->base.pkcs11->C_InitToken) (args->base.handle, + args->pin, args->length, + args->label); +} + +gboolean +gck_slot_init_token (GckSlot *self, const guchar *pin, gsize length, + const gchar *label, GCancellable *cancellable, + GError **err) +{ + InitToken args = { GCK_ARGUMENTS_INIT, pin, length, label }; + return _gck_call_sync (self, perform_init_token, NULL, &args, err); +} + +void +gck_slot_init_token_async (GckSlot *self, const guchar *pin, gsize length, + const gchar *label, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data) +{ + InitToken* args = _gck_call_async_prep (self, self, perform_init_token, + NULL, sizeof (*args)); + + args->pin = pin; + args->length = length; + args->label = label; + + _gck_call_async_go (args, cancellable, callback, user_data); +} + +gboolean +gck_slot_init_token_finish (GckSlot *self, GAsyncResult *result, GError **err) +{ + return _gck_call_basic_finish (self, result, err); +} + +#endif /* UNIMPLEMENTED */ + +typedef struct OpenSession { + GckArguments base; + GckSlot *slot; + gulong flags; + gpointer app_data; + CK_NOTIFY notify; + gchar *password; + gboolean auto_login; + CK_SESSION_HANDLE session; +} OpenSession; + +static CK_RV +perform_open_session (OpenSession *args) +{ + CK_SESSION_INFO info; + CK_RV rv = CKR_OK; + CK_ULONG pin_len; + + /* Can be called multiple times */ + + /* First step, open session */ + if (!args->session) { + rv = (args->base.pkcs11->C_OpenSession) (args->base.handle, args->flags, + args->app_data, args->notify, &args->session); + } + + if (rv != CKR_OK || !args->auto_login) + return rv; + + /* Step two, check if session is logged in */ + rv = (args->base.pkcs11->C_GetSessionInfo) (args->session, &info); + if (rv != CKR_OK) + return rv; + + /* Already logged in? */ + if (info.state != CKS_RO_PUBLIC_SESSION && info.state != CKS_RW_PUBLIC_SESSION) + return CKR_OK; + + /* Try to login */ + pin_len = args->password ? strlen (args->password) : 0; + return (args->base.pkcs11->C_Login) (args->session, CKU_USER, + (CK_UTF8CHAR_PTR)args->password, pin_len); +} + +static gboolean +complete_open_session (OpenSession *args, CK_RV result) +{ + GckModule *module; + gboolean ret = TRUE; + + g_free (args->password); + args->password = NULL; + + /* Ask the token for a password */ + module = gck_slot_get_module (args->slot); + + if (args->auto_login && result == CKR_PIN_INCORRECT) { + + ret = _gck_module_fire_authenticate_slot (module, args->slot, NULL, &args->password); + + /* If authenticate returns TRUE then call is not complete */ + ret = !ret; + } + + g_object_unref (module); + + return ret; +} + +static void +free_open_session (OpenSession *args) +{ + g_assert (!args->password); + if (args->slot) + g_object_unref (args->slot); + g_free (args); +} + +/** + * gck_slot_open_session: + * @self: The slot ot open a session on. + * @flags: The flags to open a session with. + * @err: A location to return an error, or NULL. + * + * Open a session on the slot. If the 'auto reuse' setting is set, + * then this may be a recycled session with the same flags. + * + * This call may block for an indefinite period. + * + * Return value: A new session or NULL if an error occurs. + **/ +GckSession* +gck_slot_open_session (GckSlot *self, gulong flags, GError **err) +{ + return gck_slot_open_session_full (self, flags, NULL, NULL, NULL, err); +} + +/** + * gck_slot_open_session_full: + * @self: The slot to open a session on. + * @flags: The flags to open a session with. + * @app_data: Application data for notification callback. + * @notify: PKCS#11 notification callback. + * @cancellable: Optional cancellation object, or NULL. + * @err: A location to return an error, or NULL. + * + * Open a session on the slot. If the 'auto reuse' setting is set, + * then this may be a recycled session with the same flags. + * + * This call may block for an indefinite period. + * + * Return value: A new session or NULL if an error occurs. + **/ +GckSession* +gck_slot_open_session_full (GckSlot *self, gulong flags, gpointer app_data, + CK_NOTIFY notify, GCancellable *cancellable, GError **err) +{ + GckSession *session = NULL; + GckModule *module = NULL; + CK_SESSION_HANDLE handle; + CK_SLOT_ID slot_id; + + flags |= CKF_SERIAL_SESSION; + + g_object_ref (self); + + /* Try to use a cached session */ + module = gck_slot_get_module (self); + slot_id = gck_slot_get_handle (self); + handle = _gck_module_pooled_session_handle (module, slot_id, flags); + if (handle != 0) + session = make_session_object (self, flags, handle); + + /* Open a new session */ + if (session == NULL) { + OpenSession args = { GCK_ARGUMENTS_INIT, 0, }; + + args.slot = self; + args.flags = flags; + args.app_data = app_data; + args.notify = notify; + args.password = NULL; + args.auto_login = (gck_module_get_auto_authenticate (module) & GCK_AUTHENTICATE_TOKENS) ? TRUE : FALSE; + args.session = 0; + + if (_gck_call_sync (self, perform_open_session, complete_open_session, &args, cancellable, err)) + session = make_session_object (self, flags, args.session); + } + + g_object_unref (module); + g_object_unref (self); + + return session; +} + +/** + * gck_slot_open_session_async: + * @self: The slot to open a session on. + * @flags: The flags to open a session with. + * @app_data: Application data for notification callback. + * @notify: PKCS#11 notification callback. + * @cancellable: Optional cancellation object, or NULL. + * @callback: Called when the operation completes. + * @user_data: Data to pass to the callback. + * + * Open a session on the slot. If the 'auto reuse' setting is set, + * then this may be a recycled session with the same flags. + * + * This call will return immediately and complete asynchronously. + **/ +void +gck_slot_open_session_async (GckSlot *self, gulong flags, gpointer app_data, + CK_NOTIFY notify, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data) +{ + GckModule *module = NULL; + GckCall *call; + OpenSession *args; + CK_SLOT_ID slot_id; + + flags |= CKF_SERIAL_SESSION; + + g_object_ref (self); + + args = _gck_call_async_prep (self, self, perform_open_session, complete_open_session, + sizeof (*args), free_open_session); + + args->flags = flags; + args->app_data = app_data; + args->notify = notify; + args->slot = g_object_ref (self); + + /* Try to use a cached session */ + module = gck_slot_get_module (self); + slot_id = gck_slot_get_handle (self); + args->session = _gck_module_pooled_session_handle (module, slot_id, flags); + args->auto_login = (gck_module_get_auto_authenticate (module) & GCK_AUTHENTICATE_TOKENS) ? TRUE : FALSE; + g_object_unref (module); + + call = _gck_call_async_ready (args, cancellable, callback, user_data); + if (args->session) + _gck_call_async_short (call, CKR_OK); + else + _gck_call_async_go (call); + + g_object_unref (self); +} + +/** + * gck_slot_open_session_finish: + * @self: The slot to open a session on. + * @result: The result passed to the callback. + * @err: A location to return an error or NULL. + * + * Get the result of an open session operation. If the 'auto reuse' setting is set, + * then this may be a recycled session with the same flags. + * + * Return value: The new session or NULL if an error occurs. + */ +GckSession* +gck_slot_open_session_finish (GckSlot *self, GAsyncResult *result, GError **err) +{ + GckSession *session = NULL; + + g_object_ref (self); + + { + OpenSession *args; + + if (_gck_call_basic_finish (result, err)) { + args = _gck_call_arguments (result, OpenSession); + session = make_session_object (self, args->flags, args->session); + } + } + + g_object_unref (self); + + return session; +} diff --git a/gck/gck.h b/gck/gck.h new file mode 100644 index 00000000..5e74b952 --- /dev/null +++ b/gck/gck.h @@ -0,0 +1,1297 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* gck.h - 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, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter <nielsen@memberwebs.com> +*/ + +#ifndef GCK_H +#define GCK_H + +#include <glib.h> +#include <glib-object.h> +#include <gio/gio.h> + +#include "pkcs11.h" + +G_BEGIN_DECLS + +#define GCK_VENDOR_CODE 0x47503131 /* GP11 */ + +/* An error code which results from a failure to load the PKCS11 module */ +#define CKR_GCK_MODULE_PROBLEM (CKR_VENDOR_DEFINED | (GCK_VENDOR_CODE + 1)) + +#define GCK_ERROR (gck_get_error_quark ()) + +GQuark gck_get_error_quark (void); + +GList* gck_list_ref_copy (GList *reflist); + +void gck_list_unref_free (GList *reflist); + +const gchar* gck_message_from_rv (CK_RV rv); + +gchar* gck_string_from_chars (const guchar *data, gsize max); + +typedef gpointer (*GckAllocator) (gpointer data, gsize length); + +typedef struct GckMechanism { + gulong type; + gpointer parameter; + gulong n_parameter; +} GckMechanism; + +GckMechanism* gck_mechanism_new (gulong type); + +GckMechanism* gck_mechanism_new_with_param (gulong type, + gconstpointer parameter, + gulong n_parameter); + +GckMechanism* gck_mechanism_ref (GckMechanism* mech); + +void gck_mechanism_unref (GckMechanism* mech); + +typedef struct GckAttribute { + gulong type; + gpointer value; + gulong length; +} GckAttribute; + +#define GCK_BOOLEAN ((gssize)-1) +#define GCK_ULONG ((gssize)-2) +#define GCK_STRING ((gssize)-3) +#define GCK_DATE ((gssize)-4) + +#define GCK_INVALID G_MAXULONG + +enum { + GCK_AUTHENTICATE_TOKENS = 2, + GCK_AUTHENTICATE_OBJECTS = 4 +}; + +void gck_attribute_init (GckAttribute *attr, + gulong attr_type, + gconstpointer value, + gsize length); + +void gck_attribute_init_invalid (GckAttribute *attr, + gulong attr_type); + +void gck_attribute_init_empty (GckAttribute *attr, + gulong attr_type); + +void gck_attribute_init_boolean (GckAttribute *attr, + gulong attr_type, + gboolean value); + +void gck_attribute_init_date (GckAttribute *attr, + gulong attr_type, + const GDate *value); + +void gck_attribute_init_ulong (GckAttribute *attr, + gulong attr_type, + gulong value); + +void gck_attribute_init_string (GckAttribute *attr, + gulong attr_type, + const gchar *value); + +void gck_attribute_init_copy (GckAttribute *dest, + const GckAttribute *src); + +GckAttribute* gck_attribute_new (gulong attr_type, + gpointer value, + gsize length); + +GckAttribute* gck_attribute_new_invalid (gulong attr_type); + +GckAttribute* gck_attribute_new_empty (gulong attr_type); + +GckAttribute* gck_attribute_new_boolean (gulong attr_type, + gboolean value); + +GckAttribute* gck_attribute_new_date (gulong attr_type, + const GDate *value); + +GckAttribute* gck_attribute_new_ulong (gulong attr_type, + gulong value); + +GckAttribute* gck_attribute_new_string (gulong attr_type, + const gchar *value); + +gboolean gck_attribute_is_invalid (GckAttribute *attr); + +gboolean gck_attribute_get_boolean (GckAttribute *attr); + +gulong gck_attribute_get_ulong (GckAttribute *attr); + +gchar* gck_attribute_get_string (GckAttribute *attr); + +void gck_attribute_get_date (GckAttribute *attr, + GDate* value); + +GckAttribute* gck_attribute_dup (GckAttribute *attr); + +void gck_attribute_clear (GckAttribute *attr); + +void gck_attribute_free (GckAttribute *attr); + + +typedef struct _GckAttributes GckAttributes; + +#define GCK_TYPE_ATTRIBUTES (gck_attributes_get_boxed_type ()) + +GType gck_attributes_get_boxed_type (void) G_GNUC_CONST; + +GckAttributes* gck_attributes_new (void); + +GckAttributes* gck_attributes_new_empty (gulong attr_type, + ...); + +GckAttributes* gck_attributes_new_full (GckAllocator allocator); + +GckAttributes* gck_attributes_newv (gulong attr_type, + ...); + +GckAttributes* gck_attributes_new_valist (GckAllocator allocator, + va_list va); + +GckAttribute* gck_attributes_at (GckAttributes *attrs, + guint index); + +GckAttribute* gck_attributes_add (GckAttributes *attrs, + GckAttribute *attr); + +GckAttribute* gck_attributes_add_data (GckAttributes *attrs, + gulong attr_type, + gconstpointer value, + gsize length); + +GckAttribute* gck_attributes_add_invalid (GckAttributes *attrs, + gulong attr_type); + +GckAttribute* gck_attributes_add_empty (GckAttributes *attrs, + gulong attr_type); + +GckAttribute* gck_attributes_add_boolean (GckAttributes *attrs, + gulong attr_type, + gboolean value); + +GckAttribute* gck_attributes_add_string (GckAttributes *attrs, + gulong attr_type, + const gchar *value); + +GckAttribute* gck_attributes_add_date (GckAttributes *attrs, + gulong attr_type, + const GDate *value); + +GckAttribute* gck_attributes_add_ulong (GckAttributes *attrs, + gulong attr_type, + gulong value); + +GckAttribute* gck_attributes_find (GckAttributes *attrs, + gulong attr_type); + +gboolean gck_attributes_find_boolean (GckAttributes *attrs, + gulong attr_type, + gboolean *value); + +gboolean gck_attributes_find_ulong (GckAttributes *attrs, + gulong attr_type, + gulong *value); + +gboolean gck_attributes_find_string (GckAttributes *attrs, + gulong attr_type, + gchar **value); + +gboolean gck_attributes_find_date (GckAttributes *attrs, + gulong attr_type, + GDate *value); + +gulong gck_attributes_count (GckAttributes *attrs); + +GckAttributes* gck_attributes_ref (GckAttributes *attrs); + +void gck_attributes_unref (GckAttributes *attrs); + +/* ------------------------------------------------------------------------- + * FORWARDS + */ +typedef struct _GckSlot GckSlot; +typedef struct _GckModule GckModule; +typedef struct _GckSession GckSession; +typedef struct _GckObject GckObject; + +typedef gboolean (*GckObjectForeachFunc) (GckObject *object, gpointer user_data); + +/* ------------------------------------------------------------------------- + * MODULE + */ + +typedef struct _GckModuleInfo { + guint8 pkcs11_version_major; + guint8 pkcs11_version_minor; + + gchar *manufacturer_id; + gulong flags; + + gchar *library_description; + guint8 library_version_major; + guint8 library_version_minor; +} GckModuleInfo; + +void gck_module_info_free (GckModuleInfo *module_info); + +#define GCK_TYPE_MODULE (gck_module_get_type()) +#define GCK_MODULE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GCK_TYPE_MODULE, GckModule)) +#define GCK_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GCK_TYPE_MODULE, GckModule)) +#define GCK_IS_MODULE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GCK_TYPE_MODULE)) +#define GCK_IS_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GCK_TYPE_MODULE)) +#define GCK_MODULE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GCK_TYPE_MODULE, GckModuleClass)) + +typedef struct _GckModuleClass GckModuleClass; + +struct _GckModule { + GObject parent; + gpointer reserved[4]; +}; + +struct _GckModuleClass { + GObjectClass parent; + + gboolean (*authenticate_slot) (GckModule *self, GckSlot *slot, gchar *label, gchar **password); + + gboolean (*authenticate_object) (GckModule *self, GckObject *object, gchar *label, gchar **password); + + gpointer reserved[8]; +}; + +GType gck_module_get_type (void) G_GNUC_CONST; + +GckModule* gck_module_new (CK_FUNCTION_LIST_PTR funcs); + +GckModule* gck_module_initialize (const gchar *path, + gpointer reserved, + GError **err); + +gboolean gck_module_equal (gconstpointer module1, + gconstpointer module2); + +guint gck_module_hash (gconstpointer module); + +const gchar* gck_module_get_path (GckModule *self); + +CK_FUNCTION_LIST_PTR gck_module_get_functions (GckModule *self); + +GckModuleInfo* gck_module_get_info (GckModule *self); + +GList* gck_module_get_slots (GckModule *self, + gboolean token_present); + +gboolean gck_module_get_pool_sessions (GckModule *self); + +void gck_module_set_pool_sessions (GckModule *self, + gboolean pool); + +gint gck_module_get_auto_authenticate (GckModule *self); + +void gck_module_set_auto_authenticate (GckModule *self, + gint auto_authenticate); + +gboolean gck_module_enumerate_objects (GckModule *self, + GckObjectForeachFunc func, + gpointer user_data, + ...); + +gboolean gck_module_enumerate_objects_full (GckModule *self, + GckAttributes *attrs, + GCancellable *cancellable, + GckObjectForeachFunc func, + gpointer user_data, + GError **error); + +#ifdef UNIMPLEMENTED +void gck_module_enumerate_objects_async (GckModule *self, + GckAttributes *attrs, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GckObject* gck_module_enumerate_objects_next (GckModule *self, + GAsyncResult *res, + GError **error); + +void gck_module_enumerate_objects_finish (GckModule *self, + GAsyncResult *res, + GError **error); +#endif + +enum { + GCK_IS_STRING = -1, + GCK_IS_BOOLEAN = -2, + GCK_IS_DATE = -3, + GCK_IS_ULONG = -4 +}; + +/* ------------------------------------------------------------------------ + * SLOT + */ + +typedef struct _GckSlotInfo { + gchar *slot_description; + gchar *manufacturer_id; + gulong flags; + guint8 hardware_version_major; + guint8 hardware_version_minor; + guint8 firmware_version_major; + guint8 firmware_version_minor; +} GckSlotInfo; + +void gck_slot_info_free (GckSlotInfo *slot_info); + +typedef struct _GckTokenInfo { + gchar *label; + gchar *manufacturer_id; + gchar *model; + gchar *serial_number; + gulong flags; + glong max_session_count; + glong session_count; + glong max_rw_session_count; + glong rw_session_count; + glong max_pin_len; + glong min_pin_len; + glong total_public_memory; + glong free_public_memory; + glong total_private_memory; + glong free_private_memory; + guint8 hardware_version_major; + guint8 hardware_version_minor; + guint8 firmware_version_major; + guint8 firmware_version_minor; + gint64 utc_time; +} GckTokenInfo; + +void gck_token_info_free (GckTokenInfo *token_info); + +typedef struct _GckMechanismInfo { + gulong min_key_size; + gulong max_key_size; + gulong flags; +} GckMechanismInfo; + +void gck_mechanism_info_free (GckMechanismInfo *mech_info); + +typedef GArray GckMechanisms; + +#define gck_mechanisms_length(a) ((a)->len) + +#define gck_mechanisms_at(a, i) (g_array_index(a, CK_MECHANISM_TYPE, i)) + +#define gck_mechanisms_free(a) (g_array_free(a, TRUE)) + +gboolean gck_mechanisms_check (GckMechanisms *mechanisms, + ...); + +#define GCK_TYPE_SLOT (gck_slot_get_type()) +#define GCK_SLOT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GCK_TYPE_SLOT, GckSlot)) +#define GCK_SLOT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GCK_TYPE_SLOT, GckSlot)) +#define GCK_IS_SLOT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GCK_TYPE_SLOT)) +#define GCK_IS_SLOT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GCK_TYPE_SLOT)) +#define GCK_SLOT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GCK_TYPE_SLOT, GckSlotClass)) + +typedef struct _GckSlotClass GckSlotClass; + +struct _GckSlot { + GObject parent; + gpointer reserved[4]; +}; + +struct _GckSlotClass { + GObjectClass parent; + gpointer reserved[9]; +}; + +GType gck_slot_get_type (void) G_GNUC_CONST; + +gboolean gck_slot_equal (gconstpointer slot1, + gconstpointer slot2); + +guint gck_slot_hash (gconstpointer slot); + +GckModule* gck_slot_get_module (GckSlot *self); + +CK_SLOT_ID gck_slot_get_handle (GckSlot *self); + +GckSlotInfo* gck_slot_get_info (GckSlot *self); + +GckTokenInfo* gck_slot_get_token_info (GckSlot *self); + +GckMechanisms* gck_slot_get_mechanisms (GckSlot *self); + +GckMechanismInfo* gck_slot_get_mechanism_info (GckSlot *self, + gulong mech_type); + +gboolean gck_slot_has_flags (GckSlot *self, + gulong flags); + +#if UNIMPLEMENTED + +gboolean gck_slot_init_token (GckSlot *self, + const guchar *pin, + gsize length, + const gchar *label, + GError **err); + + +void gck_slot_init_token_async (GckSlot *self, + const guchar *pin, + gsize length, + const gchar *label, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gck_slot_init_token_finish (GckSlot *self, + GAsyncResult *result, + GError **err); + +#endif /* UNIMPLEMENTED */ + +GckSession* gck_slot_open_session (GckSlot *self, + gulong flags, + GError **err); + +GckSession* gck_slot_open_session_full (GckSlot *self, + gulong flags, + gpointer app_data, + CK_NOTIFY notify, + GCancellable *cancellable, + GError **err); + +void gck_slot_open_session_async (GckSlot *self, + gulong flags, + gpointer app_data, + CK_NOTIFY notify, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GckSession* gck_slot_open_session_finish (GckSlot *self, + GAsyncResult *result, + GError **err); + +/* ------------------------------------------------------------------------ + * SESSION + */ + +typedef struct _GckSessionInfo { + gulong slot_id; + gulong state; + gulong flags; + gulong device_error; +} GckSessionInfo; + +void gck_session_info_free (GckSessionInfo *session_info); + +#define GCK_TYPE_SESSION (gck_session_get_type()) +#define GCK_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GCK_TYPE_SESSION, GckSession)) +#define GCK_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GCK_TYPE_SESSION, GckSession)) +#define GCK_IS_SESSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GCK_TYPE_SESSION)) +#define GCK_IS_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GCK_TYPE_SESSION)) +#define GCK_SESSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GCK_TYPE_SESSION, GckSessionClass)) + +typedef struct _GckSessionClass GckSessionClass; + +struct _GckSession { + GObject parent; + gpointer reserved[4]; +}; + +struct _GckSessionClass { + GObjectClass parent; + + gboolean (*discard_handle) (GckSession *session, CK_SESSION_HANDLE handle); + + gpointer reserved[8]; +}; + +GType gck_session_get_type (void) G_GNUC_CONST; + +GckSession* gck_session_from_handle (GckSlot *slot, + CK_SESSION_HANDLE handle); + +GckModule* gck_session_get_module (GckSession *self); + +GckSlot* gck_session_get_slot (GckSession *self); + +CK_SESSION_HANDLE gck_session_get_handle (GckSession *self); + +GckSessionInfo* gck_session_get_info (GckSession *self); + +gboolean gck_session_init_pin (GckSession *self, + const guchar *pin, + gsize n_pin, + GError **err); + +gboolean gck_session_init_pin_full (GckSession *self, + const guchar *pin, + gsize n_pin, + GCancellable *cancellable, + GError **err); + +void gck_session_init_pin_async (GckSession *self, + const guchar *pin, + gsize n_pin, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gck_session_init_pin_finish (GckSession *self, + GAsyncResult *result, + GError **err); + +gboolean gck_session_set_pin (GckSession *self, + const guchar *old_pin, + gsize n_old_pin, + const guchar *new_pin, + gsize n_new_pin, + GError **err); + +gboolean gck_session_set_pin_full (GckSession *self, + const guchar *old_pin, + gsize n_old_pin, + const guchar *new_pin, + gsize n_new_pin, + GCancellable *cancellable, + GError **err); + +void gck_session_set_pin_async (GckSession *self, + const guchar *old_pin, + gsize n_old_pin, + const guchar *new_pin, + gsize n_new_pin, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gck_session_set_pin_finish (GckSession *self, + GAsyncResult *result, + GError **err); + +gboolean gck_session_login (GckSession *self, + gulong user_type, + const guchar *pin, + gsize n_pin, + GError **err); + +gboolean gck_session_login_full (GckSession *self, + gulong user_type, + const guchar *pin, + gsize n_pin, + GCancellable *cancellable, + GError **err); + +void gck_session_login_async (GckSession *self, + gulong user_type, + const guchar *pin, + gsize n_pin, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gck_session_login_finish (GckSession *self, + GAsyncResult *result, + GError **err); + +gboolean gck_session_logout (GckSession *self, + GError **err); + +gboolean gck_session_logout_full (GckSession *self, + GCancellable *cancellable, + GError **err); + +void gck_session_logout_async (GckSession *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gck_session_logout_finish (GckSession *self, + GAsyncResult *result, + GError **err); + +GckObject* gck_session_create_object (GckSession *self, + GError **err, + ...); + +GckObject* gck_session_create_object_full (GckSession *self, + GckAttributes *attrs, + GCancellable *cancellable, + GError **err); + +void gck_session_create_object_async (GckSession *self, + GckAttributes *attrs, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GckObject* gck_session_create_object_finish (GckSession *self, + GAsyncResult *result, + GError **err); + +GList* gck_session_find_objects (GckSession *self, + GError **err, + ...); + +GList* gck_session_find_objects_full (GckSession *self, + GckAttributes *attrs, + GCancellable *cancellable, + GError **err); + +void gck_session_find_objects_async (GckSession *self, + GckAttributes *attrs, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GList* gck_session_find_objects_finish (GckSession *self, + GAsyncResult *result, + GError **err); + +#if UNIMPLEMENTED + +GckObject* gck_session_generate_key (GckSession *self, + GckMechanism *mechanism, + GError **err, + ...); + +void gck_session_generate_key_async (GckSession *self, + GckMechanism *mechanism, + GAsyncReadyCallback callback, + gpointer user_data, + ...); + +GckObject* gck_session_generate_key_finish (GckSession *self, + GAsyncResult *result, + GError **err, + ...); + +#endif /* UNIMPLEMENTED */ + +gboolean gck_session_generate_key_pair_full (GckSession *self, + GckMechanism *mechanism, + GckAttributes *public_attrs, + GckAttributes *private_attrs, + GckObject **public_key, + GckObject **private_key, + GCancellable *cancellable, + GError **err); + +void gck_session_generate_key_pair_async (GckSession *self, + GckMechanism *mechanism, + GckAttributes *public_attrs, + GckAttributes *private_attrs, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gck_session_generate_key_pair_finish (GckSession *self, + GAsyncResult *result, + GckObject **public_key, + GckObject **private_key, + GError **err); + +#ifdef UNIMPLEMENTED + +gboolean gck_session_seed_random (GckSession *self, + const guchar *seed, + gsize n_seed, + GError **err); + +void gck_session_seed_random_async (GckSession *self, + const guchar *seed, + gsize n_seed, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gck_session_seed_random_finish (GckSession *self, + GAsyncResult *result, + GError **err); + +guchar* gck_session_generate_random (GckSession *self, + gsize n_random, + GError **err); + +void gck_session_generate_random_async (GckSession *self, + gsize n_random, + GAsyncReadyCallback callback, + gpointer user_data); + +guchar* gck_session_generate_random_finish (GckSession *self, + GAsyncResult *result, + GError **err); + +#endif /* UNIMPLEMENTED */ + +guchar* gck_session_encrypt (GckSession *self, + GckObject *key, + gulong mech_type, + const guchar *input, + gsize n_input, + gsize *n_result, + GError **err); + +guchar* gck_session_encrypt_full (GckSession *self, + GckObject *key, + GckMechanism *mechanism, + const guchar *input, + gsize n_input, + gsize *n_result, + GCancellable *cancellable, + GError **err); + +void gck_session_encrypt_async (GckSession *self, + GckObject *key, + GckMechanism *mechanism, + const guchar *input, + gsize n_input, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +guchar* gck_session_encrypt_finish (GckSession *self, + GAsyncResult *result, + gsize *n_result, + GError **err); + +guchar* gck_session_decrypt (GckSession *self, + GckObject *key, + gulong mech_type, + const guchar *input, + gsize n_input, + gsize *n_result, + GError **err); + +guchar* gck_session_decrypt_full (GckSession *self, + GckObject *key, + GckMechanism *mechanism, + const guchar *input, + gsize n_input, + gsize *n_result, + GCancellable *cancellable, + GError **err); + +void gck_session_decrypt_async (GckSession *self, + GckObject *key, + GckMechanism *mechanism, + const guchar *input, + gsize n_input, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +guchar* gck_session_decrypt_finish (GckSession *self, + GAsyncResult *result, + gsize *n_result, + GError **err); + +#if UNIMPLEMENTED + +guchar* gck_session_digest (GckSession *self, + gulong mech_type, + const guchar *input, + gsize n_input, + gsize *n_result, + GError **err); + +guchar* gck_session_digest_full (GckSession *self, + GckMechanism *mechanism, + const guchar *input, + gsize n_input, + gsize *n_result, + GCancellable *cancellable, + GError **err); + +void gck_session_digest_async (GckSession *self, + GckMechanism *mechanism, + const guchar *input, + gsize n_input, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +guchar* gck_session_digest_finish (GckSession *self, + GAsyncResult *result, + gsize *n_result, + GError **err); + +#endif /* UNIMPLEMENTED */ + +guchar* gck_session_sign (GckSession *self, + GckObject *key, + gulong mech_type, + const guchar *input, + gsize n_input, + gsize *n_result, + GError **err); + +guchar* gck_session_sign_full (GckSession *self, + GckObject *key, + GckMechanism *mechanism, + const guchar *input, + gsize n_input, + gsize *n_result, + GCancellable *cancellable, + GError **err); + +void gck_session_sign_async (GckSession *self, + GckObject *key, + GckMechanism *mechanism, + const guchar *input, + gsize n_input, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +guchar* gck_session_sign_finish (GckSession *self, + GAsyncResult *result, + gsize *n_result, + GError **err); + +#if UNIMPLEMENTED + +guchar* gck_session_sign_recover (GckSession *self, + GckObject *key, + gulong mech_type, + const guchar *input, + gsize n_input, + gsize *n_result, + GError **err); + +guchar* gck_session_sign_recover_full (GckSession *self, + GckObject *key, + GckMechanism *mechanism, + const guchar *input, + gsize n_input, + gsize *n_result, + GCancellable *cancellable, + GError **err); + +void gck_session_sign_recover_async (GckSession *self, + GckObject *key, + GckMechanism *mechanism, + const guchar *input, + gsize n_input, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +guchar* gck_session_sign_recover_finish (GckSession *self, + GAsyncResult *result, + gsize *n_result, + GError **err); + +#endif /* UNIMPLEMENTED */ + +gboolean gck_session_verify (GckSession *self, + GckObject *key, + gulong mech_type, + const guchar *input, + gsize n_input, + const guchar *signature, + gsize n_signature, + GError **err); + +gboolean gck_session_verify_full (GckSession *self, + GckObject *key, + GckMechanism *mechanism, + const guchar *input, + gsize n_input, + const guchar *signature, + gsize n_signature, + GCancellable *cancellable, + GError **err); + +void gck_session_verify_async (GckSession *self, + GckObject *key, + GckMechanism *mechanism, + const guchar *input, + gsize n_input, + const guchar *signature, + gsize n_signature, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gck_session_verify_finish (GckSession *self, + GAsyncResult *result, + GError **err); + +#if UNIMPLEMENTED + +GkrProcessor* gck_session_batch_verify (GckSession *self, + GckObject *key, + GckMechanism *mech_type, + const guchar *input, + gsize n_input, + gsize *n_result, + GCancellable *cancellable, + GError **err); + +void gck_session_batch_verify_async (GckSession *self, + GckObject *key, + GckMechanism *mechanism, + const guchar *input, + gsize n_input, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GkrProcessor* gck_session_batch_verify_finish (GckSession *self, + GAsyncResult *result, + GError **err); + +guchar* gck_session_verify_recover (GckSession *self, + GckObject *key, + gulong mech_type, + const guchar *input, + gsize n_input, + gsize *n_result, + GError **err); + +guchar* gck_session_verify_recover_full (GckSession *self, + GckObject *key, + GckMechanism *mechanism, + const guchar *input, + gsize n_input, + gsize *n_result, + GCancellable *cancellable, + GError **err); + +void gck_session_verify_recover_async (GckSession *self, + GckObject *key, + GckMechanism *mechanism, + const guchar *input, + gsize n_input, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +guchar* gck_session_verify_recover_finish (GckSession *self, + GAsyncResult *result, + gsize *n_result, + GError **err); + +#endif /* UNIMPLEMENTED */ + +gpointer gck_session_wrap_key (GckSession *self, + GckObject *wrapper, + gulong mech_type, + GckObject *wrapped, + gsize *n_result, + GError **err); + +gpointer gck_session_wrap_key_full (GckSession *self, + GckObject *wrapper, + GckMechanism *mechanism, + GckObject *wrapped, + gsize *n_result, + GCancellable *cancellable, + GError **err); + +void gck_session_wrap_key_async (GckSession *self, + GckObject *wrapper, + GckMechanism *mechanism, + GckObject *wrapped, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gpointer gck_session_wrap_key_finish (GckSession *self, + GAsyncResult *result, + gsize *n_result, + GError **err); + +GckObject* gck_session_unwrap_key (GckSession *self, + GckObject *wrapper, + gulong mech_type, + gconstpointer input, + gsize n_input, + GError **err, + ...); + +GckObject* gck_session_unwrap_key_full (GckSession *self, + GckObject *wrapper, + GckMechanism *mechanism, + gconstpointer input, + gsize n_input, + GckAttributes *attrs, + GCancellable *cancellable, + GError **err); + +void gck_session_unwrap_key_async (GckSession *self, + GckObject *wrapper, + GckMechanism *mechanism, + gconstpointer input, + gsize n_input, + GckAttributes *attrs, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GckObject* gck_session_unwrap_key_finish (GckSession *self, + GAsyncResult *result, + GError **err); + +GckObject* gck_session_derive_key (GckSession *self, + GckObject *base, + gulong mech_type, + GError **err, + ...); + +GckObject* gck_session_derive_key_full (GckSession *self, + GckObject *base, + GckMechanism *mechanism, + GckAttributes *attrs, + GCancellable *cancellable, + GError **err); + +void gck_session_derive_key_async (GckSession *self, + GckObject *base, + GckMechanism *mechanism, + GckAttributes *attrs, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GckObject* gck_session_derive_key_finish (GckSession *self, + GAsyncResult *result, + GError **err); + +/* ------------------------------------------------------------------------ + * OBJECT + */ + +#define GCK_TYPE_OBJECT (gck_object_get_type()) +#define GCK_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GCK_TYPE_OBJECT, GckObject)) +#define GCK_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GCK_TYPE_OBJECT, GckObject)) +#define GCK_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GCK_TYPE_OBJECT)) +#define GCK_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GCK_TYPE_OBJECT)) +#define GCK_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GCK_TYPE_OBJECT, GckObjectClass)) + +typedef struct _GckObjectClass GckObjectClass; + +struct _GckObject { + GObject parent; + gpointer reserved[4]; +}; + +struct _GckObjectClass { + GObjectClass parent; + gpointer reserved[8]; +}; + +GType gck_object_get_type (void) G_GNUC_CONST; + +GckObject* gck_object_from_handle (GckSlot *slot, + CK_OBJECT_HANDLE handle); + +GList* gck_objects_from_handle_array (GckSlot *slot, + CK_OBJECT_HANDLE_PTR handles, + CK_ULONG n_handles); + +gboolean gck_object_equal (gconstpointer object1, + gconstpointer object2); + +guint gck_object_hash (gconstpointer object); + +GckModule* gck_object_get_module (GckObject *self); + +GckSlot* gck_object_get_slot (GckObject *self); + +CK_OBJECT_HANDLE gck_object_get_handle (GckObject *self); + +GckSession* gck_object_get_session (GckObject *self); + +void gck_object_set_session (GckObject *self, + GckSession *session); + + +#ifdef UNIMPLEMENTED + +GckObject* gck_object_copy (GckObject *self, + GError **err); + +GckObject* gck_object_copy_full (GckObject *self, + GckAttributes *additional, + GCancellable *cancellable, + GError **err); + +void gck_object_copy_async (GckObject *self, + GckAttributes *additional, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GckObject* gck_object_copy_finish (GckObject *self, + GAsyncResult *result, + GError **err); + +#endif /* UNIMPLEMENTED */ + +gboolean gck_object_destroy (GckObject *self, + GError **err); + +gboolean gck_object_destroy_full (GckObject *self, + GCancellable *cancellable, + GError **err); + +void gck_object_destroy_async (GckObject *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gck_object_destroy_finish (GckObject *self, + GAsyncResult *result, + GError **err); + +#if UNIMPLEMENTED + +gssize gck_object_get_size (GckObject *self, + GError **err); + +gssize gck_object_get_size_full (GckObject *self, + GCancellable *cancellable, + GError **err); + +void gck_object_get_size_async (GckObject *self, + GAsyncReadyCallback callback, + gpointer user_data); + +gssize gck_object_get_size_finish (GckObject *self, + GAsyncResult *result, + GError **err); + +#endif /* UNIMPLEMENTED */ + +gboolean gck_object_set (GckObject *self, + GError **err, + ...); + +gboolean gck_object_set_full (GckObject *self, + GckAttributes *attrs, + GCancellable *cancellable, + GError **err); + +void gck_object_set_async (GckObject *self, + GckAttributes *attrs, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gck_object_set_finish (GckObject *self, + GAsyncResult *result, + GError **err); + +GckAttributes* gck_object_get (GckObject *self, + GError **err, + ...); + +GckAttributes* gck_object_get_full (GckObject *self, + GckAttributes *attrs, + GCancellable *cancellable, + GError **err); + +void gck_object_get_async (GckObject *self, + GckAttributes *attrs, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GckAttributes* gck_object_get_finish (GckObject *self, + GAsyncResult *result, + GError **err); + +gpointer gck_object_get_data (GckObject *self, + gulong attr_type, + gsize *n_data, + GError **err); + +gpointer gck_object_get_data_full (GckObject *self, + gulong attr_type, + GckAllocator allocator, + GCancellable *cancellable, + gsize *n_data, + GError **err); + +void gck_object_get_data_async (GckObject *self, + gulong attr_type, + GckAllocator allocator, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gpointer gck_object_get_data_finish (GckObject *self, + GAsyncResult *result, + gsize *n_data, + GError **err); + +gboolean gck_object_set_template (GckObject *self, + gulong attr_type, + GckAttributes *attrs, + GError **err); + +gboolean gck_object_set_template_full (GckObject *self, + gulong attr_type, + GckAttributes *attrs, + GCancellable *cancellable, + GError **err); + +void gck_object_set_template_async (GckObject *self, + gulong attr_type, + GckAttributes *attrs, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gck_object_set_template_finish (GckObject *self, + GAsyncResult *result, + GError **err); + +GckAttributes* gck_object_get_template (GckObject *self, + gulong attr_type, + GError **err); + +GckAttributes* gck_object_get_template_full (GckObject *self, + gulong attr_type, + GCancellable *cancellable, + GError **err); + +void gck_object_get_template_async (GckObject *self, + gulong attr_type, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GckAttributes* gck_object_get_template_finish (GckObject *self, + GAsyncResult *result, + GError **err); + +G_END_DECLS + +#endif /* GCK_H */ diff --git a/gck/gck.pc.in b/gck/gck.pc.in new file mode 100644 index 00000000..b0a812d4 --- /dev/null +++ b/gck/gck.pc.in @@ -0,0 +1,14 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +datarootdir=@datarootdir@ +datadir=@datadir@ +sysconfdir=@sysconfdir@ + +Name: gp11 +Description: GObject bindings for PKCS#11 +Version: @VERSION@ +Requires: glib-2.0 +Libs: -L${libdir} -lgp11 +Cflags: -I${includedir}/gp11 diff --git a/gck/pkcs11.h b/gck/pkcs11.h new file mode 100644 index 00000000..c0981c83 --- /dev/null +++ b/gck/pkcs11.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* pkcs11.h - Dummy pkcs11 file while building + + 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, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter <nielsen@memberwebs.com> +*/ + +/* + * This file is not installed. The one pkcs11/pkcs11.h is installed with the + * gp11.h header. However while building we included it here, so that relative + * includes work from within gp11.h + */ + +#include "pkcs11/pkcs11.h" diff --git a/gck/tests/Makefile.am b/gck/tests/Makefile.am new file mode 100644 index 00000000..482f6b2e --- /dev/null +++ b/gck/tests/Makefile.am @@ -0,0 +1,42 @@ + +# Keep these in the order they should be tested +TESTING_FILES = \ + test-gck-attributes.c \ + test-gck-mechanism.c \ + test-gck-module.c \ + test-gck-slot.c \ + test-gck-session.c \ + test-gck-object.c \ + test-gck-crypto.c + +TESTING_FLAGS = \ + -I$(top_srcdir)/gck/ \ + -DEXTERNAL_TEST + +TESTING_LIBS = \ + $(GIO_LIBS) \ + $(top_builddir)/gck/libgck.la \ + libgck-test-module.la + +EXTRA_DIST = \ + gck-test.h + +include $(top_srcdir)/testing/testing.make + +# ------------------------------------------------------------------------ + +lib_LTLIBRARIES = libgck-test-module.la + +libgck_test_module_la_LDFLAGS = \ + -avoid-version + +libgck_test_module_la_CFLAGS = \ + -I$(top_srcdir)/gck \ + -I$(top_srcdir) \ + $(GLIB_CFLAGS) + +libgck_test_module_la_SOURCES = \ + gck-test-module.c + +libgck_test_module_la_LIBADD = \ + $(top_builddir)/gck/libgck.la diff --git a/gck/tests/gck-test-module.c b/gck/tests/gck-test-module.c new file mode 100644 index 00000000..e6ad4dfe --- /dev/null +++ b/gck/tests/gck-test-module.c @@ -0,0 +1,1702 @@ +#include "config.h" + +#include "gck.h" +#include "gck-test.h" + +#include "pkcs11.h" + +#include <glib.h> + +#include <string.h> + +/* + * This is *NOT* how you'd want to implement a PKCS#11 module. This + * fake module simply provides enough for gck library to test against. + * It doesn't pass any tests, or behave as expected from a PKCS#11 module. + */ + +static gboolean initialized = FALSE; +static gchar *the_pin = NULL; + +static gboolean logged_in = FALSE; +static CK_USER_TYPE user_type = 0; + +typedef enum _Operation { + OP_FIND = 1, + OP_CRYPTO +} Operation; + +typedef struct _Session { + CK_SESSION_HANDLE handle; + CK_SESSION_INFO info; + GHashTable *objects; + + Operation operation; + + /* For find operations */ + GList *matches; + + /* For crypto operations */ + CK_OBJECT_HANDLE crypto_key; + CK_ATTRIBUTE_TYPE crypto_method; + CK_MECHANISM_TYPE crypto_mechanism; + CK_BBOOL want_context_login; + + /* For 'signing' with CKM_PREFIX */ + CK_BYTE sign_prefix[128]; + CK_ULONG n_sign_prefix; + +} Session; + +static guint unique_identifier = 100; +static GHashTable *the_sessions = NULL; +static GHashTable *the_objects = NULL; + +enum { + PRIVATE_KEY_CAPITALIZE = 3, + PUBLIC_KEY_CAPITALIZE = 4, + PRIVATE_KEY_PREFIX = 5, + PUBLIC_KEY_PREFIX = 6 +}; + +#define SIGNED_PREFIX "signed-prefix:" + +/* + * This is not a generic test module, it works in concert with the + * test-gck-module.c + */ + +static void +free_session (gpointer data) +{ + Session *sess = (Session*)data; + if (sess) + g_hash_table_destroy (sess->objects); + g_free (sess); +} + +static GckAttributes* +lookup_object (Session *session, CK_OBJECT_HANDLE hObject) +{ + GckAttributes *attrs; + attrs = g_hash_table_lookup (the_objects, GUINT_TO_POINTER (hObject)); + if (!attrs) + attrs = g_hash_table_lookup (session->objects, GUINT_TO_POINTER (hObject)); + return attrs; +} + +static CK_RV +test_C_Initialize (CK_VOID_PTR pInitArgs) +{ + GckAttributes *attrs; + CK_C_INITIALIZE_ARGS_PTR args; + CK_ULONG value; + void *mutex; + CK_RV rv; + + g_assert (initialized == FALSE && "Initialized same module twice, maybe module was not finalized, outstanding refs?"); + g_assert (pInitArgs != NULL && "Missing arguments"); + + args = (CK_C_INITIALIZE_ARGS_PTR)pInitArgs; + g_assert (args->CreateMutex != NULL && "Missing CreateMutex"); + g_assert (args->DestroyMutex != NULL && "Missing DestroyMutex"); + g_assert (args->LockMutex != NULL && "Missing LockMutex"); + g_assert (args->UnlockMutex != NULL && "Missing UnlockMutex"); + + g_assert ((args->CreateMutex) (NULL) == CKR_ARGUMENTS_BAD && "CreateMutex succeeded wrong"); + g_assert ((args->DestroyMutex) (NULL) == CKR_MUTEX_BAD && "DestroyMutex succeeded wrong"); + g_assert ((args->LockMutex) (NULL) == CKR_MUTEX_BAD && "LockMutex succeeded wrong"); + g_assert ((args->UnlockMutex) (NULL) == CKR_MUTEX_BAD && "UnlockMutex succeeded wrong"); + + /* Try to create an actual mutex */ + rv = (args->CreateMutex) (&mutex); + g_assert (rv == CKR_OK && "CreateMutex g_assert_not_reacheded"); + g_assert (mutex != NULL && "CreateMutex created null mutex"); + + /* Try and lock the mutex */ + rv = (args->LockMutex) (mutex); + g_assert (rv == CKR_OK && "LockMutex g_assert_not_reacheded"); + + /* Try and unlock the mutex */ + rv = (args->UnlockMutex) (mutex); + g_assert (rv == CKR_OK && "UnlockMutex g_assert_not_reacheded"); + + /* Try and destroy the mutex */ + rv = (args->DestroyMutex) (mutex); + g_assert (rv == CKR_OK && "DestroyMutex g_assert_not_reacheded"); + + /* Flags should allow OS locking and os threads */ + g_assert ((args->flags & CKF_OS_LOCKING_OK) == CKF_OS_LOCKING_OK && "Invalid CKF_OS_LOCKING_OK flag"); + g_assert ((args->flags & CKF_LIBRARY_CANT_CREATE_OS_THREADS) == 0 && "Invalid CKF_LIBRARY_CANT_CREATE_OS_THREADS flag"); + + the_pin = g_strdup ("booo"); + the_sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, free_session); + the_objects = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)gck_attributes_unref); + + /* Our token object */ + attrs = gck_attributes_newv (CKA_CLASS, GCK_ULONG, CKO_DATA, + CKA_LABEL, GCK_STRING, "TEST LABEL", + GCK_INVALID); + g_hash_table_insert (the_objects, GUINT_TO_POINTER (2), attrs); + + /* Private capitalize key */ + value = CKM_CAPITALIZE; + attrs = gck_attributes_newv (CKA_CLASS, GCK_ULONG, CKO_PRIVATE_KEY, + CKA_LABEL, GCK_STRING, "Private Capitalize Key", + CKA_ALLOWED_MECHANISMS, sizeof (value), &value, + CKA_DECRYPT, GCK_BOOLEAN, TRUE, + CKA_PRIVATE, GCK_BOOLEAN, TRUE, + CKA_WRAP, GCK_BOOLEAN, TRUE, + CKA_UNWRAP, GCK_BOOLEAN, TRUE, + CKA_DERIVE, GCK_BOOLEAN, TRUE, + CKA_VALUE, GCK_STRING, "value", + GCK_INVALID); + g_hash_table_insert (the_objects, GUINT_TO_POINTER (PRIVATE_KEY_CAPITALIZE), attrs); + + /* Public capitalize key */ + value = CKM_CAPITALIZE; + attrs = gck_attributes_newv (CKA_CLASS, GCK_ULONG, CKO_PUBLIC_KEY, + CKA_LABEL, GCK_STRING, "Public Capitalize Key", + CKA_ALLOWED_MECHANISMS, sizeof (value), &value, + CKA_ENCRYPT, GCK_BOOLEAN, TRUE, + CKA_PRIVATE, GCK_BOOLEAN, FALSE, + CKA_VALUE, GCK_STRING, "value", + GCK_INVALID); + g_hash_table_insert (the_objects, GUINT_TO_POINTER (PUBLIC_KEY_CAPITALIZE), attrs); + + /* Private prefix key */ + value = CKM_PREFIX; + attrs = gck_attributes_newv (CKA_CLASS, GCK_ULONG, CKO_PRIVATE_KEY, + CKA_LABEL, GCK_STRING, "Private prefix key", + CKA_ALLOWED_MECHANISMS, sizeof (value), &value, + CKA_SIGN, GCK_BOOLEAN, TRUE, + CKA_PRIVATE, GCK_BOOLEAN, TRUE, + CKA_ALWAYS_AUTHENTICATE, GCK_BOOLEAN, TRUE, + CKA_VALUE, GCK_STRING, "value", + GCK_INVALID); + g_hash_table_insert (the_objects, GUINT_TO_POINTER (PRIVATE_KEY_PREFIX), attrs); + + /* Private prefix key */ + value = CKM_PREFIX; + attrs = gck_attributes_newv (CKA_CLASS, GCK_ULONG, CKO_PUBLIC_KEY, + CKA_LABEL, GCK_STRING, "Public prefix key", + CKA_ALLOWED_MECHANISMS, sizeof (value), &value, + CKA_VERIFY, GCK_BOOLEAN, TRUE, + CKA_PRIVATE, GCK_BOOLEAN, FALSE, + CKA_VALUE, GCK_STRING, "value", + GCK_INVALID); + g_hash_table_insert (the_objects, GUINT_TO_POINTER (PUBLIC_KEY_PREFIX), attrs); + + initialized = TRUE; + return CKR_OK; +} + +static CK_RV +test_C_Finalize (CK_VOID_PTR pReserved) +{ + g_assert (pReserved == NULL && "Invalid reserved pointer"); + g_assert (initialized == TRUE && "Finalize without being initialized"); + + initialized = FALSE; + g_hash_table_destroy (the_objects); + the_objects = NULL; + + g_hash_table_destroy (the_sessions); + the_sessions = NULL; + + g_free (the_pin); + return CKR_OK; +} + +static const CK_INFO TEST_INFO = { + { CRYPTOKI_VERSION_MAJOR, CRYPTOKI_VERSION_MINOR }, + "TEST MANUFACTURER ", + 0, + "TEST LIBRARY ", + { 45, 145 } +}; + +static CK_RV +test_C_GetInfo (CK_INFO_PTR pInfo) +{ + g_assert (pInfo != NULL && "Invalid pointer to GetInfo"); + memcpy (pInfo, &TEST_INFO, sizeof (*pInfo)); + return CKR_OK; +} + +static CK_RV +test_C_GetFunctionList (CK_FUNCTION_LIST_PTR_PTR list) +{ + g_assert (list != NULL && "Invalid pointer passed to GetFunctionList"); + return C_GetFunctionList (list); +} + +#define TEST_SLOT_ONE 52 +#define TEST_SLOT_TWO 134 + +/* + * Two slots + * ONE: token present + * TWO: token not present + */ + +static CK_RV +test_C_GetSlotList (CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount) +{ + CK_ULONG count; + + g_assert (pulCount != NULL && "Invalid pulCount"); + + count = tokenPresent ? 1 : 2; + + /* Application only wants to know the number of slots. */ + if (pSlotList == NULL) { + *pulCount = count; + return CKR_OK; + } + + if (*pulCount < count) { + g_assert (*pulCount && "Passed in a bad count"); + return CKR_BUFFER_TOO_SMALL; + } + + *pulCount = count; + pSlotList[0] = TEST_SLOT_ONE; + if (!tokenPresent) + pSlotList[1] = TEST_SLOT_TWO; + + return CKR_OK; +} + +static const CK_SLOT_INFO TEST_INFO_ONE = { + "TEST SLOT ", + "TEST MANUFACTURER ", + CKF_TOKEN_PRESENT | CKF_REMOVABLE_DEVICE, + { 55, 155 }, + { 65, 165 }, +}; + +static const CK_SLOT_INFO TEST_INFO_TWO = { + "TEST SLOT ", + "TEST MANUFACTURER ", + CKF_REMOVABLE_DEVICE, + { 55, 155 }, + { 65, 165 }, +}; + +static CK_RV +test_C_GetSlotInfo (CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) +{ + g_assert (pInfo != NULL && "Invalid pInfo"); + + if (slotID == TEST_SLOT_ONE) { + memcpy (pInfo, &TEST_INFO_ONE, sizeof (*pInfo)); + return CKR_OK; + } else if (slotID == TEST_SLOT_TWO) { + memcpy (pInfo, &TEST_INFO_TWO, sizeof (*pInfo)); + return CKR_OK; + } else { + g_assert_not_reached (); /* "Invalid slot id" */ + return CKR_SLOT_ID_INVALID; + } +} + +static const CK_TOKEN_INFO TEST_TOKEN_ONE = { + "TEST LABEL ", + "TEST MANUFACTURER ", + "TEST MODEL ", + "TEST SERIAL ", + CKF_LOGIN_REQUIRED | CKF_USER_PIN_INITIALIZED | CKF_CLOCK_ON_TOKEN | CKF_TOKEN_INITIALIZED, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + { 75, 175 }, + { 85, 185 }, + { '1', '9', '9', '9', '0', '5', '2', '5', '0', '9', '1', '9', '5', '9', '0', '0' } +}; + +static CK_RV +test_C_GetTokenInfo (CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) +{ + g_assert (pInfo != NULL && "Invalid pInfo"); + + if (slotID == TEST_SLOT_ONE) { + memcpy (pInfo, &TEST_TOKEN_ONE, sizeof (*pInfo)); + return CKR_OK; + } else if (slotID == TEST_SLOT_TWO) { + return CKR_TOKEN_NOT_PRESENT; + } else { + g_assert_not_reached (); /* "Invalid slot id" */ + return CKR_SLOT_ID_INVALID; + } +} + +/* + * TWO mechanisms: + * CKM_CAPITALIZE + * CKM_PREFIX + */ + +static CK_RV +test_C_GetMechanismList (CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, + CK_ULONG_PTR pulCount) +{ + g_assert (slotID == TEST_SLOT_ONE && "Invalid slotID"); + g_assert (pulCount != NULL && "Invalid pulCount"); + + /* Application only wants to know the number of slots. */ + if (pMechanismList == NULL) { + *pulCount = 2; + return CKR_OK; + } + + if (*pulCount != 2) { + g_assert (*pulCount && "Passed in a bad count"); + return CKR_BUFFER_TOO_SMALL; + } + + pMechanismList[0] = CKM_CAPITALIZE; + pMechanismList[1] = CKM_PREFIX; + return CKR_OK; +} + +static const CK_MECHANISM_INFO TEST_MECH_CAPITALIZE = { + 512, 4096, 0 +}; + +static const CK_MECHANISM_INFO TEST_MECH_PREFIX = { + 2048, 2048, 0 +}; + +static CK_RV +test_C_GetMechanismInfo (CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, + CK_MECHANISM_INFO_PTR pInfo) +{ + g_assert (slotID == TEST_SLOT_ONE && "Invalid slotID"); + g_assert (pInfo != NULL && "Invalid pInfo"); + + if (type == CKM_CAPITALIZE) { + memcpy (pInfo, &TEST_MECH_CAPITALIZE, sizeof (*pInfo)); + return CKR_OK; + } else if (type == CKM_PREFIX) { + memcpy (pInfo, &TEST_MECH_PREFIX, sizeof (*pInfo)); + return CKR_OK; + } else { + g_assert_not_reached (); /* "Invalid type" */ + return CKR_MECHANISM_INVALID; + } +} + +static CK_RV +test_C_InitToken (CK_SLOT_ID slotID, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, + CK_UTF8CHAR_PTR pLabel) +{ + g_assert (slotID == TEST_SLOT_ONE && "Invalid slotID"); + g_assert (pPin != NULL && "Invalid pPin"); + g_assert (strlen ("TEST PIN") && "Invalid ulPinLen"); + g_assert (strncmp ((gchar*)pPin, "TEST PIN", ulPinLen) == 0 && "Invalid pPin string"); + g_assert (pLabel != NULL && "Invalid pLabel"); + g_assert (strcmp ((gchar*)pPin, "TEST LABEL") == 0 && "Invalid pLabel string"); + + g_free (the_pin); + the_pin = g_strndup ((gchar*)pPin, ulPinLen); + return CKR_OK; +} + +static CK_RV +test_C_WaitForSlotEvent (CK_FLAGS flags, CK_SLOT_ID_PTR pSlot, CK_VOID_PTR pReserved) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_OpenSession (CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, + CK_NOTIFY Notify, CK_SESSION_HANDLE_PTR phSession) +{ + Session *sess; + + g_assert (slotID == TEST_SLOT_ONE && "Invalid slotID"); + g_assert (pApplication == NULL && "pApplication should be null"); + g_assert (Notify == NULL && "Notify should be null"); + g_assert (phSession != NULL && "Invalid phSession"); + g_assert ((flags & CKF_SERIAL_SESSION) == CKF_SERIAL_SESSION); + + sess = g_new0 (Session, 1); + sess->handle = ++unique_identifier; + sess->info.flags = flags; + sess->info.slotID = slotID; + sess->info.state = 0; + sess->info.ulDeviceError = 1414; + sess->objects = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)gck_attributes_unref); + *phSession = sess->handle; + + g_hash_table_replace (the_sessions, GUINT_TO_POINTER (sess->handle), sess); + return CKR_OK; +} + +static CK_RV +test_C_CloseSession (CK_SESSION_HANDLE hSession) +{ + Session *session; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + g_hash_table_remove (the_sessions, GUINT_TO_POINTER (hSession)); + return CKR_OK; +} + +static CK_RV +test_C_CloseAllSessions (CK_SLOT_ID slotID) +{ + g_assert (slotID == TEST_SLOT_ONE && "Invalid slotID"); + + g_hash_table_remove_all (the_sessions); + return CKR_OK; +} + +static CK_RV +test_C_GetFunctionStatus (CK_SESSION_HANDLE hSession) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_PARALLEL; +} + +static CK_RV +test_C_CancelFunction (CK_SESSION_HANDLE hSession) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_PARALLEL; +} + +static CK_RV +test_C_GetSessionInfo (CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo) +{ + Session *session; + + g_assert (pInfo != NULL && "Invalid pInfo"); + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + memcpy (pInfo, &session->info, sizeof (*pInfo)); + return CKR_OK; +} + +static CK_RV +test_C_InitPIN (CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pPin, + CK_ULONG ulPinLen) +{ + Session *session; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + g_free (the_pin); + the_pin = g_strndup ((gchar*)pPin, ulPinLen); + return CKR_OK; +} + +static CK_RV +test_C_SetPIN (CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pOldPin, + CK_ULONG ulOldLen, CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewLen) +{ + Session *session; + gchar *old; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + old = g_strndup ((gchar*)pOldPin, ulOldLen); + if (!g_str_equal (old, the_pin)) + return CKR_PIN_INCORRECT; + + g_free (the_pin); + the_pin = g_strndup ((gchar*)pNewPin, ulNewLen); + return CKR_OK; +} + +static CK_RV +test_C_GetOperationState (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState, + CK_ULONG_PTR pulOperationStateLen) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_SetOperationState (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState, + CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey, + CK_OBJECT_HANDLE hAuthenticationKey) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_Login (CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, + CK_UTF8CHAR_PTR pPin, CK_ULONG pPinLen) +{ + Session *session; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + if (!pPin) + return CKR_PIN_INCORRECT; + + g_assert (pPinLen == strlen (the_pin) && "Wrong PIN length"); + g_assert (strncmp ((gchar*)pPin, the_pin, pPinLen) == 0 && "Wrong PIN"); + g_assert ((userType == CKU_SO || userType == CKU_USER || userType == CKU_CONTEXT_SPECIFIC) && "Bad user type"); + g_assert (logged_in == FALSE && "Already logged in"); + + if (userType == CKU_CONTEXT_SPECIFIC) { + session->want_context_login = CK_FALSE; + } else { + logged_in = TRUE; + user_type = userType; + } + + return CKR_OK; +} + +static CK_RV +test_C_Logout (CK_SESSION_HANDLE hSession) +{ + Session *session; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + g_assert (logged_in && "Not logged in"); + logged_in = FALSE; + user_type = 0; + return CKR_OK; +} + +static CK_RV +test_C_CreateObject (CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject) +{ + GckAttributes *attrs; + Session *session; + gboolean token, priv; + CK_ULONG i; + + g_assert (phObject != NULL); + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + attrs = gck_attributes_new (); + for (i = 0; i < ulCount; ++i) + gck_attributes_add_data (attrs, pTemplate[i].type, pTemplate[i].pValue, pTemplate[i].ulValueLen); + + if (gck_attributes_find_boolean (attrs, CKA_PRIVATE, &priv) && priv) { + if (!logged_in) { + gck_attributes_unref (attrs); + return CKR_USER_NOT_LOGGED_IN; + } + } + + *phObject = ++unique_identifier; + if (gck_attributes_find_boolean (attrs, CKA_TOKEN, &token) && token) + g_hash_table_insert (the_objects, GUINT_TO_POINTER (*phObject), attrs); + else + g_hash_table_insert (session->objects, GUINT_TO_POINTER (*phObject), attrs); + + return CKR_OK; +} + +static CK_RV +test_C_CopyObject (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, + CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, + CK_OBJECT_HANDLE_PTR phNewObject) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + + +static CK_RV +test_C_DestroyObject (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject) +{ + GckAttributes *attrs; + Session *session; + gboolean priv; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + attrs = lookup_object (session, hObject); + if (!attrs) { + g_assert_not_reached (); /* "no such object found" */ + return CKR_OBJECT_HANDLE_INVALID; + } + + if (gck_attributes_find_boolean (attrs, CKA_PRIVATE, &priv) && priv) { + if (!logged_in) + return CKR_USER_NOT_LOGGED_IN; + } + + return CKR_OK; +} + +static CK_RV +test_C_GetObjectSize (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, + CK_ULONG_PTR pulSize) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_GetAttributeValue (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, + CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) +{ + CK_ATTRIBUTE_PTR result; + CK_RV ret = CKR_OK; + GckAttributes *attrs; + GckAttribute *attr; + Session *session; + CK_ULONG i; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + attrs = lookup_object (session, hObject); + if (!attrs) { + g_assert_not_reached (); /* "invalid object handle passed" */ + return CKR_OBJECT_HANDLE_INVALID; + } + + for (i = 0; i < ulCount; ++i) { + result = pTemplate + i; + attr = gck_attributes_find (attrs, result->type); + if (!attr) { + result->ulValueLen = (CK_ULONG)-1; + ret = CKR_ATTRIBUTE_TYPE_INVALID; + continue; + } + + if (!result->pValue) { + result->ulValueLen = attr->length; + continue; + } + + if (result->ulValueLen >= attr->length) { + memcpy (result->pValue, attr->value, attr->length); + continue; + } + + result->ulValueLen = (CK_ULONG)-1; + ret = CKR_BUFFER_TOO_SMALL; + } + + return ret; +} + +static CK_RV +test_C_SetAttributeValue (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, + CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) +{ + Session *session; + CK_ATTRIBUTE_PTR set; + GckAttributes *attrs; + GckAttribute *attr; + CK_ULONG i; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + attrs = lookup_object (session, hObject); + if (!attrs) { + g_assert_not_reached (); /* "invalid object handle passed" */ + return CKR_OBJECT_HANDLE_INVALID; + } + + for (i = 0; i < ulCount; ++i) { + set = pTemplate + i; + attr = gck_attributes_find (attrs, set->type); + if (!attr) { + gck_attributes_add_data (attrs, set->type, set->pValue, set->ulValueLen); + } else { + gck_attribute_clear (attr); + gck_attribute_init (attr, set->type, set->pValue, set->ulValueLen); + } + } + + return CKR_OK; +} + +static CK_RV +test_C_FindObjectsInit (CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount) +{ + GHashTableIter iter; + GckAttributes *attrs; + GckAttribute *attr; + CK_ATTRIBUTE_PTR match; + Session *session; + gpointer key, value; + gboolean matched = TRUE; + CK_ULONG i; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + /* Starting an operation, cancels any previous one */ + if (session->operation != 0) + session->operation = 0; + + session->operation = OP_FIND; + + /* Token objects */ + g_hash_table_iter_init (&iter, the_objects); + while (g_hash_table_iter_next (&iter, &key, &value)) { + attrs = (GckAttributes*)value; + matched = TRUE; + for (i = 0; i < ulCount; ++i) { + match = pTemplate + i; + attr = gck_attributes_find (attrs, match->type); + if (!attr) { + matched = FALSE; + break; + } + + if (attr->length != match->ulValueLen || + memcmp (attr->value, match->pValue, attr->length) != 0) { + matched = FALSE; + break; + } + } + + if (matched) + session->matches = g_list_prepend (session->matches, key); + } + + /* session objects */ + g_hash_table_iter_init (&iter, session->objects); + while (g_hash_table_iter_next (&iter, &key, &value)) { + attrs = (GckAttributes*)value; + matched = TRUE; + for (i = 0; i < ulCount; ++i) { + match = pTemplate + i; + attr = gck_attributes_find (attrs, match->type); + if (!attr) { + matched = FALSE; + break; + } + + if (attr->length != match->ulValueLen || + memcmp (attr->value, match->pValue, attr->length) != 0) { + matched = FALSE; + break; + } + } + + if (matched) + session->matches = g_list_prepend (session->matches, key); + } + + return CKR_OK; +} + +static CK_RV +test_C_FindObjects (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE_PTR phObject, + CK_ULONG ulMaxObjectCount, CK_ULONG_PTR pulObjectCount) +{ + Session *session; + + g_assert (phObject != NULL); + g_assert (pulObjectCount != NULL); + g_assert (ulMaxObjectCount != 0); + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + if (session->operation != OP_FIND) { + g_assert_not_reached (); /* "invalid call to FindObjects" */ + return CKR_OPERATION_NOT_INITIALIZED; + } + + *pulObjectCount = 0; + while (ulMaxObjectCount > 0 && session->matches) { + *phObject = GPOINTER_TO_UINT (session->matches->data); + ++phObject; + --ulMaxObjectCount; + ++(*pulObjectCount); + session->matches = g_list_remove (session->matches, session->matches->data); + } + + return CKR_OK; +} + +static CK_RV +test_C_FindObjectsFinal (CK_SESSION_HANDLE hSession) +{ + + Session *session; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + if (session->operation != OP_FIND) { + g_assert_not_reached (); /* "invalid call to FindObjectsFinal" */ + return CKR_OPERATION_NOT_INITIALIZED; + } + + session->operation = 0; + g_list_free (session->matches); + session->matches = NULL; + + return CKR_OK; +} + +static CK_RV +test_C_EncryptInit (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey) +{ + Session *session; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + /* Starting an operation, cancels any previous one */ + if (session->operation != 0) + session->operation = 0; + + g_assert (pMechanism); + g_assert (pMechanism->mechanism == CKM_CAPITALIZE); + g_assert (hKey == PUBLIC_KEY_CAPITALIZE); + + session->operation = OP_CRYPTO; + session->crypto_method = CKA_ENCRYPT; + session->crypto_mechanism = CKM_CAPITALIZE; + session->crypto_key = hKey; + return CKR_OK; +} + +static CK_RV +test_C_Encrypt (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, + CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) +{ + Session *session; + CK_ULONG i; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + if (session->operation != OP_CRYPTO) { + g_assert_not_reached (); /* "invalid call to Encrypt" */ + return CKR_OPERATION_NOT_INITIALIZED; + } + + g_assert (pData); + g_assert (pulEncryptedDataLen); + g_assert (session->crypto_method == CKA_ENCRYPT); + g_assert (session->crypto_mechanism == CKM_CAPITALIZE); + g_assert (session->crypto_key == PUBLIC_KEY_CAPITALIZE); + + if (!pEncryptedData) { + *pulEncryptedDataLen = ulDataLen; + return CKR_OK; + } + + if (*pulEncryptedDataLen < ulDataLen) { + *pulEncryptedDataLen = ulDataLen; + return CKR_BUFFER_TOO_SMALL; + } + + for (i = 0; i < ulDataLen; ++i) + pEncryptedData[i] = g_ascii_toupper (pData[i]); + *pulEncryptedDataLen = ulDataLen; + + session->operation = 0; + session->crypto_method = 0; + session->crypto_mechanism = 0; + session->crypto_key = 0; + + return CKR_OK; +} + +static CK_RV +test_C_EncryptUpdate (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, + CK_ULONG_PTR pulEncryptedPartLen) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_EncryptFinal (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastEncryptedPart, + CK_ULONG_PTR pulLastEncryptedPartLen) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_DecryptInit (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey) +{ + Session *session; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + /* Starting an operation, cancels any previous one */ + if (session->operation != 0) + session->operation = 0; + + g_assert (pMechanism); + g_assert (pMechanism->mechanism == CKM_CAPITALIZE); + g_assert (hKey == PRIVATE_KEY_CAPITALIZE); + + session->operation = OP_CRYPTO; + session->crypto_method = CKA_DECRYPT; + session->crypto_mechanism = CKM_CAPITALIZE; + session->crypto_key = hKey; + return CKR_OK; +} + +static CK_RV +test_C_Decrypt (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedData, + CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) +{ + Session *session; + CK_ULONG i; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + if (session->operation != OP_CRYPTO) { + g_assert_not_reached (); /* "invalid call to Encrypt" */ + return CKR_OPERATION_NOT_INITIALIZED; + } + + g_assert (pEncryptedData); + g_assert (pulDataLen); + g_assert (session->crypto_method == CKA_DECRYPT); + g_assert (session->crypto_mechanism == CKM_CAPITALIZE); + g_assert (session->crypto_key == PRIVATE_KEY_CAPITALIZE); + + if (!pData) { + *pulDataLen = ulEncryptedDataLen; + return CKR_OK; + } + + if (*pulDataLen < ulEncryptedDataLen) { + *pulDataLen = ulEncryptedDataLen; + return CKR_BUFFER_TOO_SMALL; + } + + for (i = 0; i < ulEncryptedDataLen; ++i) + pData[i] = g_ascii_tolower (pEncryptedData[i]); + *pulDataLen = ulEncryptedDataLen; + + session->operation = 0; + session->crypto_method = 0; + session->crypto_mechanism = 0; + session->crypto_key = 0; + + return CKR_OK; +} + +static CK_RV +test_C_DecryptUpdate (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, + CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_DecryptFinal (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastPart, + CK_ULONG_PTR pulLastPartLen) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_DigestInit (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_Digest (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, + CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_DigestUpdate (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_DigestKey (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_DigestFinal (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest, + CK_ULONG_PTR pulDigestLen) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_SignInit (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey) +{ + Session *session; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + /* Starting an operation, cancels any previous one */ + if (session->operation != 0) + session->operation = 0; + + g_assert (pMechanism); + g_assert (pMechanism->mechanism == CKM_PREFIX); + g_assert (hKey == PRIVATE_KEY_PREFIX); + + session->operation = OP_CRYPTO; + session->crypto_method = CKA_SIGN; + session->crypto_mechanism = CKM_PREFIX; + session->crypto_key = hKey; + + if (pMechanism->pParameter) { + g_assert (pMechanism->ulParameterLen < sizeof (session->sign_prefix)); + memcpy (session->sign_prefix, pMechanism->pParameter, pMechanism->ulParameterLen); + session->n_sign_prefix = pMechanism->ulParameterLen; + } else { + g_assert (strlen (SIGNED_PREFIX) + 1 < sizeof (session->sign_prefix)); + strcpy ((gchar*)session->sign_prefix, SIGNED_PREFIX); + session->n_sign_prefix = strlen (SIGNED_PREFIX); + } + + /* The private key has CKA_ALWAYS_AUTHENTICATE above */ + session->want_context_login = CK_TRUE; + + return CKR_OK; +} + +static CK_RV +test_C_Sign (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, + CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) +{ + Session *session; + CK_ULONG length; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + if (session->operation != OP_CRYPTO) { + g_assert_not_reached (); /* "invalid call to Encrypt" */ + return CKR_OPERATION_NOT_INITIALIZED; + } + + if (session->want_context_login) + return CKR_USER_NOT_LOGGED_IN; + + g_assert (pData); + g_assert (pulSignatureLen); + g_assert (session->crypto_method == CKA_SIGN); + g_assert (session->crypto_mechanism == CKM_PREFIX); + g_assert (session->crypto_key == PRIVATE_KEY_PREFIX); + + length = session->n_sign_prefix + ulDataLen; + + if (!pSignature) { + *pulSignatureLen = length; + return CKR_OK; + } + + if (*pulSignatureLen < length) { + *pulSignatureLen = length; + return CKR_BUFFER_TOO_SMALL; + } + + memcpy (pSignature, session->sign_prefix, session->n_sign_prefix); + memcpy (pSignature + session->n_sign_prefix, pData, ulDataLen); + *pulSignatureLen = length; + + session->operation = 0; + session->crypto_method = 0; + session->crypto_mechanism = 0; + session->crypto_key = 0; + + return CKR_OK; +} + +static CK_RV +test_C_SignUpdate (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_SignFinal (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, + CK_ULONG_PTR pulSignatureLen) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_SignRecoverInit (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_SignRecover (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, + CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_VerifyInit (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey) +{ + Session *session; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + /* Starting an operation, cancels any previous one */ + if (session->operation != 0) + session->operation = 0; + + g_assert (pMechanism); + g_assert (pMechanism->mechanism == CKM_PREFIX); + g_assert (hKey == PUBLIC_KEY_PREFIX); + + session->operation = OP_CRYPTO; + session->crypto_method = CKA_VERIFY; + session->crypto_mechanism = CKM_PREFIX; + session->crypto_key = hKey; + + if (pMechanism->pParameter) { + g_assert (pMechanism->ulParameterLen < sizeof (session->sign_prefix)); + memcpy (session->sign_prefix, pMechanism->pParameter, pMechanism->ulParameterLen); + session->n_sign_prefix = pMechanism->ulParameterLen; + } else { + g_assert (strlen (SIGNED_PREFIX) + 1 < sizeof (session->sign_prefix)); + strcpy ((gchar*)session->sign_prefix, SIGNED_PREFIX); + session->n_sign_prefix = strlen (SIGNED_PREFIX); + } + + return CKR_OK; +} + +static CK_RV +test_C_Verify (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, + CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) +{ + Session *session; + CK_ULONG length; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + if (session->operation != OP_CRYPTO) { + g_assert_not_reached (); /* "invalid call to Encrypt" */ + return CKR_OPERATION_NOT_INITIALIZED; + } + + g_assert (pData); + g_assert (pSignature); + g_assert (session->crypto_method == CKA_VERIFY); + g_assert (session->crypto_mechanism == CKM_PREFIX); + g_assert (session->crypto_key == PUBLIC_KEY_PREFIX); + + length = session->n_sign_prefix + ulDataLen; + + if (ulSignatureLen < length) { + g_assert (FALSE); + return CKR_SIGNATURE_LEN_RANGE; + } + + if (memcmp (pSignature, session->sign_prefix, session->n_sign_prefix) == 0 && + memcmp (pSignature + session->n_sign_prefix, pData, ulDataLen) == 0) + return CKR_OK; + + return CKR_SIGNATURE_INVALID; +} + +static CK_RV +test_C_VerifyUpdate (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_VerifyFinal (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, + CK_ULONG pulSignatureLen) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_VerifyRecoverInit (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_VerifyRecover (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, + CK_ULONG pulSignatureLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_DigestEncryptUpdate (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, + CK_ULONG_PTR ulEncryptedPartLen) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_DecryptDigestUpdate (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, + CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, + CK_ULONG_PTR pulPartLen) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_SignEncryptUpdate (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, + CK_ULONG_PTR ulEncryptedPartLen) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_DecryptVerifyUpdate (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, + CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, + CK_ULONG_PTR pulPartLen) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_GenerateKey (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, + CK_OBJECT_HANDLE_PTR phKey) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_GenerateKeyPair (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_ATTRIBUTE_PTR pPublicKeyTemplate, CK_ULONG ulPublicKeyAttributeCount, + CK_ATTRIBUTE_PTR pPrivateKeyTemplate, CK_ULONG ulPrivateKeyAttributeCount, + CK_OBJECT_HANDLE_PTR phPublicKey, CK_OBJECT_HANDLE_PTR phPrivateKey) +{ + GckAttributes *attrs; + Session *session; + gboolean token; + CK_ULONG i; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + g_assert (session != NULL && "No such session found"); + if (!session) + return CKR_SESSION_HANDLE_INVALID; + + g_assert (pMechanism); + g_assert (pPublicKeyTemplate); + g_assert (ulPublicKeyAttributeCount); + g_assert (pPrivateKeyTemplate); + g_assert (phPublicKey); + g_assert (phPrivateKey); + + if (pMechanism->mechanism != CKM_GENERATE) + return CKR_MECHANISM_INVALID; + + if (!pMechanism->pParameter || pMechanism->ulParameterLen != 9 || + memcmp (pMechanism->pParameter, "generate", 9) != 0) { + g_assert_not_reached (); + return CKR_MECHANISM_PARAM_INVALID; + } + + attrs = gck_attributes_new (); + gck_attributes_add_string (attrs, CKA_VALUE, "generated"); + for (i = 0; i < ulPublicKeyAttributeCount; ++i) + gck_attributes_add_data (attrs, pPublicKeyTemplate[i].type, + pPublicKeyTemplate[i].pValue, + pPublicKeyTemplate[i].ulValueLen); + *phPublicKey = ++unique_identifier; + if (gck_attributes_find_boolean (attrs, CKA_TOKEN, &token) && token) + g_hash_table_insert (the_objects, GUINT_TO_POINTER (*phPublicKey), attrs); + else + g_hash_table_insert (session->objects, GUINT_TO_POINTER (*phPublicKey), attrs); + + attrs = gck_attributes_new (); + gck_attributes_add_string (attrs, CKA_VALUE, "generated"); + for (i = 0; i < ulPrivateKeyAttributeCount; ++i) + gck_attributes_add_data (attrs, pPrivateKeyTemplate[i].type, + pPrivateKeyTemplate[i].pValue, + pPrivateKeyTemplate[i].ulValueLen); + *phPrivateKey = ++unique_identifier; + if (gck_attributes_find_boolean (attrs, CKA_TOKEN, &token) && token) + g_hash_table_insert (the_objects, GUINT_TO_POINTER (*phPrivateKey), attrs); + else + g_hash_table_insert (session->objects, GUINT_TO_POINTER (*phPrivateKey), attrs); + return CKR_OK; +} + +static CK_RV +test_C_WrapKey (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hWrappingKey, CK_OBJECT_HANDLE hKey, + CK_BYTE_PTR pWrappedKey, CK_ULONG_PTR pulWrappedKeyLen) +{ + GckAttributes *attrs; + GckAttribute *attr; + Session *session; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + if (!session) { + g_assert_not_reached (); + return CKR_SESSION_HANDLE_INVALID; + } + + g_assert (pMechanism); + g_assert (hWrappingKey); + g_assert (hKey); + g_assert (pulWrappedKeyLen); + + attrs = lookup_object (session, hWrappingKey); + if (!attrs) { + g_assert_not_reached (); + return CKR_WRAPPING_KEY_HANDLE_INVALID; + } + + attrs = lookup_object (session, hKey); + if (!attrs) { + g_assert_not_reached (); + return CKR_WRAPPED_KEY_INVALID; + } + + if (pMechanism->mechanism != CKM_WRAP) + return CKR_MECHANISM_INVALID; + + if (pMechanism->pParameter) { + if (pMechanism->ulParameterLen != 4 || + memcmp (pMechanism->pParameter, "wrap", 4) != 0) { + g_assert_not_reached (); + return CKR_MECHANISM_PARAM_INVALID; + } + } + + attr = gck_attributes_find (attrs, CKA_VALUE); + if (attr == NULL) + return CKR_WRAPPED_KEY_INVALID; + + if (!pWrappedKey) { + *pulWrappedKeyLen = attr->length; + return CKR_OK; + } + + if (*pulWrappedKeyLen < attr->length) { + *pulWrappedKeyLen = attr->length; + return CKR_BUFFER_TOO_SMALL; + } + + memcpy (pWrappedKey, attr->value, attr->length); + *pulWrappedKeyLen = attr->length; + + return CKR_OK; +} + +static CK_RV +test_C_UnwrapKey (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE pUnwrappingKey, CK_BYTE_PTR pWrappedKey, + CK_ULONG pulWrappedKeyLen, CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phKey) +{ + GckAttributes *attrs; + Session *session; + gboolean token; + CK_ULONG i; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + if (!session) { + g_assert_not_reached (); + return CKR_SESSION_HANDLE_INVALID; + } + + g_assert (pMechanism); + g_assert (pUnwrappingKey); + g_assert (pWrappedKey); + g_assert (pulWrappedKeyLen); + g_assert (phKey); + g_assert (ulCount); + g_assert (pTemplate); + g_assert (phKey); + + attrs = lookup_object (session, pUnwrappingKey); + if (!attrs) { + g_assert_not_reached (); + return CKR_WRAPPING_KEY_HANDLE_INVALID; + } + + if (pMechanism->mechanism != CKM_WRAP) + return CKR_MECHANISM_INVALID; + + if (pMechanism->pParameter) { + if (pMechanism->ulParameterLen != 4 || + memcmp (pMechanism->pParameter, "wrap", 4) != 0) { + g_assert_not_reached (); + return CKR_MECHANISM_PARAM_INVALID; + } + } + + attrs = gck_attributes_new (); + gck_attributes_add_data (attrs, CKA_VALUE, pWrappedKey, pulWrappedKeyLen); + for (i = 0; i < ulCount; ++i) + gck_attributes_add_data (attrs, pTemplate[i].type, + pTemplate[i].pValue, + pTemplate[i].ulValueLen); + *phKey = ++unique_identifier; + if (gck_attributes_find_boolean (attrs, CKA_TOKEN, &token) && token) + g_hash_table_insert (the_objects, GUINT_TO_POINTER (*phKey), attrs); + else + g_hash_table_insert (session->objects, GUINT_TO_POINTER (*phKey), attrs); + + return CKR_OK; +} + +static CK_RV +test_C_DeriveKey (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phKey) +{ + GckAttributes *attrs, *copy; + Session *session; + gboolean token; + CK_ULONG i; + + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); + if (!session) { + g_assert_not_reached (); + return CKR_SESSION_HANDLE_INVALID; + } + + g_assert (pMechanism); + g_assert (ulCount); + g_assert (pTemplate); + g_assert (phKey); + + attrs = lookup_object (session, hBaseKey); + if (!attrs) { + g_assert_not_reached (); + return CKR_WRAPPING_KEY_HANDLE_INVALID; + } + + if (pMechanism->mechanism != CKM_DERIVE) + return CKR_MECHANISM_INVALID; + + if (pMechanism->pParameter) { + if (pMechanism->ulParameterLen != 6 || + memcmp (pMechanism->pParameter, "derive", 6) != 0) { + g_assert_not_reached (); + return CKR_MECHANISM_PARAM_INVALID; + } + } + + copy = gck_attributes_new (); + gck_attributes_add_string (copy, CKA_VALUE, "derived"); + for (i = 0; i < ulCount; ++i) + gck_attributes_add_data (copy, pTemplate[i].type, + pTemplate[i].pValue, + pTemplate[i].ulValueLen); + for (i = 0; i < gck_attributes_count (attrs); ++i) + gck_attributes_add (copy, gck_attributes_at (attrs, i)); + *phKey = ++unique_identifier; + if (gck_attributes_find_boolean (copy, CKA_TOKEN, &token) && token) + g_hash_table_insert (the_objects, GUINT_TO_POINTER (*phKey), copy); + else + g_hash_table_insert (session->objects, GUINT_TO_POINTER (*phKey), copy); + + return CKR_OK; +} + +static CK_RV +test_C_SeedRandom (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed, CK_ULONG ulSeedLen) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV +test_C_GenerateRandom (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pRandomData, + CK_ULONG ulRandomLen) +{ + g_assert_not_reached (); /* Not yet used by library */ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_FUNCTION_LIST functionList = { + { 2, 11 }, /* version */ + test_C_Initialize, + test_C_Finalize, + test_C_GetInfo, + test_C_GetFunctionList, + test_C_GetSlotList, + test_C_GetSlotInfo, + test_C_GetTokenInfo, + test_C_GetMechanismList, + test_C_GetMechanismInfo, + test_C_InitToken, + test_C_InitPIN, + test_C_SetPIN, + test_C_OpenSession, + test_C_CloseSession, + test_C_CloseAllSessions, + test_C_GetSessionInfo, + test_C_GetOperationState, + test_C_SetOperationState, + test_C_Login, + test_C_Logout, + test_C_CreateObject, + test_C_CopyObject, + test_C_DestroyObject, + test_C_GetObjectSize, + test_C_GetAttributeValue, + test_C_SetAttributeValue, + test_C_FindObjectsInit, + test_C_FindObjects, + test_C_FindObjectsFinal, + test_C_EncryptInit, + test_C_Encrypt, + test_C_EncryptUpdate, + test_C_EncryptFinal, + test_C_DecryptInit, + test_C_Decrypt, + test_C_DecryptUpdate, + test_C_DecryptFinal, + test_C_DigestInit, + test_C_Digest, + test_C_DigestUpdate, + test_C_DigestKey, + test_C_DigestFinal, + test_C_SignInit, + test_C_Sign, + test_C_SignUpdate, + test_C_SignFinal, + test_C_SignRecoverInit, + test_C_SignRecover, + test_C_VerifyInit, + test_C_Verify, + test_C_VerifyUpdate, + test_C_VerifyFinal, + test_C_VerifyRecoverInit, + test_C_VerifyRecover, + test_C_DigestEncryptUpdate, + test_C_DecryptDigestUpdate, + test_C_SignEncryptUpdate, + test_C_DecryptVerifyUpdate, + test_C_GenerateKey, + test_C_GenerateKeyPair, + test_C_WrapKey, + test_C_UnwrapKey, + test_C_DeriveKey, + test_C_SeedRandom, + test_C_GenerateRandom, + test_C_GetFunctionStatus, + test_C_CancelFunction, + test_C_WaitForSlotEvent +}; + +CK_RV +C_GetFunctionList (CK_FUNCTION_LIST_PTR_PTR list) +{ + if (!list) + return CKR_ARGUMENTS_BAD; + + *list = &functionList; + return CKR_OK; +} diff --git a/gck/tests/gck-test.h b/gck/tests/gck-test.h new file mode 100644 index 00000000..078c962e --- /dev/null +++ b/gck/tests/gck-test.h @@ -0,0 +1,46 @@ +#ifndef GCK_TEST_H_ +#define GCK_TEST_H_ + +#include "gck.h" + +#define FAIL_RES(res, e) do { \ + g_assert ((res) ? FALSE : TRUE); \ + g_assert ((e) && (e)->message && "error should be set"); \ + g_clear_error (&e); \ + } while (0) + +#define SUCCESS_RES(res, err) do { \ + if (!(res)) g_printerr ("error: %s\n", err && err->message ? err->message : ""); \ + g_assert ((res) ? TRUE : FALSE && "should have succeeded"); \ + g_clear_error (&err); \ + } while(0) + +/* + * Some dumb crypto mechanisms for simple testing. + * + * CKM_CAPITALIZE (encrypt/decrypt) + * capitalizes to encrypt + * lowercase to decrypt + * + * CKM_PREFIX (sign/verify) + * sign prefixes data with key label + * verify unprefixes data with key label. + * + * CKM_GENERATE (generate-pair) + * generates a pair of keys, mechanism param should be 'generate' + * + * CKM_WRAP (wrap key) + * wraps key by returning value, mechanism param should be 'wrap' + * + * CKM_DERIVE (derive-key) + * derives key by setting value to 'derived'. + * mechanism param should be 'derive' + */ + +#define CKM_CAPITALIZE (CKM_VENDOR_DEFINED | 1) +#define CKM_PREFIX (CKM_VENDOR_DEFINED | 2) +#define CKM_GENERATE (CKM_VENDOR_DEFINED | 3) +#define CKM_WRAP (CKM_VENDOR_DEFINED | 4) +#define CKM_DERIVE (CKM_VENDOR_DEFINED | 5) + +#endif /* GCK_TEST_H_ */ diff --git a/gck/tests/test-gck-attributes.c b/gck/tests/test-gck-attributes.c new file mode 100644 index 00000000..24e8ee32 --- /dev/null +++ b/gck/tests/test-gck-attributes.c @@ -0,0 +1,528 @@ + +#include <glib.h> +#include <string.h> + +#include "test-suite.h" +#include "gck-test.h" + +#define ATTR_TYPE 55 +#define ATTR_DATA "TEST DATA" +#define N_ATTR_DATA ((gsize)9) + +DEFINE_TEST(init_memory) +{ + GckAttribute attr; + + g_assert (sizeof (attr) == sizeof (CK_ATTRIBUTE)); + + gck_attribute_init (&attr, ATTR_TYPE, ATTR_DATA, N_ATTR_DATA); + g_assert (attr.type == ATTR_TYPE); + g_assert (attr.length == N_ATTR_DATA); + g_assert (memcmp (attr.value, ATTR_DATA, attr.length) == 0); + + gck_attribute_clear (&attr); +} + +DEFINE_TEST(init_boolean) +{ + GckAttribute attr; + + gck_attribute_init_boolean (&attr, ATTR_TYPE, TRUE); + g_assert (attr.type == ATTR_TYPE); + g_assert (attr.length == sizeof (CK_BBOOL)); + g_assert (*((CK_BBOOL*)attr.value) == CK_TRUE); + + gck_attribute_clear (&attr); +} + +DEFINE_TEST(init_date) +{ + GckAttribute attr; + CK_DATE ck_date; + GDate *date; + + date = g_date_new_dmy(05, 06, 1960); + memcpy (ck_date.year, "1960", 4); + memcpy (ck_date.month, "06", 2); + memcpy (ck_date.day, "05", 2); + gck_attribute_init_date (&attr, ATTR_TYPE, date); + g_date_free (date); + g_assert (attr.type == ATTR_TYPE); + g_assert (attr.length == sizeof (CK_DATE)); + g_assert (memcmp (attr.value, &ck_date, attr.length) == 0); + + gck_attribute_clear (&attr); +} + +DEFINE_TEST(init_ulong) +{ + GckAttribute attr; + + gck_attribute_init_ulong (&attr, ATTR_TYPE, 88); + g_assert (attr.type == ATTR_TYPE); + g_assert (attr.length == sizeof (CK_ULONG)); + g_assert (*((CK_ULONG*)attr.value) == 88); + + gck_attribute_clear (&attr); +} + +DEFINE_TEST(init_string) +{ + GckAttribute attr; + + gck_attribute_init_string (&attr, ATTR_TYPE, "a test string"); + g_assert (attr.type == ATTR_TYPE); + g_assert (attr.length == strlen ("a test string")); + g_assert (memcmp (attr.value, "a test string", attr.length) == 0); + + gck_attribute_clear (&attr); +} + +DEFINE_TEST(init_invalid) +{ + GckAttribute attr; + + gck_attribute_init_invalid (&attr, ATTR_TYPE); + g_assert (attr.type == ATTR_TYPE); + g_assert (attr.length == (gulong)-1); + g_assert (attr.value == NULL); + + g_assert (gck_attribute_is_invalid (&attr)); + gck_attribute_clear (&attr); +} + +DEFINE_TEST(init_empty) +{ + GckAttribute attr; + + gck_attribute_init_empty (&attr, ATTR_TYPE); + g_assert (attr.type == ATTR_TYPE); + g_assert (attr.length == 0); + g_assert (attr.value == NULL); + + gck_attribute_clear (&attr); +} + +DEFINE_TEST(new_memory) +{ + GckAttribute *attr; + + attr = gck_attribute_new (ATTR_TYPE, ATTR_DATA, N_ATTR_DATA); + g_assert (attr->type == ATTR_TYPE); + g_assert (attr->length == N_ATTR_DATA); + g_assert (memcmp (attr->value, ATTR_DATA, attr->length) == 0); + + gck_attribute_free (attr); +} + +DEFINE_TEST(new_boolean) +{ + GckAttribute *attr; + + attr = gck_attribute_new_boolean (ATTR_TYPE, TRUE); + g_assert (attr->type == ATTR_TYPE); + g_assert (attr->length == sizeof (CK_BBOOL)); + g_assert (*((CK_BBOOL*)attr->value) == CK_TRUE); + + gck_attribute_free (attr); +} + +DEFINE_TEST(new_date) +{ + GckAttribute *attr; + CK_DATE ck_date; + GDate *date; + + date = g_date_new_dmy(05, 06, 1800); + memcpy (ck_date.year, "1800", 4); + memcpy (ck_date.month, "06", 2); + memcpy (ck_date.day, "05", 2); + attr = gck_attribute_new_date (ATTR_TYPE, date); + g_date_free (date); + g_assert (attr->type == ATTR_TYPE); + g_assert (attr->length == sizeof (CK_DATE)); + g_assert (memcmp (attr->value, &ck_date, attr->length) == 0); + + gck_attribute_free (attr); +} + +DEFINE_TEST(new_ulong) +{ + GckAttribute *attr; + + attr = gck_attribute_new_ulong (ATTR_TYPE, 88); + g_assert (attr->type == ATTR_TYPE); + g_assert (attr->length == sizeof (CK_ULONG)); + g_assert (*((CK_ULONG*)attr->value) == 88); + + gck_attribute_free (attr); +} + +DEFINE_TEST(new_string) +{ + GckAttribute *attr; + + attr = gck_attribute_new_string (ATTR_TYPE, "a test string"); + g_assert (attr->type == ATTR_TYPE); + g_assert (attr->length == strlen ("a test string")); + g_assert (memcmp (attr->value, "a test string", attr->length) == 0); + + gck_attribute_free (attr); +} + +DEFINE_TEST(new_invalid) +{ + GckAttribute *attr; + + attr = gck_attribute_new_invalid (ATTR_TYPE); + g_assert (attr->type == ATTR_TYPE); + g_assert (attr->length == (gulong)-1); + g_assert (attr->value == NULL); + + g_assert (gck_attribute_is_invalid (attr)); + + gck_attribute_free (attr); +} + +DEFINE_TEST(new_empty) +{ + GckAttribute *attr; + + attr = gck_attribute_new_empty (ATTR_TYPE); + g_assert (attr->type == ATTR_TYPE); + g_assert (attr->length == 0); + g_assert (attr->value == NULL); + + gck_attribute_free (attr); +} + +DEFINE_TEST(get_boolean) +{ + GckAttribute *attr; + + attr = gck_attribute_new_boolean (ATTR_TYPE, TRUE); + g_assert (gck_attribute_get_boolean (attr) == TRUE); + gck_attribute_free (attr); +} + +DEFINE_TEST(get_date) +{ + GckAttribute *attr; + CK_DATE ck_date; + GDate date, date2; + + g_date_set_dmy(&date, 05, 06, 1800); + memcpy (ck_date.year, "1800", 4); + memcpy (ck_date.month, "06", 2); + memcpy (ck_date.day, "05", 2); + attr = gck_attribute_new_date (ATTR_TYPE, &date); + gck_attribute_get_date (attr, &date2); + g_assert (g_date_compare (&date, &date2) == 0); + gck_attribute_free (attr); +} + +DEFINE_TEST(get_ulong) +{ + GckAttribute *attr; + + attr = gck_attribute_new_ulong (ATTR_TYPE, 88); + g_assert (gck_attribute_get_ulong (attr) == 88); + gck_attribute_free (attr); +} + +DEFINE_TEST(get_string) +{ + GckAttribute *attr; + gchar *value; + + attr = gck_attribute_new_string (ATTR_TYPE, "a test string"); + value = gck_attribute_get_string (attr); + g_assert (strcmp ("a test string", value) == 0); + g_free (value); + gck_attribute_free (attr); + + /* Should be able to store null strings */ + attr = gck_attribute_new_string (ATTR_TYPE, NULL); + value = gck_attribute_get_string (attr); + g_assert (value == NULL); + gck_attribute_free (attr); +} + +DEFINE_TEST(dup_attribute) +{ + GckAttribute attr, *dup; + + gck_attribute_init_ulong (&attr, ATTR_TYPE, 88); + dup = gck_attribute_dup (&attr); + gck_attribute_clear (&attr); + g_assert (gck_attribute_get_ulong (dup) == 88); + g_assert (dup->type == ATTR_TYPE); + gck_attribute_free (dup); + + /* Should be able to dup null */ + dup = gck_attribute_dup (NULL); + g_assert (dup == NULL); +} + +DEFINE_TEST(copy_attribute) +{ + GckAttribute attr, copy; + + gck_attribute_init_ulong (&attr, ATTR_TYPE, 88); + gck_attribute_init_copy (©, &attr); + gck_attribute_clear (&attr); + g_assert (gck_attribute_get_ulong (©) == 88); + g_assert (copy.type == ATTR_TYPE); + gck_attribute_clear (©); +} + +DEFINE_TEST(new_attributes) +{ + GckAttributes *attrs; + + attrs = gck_attributes_new (); + g_assert (attrs != NULL); + g_assert (gck_attributes_count (attrs) == 0); + + gck_attributes_ref (attrs); + gck_attributes_unref (attrs); + + gck_attributes_unref (attrs); + + /* Can unref NULL */ + gck_attributes_unref (NULL); +} + +static void +test_attributes_contents (GckAttributes *attrs, gboolean extras) +{ + GckAttribute *attr; + gchar *value; + GDate date, *check; + guint count; + + g_assert (attrs != NULL); + count = extras ? 7 : 5; + g_assert_cmpuint (gck_attributes_count (attrs), ==, count); + + attr = gck_attributes_at (attrs, 0); + g_assert (attr->type == 0); + g_assert (gck_attribute_get_boolean (attr) == TRUE); + + attr = gck_attributes_at (attrs, 1); + g_assert (attr->type == 101); + g_assert (gck_attribute_get_ulong (attr) == 888); + + attr = gck_attributes_at (attrs, 2); + g_assert (attr->type == 202); + value = gck_attribute_get_string (attr); + g_assert (strcmp (value, "string") == 0); + g_free (value); + + attr = gck_attributes_at (attrs, 3); + g_assert (attr->type == 303); + check = g_date_new_dmy (11, 12, 2008); + gck_attribute_get_date (attr, &date); + g_assert (g_date_compare (&date, check) == 0); + g_date_free (check); + + attr = gck_attributes_at (attrs, 4); + g_assert (attr->type == 404); + g_assert (attr->length == N_ATTR_DATA); + g_assert (memcmp (attr->value, ATTR_DATA, N_ATTR_DATA) == 0); + + if (!extras) + return; + + attr = gck_attributes_at (attrs, 5); + g_assert (attr->type == 505); + g_assert (attr->length == (gulong)-1); + g_assert (attr->value == NULL); + g_assert (gck_attribute_is_invalid (attr)); + + attr = gck_attributes_at (attrs, 6); + g_assert (attr->type == 606); + g_assert (attr->length == 0); + g_assert (attr->value == NULL); +} + +DEFINE_TEST(newv_attributes) +{ + GDate *date = g_date_new_dmy (11, 12, 2008); + GckAttributes *attrs; + attrs = gck_attributes_newv (0UL, GCK_BOOLEAN, TRUE, + 101UL, GCK_ULONG, 888UL, + 202UL, GCK_STRING, "string", + 303UL, GCK_DATE, date, + 404UL, N_ATTR_DATA, ATTR_DATA, + GCK_INVALID); + g_date_free (date); + + test_attributes_contents (attrs, FALSE); + gck_attributes_unref (attrs); + + /* An empty one */ + attrs = gck_attributes_newv (GCK_INVALID); + gck_attributes_unref (attrs); +} + +DEFINE_TEST(new_empty_attributes) +{ + GckAttributes *attrs = gck_attributes_new_empty (101UL, 202UL, 303UL, 404UL, GCK_INVALID); + GckAttribute *attr; + guint i; + + g_assert_cmpuint (gck_attributes_count (attrs), ==, 4); + for (i = 0; i < gck_attributes_count (attrs); ++i) { + attr = gck_attributes_at (attrs, i); + g_assert (attr->type == ((i + 1) * 100) + i + 1); + g_assert (attr->value == NULL); + g_assert (attr->length == 0); + } +} + +static GckAttributes* +help_attributes_valist (int dummy, ...) +{ + GckAttributes *attrs; + va_list va; + + va_start (va, dummy); + attrs = gck_attributes_new_valist (NULL, va); + va_end (va); + + return attrs; +} + +DEFINE_TEST(new_valist_attributes) +{ + GckAttributes *attrs; + GDate *date = g_date_new_dmy (11, 12, 2008); + + attrs = help_attributes_valist (232434243, /* Not used */ + 0UL, GCK_BOOLEAN, TRUE, + 101UL, GCK_ULONG, 888UL, + 202UL, GCK_STRING, "string", + 303UL, GCK_DATE, date, + 404UL, N_ATTR_DATA, ATTR_DATA, + GCK_INVALID); + + g_date_free (date); + test_attributes_contents (attrs, FALSE); + gck_attributes_unref (attrs); +} + +#if 0 +DEFINE_ABORT(bad_length) +{ + GckAttributes *attrs; + + /* We should catch this with a warning */ + attrs = gck_attributes_newv (1UL, G_MAXSSIZE + 500U, GCK_ULONG, "invalid data", + GCK_INVALID); + + gck_attributes_unref (attrs); +} +#endif + +DEFINE_TEST(add_data_attributes) +{ + GckAttributes *attrs; + GDate *date = g_date_new_dmy (11, 12, 2008); + attrs = gck_attributes_new (); + gck_attributes_add_boolean (attrs, 0UL, TRUE); + gck_attributes_add_ulong (attrs, 101UL, 888); + gck_attributes_add_string (attrs, 202UL, "string"); + gck_attributes_add_date (attrs, 303UL, date); + g_date_free (date); + gck_attributes_add_data (attrs, 404UL, ATTR_DATA, N_ATTR_DATA); + gck_attributes_add_invalid (attrs, 505UL); + gck_attributes_add_empty (attrs, 606UL); + test_attributes_contents (attrs, TRUE); + gck_attributes_unref (attrs); +} + +DEFINE_TEST(add_attributes) +{ + GckAttributes *attrs; + GckAttribute attr; + + GDate *date = g_date_new_dmy (11, 12, 2008); + attrs = gck_attributes_new (); + + gck_attribute_init_boolean (&attr, 0UL, TRUE); + gck_attributes_add (attrs, &attr); + gck_attribute_clear (&attr); + + gck_attribute_init_ulong (&attr, 101UL, 888); + gck_attributes_add (attrs, &attr); + gck_attribute_clear (&attr); + + gck_attribute_init_string (&attr, 202UL, "string"); + gck_attributes_add (attrs, &attr); + gck_attribute_clear (&attr); + + gck_attribute_init_date (&attr, 303UL, date); + gck_attributes_add (attrs, &attr); + gck_attribute_clear (&attr); + g_date_free (date); + + gck_attribute_init (&attr, 404UL, ATTR_DATA, N_ATTR_DATA); + gck_attributes_add (attrs, &attr); + gck_attribute_clear (&attr); + + gck_attribute_init_invalid (&attr, 505UL); + gck_attributes_add (attrs, &attr); + gck_attribute_clear (&attr); + + gck_attribute_init_empty (&attr, 606UL); + gck_attributes_add (attrs, &attr); + gck_attribute_clear (&attr); + + test_attributes_contents (attrs, TRUE); + gck_attributes_unref (attrs); +} + + +DEFINE_TEST(find_attributes) +{ + GckAttribute *attr; + GDate check, *date = g_date_new_dmy (13, 12, 2008); + gboolean bvalue, ret; + gulong uvalue; + gchar *svalue; + + GckAttributes *attrs; + attrs = gck_attributes_newv (0UL, GCK_BOOLEAN, TRUE, + 101UL, GCK_ULONG, 888UL, + 202UL, GCK_STRING, "string", + 303UL, GCK_DATE, date, + 404UL, N_ATTR_DATA, ATTR_DATA, + GCK_INVALID); + + attr = gck_attributes_find (attrs, 404); + g_assert (attr != NULL); + g_assert (attr->length == N_ATTR_DATA); + g_assert (memcmp (attr->value, ATTR_DATA, N_ATTR_DATA) == 0); + + ret = gck_attributes_find_boolean (attrs, 0UL, &bvalue); + g_assert (ret == TRUE); + g_assert (bvalue == TRUE); + + ret = gck_attributes_find_ulong (attrs, 101UL, &uvalue); + g_assert (ret == TRUE); + g_assert (uvalue == 888); + + ret = gck_attributes_find_string (attrs, 202UL, &svalue); + g_assert (ret == TRUE); + g_assert (svalue != NULL); + g_assert (strcmp (svalue, "string") == 0); + g_free (svalue); + + ret = gck_attributes_find_date (attrs, 303UL, &check); + g_assert (ret == TRUE); + g_assert (g_date_compare (date, &check) == 0); + + gck_attributes_unref (attrs); + g_date_free (date); +} diff --git a/gck/tests/test-gck-crypto.c b/gck/tests/test-gck-crypto.c new file mode 100644 index 00000000..f03b7e7c --- /dev/null +++ b/gck/tests/test-gck-crypto.c @@ -0,0 +1,595 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "test-suite.h" + +#include <glib.h> + +#include "gck-test.h" + +static GckModule *module = NULL; +static GckSlot *slot = NULL; +static GckSession *session = NULL; + +DEFINE_SETUP(crypto_session) +{ + GError *err = NULL; + GList *slots; + + /* Successful load */ + module = gck_module_initialize (".libs/libgck-test-module.so", NULL, &err); + SUCCESS_RES (module, err); + + slots = gck_module_get_slots (module, TRUE); + g_assert (slots != NULL); + + slot = GCK_SLOT (slots->data); + g_object_ref (slot); + gck_list_unref_free (slots); + + session = gck_slot_open_session (slot, 0, &err); + SUCCESS_RES(session, err); +} + +DEFINE_TEARDOWN(crypto_session) +{ + g_object_unref (session); + g_object_unref (slot); + g_object_unref (module); +} + +static void +fetch_async_result (GObject *source, GAsyncResult *result, gpointer user_data) +{ + *((GAsyncResult**)user_data) = result; + g_object_ref (result); + testing_wait_stop (); +} + +static GckObject* +find_key (GckSession *session, CK_ATTRIBUTE_TYPE method, CK_MECHANISM_TYPE mech) +{ + GList *objects, *l; + GckObject *object = NULL; + CK_MECHANISM_TYPE_PTR mechs; + gsize n_mechs; + + objects = gck_session_find_objects (session, NULL, method, GCK_BOOLEAN, TRUE, GCK_INVALID); + g_assert (objects); + + for (l = objects; l; l = g_list_next (l)) { + gck_object_set_session (l->data, session); + if (mech) { + mechs = gck_object_get_data (l->data, CKA_ALLOWED_MECHANISMS, &n_mechs, NULL); + g_assert (mechs); + g_assert (n_mechs == sizeof (CK_MECHANISM_TYPE)); + /* We know all of them only have one allowed mech */ + if (*mechs != mech) + continue; + } + object = l->data; + g_object_ref (object); + break; + } + + gck_list_unref_free (objects); + return object; +} + +static GckObject* +find_key_with_value (GckSession *session, const gchar *value) +{ + GList *objects; + GckObject *object; + + objects = gck_session_find_objects (session, NULL, CKA_VALUE, GCK_STRING, value, GCK_INVALID); + g_assert (objects); + + object = g_object_ref (objects->data); + gck_list_unref_free (objects); + return object; +} + +static void +check_key_with_value (GckSession *session, GckObject *key, CK_OBJECT_CLASS klass, const gchar *value) +{ + GckAttributes *attrs; + GckAttribute *attr; + gulong check; + + gck_object_set_session (key, session); + attrs = gck_object_get (key, NULL, CKA_CLASS, CKA_VALUE, GCK_INVALID); + g_assert (attrs); + + if (!gck_attributes_find_ulong (attrs, CKA_CLASS, &check)) + g_assert_not_reached (); + g_assert (check == klass); + + attr = gck_attributes_find (attrs, CKA_VALUE); + g_assert (attr); + g_assert (!gck_attribute_is_invalid (attr)); + g_assert_cmpsize (attr->length, ==, strlen (value)); + g_assert (memcmp (attr->value, value, attr->length) == 0); + + gck_attributes_unref (attrs); +} + +static gboolean +authenticate_object (GckSlot *module, GckObject *object, gchar *label, gchar **password) +{ + g_assert (GCK_IS_MODULE (module)); + g_assert (GCK_IS_OBJECT (object)); + g_assert (password); + g_assert (!*password); + + *password = g_strdup ("booo"); + return TRUE; +} + +DEFINE_TEST(encrypt) +{ + GckMechanism *mech; + GError *error = NULL; + GAsyncResult *result = NULL; + GckObject *key; + guchar *output; + gsize n_output; + + mech = gck_mechanism_new (CKM_CAPITALIZE); + + /* Find the right key */ + key = find_key (session, CKA_ENCRYPT, CKM_CAPITALIZE); + g_assert (key); + + /* Simple one */ + output = gck_session_encrypt (session, key, CKM_CAPITALIZE, (const guchar*)"blah blah", 10, &n_output, &error); + SUCCESS_RES (output, error); + g_assert (n_output == 10); + g_assert_cmpstr ((gchar*)output, ==, "BLAH BLAH"); + g_free (output); + + /* Full one */ + output = gck_session_encrypt_full (session, key, mech, (const guchar*)"blah blah", 10, &n_output, NULL, &error); + SUCCESS_RES (output, error); + g_assert (n_output == 10); + g_assert_cmpstr ((gchar*)output, ==, "BLAH BLAH"); + g_free (output); + + /* Asynchronous one */ + gck_session_encrypt_async (session, key, mech, (const guchar*)"second chance", 14, NULL, fetch_async_result, &result); + + testing_wait_until (500); + g_assert (result != NULL); + + /* Get the result */ + output = gck_session_encrypt_finish (session, result, &n_output, &error); + SUCCESS_RES (output, error); + g_assert (n_output == 14); + g_assert_cmpstr ((gchar*)output, ==, "SECOND CHANCE"); + g_free (output); + + gck_mechanism_unref (mech); + g_object_unref (result); + g_object_unref (key); +} + +DEFINE_TEST(decrypt) +{ + GckMechanism *mech; + GError *error = NULL; + GAsyncResult *result = NULL; + GckObject *key; + guchar *output; + gsize n_output; + + mech = gck_mechanism_new (CKM_CAPITALIZE); + + /* Find the right key */ + key = find_key (session, CKA_DECRYPT, CKM_CAPITALIZE); + g_assert (key); + + /* Simple one */ + output = gck_session_decrypt (session, key, CKM_CAPITALIZE, (const guchar*)"FRY???", 7, &n_output, &error); + SUCCESS_RES (output, error); + g_assert (n_output == 7); + g_assert_cmpstr ((gchar*)output, ==, "fry???"); + g_free (output); + + /* Full one */ + output = gck_session_decrypt_full (session, key, mech, (const guchar*)"TENNIS instructor", 18, &n_output, NULL, &error); + SUCCESS_RES (output, error); + g_assert (n_output == 18); + g_assert_cmpstr ((gchar*)output, ==, "tennis instructor"); + g_free (output); + + /* Asynchronous one */ + gck_session_decrypt_async (session, key, mech, (const guchar*)"FAT CHANCE", 11, NULL, fetch_async_result, &result); + + testing_wait_until (500); + g_assert (result != NULL); + + /* Get the result */ + output = gck_session_decrypt_finish (session, result, &n_output, &error); + SUCCESS_RES (output, error); + g_assert (n_output == 11); + g_assert_cmpstr ((gchar*)output, ==, "fat chance"); + g_free (output); + + gck_mechanism_unref (mech); + g_object_unref (result); + g_object_unref (key); +} + +DEFINE_TEST(login_context_specific) +{ + /* The test module won't let us sign without doing a login, check that */ + + GError *error = NULL; + GckObject *key; + guchar *output; + gsize n_output; + + /* Find the right key */ + key = find_key (session, CKA_SIGN, CKM_PREFIX); + g_assert (key); + + /* Simple one */ + output = gck_session_sign (session, key, CKM_PREFIX, (const guchar*)"TV Monster", 11, &n_output, &error); + g_assert (error && error->code == CKR_USER_NOT_LOGGED_IN); + FAIL_RES (output, error); + g_assert (output == NULL); + + g_object_unref (key); +} + +DEFINE_TEST(sign) +{ + GckMechanism *mech; + GError *error = NULL; + GAsyncResult *result = NULL; + GckObject *key; + guchar *output; + gsize n_output; + + mech = gck_mechanism_new_with_param (CKM_PREFIX, "my-prefix:", 10); + + /* Enable auto-login on this session, see previous test */ + gck_module_set_auto_authenticate (module, TRUE); + g_signal_connect (module, "authenticate-object", G_CALLBACK (authenticate_object), NULL); + + /* Find the right key */ + key = find_key (session, CKA_SIGN, CKM_PREFIX); + g_assert (key); + + /* Simple one */ + output = gck_session_sign (session, key, CKM_PREFIX, (const guchar*)"Labarbara", 10, &n_output, &error); + SUCCESS_RES (output, error); + g_assert_cmpuint (n_output, ==, 24); + g_assert_cmpstr ((gchar*)output, ==, "signed-prefix:Labarbara"); + g_free (output); + + /* Full one */ + output = gck_session_sign_full (session, key, mech, (const guchar*)"Labarbara", 10, &n_output, NULL, &error); + SUCCESS_RES (output, error); + g_assert_cmpuint (n_output, ==, 20); + g_assert_cmpstr ((gchar*)output, ==, "my-prefix:Labarbara"); + g_free (output); + + /* Asynchronous one */ + gck_session_sign_async (session, key, mech, (const guchar*)"Conrad", 7, NULL, fetch_async_result, &result); + + testing_wait_until (500); + g_assert (result != NULL); + + /* Get the result */ + output = gck_session_sign_finish (session, result, &n_output, &error); + SUCCESS_RES (output, error); + g_assert_cmpuint (n_output, ==, 17); + g_assert_cmpstr ((gchar*)output, ==, "my-prefix:Conrad"); + g_free (output); + + gck_mechanism_unref (mech); + g_object_unref (result); + g_object_unref (key); +} + +DEFINE_TEST(verify) +{ + GckMechanism *mech; + GError *error = NULL; + GAsyncResult *result = NULL; + GckObject *key; + gboolean ret; + + mech = gck_mechanism_new_with_param (CKM_PREFIX, "my-prefix:", 10); + + /* Enable auto-login on this session, shouldn't be needed */ + gck_module_set_auto_authenticate (module, TRUE); + g_signal_connect (module, "authenticate-object", G_CALLBACK (authenticate_object), NULL); + + /* Find the right key */ + key = find_key (session, CKA_VERIFY, CKM_PREFIX); + g_assert (key); + + /* Simple one */ + ret = gck_session_verify (session, key, CKM_PREFIX, (const guchar*)"Labarbara", 10, + (const guchar*)"signed-prefix:Labarbara", 24, &error); + SUCCESS_RES (ret, error); + + /* Full one */ + ret = gck_session_verify_full (session, key, mech, (const guchar*)"Labarbara", 10, + (const guchar*)"my-prefix:Labarbara", 20, NULL, &error); + SUCCESS_RES (ret, error); + + /* Failure one */ + ret = gck_session_verify_full (session, key, mech, (const guchar*)"Labarbara", 10, + (const guchar*)"my-prefix:Loborboro", 20, NULL, &error); + FAIL_RES (ret, error); + + /* Asynchronous one */ + gck_session_verify_async (session, key, mech, (const guchar*)"Labarbara", 10, + (const guchar*)"my-prefix:Labarbara", 20, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + ret = gck_session_verify_finish (session, result, &error); + SUCCESS_RES (ret, error); + g_object_unref (result); + + /* Asynchronous failure */ + result = NULL; + gck_session_verify_async (session, key, mech, (const guchar*)"Labarbara", 10, + (const guchar*)"my-prefix:Labarxoro", 20, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + ret = gck_session_verify_finish (session, result, &error); + FAIL_RES (ret, error); + g_object_unref (result); + + gck_mechanism_unref (mech); + g_object_unref (key); +} + +DEFINE_TEST(generate_key_pair) +{ + GckAttributes *pub_attrs, *prv_attrs; + GckMechanism *mech; + GError *error = NULL; + GAsyncResult *result = NULL; + GckObject *pub_key, *prv_key; + gboolean ret; + + mech = gck_mechanism_new_with_param (CKM_GENERATE, "generate", 9); + + pub_attrs = gck_attributes_new (); + gck_attributes_add_ulong (pub_attrs, CKA_CLASS, CKO_PUBLIC_KEY); + prv_attrs = gck_attributes_new (); + gck_attributes_add_ulong (prv_attrs, CKA_CLASS, CKO_PRIVATE_KEY); + + /* Full One*/ + ret = gck_session_generate_key_pair_full (session, mech, pub_attrs, prv_attrs, + &pub_key, &prv_key, NULL, &error); + SUCCESS_RES (ret, error); + g_object_unref (pub_key); + g_object_unref (prv_key); + + /* Failure one */ + mech->type = 0; + pub_key = prv_key = NULL; + ret = gck_session_generate_key_pair_full (session, mech, pub_attrs, prv_attrs, + &pub_key, &prv_key, NULL, &error); + FAIL_RES (ret, error); + g_assert (pub_key == NULL); + g_assert (prv_key == NULL); + + /* Asynchronous one */ + mech->type = CKM_GENERATE; + gck_session_generate_key_pair_async (session, mech, pub_attrs, prv_attrs, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + ret = gck_session_generate_key_pair_finish (session, result, &pub_key, &prv_key, &error); + SUCCESS_RES (ret, error); + g_object_unref (result); + g_object_unref (pub_key); + g_object_unref (prv_key); + + /* Asynchronous failure */ + result = NULL; + mech->type = 0; + pub_key = prv_key = NULL; + gck_session_generate_key_pair_async (session, mech, pub_attrs, prv_attrs, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + ret = gck_session_generate_key_pair_finish (session, result, &pub_key, &prv_key, &error); + FAIL_RES (ret, error); + g_object_unref (result); + g_assert (pub_key == NULL); + g_assert (prv_key == NULL); + + gck_mechanism_unref (mech); + gck_attributes_unref (pub_attrs); + gck_attributes_unref (prv_attrs); +} + +DEFINE_TEST(wrap_key) +{ + GckMechanism *mech; + GError *error = NULL; + GAsyncResult *result = NULL; + GckObject *wrapper, *wrapped; + gpointer output; + gsize n_output; + + mech = gck_mechanism_new_with_param (CKM_WRAP, "wrap", 4); + wrapper = find_key (session, CKA_WRAP, 0); + wrapped = find_key_with_value (session, "value"); + + /* Simple One */ + output = gck_session_wrap_key (session, wrapper, CKM_WRAP, wrapped, &n_output, &error); + SUCCESS_RES (output, error); + g_assert (output); + g_assert_cmpsize (n_output, ==, 5); + g_assert (memcmp (output, "value", 5) == 0); + g_free (output); + + /* Full One*/ + output = gck_session_wrap_key_full (session, wrapper, mech, wrapped, &n_output, NULL, &error); + SUCCESS_RES (output, error); + g_assert_cmpsize (n_output, ==, 5); + g_assert (memcmp (output, "value", 5) == 0); + g_free (output); + + /* Failure one */ + mech->type = 0; + n_output = 0; + output = gck_session_wrap_key_full (session, wrapper, mech, wrapped, &n_output, NULL, &error); + FAIL_RES (output, error); + g_assert_cmpsize (n_output, ==, 0); + + /* Asynchronous one */ + mech->type = CKM_WRAP; + gck_session_wrap_key_async (session, wrapper, mech, wrapped, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + output = gck_session_wrap_key_finish (session, result, &n_output, &error); + SUCCESS_RES (output, error); + g_assert_cmpsize (n_output, ==, 5); + g_assert (memcmp (output, "value", 5) == 0); + g_object_unref (result); + g_free (output); + + /* Asynchronous failure */ + result = NULL; + mech->type = 0; + n_output = 0; + gck_session_wrap_key_async (session, wrapper, mech, wrapped, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + output = gck_session_wrap_key_finish (session, result, &n_output, &error); + FAIL_RES (output, error); + g_assert_cmpsize (n_output, ==, 0); + g_object_unref (result); + + g_object_unref (wrapper); + g_object_unref (wrapped); + gck_mechanism_unref (mech); +} + +DEFINE_TEST(unwrap_key) +{ + GckMechanism *mech; + GError *error = NULL; + GAsyncResult *result = NULL; + GckObject *wrapper, *unwrapped; + GckAttributes *attrs; + + mech = gck_mechanism_new_with_param (CKM_WRAP, "wrap", 4); + wrapper = find_key (session, CKA_UNWRAP, 0); + attrs = gck_attributes_newv (CKA_CLASS, GCK_ULONG, CKO_SECRET_KEY, GCK_INVALID); + + /* Simple One */ + unwrapped = gck_session_unwrap_key (session, wrapper, CKM_WRAP, "special", 7, &error, + CKA_CLASS, GCK_ULONG, CKO_SECRET_KEY, GCK_INVALID); + SUCCESS_RES (unwrapped, error); + g_assert (GCK_IS_OBJECT (unwrapped)); + check_key_with_value (session, unwrapped, CKO_SECRET_KEY, "special"); + g_object_unref (unwrapped); + + /* Full One*/ + unwrapped = gck_session_unwrap_key_full (session, wrapper, mech, "special", 7, attrs, NULL, &error); + SUCCESS_RES (unwrapped, error); + g_assert (GCK_IS_OBJECT (unwrapped)); + check_key_with_value (session, unwrapped, CKO_SECRET_KEY, "special"); + g_object_unref (unwrapped); + + /* Failure one */ + mech->type = 0; + unwrapped = gck_session_unwrap_key_full (session, wrapper, mech, "special", 7, attrs, NULL, &error); + FAIL_RES (unwrapped, error); + + /* Asynchronous one */ + mech->type = CKM_WRAP; + gck_session_unwrap_key_async (session, wrapper, mech, "special", 7, attrs, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + unwrapped = gck_session_unwrap_key_finish (session, result, &error); + SUCCESS_RES (unwrapped, error); + g_assert (GCK_IS_OBJECT (unwrapped)); + check_key_with_value (session, unwrapped, CKO_SECRET_KEY, "special"); + g_object_unref (unwrapped); + g_object_unref (result); + + /* Asynchronous failure */ + result = NULL; + mech->type = 0; + gck_session_unwrap_key_async (session, wrapper, mech, "special", 6, attrs, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + unwrapped = gck_session_unwrap_key_finish (session, result, &error); + FAIL_RES (unwrapped, error); + g_object_unref (result); + + g_object_unref (wrapper); + gck_attributes_unref (attrs); + gck_mechanism_unref (mech); +} + +DEFINE_TEST(derive_key) +{ + GckMechanism *mech; + GError *error = NULL; + GAsyncResult *result = NULL; + GckObject *wrapper, *derived; + GckAttributes *attrs; + + mech = gck_mechanism_new_with_param (CKM_DERIVE, "derive", 6); + wrapper = find_key (session, CKA_DERIVE, 0); + attrs = gck_attributes_newv (CKA_CLASS, GCK_ULONG, CKO_SECRET_KEY, GCK_INVALID); + + /* Simple One */ + derived = gck_session_derive_key (session, wrapper, CKM_DERIVE, &error, + CKA_CLASS, GCK_ULONG, CKO_SECRET_KEY, GCK_INVALID); + SUCCESS_RES (derived, error); + g_assert (GCK_IS_OBJECT (derived)); +g_printerr ("derived is: %lu", gck_object_get_handle (derived)); + check_key_with_value (session, derived, CKO_SECRET_KEY, "derived"); + g_object_unref (derived); + + /* Full One*/ + derived = gck_session_derive_key_full (session, wrapper, mech, attrs, NULL, &error); + SUCCESS_RES (derived, error); + g_assert (GCK_IS_OBJECT (derived)); + check_key_with_value (session, derived, CKO_SECRET_KEY, "derived"); + g_object_unref (derived); + + /* Failure one */ + mech->type = 0; + derived = gck_session_derive_key_full (session, wrapper, mech, attrs, NULL, &error); + FAIL_RES (derived, error); + + /* Asynchronous one */ + mech->type = CKM_DERIVE; + gck_session_derive_key_async (session, wrapper, mech, attrs, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + derived = gck_session_derive_key_finish (session, result, &error); + SUCCESS_RES (derived, error); + g_assert (GCK_IS_OBJECT (derived)); + check_key_with_value (session, derived, CKO_SECRET_KEY, "derived"); + g_object_unref (derived); + g_object_unref (result); + + /* Asynchronous failure */ + result = NULL; + mech->type = 0; + gck_session_derive_key_async (session, wrapper, mech, attrs, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + derived = gck_session_derive_key_finish (session, result, &error); + FAIL_RES (derived, error); + g_object_unref (result); + + g_object_unref (wrapper); + gck_attributes_unref (attrs); + gck_mechanism_unref (mech); +} diff --git a/gck/tests/test-gck-mechanism.c b/gck/tests/test-gck-mechanism.c new file mode 100644 index 00000000..6667103e --- /dev/null +++ b/gck/tests/test-gck-mechanism.c @@ -0,0 +1,60 @@ + +#include <glib.h> +#include <string.h> + +#include "test-suite.h" +#include "gck-test.h" + +#define MECH_TYPE 55 +#define MECH_DATA "TEST DATA" +#define N_MECH_DATA ((gsize)9) + +DEFINE_TEST(mech_new) +{ + GckMechanism *mech; + + mech = gck_mechanism_new (MECH_TYPE); + + g_assert (mech); + g_assert (mech->type == MECH_TYPE); + g_assert (mech->parameter == NULL); + g_assert (mech->n_parameter == 0); + + gck_mechanism_unref (mech); +} + +DEFINE_TEST(mech_new_with_param) +{ + GckMechanism *mech; + gpointer parameter = MECH_DATA; + + mech = gck_mechanism_new_with_param (MECH_TYPE, parameter, N_MECH_DATA); + + g_assert (mech); + g_assert (mech->type == MECH_TYPE); + g_assert (mech->parameter != NULL); + g_assert (mech->parameter != parameter); /* Copied */ + g_assert (mech->n_parameter == N_MECH_DATA); + g_assert (memcmp (mech->parameter, MECH_DATA, N_MECH_DATA) == 0); + + gck_mechanism_unref (mech); +} + +DEFINE_TEST(mech_ref_unref) +{ + GckMechanism *mech, *check; + + mech = gck_mechanism_new (MECH_TYPE); + g_assert (mech); + + check = gck_mechanism_ref (mech); + g_assert (check == mech); + + gck_mechanism_unref (check); + gck_mechanism_unref (mech); +} + +DEFINE_TEST(mech_unref_null) +{ + gck_mechanism_unref (NULL); +} diff --git a/gck/tests/test-gck-module.c b/gck/tests/test-gck-module.c new file mode 100644 index 00000000..122d2d6c --- /dev/null +++ b/gck/tests/test-gck-module.c @@ -0,0 +1,159 @@ + +#include <glib.h> +#include <string.h> + +#include "test-suite.h" +#include "gck-test.h" + +static GckModule *module = NULL; + +DEFINE_SETUP(load_module) +{ + GError *err = NULL; + + /* Successful load */ + module = gck_module_initialize (".libs/libgck-test-module.so", NULL, &err); + SUCCESS_RES (module, err); +} + +DEFINE_TEARDOWN(load_module) +{ + g_object_unref (module); +} + +DEFINE_TEST(invalid_modules) +{ + GckModule *invalid; + GError *err = NULL; + + /* Shouldn't be able to load modules */ + invalid = gck_module_initialize ("blah-blah-non-existant", NULL, &err); + FAIL_RES (invalid, err); + + /* Shouldn't be able to load any file successfully */ + invalid = gck_module_initialize ("/usr/lib/libm.so", NULL, &err); + FAIL_RES (invalid, err); +} + +DEFINE_TEST(module_equals_hash) +{ + GckModule *other; + GObject *obj; + guint hash; + + hash = gck_module_hash (module); + g_assert (hash != 0); + + g_assert (gck_module_equal (module, module)); + + other = gck_module_new (gck_module_get_functions (module)); + obj = g_object_new (G_TYPE_OBJECT, NULL); + + g_assert (gck_module_equal (module, other)); + + /* TODO: Could do with another test for inequality */ + g_assert (!gck_module_equal (module, obj)); + + g_object_unref (other); + g_object_unref (obj); +} + +DEFINE_TEST(module_props) +{ + gchar *path; + + g_object_get (module, "path", &path, NULL); + g_assert (path != NULL && "no module-path"); + g_assert (strcmp (".libs/libgck-test-module.so", path) == 0 && "module path wrong"); + g_free (path); +} + +DEFINE_TEST(module_info) +{ + GckModuleInfo *info; + + info = gck_module_get_info (module); + g_assert (info != NULL && "no module info"); + + g_assert (info->pkcs11_version_major == CRYPTOKI_VERSION_MAJOR && "wrong major version"); + g_assert (info->pkcs11_version_minor == CRYPTOKI_VERSION_MINOR && "wrong minor version"); + g_assert (strcmp ("TEST MANUFACTURER", info->manufacturer_id) == 0); + g_assert (strcmp ("TEST LIBRARY", info->library_description) == 0); + g_assert (0 == info->flags); + g_assert (45 == info->library_version_major); + g_assert (145 == info->library_version_minor); + + gck_module_info_free (info); +} + +static int n_objects = 0; +static GckObject *last_object = NULL; + +static gboolean +for_each_object (GckObject *object, gpointer user_data) +{ + g_assert (GCK_IS_OBJECT (object)); + g_assert_cmpstr ("blah", ==, user_data); + g_assert (user_data); + + if (last_object) + g_object_unref (last_object); + last_object = g_object_ref (object); + + ++n_objects; + + return TRUE; +} + +static gboolean +for_first_object (GckObject *object, gpointer user_data) +{ + g_assert (GCK_IS_OBJECT (object)); + g_assert_cmpstr ("first", ==, user_data); + g_assert (user_data); + + if (last_object) + g_object_unref (last_object); + last_object = g_object_ref (object); + + ++n_objects; + + return FALSE; +} + +DEFINE_TEST(module_enumerate) +{ + GckSession *session; + GckAttributes *attrs; + gboolean ret; + + attrs = gck_attributes_new (); + ret = gck_module_enumerate_objects_full (module, attrs, NULL, for_first_object, "first", NULL); + g_assert (ret); + g_assert_cmpint (n_objects, ==, 1); + g_assert (GCK_IS_OBJECT (last_object)); + gck_attributes_unref (attrs); + + session = gck_object_get_session (last_object); + g_assert (GCK_IS_SESSION (session)); + g_object_unref (session); + + g_object_unref (last_object); + last_object = NULL; + n_objects = 0; + + ret = gck_module_enumerate_objects (module, for_each_object, "blah", + CKA_CLASS, GCK_ULONG, CKO_PRIVATE_KEY, + GCK_INVALID); + g_assert (ret); + g_assert_cmpint (n_objects, ==, 2); + g_assert (GCK_IS_OBJECT (last_object)); + + session = gck_object_get_session (last_object); + g_assert (GCK_IS_SESSION (session)); + g_object_unref (session); + + g_object_unref (last_object); + last_object = NULL; + n_objects = 0; +} diff --git a/gck/tests/test-gck-object.c b/gck/tests/test-gck-object.c new file mode 100644 index 00000000..2a630495 --- /dev/null +++ b/gck/tests/test-gck-object.c @@ -0,0 +1,463 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "test-suite.h" + +#include <glib.h> + +#include "gck-test.h" + +static GckModule *module = NULL; +static GckSlot *slot = NULL; +static GckSession *session = NULL; +static GckObject *object = NULL; + +DEFINE_SETUP(prep_object) +{ + GError *err = NULL; + GList *slots; + + /* Successful load */ + module = gck_module_initialize (".libs/libgck-test-module.so", NULL, &err); + SUCCESS_RES (module, err); + + slots = gck_module_get_slots (module, TRUE); + g_assert (slots != NULL); + + slot = GCK_SLOT (slots->data); + g_object_ref (slot); + gck_list_unref_free (slots); + + session = gck_slot_open_session (slot, 0, &err); + SUCCESS_RES(session, err); + + /* Our module always exports a token object with this */ + object = gck_object_from_handle (slot, 2); + g_assert (object != NULL); +} + +DEFINE_TEARDOWN(prep_object) +{ + g_object_unref (object); + g_object_unref (session); + g_object_unref (slot); + g_object_unref (module); +} + +DEFINE_TEST(object_props) +{ + GckSlot *sl; + GckModule *mod; + CK_OBJECT_HANDLE handle; + g_object_get (object, "slot", &sl, "module", &mod, "handle", &handle, NULL); + g_assert (slot == sl); + g_object_unref (sl); + g_assert (module == mod); + g_object_unref (mod); + g_assert (handle == 2); +} + +DEFINE_TEST(object_equals_hash) +{ + GckSlot *other_slot; + GckObject *other_object; + GObject *obj; + guint hash; + + hash = gck_object_hash (object); + g_assert (hash != 0); + + g_assert (gck_object_equal (object, object)); + + other_slot = g_object_new (GCK_TYPE_SLOT, "module", module, "handle", 5895, NULL); + other_object = gck_object_from_handle (other_slot, gck_object_get_handle (object)); + g_assert (!gck_object_equal (object, other_object)); + g_object_unref (other_slot); + g_object_unref (other_object); + + obj = g_object_new (G_TYPE_OBJECT, NULL); + g_assert (!gck_object_equal (object, obj)); + g_object_unref (obj); + + other_object = gck_object_from_handle (slot, 383838); + g_assert (!gck_object_equal (object, other_object)); + g_object_unref (other_object); + + other_object = gck_object_from_handle (slot, gck_object_get_handle (object)); + g_assert (gck_object_equal (object, other_object)); + g_object_unref (other_object); +} + +static void +fetch_async_result (GObject *source, GAsyncResult *result, gpointer user_data) +{ + *((GAsyncResult**)user_data) = result; + g_object_ref (result); + testing_wait_stop (); +} + +DEFINE_TEST(create_object) +{ + GAsyncResult *result = NULL; + GckAttributes *attrs; + GckObject *object; + CK_OBJECT_HANDLE last_handle; + GError *err = NULL; + + /* Using simple */ + object = gck_session_create_object (session, &err, + CKA_CLASS, GCK_ULONG, CKO_DATA, + CKA_LABEL, GCK_STRING, "TEST LABEL", + CKA_TOKEN, GCK_BOOLEAN, CK_FALSE, + CKA_VALUE, 4UL, "BLAH", + GCK_INVALID); + SUCCESS_RES (object, err); + g_assert (GCK_IS_OBJECT (object)); + + if (object) { + last_handle = gck_object_get_handle (object); + g_object_unref (object); + } + + /* Using full */ + attrs = gck_attributes_newv (CKA_CLASS, GCK_ULONG, CKO_DATA, + CKA_LABEL, GCK_STRING, "TEST LABEL", + CKA_TOKEN, GCK_BOOLEAN, CK_FALSE, + CKA_VALUE, 4UL, "BLAH", + GCK_INVALID); + + object = gck_session_create_object_full (session, attrs, NULL, &err); + g_assert (GCK_IS_OBJECT (object)); + SUCCESS_RES (object, err); + + if (object) { + g_assert (last_handle != gck_object_get_handle (object)); + last_handle = gck_object_get_handle (object); + g_object_unref (object); + } + + /* Using async */ + gck_session_create_object_async (session, attrs, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + + object = gck_session_create_object_finish (session, result, &err); + g_object_unref (result); + SUCCESS_RES (object, err); + g_assert (GCK_IS_OBJECT (object)); + + if (object) + g_object_unref (object); + gck_attributes_unref (attrs); +} + +DEFINE_TEST(destroy_object) +{ + GAsyncResult *result = NULL; + GckObject *object; + GError *err = NULL; + gboolean ret; + + /* Using simple */ + object = gck_session_create_object (session, &err, + CKA_CLASS, GCK_ULONG, CKO_DATA, + CKA_LABEL, GCK_STRING, "TEST OBJECT", + CKA_TOKEN, GCK_BOOLEAN, CK_TRUE, + GCK_INVALID); + SUCCESS_RES (object, err); + g_assert (GCK_IS_OBJECT (object)); + + if (!object) + return; + + ret = gck_object_destroy (object, &err); + SUCCESS_RES (ret, err); + g_object_unref (object); + + /* Using full */ + object = gck_session_create_object (session, &err, + CKA_CLASS, GCK_ULONG, CKO_DATA, + CKA_LABEL, GCK_STRING, "TEST OBJECT", + CKA_TOKEN, GCK_BOOLEAN, CK_TRUE, + GCK_INVALID); + SUCCESS_RES (object, err); + g_assert (GCK_IS_OBJECT (object)); + + if (!object) + return; + + ret = gck_object_destroy_full (object, NULL, &err); + SUCCESS_RES (ret, err); + g_object_unref (object); + + /* Using async */ + object = gck_session_create_object (session, &err, + CKA_CLASS, GCK_ULONG, CKO_DATA, + CKA_LABEL, GCK_STRING, "TEST OBJECT", + CKA_TOKEN, GCK_BOOLEAN, CK_TRUE, + GCK_INVALID); + SUCCESS_RES (object, err); + g_assert (GCK_IS_OBJECT (object)); + + if (!object) + return; + + /* Using async */ + gck_object_destroy_async (object, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + + ret = gck_object_destroy_finish (object, result, &err); + g_object_unref (result); + SUCCESS_RES (object, err); + g_object_unref (object); +} + +DEFINE_TEST(get_attributes) +{ + GAsyncResult *result = NULL; + GckAttributes *attrs, *attrs_ret; + GError *err = NULL; + gulong klass; + gchar *value = NULL; + + /* Simple */ + attrs = gck_object_get (object, &err, CKA_CLASS, CKA_LABEL, GCK_INVALID); + SUCCESS_RES (attrs, err); + if (attrs != NULL) { + g_assert (gck_attributes_find_ulong (attrs, CKA_CLASS, &klass) && klass == CKO_DATA); + g_assert (gck_attributes_find_string (attrs, CKA_LABEL, &value) && strcmp (value, "TEST LABEL") == 0); + g_free (value); value = NULL; + } + gck_attributes_unref (attrs); + + /* Full */ + attrs = gck_attributes_new_empty (CKA_CLASS, CKA_LABEL, GCK_INVALID); + attrs_ret = gck_object_get_full (object, attrs, NULL, &err); + SUCCESS_RES (attrs_ret, err); + if (attrs_ret != NULL) { + g_assert (attrs_ret == attrs); + g_assert (gck_attributes_find_ulong (attrs, CKA_CLASS, &klass) && klass == CKO_DATA); + g_assert (gck_attributes_find_string (attrs, CKA_LABEL, &value) && strcmp (value, "TEST LABEL") == 0); + g_free (value); value = NULL; + } + gck_attributes_unref (attrs); + + /* Async */ + attrs = gck_attributes_new_empty (CKA_CLASS, CKA_LABEL, GCK_INVALID); + gck_object_get_async (object, attrs, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + + attrs_ret = gck_object_get_finish (object, result, &err); + g_object_unref (result); + SUCCESS_RES (attrs, err); + if (attrs != NULL) { + g_assert (attrs_ret == attrs); + g_assert (gck_attributes_find_ulong (attrs, CKA_CLASS, &klass) && klass == CKO_DATA); + g_assert (gck_attributes_find_string (attrs, CKA_LABEL, &value) && strcmp (value, "TEST LABEL") == 0); + g_free (value); value = NULL; + } + gck_attributes_unref (attrs); +} + +DEFINE_TEST(get_data_attribute) +{ + GAsyncResult *result = NULL; + CK_OBJECT_CLASS_PTR klass; + gsize n_data; + GError *err = NULL; + + /* Simple */ + klass = gck_object_get_data (object, CKA_CLASS, &n_data, &err); + SUCCESS_RES (klass, err); + if (klass != NULL) { + g_assert (n_data == sizeof (CK_OBJECT_CLASS)); + g_assert (*klass == CKO_DATA); + g_free (klass); + } + + /* Full */ + klass = gck_object_get_data_full (object, CKA_CLASS, NULL, NULL, &n_data, &err); + SUCCESS_RES (klass, err); + if (klass != NULL) { + g_assert (n_data == sizeof (CK_OBJECT_CLASS)); + g_assert (*klass == CKO_DATA); + g_free (klass); + } + + /* Async */ + gck_object_get_data_async (object, CKA_CLASS, NULL, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + + klass = gck_object_get_data_finish (object, result, &n_data, &err); + g_object_unref (result); + SUCCESS_RES (klass, err); + if (klass != NULL) { + g_assert (n_data == sizeof (CK_OBJECT_CLASS)); + g_assert (*klass == CKO_DATA); + g_free (klass); + } + +} + +DEFINE_TEST(set_attributes) +{ + GAsyncResult *result = NULL; + GckAttributes *attrs, *templ; + GError *err = NULL; + gulong klass; + gchar *value = NULL; + gboolean ret; + + /* Simple */ + ret = gck_object_set (object, &err, + CKA_CLASS, GCK_ULONG, 5, + CKA_LABEL, GCK_STRING, "CHANGE ONE", + GCK_INVALID); + SUCCESS_RES (ret, err); + if (ret) { + attrs = gck_object_get (object, &err, CKA_CLASS, CKA_LABEL, GCK_INVALID); + g_assert (gck_attributes_find_ulong (attrs, CKA_CLASS, &klass) && klass == 5); + g_assert (gck_attributes_find_string (attrs, CKA_LABEL, &value) && strcmp (value, "CHANGE ONE") == 0); + g_free (value); value = NULL; + gck_attributes_unref (attrs); + } + + templ = gck_attributes_newv (CKA_CLASS, GCK_ULONG, 6, + CKA_LABEL, GCK_STRING, "CHANGE TWO", + GCK_INVALID); + + /* Full */ + ret = gck_object_set_full (object, templ, NULL, &err); + gck_attributes_unref (templ); + SUCCESS_RES (ret, err); + if (ret) { + attrs = gck_object_get (object, &err, CKA_CLASS, CKA_LABEL, GCK_INVALID); + g_assert (gck_attributes_find_ulong (attrs, CKA_CLASS, &klass) && klass == 6); + g_assert (gck_attributes_find_string (attrs, CKA_LABEL, &value) && strcmp (value, "CHANGE TWO") == 0); + g_free (value); value = NULL; + gck_attributes_unref (attrs); + } + + templ = gck_attributes_newv (CKA_CLASS, GCK_ULONG, 7, + CKA_LABEL, GCK_STRING, "CHANGE THREE", + GCK_INVALID); + + /* Async */ + gck_object_set_async (object, templ, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + + ret = gck_object_set_finish (object, result, &err); + g_object_unref (result); + SUCCESS_RES (ret, err); + if (ret) { + attrs = gck_object_get (object, &err, CKA_CLASS, CKA_LABEL, GCK_INVALID); + g_assert (gck_attributes_find_ulong (attrs, CKA_CLASS, &klass) && klass == 7); + g_assert (gck_attributes_find_string (attrs, CKA_LABEL, &value) && strcmp (value, "CHANGE THREE") == 0); + g_free (value); value = NULL; + gck_attributes_unref (attrs); + } +} + +DEFINE_TEST(find_objects) +{ + GAsyncResult *result = NULL; + GckAttributes *templ; + GList *objects; + GckObject *testobj; + GError *err = NULL; + + testobj = gck_session_create_object (session, &err, + CKA_CLASS, GCK_ULONG, CKO_DATA, + CKA_LABEL, GCK_STRING, "UNIQUE LABEL", + GCK_INVALID); + g_object_unref (testobj); + + testobj = gck_session_create_object (session, &err, + CKA_CLASS, GCK_ULONG, CKO_DATA, + CKA_LABEL, GCK_STRING, "OTHER LABEL", + GCK_INVALID); + g_object_unref (testobj); + + /* Simple, "TEST LABEL" */ + objects = gck_session_find_objects (session, &err, CKA_LABEL, GCK_STRING, "UNIQUE LABEL", GCK_INVALID); + SUCCESS_RES (objects, err); + g_assert (g_list_length (objects) == 1); + gck_list_unref_free (objects); + + /* Full, All */ + templ = gck_attributes_new (); + objects = gck_session_find_objects_full (session, templ, NULL, &err); + SUCCESS_RES (objects, err); + g_assert (g_list_length (objects) > 1); + gck_list_unref_free (objects); + + /* Async, None */ + gck_attributes_add_string (templ, CKA_LABEL, "blah blah"); + gck_session_find_objects_async (session, templ, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + + objects = gck_session_find_objects_finish (session, result, &err); + g_object_unref (result); + g_assert (objects == NULL); + gck_list_unref_free (objects); +} + +DEFINE_TEST(explicit_sessions) +{ + GckSession *sess; + GAsyncResult *result = NULL; + CK_OBJECT_CLASS_PTR klass; + GError *err = NULL; + gsize n_data; + + /* Set an explicit session */ + gck_object_set_session (object, session); + sess = gck_object_get_session (object); + g_assert (sess == session); + g_object_unref (sess); + g_object_get (object, "session", &sess, NULL); + g_assert (sess == session); + g_object_unref (sess); + + /* Simple */ + klass = gck_object_get_data (object, CKA_CLASS, &n_data, &err); + SUCCESS_RES (klass, err); + if (klass != NULL) { + g_assert (n_data == sizeof (CK_OBJECT_CLASS)); + g_assert (*klass == CKO_DATA); + g_free (klass); + } + + /* Async */ + gck_object_get_data_async (object, CKA_CLASS, NULL, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + + klass = gck_object_get_data_finish (object, result, &n_data, &err); + g_object_unref (result); + SUCCESS_RES (klass, err); + if (klass != NULL) { + g_assert (n_data == sizeof (CK_OBJECT_CLASS)); + g_assert (*klass == CKO_DATA); + g_free (klass); + } + + /* Set it to null and make sure taht works */ + gck_object_set_session (object, NULL); + g_assert (gck_object_get_session (object) == NULL); + g_object_get (object, "session", &sess, NULL); + g_assert (sess == NULL); + + /* Test property settor */ + g_object_set (object, "session", session, NULL); + sess = gck_object_get_session (object); + g_assert (sess == session); + gck_object_set_session (object, NULL); + g_object_unref (sess); +} diff --git a/gck/tests/test-gck-session.c b/gck/tests/test-gck-session.c new file mode 100644 index 00000000..ff98a0a5 --- /dev/null +++ b/gck/tests/test-gck-session.c @@ -0,0 +1,323 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "test-suite.h" + +#include <glib.h> + +#include "gck-test.h" + +static GckModule *module = NULL; +static GckSlot *slot = NULL; +static GckSession *session = NULL; + +DEFINE_SETUP(load_session) +{ + GError *err = NULL; + GList *slots; + + /* Successful load */ + module = gck_module_initialize (".libs/libgck-test-module.so", NULL, &err); + SUCCESS_RES (module, err); + + slots = gck_module_get_slots (module, TRUE); + g_assert (slots != NULL); + + slot = GCK_SLOT (slots->data); + g_object_ref (slot); + gck_list_unref_free (slots); + + session = gck_slot_open_session (slot, 0, &err); + SUCCESS_RES(session, err); +} + +DEFINE_TEARDOWN(load_session) +{ + g_object_unref (session); + g_object_unref (slot); + g_object_unref (module); +} + +DEFINE_TEST(session_props) +{ + GckModule *mod; + GckSlot *sl; + gulong handle; + + g_object_get (session, "module", &mod, "handle", &handle, "slot", &sl, NULL); + g_assert (mod == module); + g_assert (sl == slot); + g_object_unref (mod); + g_object_unref (sl); + + g_assert (handle != 0); + g_assert (gck_session_get_handle (session) == handle); +} + +DEFINE_TEST(session_info) +{ + GckSessionInfo *info; + + info = gck_session_get_info (session); + g_assert (info != NULL && "no session info"); + + g_assert (info->slot_id == gck_slot_get_handle (slot)); + g_assert ((info->flags & CKF_SERIAL_SESSION) == CKF_SERIAL_SESSION); + g_assert (info->device_error == 1414); + gck_session_info_free (info); +} + +static void +fetch_async_result (GObject *source, GAsyncResult *result, gpointer user_data) +{ + *((GAsyncResult**)user_data) = result; + g_object_ref (result); + testing_wait_stop (); +} + +DEFINE_TEST(open_close_session) +{ + GckSession *sess; + GAsyncResult *result = NULL; + GError *err = NULL; + + sess = gck_slot_open_session_full (slot, 0, NULL, NULL, NULL, &err); + SUCCESS_RES (sess, err); + + g_object_unref (sess); + + /* Test opening async */ + gck_slot_open_session_async (slot, 0, NULL, NULL, NULL, fetch_async_result, &result); + + testing_wait_until (500); + g_assert (result != NULL); + + /* Get the result */ + sess = gck_slot_open_session_finish (slot, result, &err); + SUCCESS_RES (sess, err); + + g_object_unref (result); + g_object_unref (sess); +} + +DEFINE_TEST(open_reused) +{ + CK_OBJECT_HANDLE handle; + GckSession *sess, *sess2; + GAsyncResult *result = NULL; + GError *err = NULL; + gboolean value; + + g_assert (gck_module_get_pool_sessions (module) == FALSE); + gck_module_set_pool_sessions (module, TRUE); + g_assert (gck_module_get_pool_sessions (module) == TRUE); + g_object_get (module, "pool-sessions", &value, NULL); + g_assert (value == TRUE); + + sess = gck_slot_open_session (slot, 0, &err); + SUCCESS_RES (sess, err); + if (!sess) return; + + /* Make note of the handle we saw */ + handle = gck_session_get_handle (sess); + g_object_unref (sess); + + /* Open again, and see if the same handle */ + sess = gck_slot_open_session (slot, 0, &err); + SUCCESS_RES (sess, err); + if (!sess) return; + g_assert (handle == gck_session_get_handle (sess)); + g_object_unref (sess); + + /* Test opening async */ + gck_slot_open_session_async (slot, 0, NULL, NULL, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + sess = gck_slot_open_session_finish (slot, result, &err); + SUCCESS_RES (sess, err); + if (!sess) return; + g_assert (handle == gck_session_get_handle (sess)); + g_object_unref (result); + g_object_unref (sess); + + /* Test opening with different flags, a different session should be returned */ + sess = gck_slot_open_session (slot, CKF_RW_SESSION, &err); + SUCCESS_RES (sess, err); + if (!sess) return; + g_assert (handle != gck_session_get_handle (sess)); + + /* Now open a second session, with same flags, shouldn't return the same */ + sess2 = gck_slot_open_session (slot, CKF_RW_SESSION, &err); + SUCCESS_RES (sess2, err); + if (!sess2) return; + g_assert (gck_session_get_handle (sess) != gck_session_get_handle (sess2)); + + g_object_set (module, "pool-sessions", FALSE, NULL); + g_assert (gck_module_get_pool_sessions (module) == FALSE); + + g_object_unref (sess); + g_object_unref (sess2); +} + + +DEFINE_TEST(init_set_pin) +{ + GAsyncResult *result = NULL; + GError *err = NULL; + gboolean ret; + + /* init pin */ + ret = gck_session_init_pin (session, (guchar*)"booo", 4, &err); + SUCCESS_RES (ret, err); + + /* set pin */ + ret = gck_session_set_pin (session, (guchar*)"booo", 4, (guchar*)"tooo", 4, &err); + SUCCESS_RES (ret, err); + + /* init pin async */ + gck_session_init_pin_async (session, (guchar*)"booo", 4, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + ret = gck_session_init_pin_finish (session, result, &err); + SUCCESS_RES (ret, err); + g_object_unref (result); + result = NULL; + + /* set pin async */ + gck_session_set_pin_async (session, (guchar*)"booo", 4, (guchar*)"tooo", 4, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + ret = gck_session_set_pin_finish (session, result, &err); + SUCCESS_RES (ret, err); + g_object_unref (result); + result = NULL; +} + + +DEFINE_TEST(login_logout) +{ + GAsyncResult *result = NULL; + GError *err = NULL; + gboolean ret; + + /* login/logout */ + ret = gck_session_login (session, CKU_USER, (guchar*)"booo", 4, &err); + SUCCESS_RES (ret, err); + + ret = gck_session_logout (session, &err); + SUCCESS_RES (ret, err); + + /* login/logout full */ + ret = gck_session_login_full (session, CKU_USER, (guchar*)"booo", 4, NULL, &err); + SUCCESS_RES (ret, err); + + ret = gck_session_logout_full (session, NULL, &err); + SUCCESS_RES (ret, err); + + /* login async */ + gck_session_login_async (session, CKU_USER, (guchar*)"booo", 4, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + + ret = gck_session_login_finish (session, result, &err); + SUCCESS_RES (ret, err); + + g_object_unref (result); + result = NULL; + + /* logout async */ + gck_session_logout_async (session, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + + ret = gck_session_logout_finish (session, result, &err); + SUCCESS_RES (ret, err); + + g_object_unref (result); + result = NULL; + +} + +static gboolean +authenticate_token (GckModule *module, GckSlot *slot, gchar *label, gchar **password, gpointer unused) +{ + g_assert (unused == GUINT_TO_POINTER (35)); + g_assert (password != NULL); + g_assert (*password == NULL); + g_assert (GCK_IS_MODULE (module)); + g_assert (GCK_IS_SLOT (slot)); + + *password = g_strdup ("booo"); + return TRUE; +} + +DEFINE_TEST(auto_login) +{ + GckObject *object; + GckSession *new_session; + GAsyncResult *result = NULL; + GError *err = NULL; + GckAttributes *attrs; + gboolean ret; + gint value; + + attrs = gck_attributes_newv (CKA_CLASS, GCK_ULONG, CKO_DATA, + CKA_LABEL, GCK_STRING, "TEST OBJECT", + CKA_PRIVATE, GCK_BOOLEAN, CK_TRUE, + GCK_INVALID); + + /* Try to do something that requires a login */ + object = gck_session_create_object_full (session, attrs, NULL, &err); + g_assert (!object); + g_assert (err && err->code == CKR_USER_NOT_LOGGED_IN); + g_clear_error (&err); + + /* Setup for auto login */ + g_assert (gck_module_get_auto_authenticate (module) == 0); + gck_module_set_auto_authenticate (module, TRUE); + g_assert (gck_module_get_auto_authenticate (module) == (GCK_AUTHENTICATE_TOKENS | GCK_AUTHENTICATE_OBJECTS)); + g_object_get (module, "auto-authenticate", &value, NULL); + g_assert (value == (GCK_AUTHENTICATE_TOKENS | GCK_AUTHENTICATE_OBJECTS)); + + g_signal_connect (module, "authenticate-slot", G_CALLBACK (authenticate_token), GUINT_TO_POINTER (35)); + + /* Create a new session */ + new_session = gck_slot_open_session (slot, CKF_RW_SESSION, &err); + SUCCESS_RES (new_session, err); + g_object_unref (new_session); + + /* Try again to do something that requires a login */ + object = gck_session_create_object_full (session, attrs, NULL, &err); + SUCCESS_RES (object, err); + g_object_unref (object); + + /* We should now be logged in, try to log out */ + ret = gck_session_logout (session, &err); + SUCCESS_RES (ret, err); + + /* Now try the same thing, but asyncronously */ + gck_slot_open_session_async (slot, CKF_RW_SESSION, NULL, NULL, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + new_session = gck_slot_open_session_finish (slot, result, &err); + SUCCESS_RES (new_session, err); + g_object_unref (result); + g_object_unref (new_session); + + result = NULL; + gck_session_create_object_async (session, attrs, NULL, fetch_async_result, &result); + testing_wait_until (500); + g_assert (result != NULL); + object = gck_session_create_object_finish (session, result, &err); + SUCCESS_RES (object, err); + g_object_unref (result); + g_object_unref (object); + + /* We should now be logged in, try to log out */ + ret = gck_session_logout (session, &err); + SUCCESS_RES (ret, err); + + g_object_set (module, "auto-authenticate", FALSE, NULL); + g_assert (gck_module_get_auto_authenticate (module) == FALSE); +} diff --git a/gck/tests/test-gck-slot.c b/gck/tests/test-gck-slot.c new file mode 100644 index 00000000..6743508a --- /dev/null +++ b/gck/tests/test-gck-slot.c @@ -0,0 +1,147 @@ + +#include <glib.h> +#include <string.h> + +#include "test-suite.h" +#include "gck-test.h" + +static GckModule *module = NULL; +static GckSlot *slot = NULL; + +DEFINE_SETUP(load_slots) +{ + GError *err = NULL; + GList *slots; + + /* Successful load */ + module = gck_module_initialize (".libs/libgck-test-module.so", NULL, &err); + SUCCESS_RES (module, err); + + slots = gck_module_get_slots (module, TRUE); + g_assert (slots != NULL); + + slot = GCK_SLOT (slots->data); + g_object_ref (slot); + gck_list_unref_free (slots); + +} + +DEFINE_TEARDOWN(load_slots) +{ + g_object_unref (slot); + g_object_unref (module); +} + +DEFINE_TEST(slot_info) +{ + GckSlotInfo *info; + GckTokenInfo *token; + GList *slots, *l; + + slots = gck_module_get_slots (module, FALSE); + g_assert (2 == g_list_length (slots) && "wrong number of slots returned"); + g_assert (GCK_IS_SLOT (slots->data) && "missing slot one"); + g_assert (GCK_IS_SLOT (slots->next->data) && "missing slot two"); + + for (l = slots; l; l = g_list_next (l)) { + info = gck_slot_get_info (GCK_SLOT (l->data)); + g_assert (info != NULL && "no slot info"); + + g_assert (strcmp("TEST MANUFACTURER", info->manufacturer_id) == 0); + g_assert (strcmp("TEST SLOT", info->slot_description) == 0); + g_assert (55 == info->hardware_version_major); + g_assert (155 == info->hardware_version_minor); + g_assert (65 == info->firmware_version_major); + g_assert (165 == info->firmware_version_minor); + + if (info->flags & CKF_TOKEN_PRESENT) { + token = gck_slot_get_token_info (slot); + g_assert (token != NULL && "no token info"); + + g_assert (strcmp ("TEST MANUFACTURER", token->manufacturer_id) == 0); + g_assert (strcmp ("TEST LABEL", token->label) == 0); + g_assert (strcmp ("TEST MODEL", token->model) == 0); + g_assert (strcmp ("TEST SERIAL", token->serial_number) == 0); + g_assert (1 == token->max_session_count); + g_assert (2 == token->session_count); + g_assert (3 == token->max_rw_session_count); + g_assert (4 == token->rw_session_count); + g_assert (5 == token->max_pin_len); + g_assert (6 == token->min_pin_len); + g_assert (7 == token->total_public_memory); + g_assert (8 == token->free_public_memory); + g_assert (9 == token->total_private_memory); + g_assert (10 == token->free_private_memory); + g_assert (75 == token->hardware_version_major); + g_assert (175 == token->hardware_version_minor); + g_assert (85 == token->firmware_version_major); + g_assert (185 == token->firmware_version_minor); + g_assert (927623999 == token->utc_time); + + gck_token_info_free (token); + } + + gck_slot_info_free (info); + } + + gck_list_unref_free (slots); +} + +DEFINE_TEST(slot_props) +{ + GckModule *mod; + CK_SLOT_ID slot_id; + + g_object_get (slot, "module", &mod, "handle", &slot_id, NULL); + g_assert (mod == module); + g_assert (slot_id == 52); + + g_object_unref (mod); +} + +DEFINE_TEST(slot_equals_hash) +{ + GckModule *other_mod; + GckSlot *other_slot; + GObject *obj; + guint hash; + + hash = gck_slot_hash (slot); + g_assert (hash != 0); + + g_assert (gck_slot_equal (slot, slot)); + + other_mod = gck_module_new (gck_module_get_functions (module)); + other_slot = g_object_new (GCK_TYPE_SLOT, "module", other_mod, "handle", gck_slot_get_handle (slot), NULL); + g_assert (gck_slot_equal (slot, other_slot)); + g_object_unref (other_mod); + g_object_unref (other_slot); + + obj = g_object_new (G_TYPE_OBJECT, NULL); + g_assert (!gck_slot_equal (slot, obj)); + g_object_unref (obj); + + other_slot = g_object_new (GCK_TYPE_SLOT, "module", module, "handle", 8909, NULL); + g_assert (!gck_slot_equal (slot, obj)); + g_object_unref (other_slot); +} + +DEFINE_TEST(slot_mechanisms) +{ + GckMechanisms *mechs; + GckMechanismInfo *info; + guint i; + + mechs = gck_slot_get_mechanisms (slot); + g_assert (2 == gck_mechanisms_length (mechs) && "wrong number of mech types returned"); + + for (i = 0; i < gck_mechanisms_length (mechs); ++i) { + + info = gck_slot_get_mechanism_info (slot, gck_mechanisms_at (mechs, i)); + g_assert (info != NULL && "no mech info returned"); + + gck_mechanism_info_free (info); + } + + gck_mechanisms_free (mechs); +} |