diff options
-rw-r--r-- | docs/reference/gck/gck-sections.txt | 23 | ||||
-rw-r--r-- | gck/Makefile.am | 2 | ||||
-rw-r--r-- | gck/gck-enumerator.c | 311 | ||||
-rw-r--r-- | gck/gck-interaction.c | 176 | ||||
-rw-r--r-- | gck/gck-misc.c | 15 | ||||
-rw-r--r-- | gck/gck-module.c | 82 | ||||
-rw-r--r-- | gck/gck-modules.c | 12 | ||||
-rw-r--r-- | gck/gck-password.c | 255 | ||||
-rw-r--r-- | gck/gck-private.h | 48 | ||||
-rw-r--r-- | gck/gck-session.c | 643 | ||||
-rw-r--r-- | gck/gck-slot.c | 184 | ||||
-rw-r--r-- | gck/gck.h | 56 | ||||
-rw-r--r-- | gck/gck.symbols | 9 | ||||
-rw-r--r-- | gck/tests/Makefile.am | 4 | ||||
-rw-r--r-- | gck/tests/mock-interaction.c | 97 | ||||
-rw-r--r-- | gck/tests/mock-interaction.h | 43 | ||||
-rw-r--r-- | gck/tests/test-gck-crypto.c | 2 | ||||
-rw-r--r-- | gck/tests/test-gck-enumerator.c | 76 | ||||
-rw-r--r-- | gck/tests/test-gck-session.c | 2 |
19 files changed, 1543 insertions, 497 deletions
diff --git a/docs/reference/gck/gck-sections.txt b/docs/reference/gck/gck-sections.txt index 14a7d30..0c9ea15 100644 --- a/docs/reference/gck/gck-sections.txt +++ b/docs/reference/gck/gck-sections.txt @@ -124,6 +124,8 @@ gck_slot_hash gck_slot_get_module gck_slot_get_handle gck_slot_get_info +gck_slot_get_interaction +gck_slot_set_interaction gck_slot_match gck_slot_get_token_info gck_slot_get_mechanisms @@ -172,6 +174,7 @@ gck_session_get_module gck_session_get_slot gck_session_get_handle gck_session_get_info +gck_session_get_interaction gck_session_login gck_session_login_async gck_session_login_finish @@ -319,6 +322,8 @@ gck_uri_flags_get_type <SECTION> <FILE>gck-enumerator</FILE> GckEnumerator +gck_enumerator_get_interaction +gck_enumerator_set_interaction gck_enumerator_next gck_enumerator_next_async gck_enumerator_next_finish @@ -336,6 +341,24 @@ GCK_TYPE_ENUMERATOR </SECTION> <SECTION> +<FILE>gck-password</FILE> +GckPassword +GckPasswordClass +gck_password_get_key +gck_password_get_module +gck_password_get_token +<SUBSECTION Private> +gck_password_get_type +GckPasswordPrivate +GCK_IS_PASSWORD +GCK_IS_PASSWORD_CLASS +GCK_PASSWORD +GCK_PASSWORD_CLASS +GCK_PASSWORD_GET_CLASS +GCK_TYPE_PASSWORD +</SECTION> + +<SECTION> <FILE>gck-misc</FILE> GckError gck_list_ref_copy diff --git a/gck/Makefile.am b/gck/Makefile.am index a6d9a97..6a2a6c3 100644 --- a/gck/Makefile.am +++ b/gck/Makefile.am @@ -39,6 +39,7 @@ PUBLIC_FILES = \ gck-module.c \ gck-modules.c \ gck-object.c \ + gck-password.c \ gck-session.c \ gck-slot.c \ gck-uri.c @@ -47,6 +48,7 @@ INTERNAL_FILES = \ gck-call.c \ gck-debug.c gck-debug.h \ gck-deprecated.h \ + gck-interaction.c \ gck-private.h \ pkcs11.h diff --git a/gck/gck-enumerator.c b/gck/gck-enumerator.c index dfb5bf4..c57297e 100644 --- a/gck/gck-enumerator.c +++ b/gck/gck-enumerator.c @@ -43,6 +43,11 @@ * gck_enumerator_next_async() functions. */ +enum { + PROP_0, + PROP_INTERACTION +}; + /** * GckEnumerator: * @parent: derived from this. @@ -57,7 +62,6 @@ typedef gpointer (*GckEnumeratorFunc) (GckEnumeratorState *args, gboolean forwar struct _GckEnumeratorState { /* For the current call */ gint want_objects; - gboolean want_password; /* The state we're currently in */ GckEnumeratorFunc handler; @@ -65,9 +69,8 @@ struct _GckEnumeratorState { /* Input to enumerator */ GList *modules; GckUriData *match; - guint session_options; - gboolean authenticate; - gchar *password; + GckSessionOptions session_options; + GTlsInteraction *interaction; /* state_slots */ GList *slots; @@ -88,9 +91,9 @@ struct _GckEnumeratorState { }; struct _GckEnumeratorPrivate { - /* Data here is set atomically */ - gpointer state; - gint mode; + GMutex *mutex; + GckEnumeratorState *the_state; + GTlsInteraction *interaction; }; G_DEFINE_TYPE (GckEnumerator, gck_enumerator, G_TYPE_OBJECT); @@ -152,11 +155,7 @@ cleanup_state (GckEnumeratorState *args) 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; - } + g_clear_object (&args->interaction); if (args->match) { if (args->match->attributes) @@ -321,65 +320,38 @@ state_slot (GckEnumeratorState *args, gboolean forward) static gpointer state_session (GckEnumeratorState *args, gboolean forward) { - GckSessionInfo *sinfo; - CK_ULONG n_pin; + GTlsInteraction *interaction; CK_RV rv; g_assert (args->funcs); g_assert (args->session); - g_assert (!args->want_password); g_assert (args->token_info); /* session to authenticated state */ if (forward) { /* Don't want to authenticate? */ - if (!args->authenticate) { + if ((args->session_options & GCK_SESSION_LOGIN_USER) == 0) { _gck_debug ("no authentication necessary, skipping"); return state_authenticated; } - /* No login necessary */ - if ((args->token_info->flags & CKF_LOGIN_REQUIRED) == 0) { - _gck_debug ("no login required, skipping"); - return state_authenticated; - } + /* Compatibility, hook into GckModule signals if no interaction set */ + if (args->interaction) + interaction = g_object_ref (args->interaction); + else + interaction = _gck_interaction_new (args->slot); - /* 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); - } + rv = _gck_session_authenticate_token (args->funcs, + gck_session_get_handle (args->session), + args->slot, interaction, NULL); - /* 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); - _gck_debug ("already logged in, skipping"); - return state_authenticated; - } - - gck_session_info_free (sinfo); - _gck_debug ("trying to log into session"); - - /* Try to log in */ - n_pin = args->password ? strlen (args->password) : 0; - rv = (args->funcs->C_Login) (gck_session_get_handle (args->session), CKU_USER, - (CK_BYTE_PTR)args->password, n_pin); - - /* Authentication failed, ask for a password */ - if (rv == CKR_PIN_INCORRECT) { - _gck_debug ("login was incorrect, want password"); - args->want_password = TRUE; - return NULL; + g_object_unref (interaction); - /* Any other failure continue without authentication */ - } else if (rv != CKR_OK) { + if (rv != CKR_OK) g_message ("couldn't authenticate when enumerating: %s", gck_message_from_rv (rv)); - } + /* We try to proceed anyway with the enumeration */ return state_authenticated; /* Session to slot state */ @@ -406,7 +378,6 @@ state_authenticated (GckEnumeratorState *args, gboolean forward) /* This is where we do the actual searching */ g_assert (args->session); - g_assert (!args->want_password); g_assert (args->want_objects); g_assert (args->funcs); @@ -506,38 +477,95 @@ state_results (GckEnumeratorState *args, gboolean forward) 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); - g_atomic_pointer_set (&self->pv->state, args); + self->pv->mutex = g_mutex_new (); + self->pv->the_state = g_new0 (GckEnumeratorState, 1); +} + +static void +gck_enumerator_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GckEnumerator *self = GCK_ENUMERATOR (obj); + + switch (prop_id) { + case PROP_INTERACTION: + g_value_take_object (value, gck_enumerator_get_interaction (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +gck_enumerator_set_property (GObject *obj, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GckEnumerator *self = GCK_ENUMERATOR (obj); + + switch (prop_id) { + case PROP_INTERACTION: + gck_enumerator_set_interaction (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +gck_enumerator_dispose (GObject *obj) +{ + GckEnumerator *self = GCK_ENUMERATOR (obj); + + gck_enumerator_set_interaction (self, NULL); + + G_OBJECT_CLASS (gck_enumerator_parent_class)->dispose (obj); } 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 (self->pv->interaction == NULL); + + g_assert (self->pv->the_state != NULL); + cleanup_state (self->pv->the_state); + g_free (self->pv->the_state); - g_assert (state); - cleanup_state (state); - g_free (state); + g_mutex_free (self->pv->mutex); 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->get_property = gck_enumerator_get_property; + gobject_class->set_property = gck_enumerator_set_property; + gobject_class->dispose = gck_enumerator_dispose; gobject_class->finalize = gck_enumerator_finalize; + g_type_class_add_private (klass, sizeof (GckEnumeratorPrivate)); + + /** + * GckEnumerator:interaction: + * + * Interaction object used to ask the user for pins when opening + * sessions. Used if the session_options of the enumerator have + * %GCK_SESSION_LOGIN_USER + */ + g_object_class_install_property (gobject_class, PROP_INTERACTION, + g_param_spec_object ("interaction", "Interaction", "Interaction asking for pins", + G_TYPE_TLS_INTERACTION, G_PARAM_READWRITE)); } /* ---------------------------------------------------------------------------- @@ -545,14 +573,15 @@ gck_enumerator_class_init (GckEnumeratorClass *klass) */ GckEnumerator* -_gck_enumerator_new (GList *modules_or_slots, guint session_options, +_gck_enumerator_new (GList *modules_or_slots, + GckSessionOptions session_options, GckUriData *uri_data) { GckEnumerator *self; GckEnumeratorState *state; self = g_object_new (GCK_TYPE_ENUMERATOR, NULL); - state = g_atomic_pointer_get (&self->pv->state); + state = self->pv->the_state; state->session_options = session_options; @@ -596,7 +625,6 @@ perform_enumerate_next (EnumerateNext *args) g_assert (args->state); state = args->state; - g_assert (!state->want_password); g_assert (state->handler); for (;;) { @@ -610,43 +638,105 @@ perform_enumerate_next (EnumerateNext *args) return CKR_OK; } -static gboolean -complete_enumerate_next (EnumerateNext *args, CK_RV result) +static void +free_enumerate_next (EnumerateNext *args) { - GckEnumeratorState *state; - GckModule *module; - gboolean ret = TRUE; + /* Should have been assigned back to enumerator */ + g_assert (!args->state); - g_assert (args->state); - state = args->state; + g_free (args); +} - if (state->want_password) { - g_assert (state->slot); +/** + * gck_enumerator_get_interaction: + * @self: the enumerator + * + * Get the interaction used when a pin is needed + * + * Returns: (transfer full) (allow-none): the interaction or %NULL + */ +GTlsInteraction * +gck_enumerator_get_interaction (GckEnumerator *self) +{ + GTlsInteraction *result = NULL; - _gck_debug ("wants password, emitting authenticate-slot"); + g_return_val_if_fail (GCK_IS_ENUMERATOR (self), NULL); - /* TODO: Should we be using secure memory here? */ - g_free (state->password); - state->password = NULL; + g_mutex_lock (self->pv->mutex); - module = gck_slot_get_module (state->slot); - ret = _gck_module_fire_authenticate_slot (module, state->slot, NULL, &state->password); - g_object_unref (module); + if (self->pv->interaction) + result = g_object_ref (self->pv->interaction); - /* If authenticate returns TRUE then call is not complete */ - ret = !ret; - } + g_mutex_unlock (self->pv->mutex); - return ret; + return result; +} + +/** + * gck_enumerator_set_interaction: + * @self: the enumerator + * @interaction: (allow-none): the interaction or %NULL + * + * Set the interaction used when a pin is needed + */ +void +gck_enumerator_set_interaction (GckEnumerator *self, + GTlsInteraction *interaction) +{ + GTlsInteraction *previous = NULL; + + g_return_if_fail (GCK_IS_ENUMERATOR (self)); + g_return_if_fail (interaction == NULL || G_IS_TLS_INTERACTION (interaction)); + + g_mutex_lock (self->pv->mutex); + + if (interaction != self->pv->interaction) { + previous = self->pv->interaction; + self->pv->interaction = interaction; + if (interaction) + g_object_ref (interaction); + } + + g_mutex_unlock (self->pv->mutex); + + g_clear_object (&previous); + g_object_notify (G_OBJECT (self), "interaction"); +} + +static GckEnumeratorState * +check_out_enumerator_state (GckEnumerator *self) +{ + GckEnumeratorState *state = NULL; + + g_mutex_lock (self->pv->mutex); + + if (self->pv->the_state) { + state = self->pv->the_state; + self->pv->the_state = NULL; + + g_clear_object (&state->interaction); + if (self->pv->interaction) + state->interaction = g_object_ref (self->pv->interaction); + } + + g_mutex_unlock (self->pv->mutex); + + if (state == NULL) + g_warning ("this enumerator is already running a next operation"); + + return state; } static void -free_enumerate_next (EnumerateNext *args) +check_in_enumerator_state (GckEnumerator *self, + GckEnumeratorState *state) { - /* Should have been assigned back to enumerator */ - g_assert (!args->state); + g_mutex_lock (self->pv->mutex); - g_free (args); + g_assert (self->pv->the_state == NULL); + self->pv->the_state = state; + + g_mutex_unlock (self->pv->mutex); } /** @@ -672,12 +762,8 @@ gck_enumerator_next (GckEnumerator *self, GCancellable *cancellable, GError **er g_return_val_if_fail (GCK_IS_ENUMERATOR (self), 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 = check_out_enumerator_state (self); + g_return_val_if_fail (args.state != NULL, NULL); /* A result from a previous run? */ result = extract_result (args.state); @@ -685,7 +771,7 @@ gck_enumerator_next (GckEnumerator *self, GCancellable *cancellable, GError **er args.state->want_objects = 1; /* Run the operation and steal away the results */ - if (_gck_call_sync (NULL, perform_enumerate_next, complete_enumerate_next, &args, cancellable, error)) { + if (_gck_call_sync (NULL, perform_enumerate_next, NULL, &args, cancellable, error)) { if (args.state->results) { g_assert (g_list_length (args.state->results) == 1); result = g_object_ref (args.state->results->data); @@ -698,8 +784,7 @@ gck_enumerator_next (GckEnumerator *self, GCancellable *cancellable, GError **er } /* Put the state back */ - if (!g_atomic_pointer_compare_and_exchange (&self->pv->state, NULL, args.state)) - g_assert_not_reached (); + check_in_enumerator_state (self, args.state); return result; } @@ -733,16 +818,13 @@ gck_enumerator_next_n (GckEnumerator *self, gint max_objects, GCancellable *canc 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 = check_out_enumerator_state (self); + g_return_val_if_fail (args.state != NULL, 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)) { + if (_gck_call_sync (NULL, perform_enumerate_next, NULL, &args, cancellable, error)) { results = args.state->results; args.state->results = NULL; } @@ -750,8 +832,7 @@ gck_enumerator_next_n (GckEnumerator *self, gint max_objects, GCancellable *canc 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 (); + check_in_enumerator_state (self, args.state); return results; } @@ -782,14 +863,11 @@ gck_enumerator_next_async (GckEnumerator *self, gint max_objects, GCancellable * 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 = check_out_enumerator_state (self); + g_return_if_fail (state != NULL); state->want_objects = max_objects <= 0 ? G_MAXINT : max_objects; - args = _gck_call_async_prep (NULL, self, perform_enumerate_next, complete_enumerate_next, + args = _gck_call_async_prep (NULL, self, perform_enumerate_next, NULL, sizeof (*args), free_enumerate_next); args->state = state; @@ -831,8 +909,7 @@ gck_enumerator_next_finish (GckEnumerator *self, GAsyncResult *result, GError ** } /* Put the state back */ - if (!g_atomic_pointer_compare_and_exchange (&self->pv->state, NULL, state)) - g_assert_not_reached (); + check_in_enumerator_state (self, state); g_object_unref (self); diff --git a/gck/gck-interaction.c b/gck/gck-interaction.c new file mode 100644 index 0000000..33d5ad3 --- /dev/null +++ b/gck/gck-interaction.c @@ -0,0 +1,176 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* gck-interaction.c - the GObject PKCS#11 wrapper library + + Copyright (C) 2011 Collabora Ltd + + 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 <stefw@collabora.co.uk> +*/ + +#include "config.h" + +#include "gck-private.h" + +#include <string.h> + +#define GCK_INTERACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_INTERACTION, GckInteraction)) +#define GCK_IS_INTERACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_INTERACTION)) +#define GCK_INTERACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_INTERACTION, GckInteractionClass)) + +typedef struct _GckInteractionClass GckInteractionClass; + +struct _GckInteraction { + GTlsInteraction interaction; + GckModule *module; +}; + +struct _GckInteractionClass { + GTlsInteractionClass parent; +}; + +enum { + PROP_0, + PROP_MODULE +}; + +G_DEFINE_TYPE (GckInteraction, _gck_interaction, G_TYPE_TLS_INTERACTION); + +static void +_gck_interaction_init (GckInteraction *self) +{ + +} + +static void +_gck_interaction_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GckInteraction *self = GCK_INTERACTION (obj); + + switch (prop_id) { + case PROP_MODULE: + g_value_set_object (value, self->module); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +_gck_interaction_set_property (GObject *obj, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GckInteraction *self = GCK_INTERACTION (obj); + + switch (prop_id) { + case PROP_MODULE: + g_return_if_fail (self->module == NULL); + self->module = g_value_dup_object (value); + g_return_if_fail (self->module != NULL); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +_gck_interaction_dispose (GObject *obj) +{ + GckInteraction *self = GCK_INTERACTION (obj); + + g_clear_object (&self->module); + + G_OBJECT_CLASS (_gck_interaction_parent_class)->dispose (obj); +} + +static GTlsInteractionResult +_gck_interaction_ask_password (GTlsInteraction *interaction, + GTlsPassword *password, + GCancellable *cancellable, + GError **error) +{ + GckInteraction *self = GCK_INTERACTION (interaction); + gchar *value = NULL; + gboolean ret = FALSE; + GckSlot *token; + GckObject *key; + + if (!self->module) + return G_TLS_INTERACTION_UNHANDLED; + + token = gck_password_get_token (GCK_PASSWORD (password)); + if (token != NULL) { + g_signal_emit_by_name (self->module, "authenticate-slot", token, + g_tls_password_get_description (password), + &value, &ret); + g_object_unref (token); + + } else { + key = gck_password_get_key (GCK_PASSWORD (password)); + g_return_val_if_fail (GCK_IS_OBJECT (key), G_TLS_INTERACTION_UNHANDLED); + + g_signal_emit_by_name (self->module, "authenticate-object", key, + g_tls_password_get_description (password), + &value, &ret); + } + + if (ret) { + g_tls_password_set_value_full (password, (guchar *)value, -1, g_free); + return G_TLS_INTERACTION_HANDLED; + } else { + return G_TLS_INTERACTION_UNHANDLED; + } +} + +static void +_gck_interaction_class_init (GckInteractionClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GTlsInteractionClass *interaction_class = G_TLS_INTERACTION_CLASS (klass); + + object_class->get_property = _gck_interaction_get_property; + object_class->set_property = _gck_interaction_set_property; + object_class->dispose = _gck_interaction_dispose; + + interaction_class->ask_password = _gck_interaction_ask_password; + + g_object_class_install_property (object_class, PROP_MODULE, + g_param_spec_object ("module", "Module", "PKCS11 Module", + GCK_TYPE_MODULE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} + +GTlsInteraction * +_gck_interaction_new (gpointer token_or_key) +{ + GTlsInteraction *result; + GModule *module = NULL; + + g_return_val_if_fail (GCK_IS_SLOT (token_or_key) || + GCK_IS_OBJECT (token_or_key), NULL); + + g_object_get (token_or_key, "module", &module, NULL); + result = g_object_new (GCK_TYPE_INTERACTION, "module", module, NULL); + g_object_unref (module); + + return result; +} diff --git a/gck/gck-misc.c b/gck/gck-misc.c index 595f619..47d444e 100644 --- a/gck/gck-misc.c +++ b/gck/gck-misc.c @@ -218,6 +218,21 @@ _gck_stringize_rv (CK_RV rv) } } +CK_RV +_gck_rv_from_error (GError *error, + CK_RV catch_all_code) +{ + g_return_val_if_fail (error != NULL, CKR_GENERAL_ERROR); + + if (error->domain == GCK_ERROR) + return error->code; + + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return CKR_FUNCTION_CANCELED; + + return catch_all_code; +} + /** * SECTION:gck-misc * @title: Miscellaneous Functions diff --git a/gck/gck-module.c b/gck/gck-module.c index 8806dd7..cdd9d45 100644 --- a/gck/gck-module.c +++ b/gck/gck-module.c @@ -106,76 +106,6 @@ G_DEFINE_TYPE (GckModule, gck_module, G_TYPE_OBJECT); static guint signals[LAST_SIGNAL] = { 0 }; /* ---------------------------------------------------------------------------- - * INTERNAL - */ - -gboolean -_gck_module_fire_authenticate_slot (GckModule *self, GckSlot *slot, gchar *label, gchar **password) -{ - GckTokenInfo *info; - gchar *allocated = NULL; - gboolean ret; - - g_assert (GCK_IS_MODULE (self)); - - info = gck_slot_get_token_info (slot); - if (info != NULL) { - - /* - * We'll have tried to login at least once at this point, - * with NULL password. This means that CKF_PROTECTED_AUTHENTICATION_PATH - * tokens have had their chance and we don't need to prompt for it. - */ - - if (info->flags & CKF_PROTECTED_AUTHENTICATION_PATH) - return FALSE; - - if (label == NULL) - label = allocated = g_strdup (info->label); - - gck_token_info_free (info); - } - - g_signal_emit (self, signals[AUTHENTICATE_SLOT], 0, slot, label, password, &ret); - g_free (allocated); - return ret; -} - -gboolean -_gck_module_fire_authenticate_object (GckModule *self, GckObject *object, - gchar *label, gchar **password) -{ - GckTokenInfo *info; - GckSession *session; - GckSlot *slot; - gboolean ret; - - g_assert (GCK_IS_MODULE (self)); - g_assert (GCK_IS_OBJECT (object)); - g_assert (password); - - session = gck_object_get_session (object); - slot = gck_session_get_slot (session); - g_object_unref (session); - - info = gck_slot_get_token_info (slot); - g_object_unref (slot); - - if (info != NULL) { - if (info->flags & CKF_PROTECTED_AUTHENTICATION_PATH) { - gck_token_info_free (info); - *password = NULL; - return TRUE; - } - - gck_token_info_free (info); - } - - g_signal_emit (self, signals[AUTHENTICATE_OBJECT], 0, object, label, password, &ret); - return ret; -} - -/* ---------------------------------------------------------------------------- * OBJECT */ @@ -321,11 +251,9 @@ gck_module_class_init (GckModuleClass *klass) * @string: A displayable label which describes the object. * @password: A gchar** where a password should be returned. * - * This signal is emitted when a password is needed to authenticate a PKCS#11 - * slot. If the module prompts for passwords itself, then this signal will - * not be emitted. + * Use gck_slot_set_interaction() instead of connecting to this signal. * - * Returns: FALSE if the user cancelled, TRUE if we should proceed. + * Deprecated: Since 3.4 */ signals[AUTHENTICATE_SLOT] = g_signal_new ("authenticate-slot", GCK_TYPE_MODULE, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GckModuleClass, authenticate_slot), @@ -339,11 +267,9 @@ gck_module_class_init (GckModuleClass *klass) * @label: A displayable label which describes the object. * @password: A gchar** where a password should be returned. * - * This signal is emitted when a password is needed to authenticate a PKCS#11 - * object like a key. If the module prompts for passwords itself, then this signal will - * not be emitted. + * Use gck_slot_set_interaction() instead of connecting to this signal. * - * Returns: FALSE if the user cancelled, TRUE if we should proceed. + * Deprecated: Since 3.4 */ signals[AUTHENTICATE_OBJECT] = g_signal_new ("authenticate-object", GCK_TYPE_MODULE, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GckModuleClass, authenticate_object), diff --git a/gck/gck-modules.c b/gck/gck-modules.c index 497d2ff..9a93103 100644 --- a/gck/gck-modules.c +++ b/gck/gck-modules.c @@ -341,7 +341,9 @@ gck_modules_tokens_for_uri (GList *modules, * g_object_unref(), or %NULL if no matching object was found. */ GckObject* -gck_modules_object_for_uri (GList *modules, const gchar *uri, guint session_options, +gck_modules_object_for_uri (GList *modules, + const gchar *uri, + GckSessionOptions session_options, GError **error) { GckEnumerator *en; @@ -377,7 +379,9 @@ gck_modules_object_for_uri (GList *modules, const gchar *uri, guint session_opti * was found. */ GList* -gck_modules_objects_for_uri (GList *modules, const gchar *uri, guint session_options, +gck_modules_objects_for_uri (GList *modules, + const gchar *uri, + GckSessionOptions session_options, GError **error) { GckEnumerator *en; @@ -411,7 +415,9 @@ gck_modules_objects_for_uri (GList *modules, const gchar *uri, guint session_opt * Returns: (transfer full): A new #GckEnumerator, or %NULL if an error occurs. */ GckEnumerator* -gck_modules_enumerate_uri (GList *modules, const gchar *uri, guint session_options, +gck_modules_enumerate_uri (GList *modules, + const gchar *uri, + GckSessionOptions session_options, GError **error) { GckUriData *uri_data; diff --git a/gck/gck-password.c b/gck/gck-password.c new file mode 100644 index 0000000..56200ea --- /dev/null +++ b/gck/gck-password.c @@ -0,0 +1,255 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* gck-password.c - the GObject PKCS#11 wrapper library + + Copyright (C) 2011 Collabora Ltd. + + 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 <stefw@collabora.co.uk> +*/ + +#include "config.h" + +#include "gck.h" +#include "gck-private.h" + +#include "egg/egg-timegm.h" + +#include <string.h> + +/** + * SECTION:gck-password + * @title: GckPassword + * @short_description: Represents a password hich is requested of the user + * + * This is used in conjuction with GTlsInteraction. #GckPassword is a + * GTlsPassword which contains additional information about which PKCS\#11 + * token or key the password is being requested for. + */ + +/** + * GckPassword: + * @parent: parent object + * + * A #GTlsPasswordClass that contains information about the PKCS\#11 token + * or key the password is being requested for. + */ + +/** + * GckPasswordClass: + * @parent: parent class + * + * The class for #GTlsPassword. + */ +enum { + PROP_0, + PROP_MODULE, + PROP_TOKEN, + PROP_KEY +}; + +struct _GckPasswordPrivate { + gboolean for_token; + gpointer token_or_key; +}; + +G_DEFINE_TYPE (GckPassword, gck_password, G_TYPE_TLS_PASSWORD); + +static void +gck_password_init (GckPassword *self) +{ + self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_PASSWORD, GckPasswordPrivate); +} + +static void +gck_password_constructed (GObject *obj) +{ + GckPassword *self = GCK_PASSWORD (obj); + + G_OBJECT_CLASS (gck_password_parent_class)->constructed (obj); + + g_return_if_fail (GCK_IS_SLOT (self->pv->token_or_key) || + GCK_IS_OBJECT (self->pv->token_or_key)); +} + +static void +gck_password_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GckPassword *self = GCK_PASSWORD (obj); + + switch (prop_id) { + case PROP_MODULE: + g_value_take_object (value, gck_password_get_module (self)); + break; + case PROP_TOKEN: + g_value_take_object (value, gck_password_get_token (self)); + break; + case PROP_KEY: + g_value_take_object (value, gck_password_get_key (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +gck_password_set_property (GObject *obj, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GckPassword *self = GCK_PASSWORD (obj); + gpointer object; + + /* All writes to data members below, happen only during construct phase */ + + switch (prop_id) { + case PROP_TOKEN: + object = g_value_dup_object (value); + if (object != NULL) { + g_assert (self->pv->token_or_key == NULL); + self->pv->token_or_key = object; + self->pv->for_token = TRUE; + } + break; + case PROP_KEY: + object = g_value_dup_object (value); + if (object != NULL) { + g_assert (self->pv->token_or_key == NULL); + self->pv->token_or_key = object; + self->pv->for_token = FALSE; + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +gck_password_finalize (GObject *obj) +{ + GckPassword *self = GCK_PASSWORD (obj); + + g_clear_object (&self->pv->token_or_key); + + G_OBJECT_CLASS (gck_password_parent_class)->finalize (obj); +} + +static void +gck_password_class_init (GckPasswordClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*)klass; + + gobject_class->constructed = gck_password_constructed; + gobject_class->get_property = gck_password_get_property; + gobject_class->set_property = gck_password_set_property; + gobject_class->finalize = gck_password_finalize; + + /** + * GckPassword:module: + * + * The PKCS\#11 module that is requesting the password + */ + g_object_class_install_property (gobject_class, PROP_MODULE, + g_param_spec_object ("module", "Module", "PKCS11 Module", + GCK_TYPE_MODULE, G_PARAM_READABLE)); + + /** + * GckPassword:token: + * + * The PKCS\#11 token the password is for, if this is set then + * the GckPassword:object property will be %NULL + */ + g_object_class_install_property (gobject_class, PROP_TOKEN, + g_param_spec_object ("token", "Token", "PKCS11 Token", + GCK_TYPE_SLOT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /** + * GckPassword:key: + * + * The PKCS\#11 key that the password is being requested for. If this + * is set then the GckPassword:token property will be %NULL + */ + g_object_class_install_property (gobject_class, PROP_KEY, + g_param_spec_object ("key", "Object", "PKCS11 Key Object", + GCK_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_type_class_add_private (gobject_class, sizeof (GckPasswordPrivate)); +} + +/** + * gck_password_get_module: + * @self: the password object + * + * Get the PKCS\#11 module that is requesting the password. + * + * Returns: (transfer full): the module that is requesting the password, which + * must be unreferenced after use + */ +GckModule * +gck_password_get_module (GckPassword *self) +{ + g_return_val_if_fail (GCK_IS_PASSWORD (self), NULL); + if (self->pv->for_token) + return gck_slot_get_module (self->pv->token_or_key); + else + return gck_object_get_module (self->pv->token_or_key); +} + +/** + * gck_password_get_token: + * @self: the password object + * + * If the password request is to unlock a PKCS\#11 token, then this is the + * slot containing that token. + * + * Returns: (transfer full): the slot that contains the token, or %NULL if not + * being requested for a token; must be unreferenced after use + */ +GckSlot * +gck_password_get_token (GckPassword *self) +{ + g_return_val_if_fail (GCK_IS_PASSWORD (self), NULL); + if (!self->pv->for_token) + return NULL; + g_return_val_if_fail (GCK_IS_SLOT (self->pv->token_or_key), NULL); + return g_object_ref (self->pv->token_or_key); +} + +/** + * gck_password_get_key: + * @self: the password object + * + * If the password request is to unlock a PKCS\#11 key, then this is the + * the object representing that key. + * + * Returns: (transfer full): the password is for this key, or %NULL if not + * being requested for a key; must be unreferenced after use + */ +GckObject * +gck_password_get_key (GckPassword *self) +{ + g_return_val_if_fail (GCK_IS_PASSWORD (self), NULL); + if (self->pv->for_token) + return NULL; + g_return_val_if_fail (GCK_IS_OBJECT (self->pv->token_or_key), NULL); + return g_object_ref (self->pv->token_or_key); +} diff --git a/gck/gck-private.h b/gck/gck-private.h index 64739a2..c2919cf 100644 --- a/gck/gck-private.h +++ b/gck/gck-private.h @@ -62,22 +62,15 @@ gboolean _gck_ulong_equal (gconstpointer v1, const gchar * _gck_stringize_rv (CK_RV rv); +CK_RV _gck_rv_from_error (GError *error, + CK_RV catch_all_code); + /* ---------------------------------------------------------------------------- * MODULE */ GckModule* _gck_module_new_initialized (CK_FUNCTION_LIST_PTR funcs); -gboolean _gck_module_fire_authenticate_slot (GckModule *module, - GckSlot *slot, - gchar *label, - gchar **password); - -gboolean _gck_module_fire_authenticate_object (GckModule *module, - GckObject *object, - gchar *label, - gchar **password); - GckModuleInfo* _gck_module_info_from_pkcs11 (CK_INFO_PTR info); void _gck_module_info_to_pkcs11 (GckModuleInfo* module_info, @@ -91,7 +84,7 @@ gboolean _gck_module_info_match (GckModuleInfo *matc */ GckEnumerator* _gck_enumerator_new (GList *modules, - guint session_options, + GckSessionOptions session_options, GckUriData *uri_data); /* ---------------------------------------------------------------------------- @@ -106,6 +99,39 @@ void _gck_token_info_to_pkcs11 (GckTokenInfo *token gboolean _gck_token_info_match (GckTokenInfo *match, GckTokenInfo *info); +CK_RV _gck_session_authenticate_token (CK_FUNCTION_LIST_PTR funcs, + CK_SESSION_HANDLE session, + GckSlot *token, + GTlsInteraction *interaction, + GCancellable *cancellable); + +CK_RV _gck_session_authenticate_key (CK_FUNCTION_LIST_PTR funcs, + CK_SESSION_HANDLE session, + GckObject *key, + GTlsInteraction *interaction, + GCancellable *cancellable); + +/* ---------------------------------------------------------------------------- + * PASSWORD + */ + +void _gck_password_update (GckPassword *self, + gboolean request_retry); + +/* ---------------------------------------------------------------------------- + * INTERACTION + */ + +#define GCK_TYPE_INTERACTION (_gck_interaction_get_type ()) +#define GCK_INTERACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_INTERACTION, GckInteraction)) +#define GCK_IS_INTERACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_INTERACTION)) + +typedef struct _GckInteraction GckInteraction; + +GType _gck_interaction_get_type (void) G_GNUC_CONST; + +GTlsInteraction * _gck_interaction_new (gpointer token_or_key); + /* ---------------------------------------------------------------------------- * CALL */ diff --git a/gck/gck-session.c b/gck/gck-session.c index eba1de6..c984eb6 100644 --- a/gck/gck-session.c +++ b/gck/gck-session.c @@ -77,6 +77,7 @@ enum { PROP_0, PROP_MODULE, PROP_HANDLE, + PROP_INTERACTION, PROP_SLOT, PROP_OPTIONS, }; @@ -85,7 +86,8 @@ struct _GckSessionPrivate { GckSlot *slot; GckModule *module; CK_SESSION_HANDLE handle; - guint options; + GTlsInteraction *interaction; + GckSessionOptions options; /* Modified atomically */ gint discarded; @@ -148,6 +150,12 @@ gck_session_get_property (GObject *obj, guint prop_id, GValue *value, case PROP_OPTIONS: g_value_set_uint (value, gck_session_get_options (self)); break; + case PROP_INTERACTION: + g_value_take_object (value, self->pv->interaction); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; } } @@ -169,6 +177,10 @@ gck_session_set_property (GObject *obj, guint prop_id, const GValue *value, g_return_if_fail (!self->pv->handle); self->pv->handle = g_value_get_ulong (value); break; + case PROP_INTERACTION: + g_return_if_fail (self->pv->interaction == NULL); + self->pv->interaction = g_value_dup_object (value); + break; case PROP_SLOT: g_return_if_fail (!self->pv->slot); self->pv->slot = g_value_dup_object (value); @@ -176,7 +188,10 @@ gck_session_set_property (GObject *obj, guint prop_id, const GValue *value, break; case PROP_OPTIONS: g_return_if_fail (!self->pv->options); - self->pv->options = g_value_get_uint (value); + self->pv->options = g_value_get_flags (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } @@ -210,13 +225,9 @@ gck_session_finalize (GObject *obj) g_assert (g_atomic_int_get (&self->pv->discarded) != 0); - if (self->pv->slot) - g_object_unref (self->pv->slot); - self->pv->slot = NULL; - - if (self->pv->module) - g_object_unref (self->pv->module); - self->pv->module = NULL; + g_clear_object (&self->pv->interaction); + g_clear_object (&self->pv->slot); + g_clear_object (&self->pv->module); G_OBJECT_CLASS (gck_session_parent_class)->finalize (obj); } @@ -267,8 +278,20 @@ gck_session_class_init (GckSessionClass *klass) * The options this session was opened with. */ g_object_class_install_property (gobject_class, PROP_OPTIONS, - g_param_spec_uint ("options", "Session Options", "Session Options", - 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_param_spec_flags ("options", "Session Options", "Session Options", + GCK_TYPE_SESSION_OPTIONS, GCK_SESSION_READ_ONLY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /** + * GckSession:interaction: + * + * Interaction object used to ask the user for pins when opening + * sessions. Used if the session_options of the enumerator have + * %GCK_SESSION_LOGIN_USER + */ + g_object_class_install_property (gobject_class, PROP_INTERACTION, + g_param_spec_object ("interaction", "Interaction", "Interaction asking for pins", + G_TYPE_TLS_INTERACTION, G_PARAM_READWRITE)); /** * GckSession::discard-handle: @@ -364,21 +387,27 @@ gck_session_info_free (GckSessionInfo *session_info) GckSession * gck_session_from_handle (GckSlot *slot, gulong session_handle, - guint options) + GckSessionOptions options) { + GTlsInteraction *interaction; GckModule *module; GckSession *session; g_return_val_if_fail (GCK_IS_SLOT (slot), NULL); module = gck_slot_get_module (slot); + interaction = gck_slot_get_interaction (slot); + session = g_object_new (GCK_TYPE_SESSION, "module", module, + "interaction", interaction, "handle", session_handle, "slot", slot, "options", options, NULL); + g_object_unref (module); + g_clear_object (&interaction); return session; } @@ -518,13 +547,33 @@ gck_session_get_state (GckSession *self) * * Return value: The session options. **/ -guint +GckSessionOptions gck_session_get_options (GckSession *self) { g_return_val_if_fail (GCK_IS_SESSION (self), 0); return self->pv->options; } +/** + * gck_session_get_interaction: + * @self: the session + * + * Get the interaction object set on this session, which is used to prompt + * for pins and the like. + * + * Returns: (transfer full) (allow-none): the interaction object, or %NULL + */ +GTlsInteraction * +gck_session_get_interaction (GckSession *self) +{ + g_return_val_if_fail (GCK_IS_SESSION (self), NULL); + + if (self->pv->interaction) + return g_object_ref (self->pv->interaction); + + return NULL; +} + /* --------------------------------------------------------------------------------------------- * INIT PIN */ @@ -1913,191 +1962,20 @@ gck_session_derive_key_finish (GckSession *self, GAsyncResult *result, GError ** } /* -------------------------------------------------------------------------------------------------- - * AUTHENTICATE - */ - -typedef enum _AuthenticateState { - AUTHENTICATE_NONE, - AUTHENTICATE_CAN, - AUTHENTICATE_WANT, - AUTHENTICATE_PERFORM -} AuthenticateState; - -typedef struct _Authenticate { - AuthenticateState state; - gboolean protected_auth; - GckModule *module; - GckObject *object; - gchar *label; - gchar *password; -} Authenticate; - -static CK_RV -authenticate_perform (Authenticate *args, GckArguments *base) -{ - CK_ATTRIBUTE attributes[2]; - CK_OBJECT_HANDLE handle; - CK_ULONG pin_len; - CK_BBOOL bvalue; - CK_RV rv; - - g_assert (args); - g_assert (base); - - switch (args->state) { - - /* - * Cannot authenticate for whatever reason, perhaps not - * enabled, or failed incomprehensibly etc. - * - */ - case AUTHENTICATE_NONE: - return CKR_OK; - - /* - * Can authenticate but haven't seen if we should, yet - * check out the object in question. - */ - case AUTHENTICATE_CAN: - - handle = gck_object_get_handle (args->object); - - attributes[0].type = CKA_LABEL; - attributes[0].pValue = NULL; - attributes[0].ulValueLen = 0; - attributes[1].type = CKA_ALWAYS_AUTHENTICATE; - attributes[1].pValue = &bvalue; - attributes[1].ulValueLen = sizeof (bvalue); - - rv = (base->pkcs11->C_GetAttributeValue) (base->handle, handle, attributes, 2); - if (rv == CKR_ATTRIBUTE_TYPE_INVALID) - bvalue = CK_FALSE; - else if (rv != CKR_OK) - return rv; - - /* No authentication needed, on this object */ - if (bvalue != CK_TRUE) { - args->state = AUTHENTICATE_NONE; - return CKR_OK; - } - - /* Protected authentication path, just go to perform */ - if (args->protected_auth) { - args->state = AUTHENTICATE_PERFORM; - return authenticate_perform (args, base); - } - - /* Get the label for a prompt */ - g_assert (!args->label); - if (attributes[0].ulValueLen) { - attributes[0].pValue = g_malloc0 (attributes[0].ulValueLen + 1); - rv = (base->pkcs11->C_GetAttributeValue) (base->handle, handle, attributes, 2); - if (rv == CKR_OK) { - g_assert (!args->label); - args->label = attributes[0].pValue; - args->label[attributes[0].ulValueLen] = 0; - } else { - g_free (attributes[0].pValue); - } - } - - /* Need a password */ - args->state = AUTHENTICATE_WANT; - return CKR_USER_NOT_LOGGED_IN; - - /* - * This state should be handled in verify_authenticate. - */ - case AUTHENTICATE_WANT: - g_assert (FALSE); - return CKR_GENERAL_ERROR; - - /* - * Do the actual login authentication. - */ - case AUTHENTICATE_PERFORM: - pin_len = args->password ? strlen (args->password) : 0; - rv = (base->pkcs11->C_Login) (base->handle, CKU_CONTEXT_SPECIFIC, - (CK_UTF8CHAR_PTR)args->password, pin_len); - if (rv == CKR_PIN_INCORRECT && !args->protected_auth) - args->state = AUTHENTICATE_WANT; - else - args->state = AUTHENTICATE_NONE; - return rv; - - default: - g_assert_not_reached (); - return CKR_GENERAL_ERROR; - } -} - -static gboolean -authenticate_complete (Authenticate *auth, GckArguments *base, CK_RV result) -{ - g_assert (auth); - g_assert (base); - - /* We're done here if not in this state */ - if (auth->state == AUTHENTICATE_WANT) { - - g_assert (GCK_IS_MODULE (auth->module)); - g_assert (GCK_IS_OBJECT (auth->object)); - - g_free (auth->password); - auth->password = NULL; - - if (_gck_module_fire_authenticate_object (auth->module, auth->object, auth->label, &auth->password)) { - auth->state = AUTHENTICATE_PERFORM; - return FALSE; /* Want to continue processing this call */ - } - } - - /* Free up various memory */ - if (auth->module) - g_object_unref (auth->module); - if (auth->object) - g_object_unref (auth->object); - g_free (auth->label); - g_free (auth->password); - - /* The call is complete */ - return TRUE; -} - -static void -authenticate_init (Authenticate *auth, GckSlot *slot, GckObject *object, guint options) -{ - GckModule *module; - - g_assert (GCK_IS_SLOT (slot)); - g_assert (GCK_IS_OBJECT (object)); - - module = gck_slot_get_module (slot); - if ((options & GCK_SESSION_AUTHENTICATE) == GCK_SESSION_AUTHENTICATE) { - auth->state = AUTHENTICATE_CAN; - auth->protected_auth = gck_slot_has_flags (slot, CKF_PROTECTED_AUTHENTICATION_PATH); - auth->module = module; - auth->object = g_object_ref (object); - } else { - auth->state = AUTHENTICATE_NONE; - g_object_unref (module); - } -} - -/* -------------------------------------------------------------------------------------------------- * COMMON CRYPTO ROUTINES */ typedef struct _Crypt { GckArguments base; - /* Authenticator */ - Authenticate auth; - /* Functions to call */ CK_C_EncryptInit init_func; CK_C_Encrypt complete_func; + /* Interaction */ + GckObject *key_object; + GTlsInteraction *interaction; + /* Input */ CK_OBJECT_HANDLE key; GckMechanism mechanism; @@ -2113,6 +1991,7 @@ typedef struct _Crypt { static CK_RV perform_crypt (Crypt *args) { + GTlsInteraction *interaction; CK_RV rv; g_assert (args); @@ -2126,7 +2005,17 @@ perform_crypt (Crypt *args) if (rv != CKR_OK) return rv; - rv = authenticate_perform (&args->auth, &args->base); + /* Compatibility, hook into GckModule signals if no interaction set */ + if (args->interaction) + interaction = g_object_ref (args->interaction); + else + interaction = _gck_interaction_new (args->key_object); + + rv = _gck_session_authenticate_key (args->base.pkcs11, args->base.handle, + args->key_object, interaction, NULL); + + g_object_unref (interaction); + if (rv != CKR_OK) return rv; @@ -2140,19 +2029,12 @@ perform_crypt (Crypt *args) return (args->complete_func) (args->base.handle, args->input, args->n_input, args->result, &args->n_result); } -static gboolean -complete_crypt (Crypt *args, CK_RV result) -{ - if (!authenticate_complete (&args->auth, &args->base, result)) - return FALSE; - - /* Call is complete */ - return TRUE; -} - static void free_crypt (Crypt *args) { + g_clear_object (&args->interaction); + g_clear_object (&args->key_object); + g_free (args->input); g_free (args->result); g_free (args); @@ -2164,7 +2046,6 @@ crypt_sync (GckSession *self, GckObject *key, GckMechanism *mechanism, const guc CK_C_EncryptInit init_func, CK_C_Encrypt complete_func) { Crypt args; - GckSlot *slot; g_return_val_if_fail (GCK_IS_OBJECT (key), NULL); g_return_val_if_fail (mechanism, NULL); @@ -2185,11 +2066,10 @@ crypt_sync (GckSession *self, GckObject *key, GckMechanism *mechanism, const guc args.init_func = init_func; args.complete_func = complete_func; - slot = gck_session_get_slot (self); - authenticate_init (&args.auth, slot, key, self->pv->options); - g_object_unref (slot); + args.key_object = key; + args.interaction = self->pv->interaction; - if (!_gck_call_sync (self, perform_crypt, complete_crypt, &args, cancellable, error)) { + if (!_gck_call_sync (self, perform_crypt, NULL, &args, cancellable, error)) { g_free (args.result); return NULL; } @@ -2203,8 +2083,7 @@ crypt_async (GckSession *self, GckObject *key, GckMechanism *mechanism, const gu gsize n_input, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data, CK_C_EncryptInit init_func, CK_C_Encrypt complete_func) { - Crypt *args = _gck_call_async_prep (self, self, perform_crypt, complete_crypt, sizeof (*args), free_crypt); - GckSlot *slot; + Crypt *args = _gck_call_async_prep (self, self, perform_crypt, NULL, sizeof (*args), free_crypt); g_return_if_fail (GCK_IS_OBJECT (key)); g_return_if_fail (mechanism); @@ -2223,9 +2102,8 @@ crypt_async (GckSession *self, GckObject *key, GckMechanism *mechanism, const gu args->init_func = init_func; args->complete_func = complete_func; - slot = gck_session_get_slot (self); - authenticate_init (&args->auth, slot, key, self->pv->options); - g_object_unref (slot); + args->key_object = g_object_ref (key); + args->interaction = gck_session_get_interaction (self); _gck_call_async_ready_go (args, cancellable, callback, user_data); } @@ -2614,8 +2492,9 @@ gck_session_sign_finish (GckSession *self, GAsyncResult *result, typedef struct _Verify { GckArguments base; - /* Authenticator */ - Authenticate auth; + /* Interaction */ + GckObject *key_object; + GTlsInteraction *interaction; /* Input */ CK_OBJECT_HANDLE key; @@ -2630,6 +2509,7 @@ typedef struct _Verify { static CK_RV perform_verify (Verify *args) { + GTlsInteraction *interaction; CK_RV rv; /* Initialize the crypt operation */ @@ -2637,7 +2517,18 @@ perform_verify (Verify *args) if (rv != CKR_OK) return rv; - rv = authenticate_perform (&args->auth, &args->base); + /* Compatibility, hook into GckModule signals if no interaction set */ + if (args->interaction) + interaction = g_object_ref (args->interaction); + else + interaction = _gck_interaction_new (args->key_object); + + + rv = _gck_session_authenticate_key (args->base.pkcs11, args->base.handle, + args->key_object, interaction, NULL); + + g_object_unref (interaction); + if (rv != CKR_OK) return rv; @@ -2646,19 +2537,12 @@ perform_verify (Verify *args) args->signature, args->n_signature); } -static gboolean -complete_verify (Verify *args, CK_RV result) -{ - if (!authenticate_complete (&args->auth, &args->base, result)) - return FALSE; - - /* Call is complete */ - return TRUE; -} - static void free_verify (Verify *args) { + g_clear_object (&args->interaction); + g_clear_object (&args->key_object); + g_free (args->input); g_free (args->signature); g_free (args); @@ -2713,7 +2597,6 @@ gck_session_verify_full (GckSession *self, GckObject *key, GckMechanism *mechani gsize n_signature, GCancellable *cancellable, GError **error) { Verify args; - GckSlot *slot; g_return_val_if_fail (GCK_IS_OBJECT (key), FALSE); g_return_val_if_fail (mechanism, FALSE); @@ -2731,11 +2614,10 @@ gck_session_verify_full (GckSession *self, GckObject *key, GckMechanism *mechani args.signature = (guchar*)signature; args.n_signature = n_signature; - slot = gck_session_get_slot (self); - authenticate_init (&args.auth, slot, key, self->pv->options); - g_object_unref (slot); + args.key_object = key; + args.interaction = self->pv->interaction; - return _gck_call_sync (self, perform_verify, complete_verify, &args, cancellable, error); + return _gck_call_sync (self, perform_verify, NULL, &args, cancellable, error); } /** @@ -2760,8 +2642,7 @@ gck_session_verify_async (GckSession *self, GckObject *key, GckMechanism *mechan gsize n_signature, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { - Verify *args = _gck_call_async_prep (self, self, perform_verify, complete_verify, sizeof (*args), free_verify); - GckSlot *slot; + Verify *args = _gck_call_async_prep (self, self, perform_verify, NULL, sizeof (*args), free_verify); g_return_if_fail (GCK_IS_OBJECT (key)); g_return_if_fail (mechanism); @@ -2777,9 +2658,8 @@ gck_session_verify_async (GckSession *self, GckObject *key, GckMechanism *mechan args->signature = signature && n_signature ? g_memdup (signature, n_signature) : NULL; args->n_signature = n_signature; - slot = gck_session_get_slot (self); - authenticate_init (&args->auth, slot, key, self->pv->options); - g_object_unref (slot); + args->key_object = g_object_ref (key); + args->interaction = gck_session_get_interaction (self); _gck_call_async_ready_go (args, cancellable, callback, user_data); } @@ -2799,3 +2679,302 @@ gck_session_verify_finish (GckSession *self, GAsyncResult *result, GError **erro { return _gck_call_basic_finish (result, error); } + +static void +update_password_for_token (GTlsPassword *password, + CK_TOKEN_INFO *token_info, + gboolean request_retry) +{ + GTlsPasswordFlags flags; + gchar *label; + + label = gck_string_from_chars (token_info->label, sizeof (token_info->label)); + g_tls_password_set_description (password, label); + g_free (label); + + flags = 0; + if (request_retry) + flags |= G_TLS_PASSWORD_RETRY; + if (token_info && token_info->flags & CKF_USER_PIN_COUNT_LOW) + flags |= G_TLS_PASSWORD_MANY_TRIES; + if (token_info && token_info->flags & CKF_USER_PIN_FINAL_TRY) + flags |= G_TLS_PASSWORD_FINAL_TRY; + g_tls_password_set_flags (password, flags); +} + +CK_RV +_gck_session_authenticate_token (CK_FUNCTION_LIST_PTR funcs, + CK_SESSION_HANDLE session, + GckSlot *token, + GTlsInteraction *interaction, + GCancellable *cancellable) +{ + CK_SESSION_INFO session_info; + GTlsPassword *password = NULL; + CK_TOKEN_INFO token_info; + GTlsInteractionResult res; + gboolean request_retry; + CK_SLOT_ID slot_id; + CK_BYTE_PTR pin; + CK_ULONG n_pin; + CK_RV rv = CKR_OK; + GError *error = NULL; + + g_assert (funcs != NULL); + g_assert (GCK_IS_SLOT (token)); + + slot_id = gck_slot_get_handle (token); + request_retry = FALSE; + + do { + if (g_cancellable_is_cancelled (cancellable)) { + rv = CKR_FUNCTION_CANCELED; + break; + } + + rv = (funcs->C_GetTokenInfo) (slot_id, &token_info); + if (rv != CKR_OK) { + g_warning ("couldn't get token info when logging in: %s", + gck_message_from_rv (rv)); + break; + } + + /* No login necessary? */ + if ((token_info.flags & CKF_LOGIN_REQUIRED) == 0) { + _gck_debug ("no login required for token, skipping login"); + rv = CKR_OK; + break; + } + + /* Next check if session is logged in? */ + rv = (funcs->C_GetSessionInfo) (session, &session_info); + if (rv != CKR_OK) { + g_warning ("couldn't get session info when logging in: %s", + gck_message_from_rv (rv)); + break; + } + + /* Already logged in? */ + if (session_info.state == CKS_RW_USER_FUNCTIONS || + session_info.state == CKS_RO_USER_FUNCTIONS || + session_info.state == CKS_RW_SO_FUNCTIONS) { + _gck_debug ("already logged in, skipping login"); + rv = CKR_OK; + break; + } + + if (token_info.flags & CKF_PROTECTED_AUTHENTICATION_PATH) { + _gck_debug ("trying to log into session: protected authentication path, no password"); + + /* No password passed for PAP */ + pin = NULL; + n_pin = 0; + + + /* Not protected auth path */ + } else { + _gck_debug ("trying to log into session: want password %s", + request_retry ? "login was incorrect" : ""); + + if (password == NULL) + password = g_object_new (GCK_TYPE_PASSWORD, "token", token, NULL); + + update_password_for_token (password, &token_info, request_retry); + + if (interaction == NULL) + res = G_TLS_INTERACTION_UNHANDLED; + + else + res = g_tls_interaction_invoke_ask_password (interaction, + G_TLS_PASSWORD (password), + NULL, &error); + + if (res == G_TLS_INTERACTION_FAILED) { + g_message ("interaction couldn't ask password: %s", error->message); + rv = _gck_rv_from_error (error, CKR_USER_NOT_LOGGED_IN); + g_clear_error (&error); + break; + + } else if (res == G_TLS_INTERACTION_UNHANDLED) { + g_message ("couldn't authenticate: no interaction handler"); + rv = CKR_USER_NOT_LOGGED_IN; + break; + } + + pin = (CK_BYTE_PTR)g_tls_password_get_value (password, &n_pin); + } + + /* Try to log in */ + rv = (funcs->C_Login) (session, CKU_USER, (CK_BYTE_PTR)pin, n_pin); + + /* Only one C_Login call if protected auth path */ + if (token_info.flags & CKF_PROTECTED_AUTHENTICATION_PATH) + break; + + request_retry = TRUE; + } while (rv == CKR_PIN_INCORRECT); + + g_clear_object (&password); + + return rv; +} + +static void +update_password_for_key (GTlsPassword *password, + CK_TOKEN_INFO *token_info, + gboolean request_retry) +{ + GTlsPasswordFlags flags; + + flags = 0; + if (request_retry) + flags |= G_TLS_PASSWORD_RETRY; + if (token_info && token_info->flags & CKF_USER_PIN_COUNT_LOW) + flags |= G_TLS_PASSWORD_MANY_TRIES; + if (token_info && token_info->flags & CKF_USER_PIN_FINAL_TRY) + flags |= G_TLS_PASSWORD_FINAL_TRY; + g_tls_password_set_flags (password, flags); +} + +CK_RV +_gck_session_authenticate_key (CK_FUNCTION_LIST_PTR funcs, + CK_SESSION_HANDLE session, + GckObject *key, + GTlsInteraction *interaction, + GCancellable *cancellable) +{ + CK_ATTRIBUTE attrs[2]; + CK_SESSION_INFO session_info; + CK_TOKEN_INFO token_info; + GTlsPassword *password = NULL; + CK_OBJECT_HANDLE handle; + GTlsInteractionResult res; + gboolean request_retry; + GError *error = NULL; + CK_BYTE_PTR pin; + gsize pin_len; + CK_BBOOL bvalue; + gboolean got_label; + CK_RV rv; + + g_assert (funcs != NULL); + + handle = gck_object_get_handle (key); + + attrs[0].type = CKA_LABEL; + attrs[0].pValue = NULL; + attrs[0].ulValueLen = 0; + attrs[1].type = CKA_ALWAYS_AUTHENTICATE; + attrs[1].pValue = &bvalue; + attrs[1].ulValueLen = sizeof (bvalue); + + rv = (funcs->C_GetAttributeValue) (session, handle, attrs, 2); + if (rv == CKR_ATTRIBUTE_TYPE_INVALID) { + bvalue = CK_FALSE; + + } else if (rv != CKR_OK) { + g_message ("couldn't check whether key requires authentication, assuming it doesn't: %s", + gck_message_from_rv (rv)); + return CKR_OK; + } + + /* No authentication needed, on this object */ + if (bvalue != CK_TRUE) { + _gck_debug ("key does not require authentication"); + return CKR_OK; + } + + got_label = FALSE; + request_retry = FALSE; + + do { + if (g_cancellable_is_cancelled (cancellable)) { + rv = CKR_FUNCTION_CANCELED; + break; + } + + rv = (funcs->C_GetSessionInfo) (session, &session_info); + if (rv != CKR_OK) { + g_warning ("couldn't get session info when authenticating key: %s", + gck_message_from_rv (rv)); + return rv; + } + + rv = (funcs->C_GetTokenInfo) (session_info.slotID, &token_info); + if (rv != CKR_OK) { + g_warning ("couldn't get token info when authenticating key: %s", + gck_message_from_rv (rv)); + return rv; + } + + /* Protected authentication path, just use NULL passwords */ + if (token_info.flags & CKF_PROTECTED_AUTHENTICATION_PATH) { + + password = NULL; + pin = NULL; + pin_len = 0; + + /* Need to prompt for a password */ + } else { + _gck_debug ("trying to log into session: want password %s", + request_retry ? "login was incorrect" : ""); + + if (password == NULL) + password = g_object_new (GCK_TYPE_PASSWORD, "key", key, NULL); + + /* Set the password */ + update_password_for_key (password, &token_info, request_retry); + + /* Set the label properly */ + if (!got_label) { + if (attrs[0].ulValueLen && attrs[0].ulValueLen != GCK_INVALID) { + attrs[0].pValue = g_malloc0 (attrs[0].ulValueLen + 1); + rv = (funcs->C_GetAttributeValue) (session, handle, attrs, 1); + if (rv == CKR_OK) { + ((gchar *)attrs[0].pValue)[attrs[0].ulValueLen] = 0; + g_tls_password_set_description (password, attrs[0].pValue); + } + g_free (attrs[0].pValue); + attrs[0].pValue = NULL; + } + + got_label = TRUE; + } + + if (interaction == NULL) + res = G_TLS_INTERACTION_UNHANDLED; + + else + res = g_tls_interaction_invoke_ask_password (interaction, + G_TLS_PASSWORD (password), + NULL, &error); + + if (res == G_TLS_INTERACTION_FAILED) { + g_message ("interaction couldn't ask password: %s", error->message); + rv = _gck_rv_from_error (error, CKR_USER_NOT_LOGGED_IN); + g_clear_error (&error); + break; + + } else if (res == G_TLS_INTERACTION_UNHANDLED) { + g_message ("couldn't authenticate: no interaction handler"); + rv = CKR_USER_NOT_LOGGED_IN; + break; + } + + pin = (CK_BYTE_PTR)g_tls_password_get_value (G_TLS_PASSWORD (password), &pin_len); + } + + /* Try to log in */ + rv = (funcs->C_Login) (session, CKU_CONTEXT_SPECIFIC, pin, pin_len); + + /* Only one C_Login call if protected auth path */ + if (token_info.flags & CKF_PROTECTED_AUTHENTICATION_PATH) + break; + + request_retry = TRUE; + } while (rv == CKR_PIN_INCORRECT); + + g_clear_object (&password); + + return rv; +} diff --git a/gck/gck-slot.c b/gck/gck-slot.c index 05c7d8e..2be6a83 100644 --- a/gck/gck-slot.c +++ b/gck/gck-slot.c @@ -49,12 +49,17 @@ enum { PROP_0, PROP_MODULE, - PROP_HANDLE + PROP_HANDLE, + PROP_INTERACTION }; struct _GckSlotPrivate { GckModule *module; CK_SLOT_ID handle; + + /* Changable data locked by mutex */ + GMutex *mutex; + GTlsInteraction *interaction; }; G_DEFINE_TYPE (GckSlot, gck_slot, G_TYPE_OBJECT); @@ -64,7 +69,9 @@ G_DEFINE_TYPE (GckSlot, gck_slot, G_TYPE_OBJECT); */ static GckSession* -make_session_object (GckSlot *self, guint options, CK_SESSION_HANDLE handle) +make_session_object (GckSlot *self, + GckSessionOptions options, + CK_SESSION_HANDLE handle) { GckSession *session; GckModule *module; @@ -89,6 +96,7 @@ static void gck_slot_init (GckSlot *self) { self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_SLOT, GckSlotPrivate); + self->pv->mutex = g_mutex_new (); } static void @@ -104,6 +112,12 @@ gck_slot_get_property (GObject *obj, guint prop_id, GValue *value, case PROP_HANDLE: g_value_set_ulong (value, gck_slot_get_handle (self)); break; + case PROP_INTERACTION: + g_value_take_object (value, gck_slot_get_interaction (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; } } @@ -126,12 +140,22 @@ gck_slot_set_property (GObject *obj, guint prop_id, const GValue *value, g_assert (!self->pv->handle); self->pv->handle = g_value_get_ulong (value); break; + case PROP_INTERACTION: + gck_slot_set_interaction (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; } } static void gck_slot_dispose (GObject *obj) { + GckSlot *self = GCK_SLOT (obj); + + gck_slot_set_interaction (self, NULL); + G_OBJECT_CLASS (gck_slot_parent_class)->dispose (obj); } @@ -139,16 +163,16 @@ static void gck_slot_finalize (GObject *obj) { GckSlot *self = GCK_SLOT (obj); - self->pv->handle = 0; - if (self->pv->module) - g_object_unref (self->pv->module); - self->pv->module = NULL; + g_assert (self->pv->interaction == NULL); + + self->pv->handle = 0; + g_clear_object (&self->pv->module); + g_mutex_free (self->pv->mutex); G_OBJECT_CLASS (gck_slot_parent_class)->finalize (obj); } - static void gck_slot_class_init (GckSlotClass *klass) { @@ -178,6 +202,17 @@ gck_slot_class_init (GckSlotClass *klass) g_param_spec_ulong ("handle", "Handle", "PKCS11 Slot ID", 0, G_MAXULONG, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + /** + * GckSlot:interaction: + * + * Interaction object used to ask the user for pins when opening + * sessions. Used if the session_options of the enumerator have + * %GCK_SESSION_LOGIN_USER or %GCK_SESSION_AUTHENTICATE + */ + g_object_class_install_property (gobject_class, PROP_INTERACTION, + g_param_spec_object ("interaction", "Interaction", "Interaction asking for pins", + G_TYPE_TLS_INTERACTION, G_PARAM_READWRITE)); + g_type_class_add_private (gobject_class, sizeof (GckSlotPrivate)); } @@ -909,6 +944,62 @@ gck_slot_has_flags (GckSlot *self, gulong flags) } /** + * gck_slot_get_interaction: + * @self: the slot + * + * Get the interaction used when a pin is needed + * + * Returns: (transfer full) (allow-none): the interaction or %NULL + */ +GTlsInteraction * +gck_slot_get_interaction (GckSlot *self) +{ + GTlsInteraction *result = NULL; + + g_return_val_if_fail (GCK_IS_SLOT (self), NULL); + + g_mutex_lock (self->pv->mutex); + + if (self->pv->interaction) + result = g_object_ref (self->pv->interaction); + + g_mutex_unlock (self->pv->mutex); + + return result; +} + +/** + * gck_slot_set_interaction: + * @self: the slot + * @interaction: (allow-none): the interaction or %NULL + * + * Set the interaction used when a pin is needed + */ +void +gck_slot_set_interaction (GckSlot *self, + GTlsInteraction *interaction) +{ + GTlsInteraction *previous = NULL; + + g_return_if_fail (GCK_IS_SLOT (self)); + g_return_if_fail (interaction == NULL || G_IS_TLS_INTERACTION (interaction)); + + g_mutex_lock (self->pv->mutex); + + if (interaction != self->pv->interaction) { + previous = self->pv->interaction; + self->pv->interaction = interaction; + if (interaction) + g_object_ref (interaction); + } + + g_mutex_unlock (self->pv->mutex); + + g_clear_object (&previous); + g_object_notify (G_OBJECT (self), "interaction"); +} + +/** * gck_slots_enumerate_objects: * @slots: (element-type Gck.Slot): a list of #GckSlot to enumerate objects on. * @attrs: Attributes that the objects must have, or empty for all objects. @@ -938,6 +1029,7 @@ gck_slots_enumerate_objects (GList *slots, typedef struct OpenSession { GckArguments base; + GTlsInteraction *interaction; GckSlot *slot; gulong flags; gpointer app_data; @@ -950,9 +1042,8 @@ typedef struct OpenSession { static CK_RV perform_open_session (OpenSession *args) { - CK_SESSION_INFO info; + GTlsInteraction *interaction; CK_RV rv = CKR_OK; - CK_ULONG pin_len; /* Can be called multiple times */ @@ -965,52 +1056,26 @@ perform_open_session (OpenSession *args) if (rv != CKR_OK || !args->auto_login) return rv; - /* Step two, check if session is logged in */ - rv = (args->base.pkcs11->C_GetSessionInfo) (args->session, &info); - if (rv != CKR_OK) - return rv; - - /* Already logged in? */ - if (info.state != CKS_RO_PUBLIC_SESSION && info.state != CKS_RW_PUBLIC_SESSION) - return CKR_OK; - - /* Try to login */ - pin_len = args->password ? strlen (args->password) : 0; - return (args->base.pkcs11->C_Login) (args->session, CKU_USER, - (CK_UTF8CHAR_PTR)args->password, pin_len); -} - -static gboolean -complete_open_session (OpenSession *args, CK_RV result) -{ - GckModule *module; - gboolean ret = TRUE; - - g_free (args->password); - args->password = NULL; - - /* Ask the token for a password */ - module = gck_slot_get_module (args->slot); - - if (args->auto_login && result == CKR_PIN_INCORRECT) { + /* Compatibility, hook into GckModule signals if no interaction set */ + if (args->interaction) + interaction = g_object_ref (args->interaction); + else + interaction = _gck_interaction_new (args->slot); - ret = _gck_module_fire_authenticate_slot (module, args->slot, NULL, &args->password); + rv = _gck_session_authenticate_token (args->base.pkcs11, args->session, + args->slot, interaction, NULL); - /* If authenticate returns TRUE then call is not complete */ - ret = !ret; - } - - g_object_unref (module); + g_object_unref (interaction); - return ret; + return rv; } static void free_open_session (OpenSession *args) { + g_clear_object (&args->interaction); + g_clear_object (&args->slot); g_assert (!args->password); - if (args->slot) - g_object_unref (args->slot); g_free (args); } @@ -1055,8 +1120,13 @@ gck_slot_open_session (GckSlot *self, * Returns: (transfer full): a new session or %NULL if an error occurs **/ GckSession * -gck_slot_open_session_full (GckSlot *self, guint options, gulong pkcs11_flags, gpointer app_data, - CK_NOTIFY notify, GCancellable *cancellable, GError **error) +gck_slot_open_session_full (GckSlot *self, + GckSessionOptions options, + gulong pkcs11_flags, + gpointer app_data, + CK_NOTIFY notify, + GCancellable *cancellable, + GError **error) { OpenSession args = { GCK_ARGUMENTS_INIT, 0, }; GckSession *session = NULL; @@ -1072,6 +1142,7 @@ gck_slot_open_session_full (GckSlot *self, guint options, gulong pkcs11_flags, g args.notify = notify; args.password = NULL; args.session = 0; + args.interaction = gck_slot_get_interaction (self); args.auto_login = ((options & GCK_SESSION_LOGIN_USER) == GCK_SESSION_LOGIN_USER); @@ -1079,9 +1150,10 @@ gck_slot_open_session_full (GckSlot *self, guint options, gulong pkcs11_flags, g if ((options & GCK_SESSION_READ_WRITE) == GCK_SESSION_READ_WRITE) args.flags |= CKF_RW_SESSION; - if (_gck_call_sync (self, perform_open_session, complete_open_session, &args, cancellable, error)) + if (_gck_call_sync (self, perform_open_session, NULL, &args, cancellable, error)) session = make_session_object (self, options, args.session); + g_clear_object (&args.interaction); g_object_unref (module); g_object_unref (self); @@ -1128,20 +1200,26 @@ gck_slot_open_session_async (GckSlot *self, * This call will return immediately and complete asynchronously. **/ void -gck_slot_open_session_full_async (GckSlot *self, guint options, gulong pkcs11_flags, gpointer app_data, - CK_NOTIFY notify, GCancellable *cancellable, - GAsyncReadyCallback callback, gpointer user_data) +gck_slot_open_session_full_async (GckSlot *self, + GckSessionOptions options, + gulong pkcs11_flags, + gpointer app_data, + CK_NOTIFY notify, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { OpenSession *args; g_object_ref (self); - args = _gck_call_async_prep (self, self, perform_open_session, complete_open_session, + args = _gck_call_async_prep (self, self, perform_open_session, NULL, sizeof (*args), free_open_session); args->app_data = app_data; args->notify = notify; args->slot = g_object_ref (self); + args->interaction = gck_slot_get_interaction (self); args->auto_login = ((options & GCK_SESSION_LOGIN_USER) == GCK_SESSION_LOGIN_USER); @@ -434,6 +434,11 @@ struct _GckEnumeratorClass { GType gck_enumerator_get_type (void) G_GNUC_CONST; +GTlsInteraction * gck_enumerator_get_interaction (GckEnumerator *self); + +void gck_enumerator_set_interaction (GckEnumerator *self, + GTlsInteraction *interaction); + GckObject* gck_enumerator_next (GckEnumerator *self, GCancellable *cancellable, GError **error); @@ -587,9 +592,10 @@ GckMechanismInfo* gck_slot_get_mechanism_info (GckSlot *self, gboolean gck_slot_has_flags (GckSlot *self, gulong flags); -GckEnumerator* gck_slots_enumerate_objects (GList *slots, - GckAttributes *attrs, - GckSessionOptions options); +GTlsInteraction * gck_slot_get_interaction (GckSlot *self); + +void gck_slot_set_interaction (GckSlot *self, + GTlsInteraction *interaction); GckSession* gck_slot_open_session (GckSlot *self, GckSessionOptions options, @@ -623,6 +629,10 @@ GckSession* gck_slot_open_session_finish (GckSlot *self, GAsyncResult *result, GError **error); +GckEnumerator* gck_slots_enumerate_objects (GList *slots, + GckAttributes *attrs, + GckSessionOptions options); + /* ------------------------------------------------------------------------ * SESSION */ @@ -689,6 +699,8 @@ gulong gck_session_get_state (GckSession *self); GckSessionOptions gck_session_get_options (GckSession *self); +GTlsInteraction * gck_session_get_interaction (GckSession *self); + gboolean gck_session_init_pin (GckSession *self, const guchar *pin, gsize n_pin, @@ -1193,6 +1205,44 @@ GckAttributes* gck_object_get_template_finish (GckObject *self, GAsyncResult *result, GError **error); +/* ------------------------------------------------------------------------ + * PASSWORD + */ + +#define GCK_TYPE_PASSWORD (gck_password_get_type ()) +#define GCK_PASSWORD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_PASSWORD, GckPassword)) +#define GCK_PASSWORD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_PASSWORD, GckPassword)) +#define GCK_IS_PASSWORD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_PASSWORD)) +#define GCK_IS_PASSWORD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_PASSWORD)) +#define GCK_PASSWORD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_PASSWORD, GckPasswordClass)) + +typedef struct _GckPassword GckPassword; +typedef struct _GckPasswordClass GckPasswordClass; +typedef struct _GckPasswordPrivate GckPasswordPrivate; + +struct _GckPassword { + GTlsPassword parent; + + /*< private >*/ + GckPasswordPrivate *pv; + gpointer reserved[4]; +}; + +struct _GckPasswordClass { + GTlsPasswordClass parent; + + /*< private >*/ + gpointer reserved[4]; +}; + +GType gck_password_get_type (void) G_GNUC_CONST; + +GckModule * gck_password_get_module (GckPassword *self); + +GckSlot * gck_password_get_token (GckPassword *self); + +GckObject * gck_password_get_key (GckPassword *self); + /* ---------------------------------------------------------------------------- * URI */ diff --git a/gck/gck.symbols b/gck/gck.symbols index 37f7f67..d5afd66 100644 --- a/gck/gck.symbols +++ b/gck/gck.symbols @@ -53,11 +53,13 @@ gck_attributes_new_empty gck_attributes_new_full gck_attributes_ref gck_attributes_unref +gck_enumerator_get_interaction gck_enumerator_get_type gck_enumerator_next gck_enumerator_next_async gck_enumerator_next_finish gck_enumerator_next_n +gck_enumerator_set_interaction gck_error_get_quark gck_error_get_type gck_get_error_quark @@ -122,6 +124,10 @@ gck_object_set_template gck_object_set_template_async gck_object_set_template_finish gck_objects_from_handle_array +gck_password_get_key +gck_password_get_module +gck_password_get_token +gck_password_get_type gck_session_create_object gck_session_create_object_async gck_session_create_object_finish @@ -147,6 +153,7 @@ gck_session_generate_key_pair_finish gck_session_generate_key_pair_full gck_session_get_handle gck_session_get_info +gck_session_get_interaction gck_session_get_module gck_session_get_options gck_session_get_slot @@ -188,6 +195,7 @@ gck_slot_equal gck_slot_from_handle gck_slot_get_handle gck_slot_get_info +gck_slot_get_interaction gck_slot_get_mechanism_info gck_slot_get_mechanisms gck_slot_get_module @@ -204,6 +212,7 @@ gck_slot_open_session_async gck_slot_open_session_finish gck_slot_open_session_full gck_slot_open_session_full_async +gck_slot_set_interaction gck_slots_enumerate_objects gck_string_from_chars gck_string_to_chars diff --git a/gck/tests/Makefile.am b/gck/tests/Makefile.am index 99aaf10..734e9d8 100644 --- a/gck/tests/Makefile.am +++ b/gck/tests/Makefile.am @@ -27,6 +27,10 @@ TEST_PROGS = \ test-gck-enumerator \ test-gck-modules +test_gck_enumerator_SOURCES = \ + test-gck-enumerator.c \ + mock-interaction.c mock-interaction.h + check_PROGRAMS = $(TEST_PROGS) test: $(TEST_PROGS) diff --git a/gck/tests/mock-interaction.c b/gck/tests/mock-interaction.c new file mode 100644 index 0000000..ed25a47 --- /dev/null +++ b/gck/tests/mock-interaction.c @@ -0,0 +1,97 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* mock-interaction.c + + Copyright (C) 2011 Collabora Ltd + + 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 <stefw@collabora.co.uk> +*/ + +#include "config.h" + +#include "mock-interaction.h" + +#define MOCK_INTERACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOCK_TYPE_INTERACTION, MockInteraction)) +#define MOCK_IS_INTERACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MOCK_TYPE_INTERACTION)) +#define MOCK_INTERACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOCK_TYPE_INTERACTION, MockInteractionClass)) + +typedef struct _MockInteractionClass MockInteractionClass; + +struct _MockInteraction { + GTlsInteraction interaction; + gchar *password; +}; + +struct _MockInteractionClass { + GTlsInteractionClass parent; +}; + +G_DEFINE_TYPE (MockInteraction, mock_interaction, G_TYPE_TLS_INTERACTION); + +static void +mock_interaction_init (MockInteraction *self) +{ + +} + +static void +mock_interaction_finalize (GObject *obj) +{ + MockInteraction *self = MOCK_INTERACTION (obj); + + g_free (self->password); + + G_OBJECT_CLASS (mock_interaction_parent_class)->dispose (obj); +} + +static GTlsInteractionResult +mock_interaction_ask_password (GTlsInteraction *interaction, + GTlsPassword *password, + GCancellable *cancellable, + GError **error) +{ + MockInteraction *self = MOCK_INTERACTION (interaction); + + if (self->password) { + g_tls_password_set_value (password, (const guchar *)self->password, -1); + return G_TLS_INTERACTION_HANDLED; + } else { + return G_TLS_INTERACTION_UNHANDLED; + } +} + +static void +mock_interaction_class_init (MockInteractionClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GTlsInteractionClass *interaction_class = G_TLS_INTERACTION_CLASS (klass); + + object_class->finalize = mock_interaction_finalize; + + interaction_class->ask_password = mock_interaction_ask_password; +} + +GTlsInteraction * +mock_interaction_new (const gchar *password) +{ + MockInteraction *result; + + result = g_object_new (MOCK_TYPE_INTERACTION, NULL); + result->password = g_strdup (password); + + return G_TLS_INTERACTION (result); +} diff --git a/gck/tests/mock-interaction.h b/gck/tests/mock-interaction.h new file mode 100644 index 0000000..0747f4b --- /dev/null +++ b/gck/tests/mock-interaction.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* mock-interaction.h + + Copyright (C) 2011 Collabora Ltd + + 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 <stefw@collabora.co.uk> +*/ + +#ifndef MOCK_INTERACTION_H +#define MOCK_INTERACTION_H + +#include <gio/gio.h> + +G_BEGIN_DECLS + +#define MOCK_TYPE_INTERACTION (mock_interaction_get_type ()) +#define MOCK_INTERACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOCK_TYPE_INTERACTION, MockInteraction)) +#define MOCK_IS_INTERACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOCK_TYPE_INTERACTION)) + +typedef struct _MockInteraction MockInteraction; + +GType mock_interaction_get_type (void) G_GNUC_CONST; + +GTlsInteraction * mock_interaction_new (const gchar *password); + +G_END_DECLS + +#endif /* MOCK_INTERACTION_H */ diff --git a/gck/tests/test-gck-crypto.c b/gck/tests/test-gck-crypto.c index 3aac0fc..9630123 100644 --- a/gck/tests/test-gck-crypto.c +++ b/gck/tests/test-gck-crypto.c @@ -631,6 +631,8 @@ main (int argc, char **argv) g_type_init (); g_test_init (&argc, &argv, NULL); + g_set_prgname ("test-gck-crypto"); + g_test_add ("/gck/crypto/encrypt", Test, NULL, setup, test_encrypt, teardown); g_test_add ("/gck/crypto/decrypt", Test, NULL, setup, test_decrypt, teardown); g_test_add ("/gck/crypto/login_context_specific", Test, NULL, setup, test_login_context_specific, teardown); diff --git a/gck/tests/test-gck-enumerator.c b/gck/tests/test-gck-enumerator.c index 9287b1b..d5d669e 100644 --- a/gck/tests/test-gck-enumerator.c +++ b/gck/tests/test-gck-enumerator.c @@ -28,6 +28,8 @@ #include "gck/gck-private.h" #include "gck/gck-test.h" +#include "mock-interaction.h" + #include "egg/egg-testing.h" #include <glib.h> @@ -242,6 +244,76 @@ test_attributes (Test *test, gconstpointer unused) } static void +test_authenticate_interaction (Test *test, + gconstpointer unused) +{ + GTlsInteraction *interaction; + GTlsInteraction *check; + GckUriData *uri_data; + GError *error = NULL; + GckEnumerator *en; + GckObject *obj; + + uri_data = gck_uri_data_new (); + en = _gck_enumerator_new (test->modules, GCK_SESSION_LOGIN_USER, uri_data); + g_assert (GCK_IS_ENUMERATOR (en)); + + interaction = mock_interaction_new ("booo"); + g_object_set (en, "interaction", interaction, NULL); + + check = NULL; + g_object_get (en, "interaction", &check, NULL); + g_assert (interaction == check); + g_object_unref (interaction); + + obj = gck_enumerator_next (en, NULL, &error); + g_assert (GCK_IS_OBJECT (obj)); + + g_object_unref (obj); + g_object_unref (en); +} + +static gboolean +on_authenticate_token (GckModule *module, + GckSlot *slot, + gchar *label, + gchar **password, + gpointer unused) +{ + g_assert (unused == GUINT_TO_POINTER (35)); + g_assert (password != NULL); + g_assert (*password == NULL); + g_assert (GCK_IS_MODULE (module)); + g_assert (GCK_IS_SLOT (slot)); + + *password = g_strdup ("booo"); + return TRUE; +} + +static void +test_authenticate_compat (Test *test, + gconstpointer unused) +{ + GckUriData *uri_data; + GError *error = NULL; + GckEnumerator *en; + GckObject *obj; + + g_signal_connect (test->modules->data, "authenticate-slot", + G_CALLBACK (on_authenticate_token), GUINT_TO_POINTER (35)); + + uri_data = gck_uri_data_new (); + en = _gck_enumerator_new (test->modules, GCK_SESSION_LOGIN_USER, uri_data); + 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); +} + +static void test_token_match (Test *test, gconstpointer unused) { GckUriData *uri_data; @@ -269,6 +341,8 @@ main (int argc, char **argv) g_type_init (); g_test_init (&argc, &argv, NULL); + g_set_prgname ("test-gck-enumerator"); + g_test_add ("/gck/enumerator/create", Test, NULL, setup, test_create, teardown); g_test_add ("/gck/enumerator/create_slots", Test, NULL, setup, test_create_slots, teardown); g_test_add ("/gck/enumerator/next", Test, NULL, setup, test_next, teardown); @@ -276,6 +350,8 @@ main (int argc, char **argv) g_test_add ("/gck/enumerator/next_and_resume", Test, NULL, setup, test_next_and_resume, teardown); g_test_add ("/gck/enumerator/next_n", Test, NULL, setup, test_next_n, teardown); g_test_add ("/gck/enumerator/next_async", Test, NULL, setup, test_next_async, teardown); + g_test_add ("/gck/enumerator/authenticate-interaction", Test, NULL, setup, test_authenticate_interaction, teardown); + g_test_add ("/gck/enumerator/authenticate-compat", Test, NULL, setup, test_authenticate_compat, teardown); g_test_add ("/gck/enumerator/attributes", Test, NULL, setup, test_attributes, teardown); g_test_add ("/gck/enumerator/token_match", Test, NULL, setup, test_token_match, teardown); diff --git a/gck/tests/test-gck-session.c b/gck/tests/test-gck-session.c index e89ce54..632a760 100644 --- a/gck/tests/test-gck-session.c +++ b/gck/tests/test-gck-session.c @@ -306,6 +306,8 @@ main (int argc, char **argv) g_type_init (); g_test_init (&argc, &argv, NULL); + g_set_prgname ("test-gck-session"); + g_test_add ("/gck/session/session_props", Test, NULL, setup, test_session_props, teardown); g_test_add ("/gck/session/session_info", Test, NULL, setup, test_session_info, teardown); g_test_add ("/gck/session/open_close_session", Test, NULL, setup, test_open_close_session, teardown); |