summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--gck/Makefile.am3
-rw-r--r--gck/gck-call.c18
-rw-r--r--gck/gck-enumerator.c689
-rw-r--r--gck/gck-misc.c2
-rw-r--r--gck/gck-modules.c166
-rw-r--r--gck/gck-private.h13
-rw-r--r--gck/gck-slot.c27
-rw-r--r--gck/gck-uri.c350
-rw-r--r--gck/gck.h113
-rw-r--r--gck/tests/Makefile.am5
-rw-r--r--gck/tests/test-gck-enumerator.c170
-rw-r--r--gck/tests/test-gck-module.c4
-rw-r--r--gck/tests/test-gck-modules.c145
-rw-r--r--gck/tests/test-gck-slot.c56
-rw-r--r--gck/tests/test-gck-uri.c229
16 files changed, 1879 insertions, 113 deletions
diff --git a/Makefile.am b/Makefile.am
index 88c56cea..f41c9de2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -12,9 +12,9 @@ endif
SUBDIRS = \
. \
+ egg \
gck \
gp11 \
- egg \
gcr \
ui \
pkcs11 \
diff --git a/gck/Makefile.am b/gck/Makefile.am
index a860f71f..bf7533a2 100644
--- a/gck/Makefile.am
+++ b/gck/Makefile.am
@@ -20,12 +20,14 @@ libgck_la_SOURCES = \
gck.h gck-private.h pkcs11.h \
gck-attributes.c \
gck-call.c \
+ gck-enumerator.c \
gck-misc.c \
gck-module.c \
gck-modules.c \
gck-object.c \
gck-session.c \
gck-slot.c \
+ gck-uri.c \
$(BUILT_SOURCES)
libgck_la_LDFLAGS = \
@@ -33,6 +35,7 @@ libgck_la_LDFLAGS = \
-no-undefined -export-symbols-regex 'gck_*'
libgck_la_LIBADD = \
+ $(top_builddir)/egg/libegg.la \
$(GOBJECT_LIBS) \
$(GTHREAD_LIBS) \
$(GIO_LIBS) \
diff --git a/gck/gck-call.c b/gck/gck-call.c
index 939d81bb..6e46632a 100644
--- a/gck/gck-call.c
+++ b/gck/gck-call.c
@@ -385,16 +385,18 @@ _gck_call_sync (gpointer object, gpointer perform, gpointer complete,
GckModule *module = NULL;
CK_RV rv;
- g_assert (G_IS_OBJECT (object));
+ g_assert (!object || 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));
+ if (object) {
+ 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);
+ /* 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);
@@ -403,7 +405,8 @@ _gck_call_sync (gpointer object, gpointer perform, gpointer complete,
} while (!complete_call (complete, args, rv));
- g_object_unref (module);
+ if (module)
+ g_object_unref (module);
if (rv == CKR_OK)
return TRUE;
@@ -488,7 +491,6 @@ 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));
diff --git a/gck/gck-enumerator.c b/gck/gck-enumerator.c
new file mode 100644
index 00000000..0a57f90c
--- /dev/null
+++ b/gck/gck-enumerator.c
@@ -0,0 +1,689 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-enumerator.c - the GObject PKCS#11 wrapper library
+
+ Copyright (C) 2010, 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-enumerator
+ * @title: GckEnumerator
+ * @short_description: Enumerates through PKCS#11 objects.
+ *
+ * Xxxxxx
+ */
+
+/**
+ * GckEnumerator:
+ *
+ * Xxxxxx
+ */
+
+typedef struct _GckEnumeratorState GckEnumeratorState;
+
+typedef gpointer (*GckEnumeratorFunc) (GckEnumeratorState *args, gboolean forward);
+
+struct _GckEnumeratorState {
+ /* For the current call */
+ gint want_objects;
+ gboolean want_password;
+
+ /* The state we're currently in */
+ GckEnumeratorFunc handler;
+
+ /* Input to enumerator */
+ GList *modules;
+ GckTokenInfo *match_token;
+ GckAttributes *match_attrs;
+ CK_FLAGS session_flags;
+ gchar *password;
+
+ /* state_module */
+ GckModule *module;
+
+ /* state_slots */
+ GList *slots;
+
+ /* state_slot */
+ GckSlot *slot;
+ GckTokenInfo *token_info;
+
+ /* state_session */
+ GckSession *session;
+
+ /* state_results */
+ GArray *objects;
+
+ /* Output from enumerator */
+ GList *results;
+};
+
+struct _GckEnumeratorPrivate {
+ /* Data here is set atomically */
+ gpointer state;
+ gint mode;
+};
+
+G_DEFINE_TYPE (GckEnumerator, gck_enumerator, G_TYPE_OBJECT);
+
+static gpointer state_start (GckEnumeratorState *args, gboolean forward);
+static gpointer state_module (GckEnumeratorState *args, gboolean forward);
+static gpointer state_slots (GckEnumeratorState *args, gboolean forward);
+static gpointer state_slot (GckEnumeratorState *args, gboolean forward);
+static gpointer state_session (GckEnumeratorState *args, gboolean forward);
+static gpointer state_authenticated (GckEnumeratorState *args, gboolean forward);
+static gpointer state_results (GckEnumeratorState *args, gboolean forward);
+
+/* ----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+static gpointer
+rewind_state (GckEnumeratorState *args, GckEnumeratorFunc handler)
+{
+ g_assert (args);
+ g_assert (handler);
+ g_assert (args->handler);
+
+ while (handler != args->handler) {
+ args->handler = (args->handler) (args, FALSE);
+ g_assert (args->handler);
+ }
+
+ return handler;
+}
+
+static void
+cleanup_state (GckEnumeratorState *args)
+{
+ g_assert (args);
+
+ /* Have each state cleanup */
+ rewind_state (args, state_start);
+
+ /* state_module */
+ g_assert (!args->module);
+
+ /* state_slots */
+ g_assert (!args->slots);
+
+ /* state_slot */
+ g_assert (!args->slot);
+ g_assert (!args->token_info);
+
+ /* state_session */
+ g_assert (!args->session);
+
+ /* state_results */
+ if (args->objects)
+ g_array_free (args->objects, TRUE);
+ args->objects = NULL;
+
+ /* Other cleanup */
+ gck_list_unref_free (args->results);
+ args->results = NULL;
+
+ gck_list_unref_free (args->modules);
+ args->modules = NULL;
+
+ /* TODO: Can we use secure memory here? */
+ if (args->password) {
+ g_free (args->password);
+ args->password = NULL;
+ }
+
+ gck_token_info_free (args->match_token);
+ args->match_token = NULL;
+
+ if (args->match_attrs) {
+ _gck_attributes_unlock (args->match_attrs);
+ gck_attributes_unref (args->match_attrs);
+ }
+}
+
+static gpointer
+state_start (GckEnumeratorState *args, gboolean forward)
+{
+ g_assert (args->module == NULL);
+
+ if (forward) {
+
+ /* There is no no more modules? */
+ if (!args->modules)
+ return NULL;
+
+ /* Pop off the current module */
+ args->module = args->modules->data;
+ g_assert (GCK_IS_MODULE (args->module));
+ args->modules = g_list_delete_link (args->modules, args->modules);
+ return state_module;
+ }
+
+ /* Should never be asked to go backward from start state */
+ g_assert_not_reached ();
+}
+
+static gpointer
+state_module (GckEnumeratorState *args, gboolean forward)
+{
+ g_assert (args->module);
+ g_assert (!args->slots);
+
+ /* module to slots state */
+ if (forward) {
+
+ args->slots = gck_module_get_slots (args->module, TRUE);
+ return state_slots;
+
+ /* module to start state */
+ } else {
+ g_object_unref (args->module);
+ args->module = NULL;
+ return state_start;
+ }
+}
+
+static gpointer
+state_slots (GckEnumeratorState *args, gboolean forward)
+{
+ GckSlot *slot;
+ GckTokenInfo *token_info;
+
+ g_assert (args->slot == NULL);
+
+ /* slots to slot state */
+ if (forward) {
+
+ /* If there are no more slots go back to start state */
+ if (!args->slots)
+ return rewind_state (args, state_start);
+
+ /* Pop the next slot off the stack */
+ slot = args->slots->data;
+ args->slots = g_list_delete_link (args->slots, args->slots);
+
+ token_info = gck_slot_get_token_info (slot);
+ if (!token_info) {
+ g_message ("couldn't get token info while enumerating");
+ g_object_unref (slot);
+ return rewind_state (args, state_start);
+ }
+
+ /* Are we trying to match the slot? */
+ if (args->match_token) {
+
+ /* No match? Go to next slot */
+ if (!_gck_token_info_match (args->match_token, token_info)) {
+ g_object_unref (slot);
+ gck_token_info_free (token_info);
+ return state_slots;
+ }
+ }
+
+ /* We have a slot */
+ args->slot = slot;
+ args->token_info = token_info;
+ return state_slot;
+
+ /* slots state to module state */
+ } else {
+
+ gck_list_unref_free (args->slots);
+ return state_module;
+ }
+}
+
+static gpointer
+state_slot (GckEnumeratorState *args, gboolean forward)
+{
+ CK_FUNCTION_LIST_PTR funcs;
+ CK_SESSION_HANDLE session;
+ CK_RV rv;
+
+ g_assert (args->slot);
+ g_assert (args->module);
+ g_assert (args->session == NULL);
+
+ /* slot to session state */
+ if (forward) {
+ funcs = gck_module_get_functions (args->module);
+ g_return_val_if_fail (funcs, NULL);
+
+ rv = (funcs->C_OpenSession) (gck_slot_get_handle (args->slot),
+ args->session_flags, NULL, NULL, &session);
+ if (rv != CKR_OK) {
+ g_message ("couldn't open session on module while enumerating objects: %s",
+ gck_message_from_rv (rv));
+ return rewind_state (args, state_slots);
+ }
+
+ args->session = gck_session_from_handle (args->slot, session);
+ return state_session;
+
+ /* slot to slots state */
+ } else {
+ g_object_unref (args->slot);
+ args->slot = NULL;
+
+ gck_token_info_free (args->token_info);
+ args->token_info = NULL;
+
+ return state_slots;
+ }
+}
+
+static gpointer
+state_session (GckEnumeratorState *args, gboolean forward)
+{
+ CK_FUNCTION_LIST_PTR funcs;
+ GckSessionInfo *sinfo;
+ CK_ULONG n_pin;
+ CK_RV rv;
+
+ g_assert (args->session);
+ g_assert (args->module);
+ g_assert (!args->want_password);
+ g_assert (args->token_info);
+
+ /* session to authenticated state */
+ if (forward) {
+
+ /* No login necessary */
+ if ((args->token_info->flags & CKF_LOGIN_REQUIRED) == 0)
+ return state_authenticated;
+
+ /* Next check if session is logged in */
+ sinfo = gck_session_get_info (args->session);
+ if (sinfo == NULL) {
+ g_message ("couldn't get session info when enumerating");
+ return rewind_state (args, state_slots);
+ }
+
+ /* Already logged in? */
+ if (sinfo->state == CKS_RW_USER_FUNCTIONS ||
+ sinfo->state == CKS_RO_USER_FUNCTIONS ||
+ sinfo->state == CKS_RW_SO_FUNCTIONS) {
+ gck_session_info_free (sinfo);
+ return state_authenticated;
+ }
+
+ gck_session_info_free (sinfo);
+
+ funcs = gck_module_get_functions (args->module);
+ g_return_val_if_fail (funcs, NULL);
+
+ /* Try to log in */
+ n_pin = args->password ? strlen (args->password) : 0;
+ rv = (funcs->C_Login) (gck_session_get_handle (args->session), CKU_USER,
+ (CK_BYTE_PTR)args->password, n_pin);
+
+ /* Authentication failed can we ask for a password? */
+ if (rv == CKR_PIN_INCORRECT && gck_module_get_options (args->module) & GCK_AUTHENTICATE_TOKENS) {
+ args->want_password = TRUE;
+ return NULL;
+
+ /* Any other failure continue without authentication */
+ } else if (rv != CKR_OK) {
+ g_message ("couldn't authenticate when enumerating: %s", gck_message_from_rv (rv));
+ }
+
+ return state_authenticated;
+
+ /* Session to slot state */
+ } else {
+ g_object_unref (args->session);
+ args->session = NULL;
+ return state_slot;
+ }
+}
+
+static gpointer
+state_authenticated (GckEnumeratorState *args, gboolean forward)
+{
+ CK_FUNCTION_LIST_PTR funcs;
+ CK_OBJECT_HANDLE objects[128];
+ CK_SESSION_HANDLE session;
+ CK_ATTRIBUTE_PTR attrs;
+ CK_ULONG n_attrs, count;
+ CK_RV rv;
+
+ /* Just go back, no logout */
+ if (!forward)
+ return state_session;
+
+ /* This is where we do the actual searching */
+
+ g_assert (args->session);
+ g_assert (!args->want_password);
+ g_assert (args->want_objects);
+
+ if (args->match_attrs) {
+ attrs = _gck_attributes_commit_out (args->match_attrs, &n_attrs);
+ } else {
+ attrs = NULL;
+ n_attrs = 0;
+ }
+
+ funcs = gck_module_get_functions (args->module);
+ g_return_val_if_fail (funcs, NULL);
+
+ session = gck_session_get_handle (args->session);
+ g_return_val_if_fail (session, NULL);
+
+ /* Get all the objects */
+ rv = (funcs->C_FindObjectsInit) (session, attrs, n_attrs);
+
+ if (rv == CKR_OK) {
+ while (rv == CKR_OK) {
+ rv = (funcs->C_FindObjects) (session, objects, G_N_ELEMENTS (objects), &count);
+
+ if (count == 0)
+ break;
+
+ if (!args->objects)
+ args->objects = g_array_new (FALSE, TRUE, sizeof (CK_OBJECT_HANDLE));
+ g_array_append_vals (args->objects, objects, count);
+ }
+
+ (funcs->C_FindObjectsFinal) (session);
+ }
+
+ return state_results;
+}
+
+static gpointer
+state_results (GckEnumeratorState *args, gboolean forward)
+{
+ CK_OBJECT_HANDLE handle;
+ GckObject *object;
+ guint have;
+
+ g_assert (args->session);
+
+ /* No cleanup, just unwind */
+ if (!forward)
+ return state_authenticated;
+
+ /* Create result objects from what we have */
+ have = g_list_length (args->results);
+
+ while (have < args->want_objects) {
+
+ /* Need more objects! */
+ if (!args->objects || args->objects->len == 0)
+ return rewind_state (args, state_slots);
+
+ handle = g_array_index (args->objects, CK_OBJECT_HANDLE, 0);
+ g_array_remove_index_fast (args->objects, 0);
+
+ object = gck_object_from_handle (args->session, handle);
+ args->results = g_list_append (args->results, object);
+ ++have;
+ }
+
+ /* We got all the results we wanted */
+ return NULL;
+}
+
+/* ----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static void
+gck_enumerator_init (GckEnumerator *self)
+{
+ GckEnumeratorState *args;
+
+ self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_ENUMERATOR, GckEnumeratorPrivate);
+ args = g_new0 (GckEnumeratorState, 1);
+ args->handler = state_start;
+ g_atomic_pointer_set (&self->pv->state, args);
+}
+
+static void
+gck_enumerator_finalize (GObject *obj)
+{
+ GckEnumerator *self = GCK_ENUMERATOR (obj);
+ GckEnumeratorState *state = g_atomic_pointer_get (&self->pv->state);
+
+ if (!g_atomic_pointer_compare_and_exchange (&self->pv->state, state, NULL))
+ g_assert_not_reached ();
+
+ g_assert (state);
+ cleanup_state (state);
+ g_free (state);
+
+ G_OBJECT_CLASS (gck_enumerator_parent_class)->finalize (obj);
+}
+
+
+static void
+gck_enumerator_class_init (GckEnumeratorClass *klass)
+{
+ GObjectClass *gobject_class = (GObjectClass*)klass;
+ gck_enumerator_parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->finalize = gck_enumerator_finalize;
+ g_type_class_add_private (klass, sizeof (GckEnumeratorPrivate));
+}
+
+/* ----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+GckEnumerator*
+_gck_enumerator_new (GList *modules, guint session_flags, GckTokenInfo *match_token, GckAttributes *match_attrs)
+{
+ GckEnumerator *self;
+ GckEnumeratorState *state;
+
+ self = g_object_new (GCK_TYPE_ENUMERATOR, NULL);
+ state = g_atomic_pointer_get (&self->pv->state);
+
+ state->session_flags = session_flags | CKF_SERIAL_SESSION;
+ state->modules = gck_list_ref_copy (modules);
+
+ if (match_attrs) {
+ state->match_attrs = gck_attributes_ref (match_attrs);
+ _gck_attributes_lock (state->match_attrs);
+ }
+ state->match_token = match_token;
+
+ return self;
+}
+
+typedef struct _EnumerateNext {
+ GckArguments base;
+ GckEnumeratorState *state;
+} EnumerateNext;
+
+static CK_RV
+perform_enumerate_next (EnumerateNext *args)
+{
+ GckEnumeratorFunc handler;
+ GckEnumeratorState *state;
+
+ g_assert (args->state);
+ state = args->state;
+
+ g_assert (!state->want_password);
+ g_assert (state->handler);
+
+ for (;;) {
+ handler = (state->handler) (state, TRUE);
+ if (!handler)
+ break;
+ state->handler = handler;
+ }
+
+ /* TODO: In some modes, errors */
+ return CKR_OK;
+}
+
+static gboolean
+complete_enumerate_next (EnumerateNext *args, CK_RV result)
+{
+ GckEnumeratorState *state;
+ gboolean ret = TRUE;
+
+ g_assert (args->state);
+ state = args->state;
+
+ if (state->want_password) {
+ g_assert (state->module);
+ g_assert (state->slot);
+
+ /* TODO: Should we be using secure memory here? */
+ g_free (state->password);
+ state->password = NULL;
+
+ ret = _gck_module_fire_authenticate_slot (state->module, state->slot, NULL, &state->password);
+
+ /* If authenticate returns TRUE then call is not complete */
+ ret = !ret;
+ }
+
+ return ret;
+}
+
+static void
+free_enumerate_next (EnumerateNext *args)
+{
+ /* Should have been assigned back to enumerator */
+ g_assert (!args->state);
+
+ g_free (args);
+}
+
+GckObject*
+gck_enumerator_next (GckEnumerator *self, GCancellable *cancellable, GError **error)
+{
+ GckObject *result = NULL;
+ GList *results;
+
+ g_return_val_if_fail (GCK_IS_ENUMERATOR (self), NULL);
+ g_return_val_if_fail (!error || !*error, NULL);
+
+ results = gck_enumerator_next_n (self, 1, cancellable, error);
+ if (results) {
+ g_assert (GCK_IS_OBJECT (results->data));
+ result = g_object_ref (results->data);
+ gck_list_unref_free (results);
+ }
+
+ return result;
+}
+
+GList*
+gck_enumerator_next_n (GckEnumerator *self, gint max_objects, GCancellable *cancellable,
+ GError **error)
+{
+ EnumerateNext args = { GCK_ARGUMENTS_INIT, NULL, };
+ GList *results = NULL;
+
+ g_return_val_if_fail (GCK_IS_ENUMERATOR (self), NULL);
+ g_return_val_if_fail (max_objects == -1 || max_objects > 0, NULL);
+ g_return_val_if_fail (!error || !*error, NULL);
+
+ /* Remove the state and own it ourselves */
+ args.state = g_atomic_pointer_get (&self->pv->state);
+ if (!args.state || !g_atomic_pointer_compare_and_exchange (&self->pv->state, args.state, NULL)) {
+ g_warning ("this enumerator is already running a next operation");
+ return NULL;
+ }
+
+ args.state->want_objects = max_objects <= 0 ? G_MAXINT : max_objects;
+
+ /* Run the operation and steal away the results */
+ if (_gck_call_sync (NULL, perform_enumerate_next, complete_enumerate_next, &args, cancellable, error)) {
+ results = args.state->results;
+ args.state->results = NULL;
+ }
+
+ args.state->want_objects = 0;
+
+ /* Put the state back */
+ if (!g_atomic_pointer_compare_and_exchange (&self->pv->state, NULL, args.state))
+ g_assert_not_reached ();
+
+ return results;
+}
+
+void
+gck_enumerator_next_async (GckEnumerator *self, gint max_objects, GCancellable *cancellable,
+ GAsyncReadyCallback callback, gpointer user_data)
+{
+ GckEnumeratorState *state;
+ EnumerateNext *args;
+
+ g_return_if_fail (GCK_IS_ENUMERATOR (self));
+ g_return_if_fail (max_objects == -1 || max_objects > 0);
+
+ g_object_ref (self);
+
+ /* Remove the state and own it ourselves */
+ state = g_atomic_pointer_get (&self->pv->state);
+ if (!state || !g_atomic_pointer_compare_and_exchange (&self->pv->state, state, NULL)) {
+ g_warning ("this enumerator is already running a next operation");
+ return;
+ }
+
+ state->want_objects = max_objects <= 0 ? G_MAXINT : max_objects;
+ args = _gck_call_async_prep (NULL, self, perform_enumerate_next, complete_enumerate_next,
+ sizeof (*args), free_enumerate_next);
+
+ args->state = state;
+ _gck_call_async_ready_go (args, cancellable, callback, user_data);
+ g_object_unref (self);
+}
+
+GList*
+gck_enumerator_next_finish (GckEnumerator *self, GAsyncResult *result, GError **error)
+{
+ EnumerateNext *args;
+ GckEnumeratorState *state;
+ GList *results = NULL;
+
+ g_object_ref (self);
+
+ args = _gck_call_arguments (result, EnumerateNext);
+ state = args->state;
+ args->state = NULL;
+ state->want_objects = 0;
+
+ if (_gck_call_basic_finish (result, error)) {
+ results = state->results;
+ state->results = NULL;
+ }
+
+ /* Put the state back */
+ if (!g_atomic_pointer_compare_and_exchange (&self->pv->state, NULL, state))
+ g_assert_not_reached ();
+
+ g_object_unref (self);
+
+ return results;
+}
diff --git a/gck/gck-misc.c b/gck/gck-misc.c
index ae8c66d5..6fc691be 100644
--- a/gck/gck-misc.c
+++ b/gck/gck-misc.c
@@ -26,7 +26,7 @@
#include "gck.h"
#include "gck-private.h"
-#include <glib/gi18n.h>
+#include <glib/gi18n-lib.h>
/**
* SECTION:gck-error
diff --git a/gck/gck-modules.c b/gck/gck-modules.c
index c27062da..38ff1fc6 100644
--- a/gck/gck-modules.c
+++ b/gck/gck-modules.c
@@ -1,7 +1,7 @@
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* gck-modules.c - the GObject PKCS#11 wrapper library
- Copyright (C) 2008, Stefan Walter
+ Copyright (C) 2010, 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
@@ -18,7 +18,7 @@
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
- Author: Stef Walter <nielsen@memberwebs.com>
+ Author: Stef Walter <stef@memberwebs.com>
*/
#include "config.h"
@@ -120,101 +120,101 @@ gck_modules_get_slots (GList *modules, gboolean token_present)
* @self: The module to enumerate objects.
* @attrs: Attributes that the objects must have, or empty for all objects.
* @session_flags: PKCS#11 flags for opening a session.
- * @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.
+ * Setup an enumerator for listing matching objects on the modules.
*
- * 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.
+ * This call will not block but will return an enumerator immediately.
*
- * You can access the session in which the object was found, by using the
- * gck_object_get_session() function on the resulting objects.
+ * XXX
*
- * The function can return FALSE to stop the enumeration.
- *
- * Return value: If FALSE then an error prevented all matching objects from being enumerated.
+ * Return value: A new enumerator
**/
-gboolean
-gck_modules_enumerate_objects (GList *modules, GckAttributes *attrs, guint session_flags,
- GCancellable *cancellable, GckObjectForeachFunc func,
- gpointer user_data, GError **err)
+GckEnumerator*
+gck_modules_enumerate_objects (GList *modules, GckAttributes *attrs, guint session_flags)
{
- gboolean stop = FALSE;
- gboolean ret = TRUE;
- GList *objects, *o;
- GList *slots, *l, *m;
- GError *error = NULL;
- GckSession *session;
+ return _gck_enumerator_new (modules, session_flags, NULL, attrs);
+}
- g_return_val_if_fail (attrs, FALSE);
- g_return_val_if_fail (func, FALSE);
+GckSlot*
+gck_modules_token_for_uri (GList *modules, const gchar *uri, GError **error)
+{
+ GckTokenInfo *match, *token;
+ GckSlot *result = NULL;
+ GList *slots;
+ GList *m, *s;
- gck_attributes_ref (attrs);
+ if (!gck_uri_parse (uri, &match, NULL, error))
+ return NULL;
- for (m = modules; ret && !stop && m; m = g_list_next (m)) {
+ for (m = modules; result == NULL && m != NULL; m = g_list_next (m)) {
slots = gck_module_get_slots (m->data, TRUE);
-
- for (l = slots; ret && !stop && l; l = g_list_next (l)) {
-
- session = gck_slot_open_session (l->data, session_flags, &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 (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)) {
- if (!(func)(o->data, user_data)) {
- stop = TRUE;
- break;
- }
- }
-
- g_object_unref (session);
- gck_list_unref_free (objects);
+ for (s = slots; result == NULL && s != NULL; s = g_list_next (s)) {
+ token = gck_slot_get_token_info (s->data);
+ if (token && _gck_token_info_match (match, token))
+ result = g_object_ref (s->data);
+ gck_token_info_free (token);
}
-
gck_list_unref_free (slots);
}
- gck_attributes_unref (attrs);
+ gck_token_info_free (match);
+ return result;
+}
- return ret;
+GckObject*
+gck_modules_object_for_uri (GList *modules, const gchar *uri, guint session_flags,
+ GError **error)
+{
+ GckEnumerator *en;
+ GckObject *result;
+
+ g_return_val_if_fail (uri, NULL);
+ g_return_val_if_fail (!error || !*error, NULL);
+
+ en = gck_modules_enumerate_uri (modules, uri, session_flags, error);
+ if (en == NULL)
+ return NULL;
+
+ result = gck_enumerator_next (en, NULL, error);
+ g_object_unref (en);
+
+ return result;
}
-/**
- * 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.
- */
+GList*
+gck_modules_objects_for_uri (GList *modules, const gchar *uri, guint session_flags,
+ GError **error)
+{
+ GckEnumerator *en;
+ GList *results;
+
+ g_return_val_if_fail (uri, NULL);
+ g_return_val_if_fail (!error || !*error, NULL);
+
+ en = gck_modules_enumerate_uri (modules, uri, session_flags, error);
+ if (en == NULL)
+ return NULL;
+
+ results = gck_enumerator_next_n (en, -1, NULL, error);
+ g_object_unref (en);
+
+ return results;
+}
+
+GckEnumerator*
+gck_modules_enumerate_uri (GList *modules, const gchar *uri, guint session_flags,
+ GError **error)
+{
+ GckTokenInfo *token;
+ GckAttributes *attrs;
+ GckEnumerator *en;
+
+ if (!gck_uri_parse (uri, &token, &attrs, error))
+ return NULL;
+
+ /* Takes ownership of token info */
+ en = _gck_enumerator_new (modules, session_flags, token, attrs);
+ gck_attributes_unref (attrs);
+
+ return en;
+}
diff --git a/gck/gck-private.h b/gck/gck-private.h
index 5a337cf1..4a4aa9d5 100644
--- a/gck/gck-private.h
+++ b/gck/gck-private.h
@@ -72,12 +72,21 @@ gboolean _gck_module_fire_authenticate_object (GckModule *module,
gchar *label,
gchar **password);
+/* -----------------------------------------------------------------------------
+ * ENUMERATOR
+ */
+
+GckEnumerator* _gck_enumerator_new (GList *modules,
+ guint session_flags,
+ GckTokenInfo *match_token,
+ GckAttributes *match_attrs);
+
/* ----------------------------------------------------------------------------
* SLOT
*/
-GckObject* _gck_slot_object_from_handle (GckSlot *slot,
- CK_OBJECT_HANDLE handle);
+gboolean _gck_token_info_match (GckTokenInfo *match,
+ GckTokenInfo *info);
/* ----------------------------------------------------------------------------
* CALL
diff --git a/gck/gck-slot.c b/gck/gck-slot.c
index 2b0aec93..48c3f17d 100644
--- a/gck/gck-slot.c
+++ b/gck/gck-slot.c
@@ -300,6 +300,33 @@ gck_token_info_free (GckTokenInfo *token_info)
g_free (token_info);
}
+static gboolean
+match_token_string (const gchar *match, const gchar *string)
+{
+ /* NULL matches anything */
+ if (match == NULL)
+ return TRUE;
+
+ if (string == NULL)
+ return FALSE;
+
+ return g_str_equal (match, string);
+}
+
+gboolean
+_gck_token_info_match (GckTokenInfo *match, GckTokenInfo *info)
+{
+ /* Matches two GckTokenInfo for use in PKCS#11 URI's */
+
+ g_return_val_if_fail (match, FALSE);
+ g_return_val_if_fail (info, FALSE);
+
+ return (match_token_string (match->label, info->label) &&
+ match_token_string (match->manufacturer_id, info->manufacturer_id) &&
+ match_token_string (match->model, info->model) &&
+ match_token_string (match->serial_number, info->serial_number));
+}
+
/**
* GckMechanismInfo:
* @min_key_size: The minimum key size that can be used with this mechanism.
diff --git a/gck/gck-uri.c b/gck/gck-uri.c
new file mode 100644
index 00000000..be6e6a1d
--- /dev/null
+++ b/gck/gck-uri.c
@@ -0,0 +1,350 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-uri.c - the GObject PKCS#11 wrapper library
+
+ Copyright (C) 2010, 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 <stef@memberwebs.com>
+*/
+
+#include "config.h"
+
+#include "gck.h"
+#include "gck-private.h"
+#include "gck-marshal.h"
+
+#include <glib/gi18n-lib.h>
+
+#include <string.h>
+
+#include "egg/egg-hex.h"
+
+/**
+ * SECTION:gck-modules
+ * @title: GckModule lists
+ * @short_description: Dealing with lists of PKCS#11 modules.
+ *
+ * Xxxxx
+ */
+
+#define URI_PREFIX "pkcs11:"
+#define N_URI_PREFIX 7
+
+GQuark
+gck_uri_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-uri-error");
+ g_once_init_leave (&quark_inited, 1);
+ }
+
+ return domain;
+}
+
+static gint
+parse_string_attribute (const gchar *name, const gchar *start, const gchar *end,
+ GckAttributes *attrs, GError **error)
+{
+ gchar *value;
+ gint res = 0;
+
+ g_assert (name);
+ g_assert (start);
+ g_assert (end);
+
+ if (!g_str_equal (name, "object") && !g_str_equal (name, "objecttype"))
+ return 0;
+
+ value = g_uri_unescape_segment (start, end, "");
+ if (value == NULL) {
+ g_set_error (error, GCK_URI_ERROR, GCK_URI_BAD_ENCODING,
+ _("The URI has invalid syntax. The '%s' field encoding is invalid."), name);
+ return -1;
+ }
+
+ if (g_str_equal (name, "object")) {
+ gck_attributes_add_string (attrs, CKA_LABEL, value);
+ res = 1;
+
+ } else if (g_str_equal (name, "objecttype")) {
+
+ res = 1;
+ if (g_str_equal (value, "cert"))
+ gck_attributes_add_ulong (attrs, CKA_CLASS, CKO_CERTIFICATE);
+ else if (g_str_equal (value, "public"))
+ gck_attributes_add_ulong (attrs, CKA_CLASS, CKO_PUBLIC_KEY);
+ else if (g_str_equal (value, "private"))
+ gck_attributes_add_ulong (attrs, CKA_CLASS, CKO_PRIVATE_KEY);
+ else if (g_str_equal (value, "secretkey"))
+ gck_attributes_add_ulong (attrs, CKA_CLASS, CKO_SECRET_KEY);
+ else if (g_str_equal (value, "data"))
+ gck_attributes_add_ulong (attrs, CKA_CLASS, CKO_DATA);
+ else {
+ g_message ("ignoring unsupported value for '%s'", value);
+ res = 0;
+ }
+ } else {
+ g_assert_not_reached ();
+ }
+
+ g_free (value);
+ return res;
+}
+
+static gint
+parse_binary_attribute (const gchar *name, const gchar *start, const gchar *end,
+ GckAttributes *attrs, GError **error)
+{
+ guchar *data;
+ gsize n_data;
+
+ g_assert (name);
+ g_assert (start);
+ g_assert (end);
+ g_assert (attrs);
+
+ if (!g_str_equal (name, "id"))
+ return 0;
+
+ /*
+ * TODO: This requires some work. We're not yet sure about the actual
+ * encoding that's supported here.
+ */
+
+ g_assert (end >= start);
+ data = egg_hex_decode_full (start, end - start, ':', 1, &n_data);
+ if (data == NULL) {
+ g_set_error (error, GCK_URI_ERROR, GCK_URI_BAD_ENCODING,
+ _("The URI has invalid syntax. The '%s' field encoding is invalid."), name);
+ return -1;
+ }
+
+ gck_attributes_add_data (attrs, CKA_ID, data, n_data);
+ g_free (data);
+ return 1;
+}
+
+static gint
+parse_token_attribute (const gchar *name, const gchar *start, const gchar *end,
+ GckTokenInfo *token, GError **error)
+{
+ gchar **value;
+ gchar *string;
+
+ g_assert (name);
+ g_assert (start);
+ g_assert (end);
+ g_assert (token);
+
+ if (g_str_equal (name, "model"))
+ value = &(token->model);
+ else if (g_str_equal (name, "manufacturer"))
+ value = &(token->manufacturer_id);
+ else if (g_str_equal (name, "serial"))
+ value = &(token->serial_number);
+ else if (g_str_equal (name, "token"))
+ value = &(token->label);
+ else
+ return 0;
+
+ string = g_uri_unescape_segment (start, end, "");
+ if (string == NULL) {
+ g_set_error (error, GCK_URI_ERROR, GCK_URI_BAD_ENCODING,
+ _("The URI has invalid syntax. The '%s' field encoding is invalid."), name);
+ return -1;
+ }
+
+ g_free (*value);
+ *value = string;
+
+ return 1;
+}
+
+gboolean
+gck_uri_parse (const gchar *uri, GckTokenInfo **token, GckAttributes **attrs, GError **error)
+{
+ GckAttributes *rattrs = NULL;
+ GckTokenInfo *rtoken = NULL;
+ const gchar *spos, *epos;
+ gchar *key = NULL;
+ gboolean ret = FALSE;
+ gint res;
+
+ g_return_val_if_fail (uri, FALSE);
+ g_return_val_if_fail (!error || !*error, FALSE);
+
+ if (!g_str_has_prefix (uri, URI_PREFIX)) {
+ g_set_error_literal (error, GCK_URI_ERROR, GCK_URI_BAD_PREFIX,
+ _("The URI has does not have the 'pkcs11' scheme."));
+ goto cleanup;
+ }
+
+ uri += N_URI_PREFIX;
+ rattrs = gck_attributes_new ();
+ rtoken = g_new0 (GckTokenInfo, 1);
+
+ for (;;) {
+ spos = strchr (uri, ';');
+ if (spos == NULL) {
+ spos = uri + strlen (uri);
+ g_assert (*spos == '\0');
+ if (spos == uri)
+ break;
+ }
+
+ epos = strchr (uri, '=');
+ if (epos == NULL || spos == uri || epos == uri || epos >= spos) {
+ g_set_error_literal (error, GCK_URI_ERROR, GCK_URI_BAD_SYNTAX,
+ "The URI has invalid syntax. It must consist of key=value pairs.");
+ goto cleanup;
+ }
+
+ g_free (key);
+ key = g_strndup (uri, epos - uri);
+ epos++;
+
+ res = parse_string_attribute (key, epos, spos, rattrs, error);
+ if (res == 0)
+ res = parse_binary_attribute (key, epos, spos, rattrs, error);
+ if (res == 0)
+ res = parse_token_attribute (key, epos, spos, rtoken, error);
+ if (res < 0)
+ goto cleanup;
+ if (res == 0)
+ g_message ("Ignoring unsupported field '%s'", key);
+
+ if (*spos == '\0')
+ break;
+ uri = spos + 1;
+ }
+
+ ret = TRUE;
+
+cleanup:
+ if (ret && token) {
+ *token = rtoken;
+ rtoken = NULL;
+ }
+ if (ret && attrs) {
+ *attrs = rattrs;
+ rattrs = NULL;
+ }
+
+ gck_token_info_free (rtoken);
+ if (rattrs)
+ gck_attributes_unref (rattrs);
+
+ g_free (key);
+ return ret;
+}
+
+static void
+build_string_attribute (const gchar *name, const gchar *value,
+ GString *result, gboolean *first)
+{
+ gchar *segment;
+
+ g_assert (first);
+ g_assert (result);
+ g_assert (name);
+
+ if (!value)
+ return;
+ if (!value[0])
+ return;
+
+ segment = g_uri_escape_string (value, "", FALSE);
+ if (!*first)
+ g_string_append_c (result, ';');
+ *first = FALSE;
+
+ g_string_append (result, name);
+ g_string_append_c (result, '=');
+ g_string_append (result, segment);
+ g_free (segment);
+}
+
+static void
+build_binary_attribute (const gchar *name, gconstpointer data, gsize n_data,
+ GString *result, gboolean *first)
+{
+ gchar *segment;
+
+ g_assert (first);
+ g_assert (result);
+ g_assert (name);
+
+ if (!n_data)
+ return;
+ g_assert (data);
+
+ segment = egg_hex_encode_full (data, n_data, FALSE, ':', 1);
+ if (!*first)
+ g_string_append_c (result, ';');
+ *first = FALSE;
+
+ g_string_append (result, name);
+ g_string_append_c (result, '=');
+ g_string_append (result, segment);
+ g_free (segment);
+}
+
+gchar*
+gck_uri_build (GckTokenInfo *token, GckAttributes *attrs)
+{
+ GckAttribute *attr;
+ GString *result;
+ gchar *value;
+ gulong klass;
+ gboolean first = TRUE;
+
+ result = g_string_new (URI_PREFIX);
+
+ if (token) {
+ build_string_attribute ("model", token->model, result, &first);
+ build_string_attribute ("manufacturer", token->manufacturer_id, result, &first);
+ build_string_attribute ("serial", token->serial_number, result, &first);
+ build_string_attribute ("token", token->label, result, &first);
+ }
+
+ if (attrs) {
+ if (gck_attributes_find_string (attrs, CKA_LABEL, &value)) {
+ build_string_attribute ("object", value, result, &first);
+ g_free (value);
+ }
+ if (gck_attributes_find_ulong (attrs, CKA_CLASS, &klass)) {
+ if (klass == CKO_CERTIFICATE)
+ build_string_attribute ("objecttype", "cert", result, &first);
+ else if (klass == CKO_PUBLIC_KEY)
+ build_string_attribute ("objecttype", "public", result, &first);
+ else if (klass == CKO_PRIVATE_KEY)
+ build_string_attribute ("objecttype", "private", result, &first);
+ else if (klass == CKO_SECRET_KEY)
+ build_string_attribute ("objecttype", "secretkey", result, &first);
+ else if (klass == CKO_DATA)
+ build_string_attribute ("objecttype", "data", result, &first);
+ }
+ attr = gck_attributes_find (attrs, CKA_ID);
+ if (attr != NULL)
+ build_binary_attribute ("id", attr->value, attr->length, result, &first);
+ }
+
+ return g_string_free (result, FALSE);
+}
diff --git a/gck/gck.h b/gck/gck.h
index d8040712..a24cad79 100644
--- a/gck/gck.h
+++ b/gck/gck.h
@@ -226,8 +226,7 @@ typedef struct _GckSlot GckSlot;
typedef struct _GckModule GckModule;
typedef struct _GckSession GckSession;
typedef struct _GckObject GckObject;
-
-typedef gboolean (*GckObjectForeachFunc) (GckObject *object, gpointer user_data);
+typedef struct _GckEnumerator GckEnumerator;
/* -------------------------------------------------------------------------
* MODULE
@@ -306,31 +305,75 @@ GList* gck_modules_initialize_registered (guint options);
GList* gck_modules_get_slots (GList *modules,
gboolean token_present);
-gboolean gck_modules_enumerate_objects (GList *modules,
+GckEnumerator* gck_modules_enumerate_objects (GList *modules,
GckAttributes *attrs,
+ guint session_flags);
+
+GckSlot* gck_modules_token_for_uri (GList *modules,
+ const gchar *uri,
+ GError **error);
+
+GckObject* gck_modules_object_for_uri (GList *modules,
+ const gchar *uri,
guint session_flags,
- GCancellable *cancellable,
- GckObjectForeachFunc func,
- gpointer user_data,
GError **error);
-#ifdef UNIMPLEMENTED
-void gck_modules_enumerate_objects_async (GList *modules,
- GckAttributes *attrs,
+GList* gck_modules_objects_for_uri (GList *modules,
+ const gchar *uri,
guint session_flags,
- GckObjectForeachFunc func,
+ GError **error);
+
+GckEnumerator* gck_modules_enumerate_uri (GList *modules,
+ const gchar *uri,
+ guint session_flags,
+ GError **error);
+
+
+/* ------------------------------------------------------------------------
+ * ENUMERATOR
+ */
+
+#define GCK_TYPE_ENUMERATOR (gck_enumerator_get_type())
+#define GCK_ENUMERATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GCK_TYPE_ENUMERATOR, GckEnumerator))
+#define GCK_ENUMERATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GCK_TYPE_ENUMERATOR, GckEnumerator))
+#define GCK_IS_ENUMERATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GCK_TYPE_ENUMERATOR))
+#define GCK_IS_ENUMERATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GCK_TYPE_ENUMERATOR))
+#define GCK_ENUMERATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GCK_TYPE_ENUMERATOR, GckEnumeratorClass))
+
+typedef struct _GckEnumeratorClass GckEnumeratorClass;
+typedef struct _GckEnumeratorPrivate GckEnumeratorPrivate;
+
+struct _GckEnumerator {
+ GObject parent;
+ GckEnumeratorPrivate *pv;
+ gpointer reserved[2];
+};
+
+struct _GckEnumeratorClass {
+ GObjectClass parent;
+ gpointer reserved[2];
+};
+
+GType gck_enumerator_get_type (void) G_GNUC_CONST;
+
+GckObject* gck_enumerator_next (GckEnumerator *self,
+ GCancellable *cancellable,
+ GError **err);
+
+GList* gck_enumerator_next_n (GckEnumerator *self,
+ gint max_objects,
+ GCancellable *cancellable,
+ GError **err);
+
+void gck_enumerator_next_async (GckEnumerator *self,
+ gint max_objects,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
-GckObject* gck_modules_enumerate_objects_next (GList *modules,
+GList* gck_enumerator_next_finish (GckEnumerator *self,
GAsyncResult *res,
- GError **error);
-
-void gck_modules_enumerate_objects_finish (GList *modules,
- GAsyncResult *res,
- GError **error);
-#endif
+ GError **err);
/* ------------------------------------------------------------------------
* SLOT
@@ -1042,6 +1085,20 @@ CK_OBJECT_HANDLE gck_object_get_handle (GckObject *self);
GckSession* gck_object_get_session (GckObject *self);
+gchar* gck_object_build_uri (GckObject *self,
+ guint options,
+ GCancellable *cancellable,
+ GError **err);
+
+void gck_object_build_uri_async (GckObject *self,
+ guint options,
+ GCancellable *cancellable,
+ GError **err);
+
+gchar* gck_object_build_uri_finish (GckObject *self,
+ GAsyncResult *result,
+ GError **err);
+
#ifdef UNIMPLEMENTED
GckObject* gck_object_copy (GckObject *self,
@@ -1198,6 +1255,28 @@ GckAttributes* gck_object_get_template_finish (GckObject *self,
GAsyncResult *result,
GError **err);
+/* ----------------------------------------------------------------------------
+ * URI
+ */
+
+enum {
+ GCK_URI_BAD_PREFIX = 1,
+ GCK_URI_BAD_ENCODING = 2,
+ GCK_URI_BAD_SYNTAX = 3
+};
+
+#define GCK_URI_ERROR (gck_uri_get_error_quark ())
+
+GQuark gck_uri_get_error_quark (void);
+
+gchar* gck_uri_build (GckTokenInfo *token,
+ GckAttributes *attrs);
+
+gboolean gck_uri_parse (const gchar *uri,
+ GckTokenInfo **token,
+ GckAttributes **attrs,
+ GError **err);
+
G_END_DECLS
#endif /* GCK_H */
diff --git a/gck/tests/Makefile.am b/gck/tests/Makefile.am
index 482f6b2e..b2a319bf 100644
--- a/gck/tests/Makefile.am
+++ b/gck/tests/Makefile.am
@@ -7,7 +7,10 @@ TESTING_FILES = \
test-gck-slot.c \
test-gck-session.c \
test-gck-object.c \
- test-gck-crypto.c
+ test-gck-crypto.c \
+ test-gck-uri.c \
+ test-gck-enumerator.c \
+ test-gck-modules.c
TESTING_FLAGS = \
-I$(top_srcdir)/gck/ \
diff --git a/gck/tests/test-gck-enumerator.c b/gck/tests/test-gck-enumerator.c
new file mode 100644
index 00000000..bcae887b
--- /dev/null
+++ b/gck/tests/test-gck-enumerator.c
@@ -0,0 +1,170 @@
+
+#include <glib.h>
+#include <string.h>
+
+#include "test-suite.h"
+#include "gck-test.h"
+#include "gck-private.h"
+
+static GList *modules = NULL;
+
+DEFINE_SETUP(enumerator)
+{
+ GckModule *module;
+ GError *err = NULL;
+
+ /* Successful load */
+ module = gck_module_initialize (".libs/libgck-test-module.so", NULL, 0, &err);
+ SUCCESS_RES (module, err);
+
+ modules = g_list_append (NULL, module);
+}
+
+DEFINE_TEARDOWN(enumerator)
+{
+ gck_list_unref_free (modules);
+ modules = NULL;
+}
+
+DEFINE_TEST(enumerator_create)
+{
+ GckEnumerator *en;
+
+ en = _gck_enumerator_new (modules, 0, NULL, NULL);
+ g_assert (GCK_IS_ENUMERATOR (en));
+ g_object_unref (en);
+}
+
+DEFINE_TEST(enumerator_next)
+{
+ GError *error = NULL;
+ GckEnumerator *en;
+ GckObject *obj;
+
+ en = _gck_enumerator_new (modules, 0, NULL, NULL);
+ g_assert (GCK_IS_ENUMERATOR (en));
+
+ obj = gck_enumerator_next (en, NULL, &error);
+ g_assert (GCK_IS_OBJECT (obj));
+
+ g_object_unref (obj);
+ g_object_unref (en);
+}
+
+DEFINE_TEST(enumerator_next_and_resume)
+{
+ GError *error = NULL;
+ GckEnumerator *en;
+ GckObject *obj, *obj2;
+
+ en = _gck_enumerator_new (modules, 0, NULL, NULL);
+ g_assert (GCK_IS_ENUMERATOR (en));
+
+ obj = gck_enumerator_next (en, NULL, &error);
+ SUCCESS_RES (obj, error);
+ g_assert (GCK_IS_OBJECT (obj));
+
+ obj2 = gck_enumerator_next (en, NULL, &error);
+ SUCCESS_RES (obj2, error);
+ g_assert (GCK_IS_OBJECT (obj2));
+
+ g_assert (!gck_object_equal (obj, obj2));
+
+ g_object_unref (obj);
+ g_object_unref (obj2);
+ g_object_unref (en);
+}
+
+DEFINE_TEST(enumerator_next_n)
+{
+ GError *error = NULL;
+ GckEnumerator *en;
+ GList *objects, *l;
+
+ en = _gck_enumerator_new (modules, 0, NULL, NULL);
+ g_assert (GCK_IS_ENUMERATOR (en));
+
+ objects = gck_enumerator_next_n (en, -1, NULL, &error);
+ SUCCESS_RES (objects, error);
+ g_assert_cmpint (g_list_length (objects), ==, 5);
+ for (l = objects; l; l = g_list_next (l))
+ g_assert (GCK_IS_OBJECT (l->data));
+
+ gck_list_unref_free (objects);
+ g_object_unref (en);
+}
+
+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(enumerator_next_async)
+{
+ GAsyncResult *result = NULL;
+ GError *error = NULL;
+ GckEnumerator *en;
+ GList *objects, *l;
+
+ en = _gck_enumerator_new (modules, 0, NULL, NULL);
+ g_assert (GCK_IS_ENUMERATOR (en));
+
+ gck_enumerator_next_async (en, -1, NULL, fetch_async_result, &result);
+ testing_wait_until (500);
+ g_assert (result);
+
+ objects = gck_enumerator_next_finish (en, result, &error);
+ SUCCESS_RES (objects, error);
+ g_assert_cmpint (g_list_length (objects), ==, 5);
+ for (l = objects; l; l = g_list_next (l))
+ g_assert (GCK_IS_OBJECT (l->data));
+
+ g_object_unref (result);
+ gck_list_unref_free (objects);
+ g_object_unref (en);
+}
+
+DEFINE_TEST(enumerator_attributes)
+{
+ GckAttributes *attrs;
+ GError *error = NULL;
+ GckEnumerator *en;
+ GList *objects;
+
+ attrs = gck_attributes_new ();
+ gck_attributes_add_string (attrs, CKA_LABEL, "Private Capitalize Key");
+ en = _gck_enumerator_new (modules, 0, NULL, attrs);
+ g_assert (GCK_IS_ENUMERATOR (en));
+ gck_attributes_unref (attrs);
+
+ objects = gck_enumerator_next_n (en, -1, NULL, &error);
+ SUCCESS_RES (objects, error);
+ g_assert_cmpint (g_list_length (objects), ==, 1);
+ g_assert (GCK_IS_OBJECT (objects->data));
+
+ gck_list_unref_free (objects);
+ g_object_unref (en);
+}
+
+DEFINE_TEST(enumerator_token_match)
+{
+ GckTokenInfo *token;
+ GError *error = NULL;
+ GckEnumerator *en;
+ GList *objects;
+
+ token = g_new0 (GckTokenInfo, 1);
+ token->label = g_strdup ("Invalid token name");
+ en = _gck_enumerator_new (modules, 0, token, NULL);
+ g_assert (GCK_IS_ENUMERATOR (en));
+
+ objects = gck_enumerator_next_n (en, -1, NULL, &error);
+ g_assert_cmpint (g_list_length (objects), ==, 0);
+ g_assert (error == NULL);
+
+ gck_list_unref_free (objects);
+ g_object_unref (en);
+}
diff --git a/gck/tests/test-gck-module.c b/gck/tests/test-gck-module.c
index 67b2be33..7ad23569 100644
--- a/gck/tests/test-gck-module.c
+++ b/gck/tests/test-gck-module.c
@@ -86,6 +86,7 @@ DEFINE_TEST(module_info)
gck_module_info_free (info);
}
+#if 0
static int n_objects = 0;
static GckObject *last_object = NULL;
@@ -120,9 +121,11 @@ for_first_object (GckObject *object, gpointer user_data)
return FALSE;
}
+#endif
DEFINE_TEST(module_enumerate)
{
+#if 0
GckSession *session;
GckAttributes *attrs;
gboolean ret;
@@ -162,4 +165,5 @@ DEFINE_TEST(module_enumerate)
n_objects = 0;
gck_list_unref_free (modules);
+#endif
}
diff --git a/gck/tests/test-gck-modules.c b/gck/tests/test-gck-modules.c
new file mode 100644
index 00000000..64ac9929
--- /dev/null
+++ b/gck/tests/test-gck-modules.c
@@ -0,0 +1,145 @@
+
+#include <glib.h>
+#include <string.h>
+
+#include "test-suite.h"
+#include "gck-test.h"
+
+static GList *modules = NULL;
+
+DEFINE_SETUP(modules)
+{
+ GckModule *module;
+ GError *err = NULL;
+
+ /* Successful load */
+ module = gck_module_initialize (".libs/libgck-test-module.so", NULL, 0, &err);
+ SUCCESS_RES (module, err);
+
+ modules = g_list_append (NULL, module);
+}
+
+DEFINE_TEARDOWN(modules)
+{
+ gck_list_unref_free (modules);
+ modules = NULL;
+}
+
+DEFINE_TEST(modules_enumerate_objects)
+{
+ GckAttributes *attrs;
+ GError *error = NULL;
+ GckEnumerator *en;
+ GList *objects;
+
+ attrs = gck_attributes_new ();
+ gck_attributes_add_string (attrs, CKA_LABEL, "Private Capitalize Key");
+ en = gck_modules_enumerate_objects (modules, attrs, 0);
+ g_assert (GCK_IS_ENUMERATOR (en));
+ gck_attributes_unref (attrs);
+
+ objects = gck_enumerator_next_n (en, -1, NULL, &error);
+ SUCCESS_RES (objects, error);
+ g_assert_cmpint (g_list_length (objects), ==, 1);
+ g_assert (GCK_IS_OBJECT (objects->data));
+
+ gck_list_unref_free (objects);
+ g_object_unref (en);
+}
+
+
+DEFINE_TEST(modules_token_for_uri)
+{
+ GckSlot *slot;
+ GError *error = NULL;
+
+ slot = gck_modules_token_for_uri (modules, "pkcs11:token=TEST%20LABEL", &error);
+ g_assert (GCK_IS_SLOT (slot));
+
+ g_object_unref (slot);
+}
+
+DEFINE_TEST(modules_token_for_uri_not_found)
+{
+ GckSlot *slot;
+ GError *error = NULL;
+
+ slot = gck_modules_token_for_uri (modules, "pkcs11:token=UNKNOWN", &error);
+ g_assert (slot == NULL);
+ g_assert (error == NULL);
+}
+
+DEFINE_TEST(modules_token_for_uri_error)
+{
+ GckSlot *slot;
+ GError *error = NULL;
+
+ slot = gck_modules_token_for_uri (modules, "http://invalid.uri", &error);
+ g_assert (slot == NULL);
+ g_assert (error != NULL);
+ g_assert (g_error_matches (error, GCK_URI_ERROR, GCK_URI_BAD_PREFIX));
+ g_error_free (error);
+}
+
+DEFINE_TEST(modules_object_for_uri)
+{
+ GckObject *object;
+ GError *error = NULL;
+
+ object = gck_modules_object_for_uri (modules, "pkcs11:object=Public%20Capitalize%20Key;objecttype=public", 0, &error);
+ g_assert (GCK_IS_OBJECT (object));
+ g_object_unref (object);
+}
+
+DEFINE_TEST(modules_object_for_uri_not_found)
+{
+ GckObject *object;
+ GError *error = NULL;
+
+ object = gck_modules_object_for_uri (modules, "pkcs11:object=Unknown%20Label", 0, &error);
+ g_assert (object == NULL);
+ g_assert (error == NULL);
+}
+
+DEFINE_TEST(modules_object_for_uri_error)
+{
+ GckObject *object;
+ GError *error = NULL;
+
+ object = gck_modules_object_for_uri (modules, "http://invalid.uri", 0, &error);
+ g_assert (object == NULL);
+ g_assert (error != NULL);
+ g_assert (g_error_matches (error, GCK_URI_ERROR, GCK_URI_BAD_PREFIX));
+ g_error_free (error);
+}
+
+DEFINE_TEST(modules_objects_for_uri)
+{
+ GList *objects;
+ GError *error = NULL;
+
+ objects = gck_modules_objects_for_uri (modules, "pkcs11:token=TEST%20LABEL", 0, &error);
+ g_assert (objects);
+ g_assert (!error);
+ g_assert_cmpint (g_list_length (objects), ==, 5);
+
+ gck_list_unref_free (objects);
+}
+
+DEFINE_TEST(modules_enumerate_uri)
+{
+ GckEnumerator *en;
+ GList *objects;
+ GError *error = NULL;
+
+ en = gck_modules_enumerate_uri (modules, "pkcs11:token=TEST%20LABEL", 0, &error);
+ g_assert (GCK_IS_ENUMERATOR (en));
+ g_assert (!error);
+
+ objects = gck_enumerator_next_n (en, -1, NULL, &error);
+ g_assert_cmpint (g_list_length (objects), ==, 5);
+ g_assert (!error);
+
+ g_object_unref (en);
+ gck_list_unref_free (objects);
+}
diff --git a/gck/tests/test-gck-slot.c b/gck/tests/test-gck-slot.c
index 3e4bd647..8ea75f07 100644
--- a/gck/tests/test-gck-slot.c
+++ b/gck/tests/test-gck-slot.c
@@ -4,6 +4,7 @@
#include "test-suite.h"
#include "gck-test.h"
+#include "gck-private.h"
static GckModule *module = NULL;
static GckSlot *slot = NULL;
@@ -145,3 +146,58 @@ DEFINE_TEST(slot_mechanisms)
gck_mechanisms_free (mechs);
}
+
+DEFINE_TEST(token_info_match_null)
+{
+ GckTokenInfo *match;
+ GckTokenInfo *token;
+ gboolean ret;
+
+ token = gck_slot_get_token_info (slot);
+ match = g_new0 (GckTokenInfo, 1);
+
+ /* Should match, since no fields are set */
+ ret = _gck_token_info_match (match, token);
+ g_assert (ret);
+
+ gck_token_info_free (match);
+ gck_token_info_free (token);
+}
+
+DEFINE_TEST(token_info_match_label)
+{
+ GckTokenInfo *match;
+ GckTokenInfo *token;
+ gboolean ret;
+
+ token = gck_slot_get_token_info (slot);
+ match = g_new0 (GckTokenInfo, 1);
+
+ /* Should match since the label and serial are matching */
+ match->label = g_strdup (token->label);
+ match->serial_number = g_strdup (token->serial_number);
+ ret = _gck_token_info_match (match, token);
+ g_assert (ret);
+
+ gck_token_info_free (match);
+ gck_token_info_free (token);
+}
+
+DEFINE_TEST(token_info_match_different)
+{
+ GckTokenInfo *match;
+ GckTokenInfo *token;
+ gboolean ret;
+
+ token = gck_slot_get_token_info (slot);
+ match = g_new0 (GckTokenInfo, 1);
+
+ /* Should not match since serial is different */
+ match->label = g_strdup (token->label);
+ match->serial_number = g_strdup ("393939393939393");
+ ret = _gck_token_info_match (match, token);
+ g_assert (!ret);
+
+ gck_token_info_free (match);
+ gck_token_info_free (token);
+}
diff --git a/gck/tests/test-gck-uri.c b/gck/tests/test-gck-uri.c
new file mode 100644
index 00000000..b129f131
--- /dev/null
+++ b/gck/tests/test-gck-uri.c
@@ -0,0 +1,229 @@
+
+#include <glib.h>
+#include <string.h>
+
+#include "test-suite.h"
+#include "gck-test.h"
+#include "gck-private.h"
+
+DEFINE_SETUP(uri)
+{
+
+}
+
+DEFINE_TEARDOWN(uri)
+{
+
+}
+
+DEFINE_TEST(uri_parse)
+{
+ GError *error = NULL;
+ if (!gck_uri_parse ("pkcs11:", NULL, NULL, &error))
+ g_assert_not_reached ();
+}
+
+DEFINE_TEST(uri_parse_bad_scheme)
+{
+ GError *error = NULL;
+ if (gck_uri_parse ("http:\\example.com\test", NULL, NULL, &error))
+ g_assert_not_reached ();
+ g_assert (g_error_matches (error, GCK_URI_ERROR, GCK_URI_BAD_PREFIX));
+ g_error_free (error);
+}
+
+DEFINE_TEST(uri_parse_with_label)
+{
+ GError *error = NULL;
+ GckAttributes *attrs;
+ gchar *value;
+
+ if (!gck_uri_parse ("pkcs11:object=Test%20Label", NULL, &attrs, &error))
+ g_assert_not_reached ();
+
+ if (!gck_attributes_find_string (attrs, CKA_LABEL, &value))
+ g_assert_not_reached ();
+
+ g_assert_cmpstr (value, ==, "Test Label");
+ g_free (value);
+}
+DEFINE_TEST(uri_parse_with_label_and_klass)
+{
+ GError *error = NULL;
+ GckAttributes *attrs;
+ gchar *value;
+ gulong klass;
+
+ if (!gck_uri_parse ("pkcs11:object=Test%20Label;objecttype=cert", NULL, &attrs, &error))
+ g_assert_not_reached ();
+
+ if (!gck_attributes_find_string (attrs, CKA_LABEL, &value))
+ g_assert_not_reached ();
+
+ if (!gck_attributes_find_ulong (attrs, CKA_CLASS, &klass))
+ g_assert_not_reached ();
+
+ g_assert_cmpstr (value, ==, "Test Label");
+ g_assert (klass == CKO_CERTIFICATE);
+ g_free (value);
+}
+
+DEFINE_TEST(uri_parse_with_id)
+{
+ GError *error = NULL;
+ GckAttributes *attrs;
+ GckAttribute *attr;
+
+ if (!gck_uri_parse ("pkcs11:id=54:45:53:54", NULL, &attrs, &error))
+ g_assert_not_reached ();
+
+ attr = gck_attributes_find (attrs, CKA_ID);
+ g_assert (attr);
+ g_assert (attr->value);
+ g_assert (attr->length == 4);
+ g_assert (memcmp (attr->value, "TEST", 4) == 0);
+
+ gck_attributes_unref (attrs);
+}
+
+DEFINE_TEST(uri_parse_with_bad_string_encoding)
+{
+ GError *error = NULL;
+ if (gck_uri_parse ("pkcs11:object=Test%", NULL, NULL, &error))
+ g_assert_not_reached ();
+ g_assert (g_error_matches (error, GCK_URI_ERROR, GCK_URI_BAD_ENCODING));
+ g_error_free (error);
+}
+
+DEFINE_TEST(uri_parse_with_bad_binary_encoding)
+{
+ GError *error = NULL;
+ if (gck_uri_parse ("pkcs11:id=xxxxx", NULL, NULL, &error))
+ g_assert_not_reached ();
+ g_assert (g_error_matches (error, GCK_URI_ERROR, GCK_URI_BAD_ENCODING));
+ g_error_free (error);
+}
+
+DEFINE_TEST(uri_parse_with_token)
+{
+ GError *error = NULL;
+ GckTokenInfo *token = NULL;
+
+ if (!gck_uri_parse ("pkcs11:token=Token%20Label;serial=3333;model=Deluxe;manufacturer=Me",
+ &token, NULL, &error))
+ g_assert_not_reached ();
+
+ g_assert (token);
+ g_assert_cmpstr (token->label, ==, "Token Label");
+ g_assert_cmpstr (token->serial_number, ==, "3333");
+ g_assert_cmpstr (token->model, ==, "Deluxe");
+ g_assert_cmpstr (token->manufacturer_id, ==, "Me");
+ gck_token_info_free (token);
+}
+
+DEFINE_TEST(uri_parse_with_token_bad_encoding)
+{
+ GError *error = NULL;
+
+ if (gck_uri_parse ("pkcs11:token=Token%", NULL, NULL, &error))
+ g_assert_not_reached ();
+
+ g_assert (g_error_matches (error, GCK_URI_ERROR, GCK_URI_BAD_ENCODING));
+ g_error_free (error);
+}
+
+DEFINE_TEST(uri_parse_with_bad_syntax)
+{
+ GError *error = NULL;
+
+ if (gck_uri_parse ("pkcs11:token", NULL, NULL, &error))
+ g_assert_not_reached ();
+
+ g_assert (g_error_matches (error, GCK_URI_ERROR, GCK_URI_BAD_SYNTAX));
+ g_error_free (error);
+}
+
+DEFINE_TEST(uri_build_empty)
+{
+ gchar *uri = NULL;
+
+ uri = gck_uri_build (NULL, NULL);
+ g_assert_cmpstr (uri, ==, "pkcs11:");
+ g_free (uri);
+}
+
+DEFINE_TEST(uri_build_with_token_info)
+{
+ gchar *uri = NULL;
+ GckTokenInfo *token;
+ GckTokenInfo *check;
+
+ token = g_new0 (GckTokenInfo, 1);
+ token->label = g_strdup ("The Label");
+ token->serial_number = g_strdup ("44444");
+ token->manufacturer_id = g_strdup ("Me");
+ token->model = g_strdup ("Deluxe");
+
+ uri = gck_uri_build (token, NULL);
+ g_assert (uri);
+
+ if (!gck_uri_parse (uri, &check, NULL, NULL))
+ g_assert_not_reached ();
+
+ g_assert (_gck_token_info_match (token, check));
+
+ gck_token_info_free (check);
+ gck_token_info_free (token);
+
+ g_assert (g_str_has_prefix (uri, "pkcs11:"));
+ g_assert (strstr (uri, "token=The%20Label"));
+ g_assert (strstr (uri, "serial=44444"));
+ g_assert (strstr (uri, "manufacturer=Me"));
+ g_assert (strstr (uri, "model=Deluxe"));
+
+ g_free (uri);
+}
+
+DEFINE_TEST(uri_build_with_attributes)
+{
+ gchar *uri = NULL;
+ GckAttributes *attrs;
+ gchar *string;
+ gulong value;
+ GckAttribute *attr;
+
+ attrs = gck_attributes_new ();
+ gck_attributes_add_string (attrs, CKA_LABEL, "The Label");
+ gck_attributes_add_ulong (attrs, CKA_CLASS, CKO_DATA);
+ gck_attributes_add_data (attrs, CKA_ID, "TEST", 4);
+
+ uri = gck_uri_build (NULL, attrs);
+ g_assert (uri);
+
+ gck_attributes_unref (attrs);
+
+ if (!gck_uri_parse (uri, NULL, &attrs, NULL))
+ g_assert_not_reached ();
+
+ if (!gck_attributes_find_string (attrs, CKA_LABEL, &string))
+ g_assert_not_reached ();
+ g_assert_cmpstr (string, ==, "The Label");
+
+ if (!gck_attributes_find_ulong (attrs, CKA_CLASS, &value))
+ g_assert_not_reached ();
+ g_assert (value == CKO_DATA);
+
+ attr = gck_attributes_find (attrs, CKA_ID);
+ g_assert (attr);
+ g_assert (attr->length == 4);
+ g_assert (memcmp (attr->value, "TEST", 4) == 0);
+
+ gck_attributes_unref (attrs);
+
+ g_assert (g_str_has_prefix (uri, "pkcs11:"));
+ g_assert (strstr (uri, "object=The%20Label"));
+ g_assert (strstr (uri, "objecttype=data"));
+ g_assert (strstr (uri, "id=54:45:53:54"));
+
+ g_free (uri);
+}