diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | gck/Makefile.am | 3 | ||||
-rw-r--r-- | gck/gck-call.c | 18 | ||||
-rw-r--r-- | gck/gck-enumerator.c | 689 | ||||
-rw-r--r-- | gck/gck-misc.c | 2 | ||||
-rw-r--r-- | gck/gck-modules.c | 166 | ||||
-rw-r--r-- | gck/gck-private.h | 13 | ||||
-rw-r--r-- | gck/gck-slot.c | 27 | ||||
-rw-r--r-- | gck/gck-uri.c | 350 | ||||
-rw-r--r-- | gck/gck.h | 113 | ||||
-rw-r--r-- | gck/tests/Makefile.am | 5 | ||||
-rw-r--r-- | gck/tests/test-gck-enumerator.c | 170 | ||||
-rw-r--r-- | gck/tests/test-gck-module.c | 4 | ||||
-rw-r--r-- | gck/tests/test-gck-modules.c | 145 | ||||
-rw-r--r-- | gck/tests/test-gck-slot.c | 56 | ||||
-rw-r--r-- | gck/tests/test-gck-uri.c | 229 |
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); +} @@ -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); +} |