diff options
author | Stef Walter <stef@memberwebs.com> | 2010-06-12 17:38:27 +0000 |
---|---|---|
committer | Stef Walter <stef@memberwebs.com> | 2010-06-12 21:22:07 +0000 |
commit | 92852897d420071033557bf77123a447c45b0ea1 (patch) | |
tree | e2c553b3a2661ac0a9aca11f6a2076082072f239 | |
parent | 2954ca8774c95bd2a033b3d62a64ce78c1a2fe93 (diff) | |
download | gnome-keyring-92852897d420071033557bf77123a447c45b0ea1.tar.gz |
Implement auto unlock in wrap-layer.
* Including tests and necessary infrastructure.
23 files changed, 2044 insertions, 215 deletions
diff --git a/Makefile.am b/Makefile.am index 4106ac31..ca70b917 100644 --- a/Makefile.am +++ b/Makefile.am @@ -66,5 +66,5 @@ coverage: check mkdir -p testing/coverage $(LCOV) --directory . --capture --output-file testing/coverage.info $(GENHTML) --output-directory testing/coverage testing/coverage.info - echo "Coverage info in: testing/coverage/index.html" + @echo "file://$(abs_top_builddir)/testing/coverage/index.html" endif
\ No newline at end of file diff --git a/pkcs11/gkm/gkm-mock.c b/pkcs11/gkm/gkm-mock.c index 2f09df51..a49c3824 100644 --- a/pkcs11/gkm/gkm-mock.c +++ b/pkcs11/gkm/gkm-mock.c @@ -107,6 +107,138 @@ lookup_object (Session *session, CK_OBJECT_HANDLE hObject) return attrs; } +CK_OBJECT_HANDLE +gkm_mock_module_take_object (GArray *template) +{ + gboolean token; + guint handle; + + g_return_val_if_fail (the_objects, 0); + + handle = ++unique_identifier; + if (gkm_template_find_boolean (template, CKA_TOKEN, &token)) + g_return_val_if_fail (token == TRUE, 0); + else + gkm_template_set_boolean (template, CKA_TOKEN, CK_TRUE); + g_hash_table_insert (the_objects, GUINT_TO_POINTER (handle), template); + return handle; +} + +void +gkm_mock_module_enumerate_objects (CK_SESSION_HANDLE handle, GkmMockEnumerator func, + gpointer user_data) +{ + GHashTableIter iter; + gpointer key; + gpointer value; + Session *session; + + g_assert (the_objects); + g_assert (func); + + /* Token objects */ + g_hash_table_iter_init (&iter, the_objects); + while (g_hash_table_iter_next (&iter, &key, &value)) { + if (!(func) (GPOINTER_TO_UINT (key), value, user_data)) + return; + } + + /* session objects */ + if (handle) { + session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (handle)); + if (session) { + g_hash_table_iter_init (&iter, session->objects); + while (g_hash_table_iter_next (&iter, &key, &value)) { + if (!(func) (GPOINTER_TO_UINT (key), value, user_data)) + return; + } + } + } +} + +typedef struct _FindObject { + CK_ATTRIBUTE_PTR attrs; + CK_ULONG n_attrs; + CK_OBJECT_HANDLE object; +} FindObject; + +static gboolean +enumerate_and_find_object (CK_OBJECT_HANDLE object, GArray *template, gpointer user_data) +{ + FindObject *ctx = user_data; + CK_ATTRIBUTE_PTR match, attr; + CK_ULONG i; + + for (i = 0; i < ctx->n_attrs; ++i) { + match = ctx->attrs + i; + attr = gkm_template_find (template, match->type); + if (!attr) + return TRUE; /* Continue */ + + if (attr->ulValueLen != match->ulValueLen || + memcmp (attr->pValue, match->pValue, attr->ulValueLen) != 0) + return TRUE; /* Continue */ + } + + ctx->object = object; + return FALSE; /* Stop iteration */ +} + +CK_OBJECT_HANDLE +gkm_mock_module_find_object (CK_SESSION_HANDLE session, CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs) +{ + FindObject ctx; + + ctx.attrs = attrs; + ctx.n_attrs = n_attrs; + ctx.object = 0; + + gkm_mock_module_enumerate_objects (session, enumerate_and_find_object, &ctx); + + return ctx.object; +} + +static gboolean +enumerate_and_count_objects (CK_OBJECT_HANDLE object, GArray *template, gpointer user_data) +{ + guint *n_objects = user_data; + ++(*n_objects); + return TRUE; /* Continue */ +} + +guint +gkm_mock_module_count_objects (CK_SESSION_HANDLE session) +{ + guint n_objects = 0; + gkm_mock_module_enumerate_objects (session, enumerate_and_count_objects, &n_objects); + return n_objects; +} + +void +gkm_mock_module_set_object (CK_OBJECT_HANDLE object, CK_ATTRIBUTE_PTR attrs, + CK_ULONG n_attrs) +{ + CK_ULONG i; + GArray *template; + + g_return_if_fail (object != 0); + g_return_if_fail (the_objects); + + template = g_hash_table_lookup (the_objects, GUINT_TO_POINTER (object)); + g_return_if_fail (template); + + for (i = 0; i < n_attrs; ++i) + gkm_template_set (template, attrs + i); +} + +void +gkm_mock_module_set_pin (const gchar *password) +{ + g_free (the_pin); + the_pin = g_strdup (password); + n_the_pin = strlen (password); +} + CK_RV gkm_mock_C_Initialize (CK_VOID_PTR pInitArgs) { @@ -153,6 +285,7 @@ gkm_mock_C_Initialize (CK_VOID_PTR pInitArgs) gkm_template_set_boolean (attrs, CKA_UNWRAP, CK_TRUE); gkm_template_set_boolean (attrs, CKA_DERIVE, CK_TRUE); gkm_template_set_string (attrs, CKA_VALUE, "value"); + gkm_template_set_string (attrs, CKA_GNOME_UNIQUE, "unique1"); g_hash_table_insert (the_objects, GUINT_TO_POINTER (PRIVATE_KEY_CAPITALIZE), attrs); /* Public capitalize key */ @@ -164,6 +297,7 @@ gkm_mock_C_Initialize (CK_VOID_PTR pInitArgs) gkm_template_set_boolean (attrs, CKA_ENCRYPT, CK_TRUE); gkm_template_set_boolean (attrs, CKA_PRIVATE, CK_FALSE); gkm_template_set_string (attrs, CKA_VALUE, "value"); + gkm_template_set_string (attrs, CKA_GNOME_UNIQUE, "unique2"); g_hash_table_insert (the_objects, GUINT_TO_POINTER (PUBLIC_KEY_CAPITALIZE), attrs); /* Private prefix key */ @@ -176,6 +310,7 @@ gkm_mock_C_Initialize (CK_VOID_PTR pInitArgs) gkm_template_set_boolean (attrs, CKA_PRIVATE, CK_TRUE); gkm_template_set_boolean (attrs, CKA_ALWAYS_AUTHENTICATE, CK_TRUE); gkm_template_set_string (attrs, CKA_VALUE, "value"); + gkm_template_set_string (attrs, CKA_GNOME_UNIQUE, "unique3"); g_hash_table_insert (the_objects, GUINT_TO_POINTER (PRIVATE_KEY_PREFIX), attrs); /* Private prefix key */ @@ -187,6 +322,7 @@ gkm_mock_C_Initialize (CK_VOID_PTR pInitArgs) gkm_template_set_boolean (attrs, CKA_VERIFY, CK_TRUE); gkm_template_set_boolean (attrs, CKA_PRIVATE, CK_FALSE); gkm_template_set_string (attrs, CKA_VALUE, "value"); + gkm_template_set_string (attrs, CKA_GNOME_UNIQUE, "unique4"); g_hash_table_insert (the_objects, GUINT_TO_POINTER (PUBLIC_KEY_PREFIX), attrs); initialized = TRUE; @@ -448,6 +584,13 @@ gkm_mock_C_OpenSession (CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplicat } CK_RV +gkm_mock_fail_C_OpenSession (CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, + CK_NOTIFY Notify, CK_SESSION_HANDLE_PTR phSession) +{ + return CKR_GENERAL_ERROR; +} + +CK_RV gkm_mock_C_CloseSession (CK_SESSION_HANDLE hSession) { Session *session; @@ -494,6 +637,18 @@ gkm_mock_C_GetSessionInfo (CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo if (!session) return CKR_SESSION_HANDLE_INVALID; + if (logged_in) { + if (session->info.flags & CKF_RW_SESSION) + session->info.state = CKS_RW_USER_FUNCTIONS; + else + session->info.state = CKS_RO_USER_FUNCTIONS; + } else { + if (session->info.flags & CKF_RW_SESSION) + session->info.state = CKS_RW_PUBLIC_SESSION; + else + session->info.state = CKS_RO_PUBLIC_SESSION; + } + memcpy (pInfo, &session->info, sizeof (*pInfo)); return CKR_OK; } @@ -672,21 +827,19 @@ gkm_mock_C_DestroyObject (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject) gboolean priv; session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); - g_assert (session != NULL && "No such session found"); - if (!session) - return CKR_SESSION_HANDLE_INVALID; + g_return_val_if_fail (session, CKR_SESSION_HANDLE_INVALID); attrs = lookup_object (session, hObject); - if (!attrs) { - g_assert_not_reached (); /* "no such object found" */ - return CKR_OBJECT_HANDLE_INVALID; - } + g_return_val_if_fail (attrs, CKR_OBJECT_HANDLE_INVALID); if (gkm_template_find_boolean (attrs, CKA_PRIVATE, &priv) && priv) { if (!logged_in) return CKR_USER_NOT_LOGGED_IN; } + g_hash_table_remove (the_objects, GUINT_TO_POINTER (hObject)); + g_hash_table_remove (session->objects, GUINT_TO_POINTER (hObject)); + return CKR_OK; } @@ -770,18 +923,40 @@ gkm_mock_C_SetAttributeValue (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObje return CKR_OK; } +typedef struct _FindObjects { + CK_ATTRIBUTE_PTR template; + CK_ULONG count; + Session *session; +} FindObjects; + +static gboolean +enumerate_and_find_objects (CK_OBJECT_HANDLE object, GArray *attrs, gpointer user_data) +{ + FindObjects *ctx = user_data; + CK_ATTRIBUTE_PTR match, attr; + CK_ULONG i; + + for (i = 0; i < ctx->count; ++i) { + match = ctx->template + i; + attr = gkm_template_find (attrs, match->type); + if (!attr) + return TRUE; /* Continue */ + + if (attr->ulValueLen != match->ulValueLen || + memcmp (attr->pValue, match->pValue, attr->ulValueLen) != 0) + return TRUE; /* Continue */ + } + + ctx->session->matches = g_list_prepend (ctx->session->matches, GUINT_TO_POINTER (object)); + return TRUE; /* Continue */ +} + CK_RV gkm_mock_C_FindObjectsInit (CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { - GHashTableIter iter; - GArray *attrs; - CK_ATTRIBUTE_PTR attr; - CK_ATTRIBUTE_PTR match; Session *session; - gpointer key, value; - gboolean matched = TRUE; - CK_ULONG i; + FindObjects ctx; session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession)); g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID); @@ -792,54 +967,11 @@ gkm_mock_C_FindObjectsInit (CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTempla session->operation = OP_FIND; - /* Token objects */ - g_hash_table_iter_init (&iter, the_objects); - while (g_hash_table_iter_next (&iter, &key, &value)) { - attrs = (GArray*)value; - matched = TRUE; - for (i = 0; i < ulCount; ++i) { - match = pTemplate + i; - attr = gkm_template_find (attrs, match->type); - if (!attr) { - matched = FALSE; - break; - } - - if (attr->ulValueLen != match->ulValueLen || - memcmp (attr->pValue, match->pValue, attr->ulValueLen) != 0) { - matched = FALSE; - break; - } - } - - if (matched) - session->matches = g_list_prepend (session->matches, key); - } - - /* session objects */ - g_hash_table_iter_init (&iter, session->objects); - while (g_hash_table_iter_next (&iter, &key, &value)) { - attrs = (GArray*)value; - matched = TRUE; - for (i = 0; i < ulCount; ++i) { - match = pTemplate + i; - attr = gkm_template_find (attrs, match->type); - if (!attr) { - matched = FALSE; - break; - } - - if (attr->ulValueLen != match->ulValueLen || - memcmp (attr->pValue, match->pValue, attr->ulValueLen) != 0) { - matched = FALSE; - break; - } - } - - if (matched) - session->matches = g_list_prepend (session->matches, key); - } + ctx.template = pTemplate; + ctx.count = ulCount; + ctx.session = session; + gkm_mock_module_enumerate_objects (hSession, enumerate_and_find_objects, &ctx); return CKR_OK; } diff --git a/pkcs11/gkm/gkm-mock.h b/pkcs11/gkm/gkm-mock.h index fd6c86b0..dd4dd9e1 100644 --- a/pkcs11/gkm/gkm-mock.h +++ b/pkcs11/gkm/gkm-mock.h @@ -71,6 +71,12 @@ CK_RV gkm_mock_C_OpenSession (CK_SLOT_ID s CK_NOTIFY Notify, CK_SESSION_HANDLE_PTR phSession); +CK_RV gkm_mock_fail_C_OpenSession (CK_SLOT_ID slotID, + CK_FLAGS flags, + CK_VOID_PTR pApplication, + CK_NOTIFY Notify, + CK_SESSION_HANDLE_PTR phSession); + CK_RV gkm_mock_C_CloseSession (CK_SESSION_HANDLE hSession); CK_RV gkm_mock_C_CloseAllSessions (CK_SLOT_ID slotID); @@ -337,8 +343,28 @@ CK_RV gkm_mock_unsupported_C_GenerateRandom (CK_SESSION_H CK_BYTE_PTR pRandomData, CK_ULONG ulRandomLen); +CK_OBJECT_HANDLE gkm_mock_module_find_object (CK_SESSION_HANDLE session, + CK_ATTRIBUTE_PTR attrs, + CK_ULONG n_attrs); + +guint gkm_mock_module_count_objects (CK_SESSION_HANDLE session); + +typedef gboolean (*GkmMockEnumerator) (CK_OBJECT_HANDLE handle, + GArray *attrs, + gpointer user_data); + +void gkm_mock_module_enumerate_objects (CK_SESSION_HANDLE session, + GkmMockEnumerator func, + gpointer user_data); + CK_OBJECT_HANDLE gkm_mock_module_take_object (GArray *template); +void gkm_mock_module_set_object (CK_OBJECT_HANDLE object, + CK_ATTRIBUTE_PTR attrs, + CK_ULONG n_attrs); + +void gkm_mock_module_set_pin (const gchar *password); + /* * Some dumb crypto mechanisms for simple testing. * diff --git a/pkcs11/gkm/gkm-secret.c b/pkcs11/gkm/gkm-secret.c index 3bf8d054..ca9a36e9 100644 --- a/pkcs11/gkm/gkm-secret.c +++ b/pkcs11/gkm/gkm-secret.c @@ -175,3 +175,10 @@ gkm_secret_equals (GkmSecret *self, const guchar* pin, gssize n_pin) /* Compare actual memory */ return memcmp (pin, self->memory, n_pin) == 0; } + +gboolean +gkm_secret_is_trivially_weak (GkmSecret *self) +{ + return gkm_secret_equals (self, NULL, 0) || + gkm_secret_equals (self, (const guchar*)"", 0); +} diff --git a/pkcs11/gkm/gkm-secret.h b/pkcs11/gkm/gkm-secret.h index 4920b8d5..e5ac4046 100644 --- a/pkcs11/gkm/gkm-secret.h +++ b/pkcs11/gkm/gkm-secret.h @@ -64,4 +64,6 @@ gboolean gkm_secret_equals (GkmSecret *self, const guchar *data, gssize n_data); +gboolean gkm_secret_is_trivially_weak (GkmSecret *self); + #endif /* __GKM_SECRET_H__ */ diff --git a/pkcs11/pkcs11i.h b/pkcs11/pkcs11i.h index 08337f14..44f285a1 100644 --- a/pkcs11/pkcs11i.h +++ b/pkcs11/pkcs11i.h @@ -86,6 +86,8 @@ typedef CK_G_APPLICATION* CK_G_APPLICATION_PTR; #define CKA_G_SCHEMA (CKA_GNOME + 216) +#define CKA_G_LOGIN_COLLECTION (CKA_GNOME + 218) + /* ------------------------------------------------------------------- * MECHANISMS */ diff --git a/pkcs11/secret-store/gkm-secret-collection.c b/pkcs11/secret-store/gkm-secret-collection.c index 04e7718c..970da454 100644 --- a/pkcs11/secret-store/gkm-secret-collection.c +++ b/pkcs11/secret-store/gkm-secret-collection.c @@ -398,12 +398,23 @@ static CK_RV gkm_secret_collection_get_attribute (GkmObject *base, GkmSession *session, CK_ATTRIBUTE_PTR attr) { GkmSecretCollection *self = GKM_SECRET_COLLECTION (base); + const gchar *identifier; + GkmSecret *master; switch (attr->type) { case CKA_CLASS: return gkm_attribute_set_ulong (attr, CKO_G_COLLECTION); case CKA_G_CREDENTIAL_TEMPLATE: return gkm_attribute_set_template (attr, self->template); + case CKA_G_LOGIN_COLLECTION: + identifier = gkm_secret_object_get_identifier (GKM_SECRET_OBJECT (base)); + g_return_val_if_fail (identifier, CKR_GENERAL_ERROR); + return gkm_attribute_set_bool (attr, g_str_equal (identifier, "login")); + case CKA_TRUSTED: + if (self->sdata) + return gkm_attribute_set_bool (attr, CK_FALSE); + master = gkm_secret_data_get_master (self->sdata); + return gkm_attribute_set_bool (attr, (master && !gkm_secret_is_trivially_weak (master))); } return GKM_OBJECT_CLASS (gkm_secret_collection_parent_class)->get_attribute (base, session, attr); } diff --git a/pkcs11/wrap-layer/Makefile.am b/pkcs11/wrap-layer/Makefile.am index fee3a599..b12be4b3 100644 --- a/pkcs11/wrap-layer/Makefile.am +++ b/pkcs11/wrap-layer/Makefile.am @@ -12,6 +12,7 @@ INCLUDES = -I. \ libgkm_wrap_layer_la_SOURCES = \ gkm-wrap-layer.c gkm-wrap-layer.h \ + gkm-wrap-login.c gkm-wrap-login.h \ gkm-wrap-prompt.c gkm-wrap-prompt.h libgkm_wrap_layer_la_LIBADD = \ diff --git a/pkcs11/wrap-layer/gkm-wrap-layer.h b/pkcs11/wrap-layer/gkm-wrap-layer.h index a8d73c66..c9d0e1e0 100644 --- a/pkcs11/wrap-layer/gkm-wrap-layer.h +++ b/pkcs11/wrap-layer/gkm-wrap-layer.h @@ -24,10 +24,14 @@ #include "pkcs11/pkcs11.h" -CK_FUNCTION_LIST_PTR gkm_wrap_layer_get_functions (void); +CK_FUNCTION_LIST_PTR gkm_wrap_layer_get_functions (void); -void gkm_wrap_layer_reset_modules (void); +void gkm_wrap_layer_reset_modules (void); -void gkm_wrap_layer_add_module (CK_FUNCTION_LIST_PTR funcs); +void gkm_wrap_layer_add_module (CK_FUNCTION_LIST_PTR funcs); + +void gkm_wrap_layer_hint_login_unlock_success (void); + +void gkm_wrap_layer_hint_login_unlock_failure (void); #endif /* __GKM_WRAP_LAYER_H__ */ diff --git a/pkcs11/wrap-layer/gkm-wrap-login.c b/pkcs11/wrap-layer/gkm-wrap-login.c new file mode 100644 index 00000000..0e4518b2 --- /dev/null +++ b/pkcs11/wrap-layer/gkm-wrap-login.c @@ -0,0 +1,465 @@ +/* + * gnome-keyring + * + * Copyright (C) 2010 Stefan Walter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "config.h" + +#include "gkm-wrap-layer.h" +#include "gkm-wrap-login.h" + +#include "gkm/gkm-attributes.h" +#include "gkm/gkm-util.h" + +#include "egg/egg-secure-memory.h" + +#include "pkcs11/pkcs11.h" +#include "pkcs11/pkcs11i.h" + +#include <glib/gi18n.h> + +#include <string.h> + +static gint unlock_failures = 0; + +void +gkm_wrap_layer_hint_login_unlock_success (void) +{ + g_atomic_int_set (&unlock_failures, 0); +} + +void +gkm_wrap_layer_hint_login_unlock_failure (void) +{ + g_atomic_int_inc (&unlock_failures); +} + +gboolean +gkm_wrap_login_did_unlock_fail (void) +{ + return g_atomic_int_get (&unlock_failures) ? TRUE : FALSE; +} + +static gboolean +prepare_template_for_storage (CK_FUNCTION_LIST_PTR module, + CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE collection, + GArray *template) +{ + CK_ATTRIBUTE attr; + CK_RV rv; + + g_assert (module); + g_assert (session); + g_assert (template); + + /* Lookup the ID attribute */ + attr.type = CKA_ID; + attr.pValue = NULL; + attr.ulValueLen = 0; + + rv = (module->C_GetAttributeValue) (session, collection, &attr, 1); + if (rv != CKR_OK || attr.ulValueLen == (CK_ULONG)-1) + return FALSE; + + attr.pValue = g_malloc0 (attr.ulValueLen); + + rv = (module->C_GetAttributeValue) (session, collection, &attr, 1); + g_return_val_if_fail (rv == CKR_OK, FALSE); + + /* Use the ID as the collection attribute */ + attr.type = CKA_G_COLLECTION; + gkm_template_set (template, &attr); + g_free (attr.pValue); + + gkm_template_set_ulong (template, CKA_CLASS, CKO_SECRET_KEY); + gkm_template_set_boolean (template, CKA_TOKEN, TRUE); + return TRUE; +} + +static gboolean +prepare_module_session_and_collection (CK_FUNCTION_LIST_PTR_PTR module, + CK_SESSION_HANDLE_PTR session, + CK_OBJECT_HANDLE_PTR object) +{ + CK_OBJECT_CLASS klass = CKO_G_COLLECTION; + CK_BBOOL trueval = CK_TRUE; + CK_BBOOL falseval = CK_FALSE; + CK_ATTRIBUTE attrs[] = { + { CKA_G_LOGIN_COLLECTION, &trueval, sizeof (trueval) }, + { CKA_G_LOCKED, &falseval, sizeof (falseval) }, + { CKA_CLASS, &klass, sizeof (klass) }, + { CKA_TOKEN, &trueval, sizeof (trueval) }, + { CKA_TRUSTED, &trueval, sizeof (trueval) }, + }; + + CK_SESSION_INFO sinfo; + CK_SLOT_ID_PTR slots = NULL; + CK_FUNCTION_LIST_PTR funcs; + CK_ULONG n_slots = 0, i; + gboolean ret = FALSE; + CK_ULONG n_objects = 0; + CK_RV rv; + + g_assert (module); + g_assert (session); + + funcs = gkm_wrap_layer_get_functions (); + g_return_val_if_fail (funcs, FALSE); + + rv = (funcs->C_GetSlotList) (CK_TRUE, NULL, &n_slots); + g_return_val_if_fail (rv == CKR_OK, FALSE); + slots = g_new0 (CK_SLOT_ID, n_slots); + rv = (funcs->C_GetSlotList) (CK_TRUE, slots, &n_slots); + g_return_val_if_fail (rv == CKR_OK, FALSE); + + for (i = 0; !ret && i < n_slots; ++i) { + /* Open a session with this module */ + rv = (funcs->C_OpenSession) (slots[i], CKF_SERIAL_SESSION, NULL, NULL, session); + if (rv != CKR_OK) + continue; + + rv = (funcs->C_GetSessionInfo) (*session, &sinfo); + if (rv != CKR_OK) + continue; + + /* Log into the session with no password, in case its needed */ + if (sinfo.state == CKS_RO_PUBLIC_SESSION || sinfo.state == CKS_RW_PUBLIC_SESSION) + (funcs->C_Login) (*session, CKU_USER, (guchar*)"", 0); + + rv = (funcs->C_FindObjectsInit) (*session, attrs, G_N_ELEMENTS (attrs)); + if (rv == CKR_OK) { + rv = (funcs->C_FindObjects) (*session, object, 1, &n_objects); + (funcs->C_FindObjectsFinal) (*session); + if (rv == CKR_OK && n_objects == 1) + ret = TRUE; + } + + if (!ret) + (funcs->C_CloseSession) (*session); + } + + g_free (slots); + + *module = funcs; + return ret; +} + +static void +string_fields_to_template_va (va_list args, const gchar *name, + GArray *template) +{ + GString *fields = g_string_sized_new (128); + const gchar *last = NULL; + gint cmp; + + g_assert (name); + g_assert (template); + + while (name != NULL) { + g_string_append (fields, name); + g_string_append_c (fields, '\0'); + g_string_append (fields, va_arg (args, const gchar*)); + g_string_append_c (fields, '\0'); + + /* Names must be in alphabetical order */ + if (name && last) { + cmp = strcmp (last, name); + if (cmp == 0) + g_warning ("duplicate names in attributes not allowed: %s %s", last, name); + else if (cmp > 0) + g_warning ("names in attributes must in alphabetical order: %s %s", last, name); + } + + last = name; + name = va_arg (args, const gchar*); + } + + gkm_template_set_value (template, CKA_G_FIELDS, fields->str, fields->len); + g_string_free (fields, TRUE); +} + +static CK_OBJECT_HANDLE +find_login_keyring_item (CK_FUNCTION_LIST_PTR module, CK_SESSION_HANDLE session, + GArray *template) +{ + CK_OBJECT_HANDLE item = 0; + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE matched; + CK_OBJECT_HANDLE search; + GArray *attrs; + CK_RV rv; + + g_assert (module); + g_assert (template); + + /* Template for search object */ + attrs = gkm_template_new (NULL, 0); + gkm_template_set_ulong (attrs, CKA_CLASS, CKO_G_SEARCH); + gkm_template_set_boolean (attrs, CKA_TOKEN, CK_FALSE); + + attr = gkm_template_find (template, CKA_G_COLLECTION); + if (attr != NULL) + gkm_template_set (attrs, attr); + + attr = gkm_template_find (template, CKA_G_FIELDS); + g_return_val_if_fail (attr, 0); + gkm_template_set (attrs, attr); + + /* Create new search object */ + rv = (module->C_CreateObject) (session, (CK_ATTRIBUTE_PTR)attrs->data, attrs->len, &search); + gkm_template_free (attrs); + + if (rv != CKR_OK) { + g_warning ("couldn't create search for login keyring: %s", gkm_util_rv_stringize (rv)); + return 0; + } + + matched.type = CKA_G_MATCHED; + matched.pValue = NULL; + matched.ulValueLen = 0; + + rv = (module->C_GetAttributeValue) (session, search, &matched, 1); + g_return_val_if_fail (rv == CKR_OK, 0); + g_return_val_if_fail (matched.ulValueLen != (CK_ULONG)-1, 0); + + if (matched.ulValueLen >= sizeof (CK_OBJECT_HANDLE)) { + matched.pValue = g_malloc (matched.ulValueLen); + rv = (module->C_GetAttributeValue) (session, search, &matched, 1); + g_return_val_if_fail (rv == CKR_OK, 0); + + item = *((CK_OBJECT_HANDLE_PTR)matched.pValue); + g_return_val_if_fail (item != 0, 0); + g_free (matched.pValue); + } + + /* Destroy the search object */ + (module->C_DestroyObject) (session, search); + + return item; +} + +void +gkm_wrap_login_attach_secret (const gchar *label, const gchar *secret, + const gchar *first, ...) +{ + CK_FUNCTION_LIST_PTR module; + CK_SESSION_HANDLE session; + CK_OBJECT_HANDLE collection; + CK_OBJECT_HANDLE item; + CK_ATTRIBUTE attr; + gchar *display_name; + GArray *template; + gsize original_len; + va_list va; + CK_RV rv; + + if (first == NULL) + return; + + if (secret == NULL) + secret = ""; + + /* We only support storing utf-8 strings */ + g_return_if_fail (g_utf8_validate (secret, -1, NULL)); + + if (!prepare_module_session_and_collection (&module, &session, &collection)) + return; + + template = gkm_template_new (NULL, 0); + if (!prepare_template_for_storage (module, session, collection, template)) { + gkm_template_free (template); + return; + } + + va_start(va, first); + string_fields_to_template_va (va, first, template); + va_end(va); + + /* + * If there already is such an item, then include its identifier. + * What this does is overwrite that item, rather than creating new. + */ + item = find_login_keyring_item (module, session, template); + if (item != 0) { + + attr.type = CKA_ID; + attr.ulValueLen = 0; + attr.pValue = NULL; + + rv = (module->C_GetAttributeValue) (session, item, &attr, 1); + if (rv == CKR_OK && attr.ulValueLen != (CK_ULONG)-1) { + attr.pValue = g_malloc (attr.ulValueLen); + rv = (module->C_GetAttributeValue) (session, item, &attr, 1); + if (rv == CKR_OK) + gkm_template_set (template, &attr); + g_free (attr.pValue); + } + } + + /* Get the label ready */ + display_name = g_strdup_printf (_("Unlock password for: %s"), label ? label : _("Unnamed")); + gkm_template_set_string (template, CKA_LABEL, display_name); + g_free (display_name); + + /* Instead of duplicating the password, we tack it into the template */ + original_len = template->len; + attr.type = CKA_VALUE; + attr.pValue = (CK_VOID_PTR)secret; + attr.ulValueLen = strlen (secret); + g_array_append_val (template, attr); + + /* Actually make the object */ + rv = (module->C_CreateObject) (session, ((CK_ATTRIBUTE_PTR)template->data), + template->len, &item); + if (rv != CKR_OK) + g_warning ("couldn't store secret in login keyring: %s", gkm_util_rv_stringize (rv)); + + /* Before freeing, truncate our password attribute we tacked on the end */ + g_array_set_size (template, original_len); + gkm_template_free (template); + + (module->C_CloseSession) (session); +} + +gchar* +gkm_wrap_login_lookup_secret (const gchar *first, ...) +{ + CK_FUNCTION_LIST_PTR module; + CK_SESSION_HANDLE session; + CK_OBJECT_HANDLE collection; + CK_OBJECT_HANDLE item; + CK_ATTRIBUTE attr; + GArray *template; + gchar *password = NULL; + va_list va; + CK_RV rv; + + if (first == NULL) + return NULL; + + if (!prepare_module_session_and_collection (&module, &session, &collection)) + return NULL; + + template = gkm_template_new (NULL, 0); + gkm_template_set_ulong (template, CKA_CLASS, CKO_SECRET_KEY); + gkm_template_set_boolean (template, CKA_G_LOCKED, FALSE); + + va_start(va, first); + string_fields_to_template_va (va, first, template); + va_end(va); + + item = find_login_keyring_item (module, session, template); + gkm_template_free (template); + + if (item != 0) { + + attr.type = CKA_VALUE; + attr.pValue = NULL; + attr.ulValueLen = 0; + + rv = (module->C_GetAttributeValue) (session, item, &attr, 1); + if (rv == CKR_OK && attr.ulValueLen != (CK_ULONG)-1) { + + /* Allocate memory for password. Note we're null terminating */ + password = attr.pValue = egg_secure_alloc (attr.ulValueLen + 1); + rv = (module->C_GetAttributeValue) (session, item, &attr, 1); + + /* Double check that this is a password */ + if (rv == CKR_OK) { + if (!g_utf8_validate (password, -1, NULL)) { + g_message ("expected string, but found binary secret in login keyring"); + egg_secure_strfree (password); + password = NULL; + } + + /* Couldn't read the value. Remember object can go away due to race */ + } else { + if (rv != CKR_OBJECT_HANDLE_INVALID) + g_warning ("couldn't read stored secret from login keyring: %s", + gkm_util_rv_stringize (rv)); + egg_secure_free (password); + password = NULL; + } + + /* Failure. Remember object can go away due to race */ + } else if (rv != CKR_OK && rv != CKR_OBJECT_HANDLE_INVALID) { + g_warning ("couldn't get stored secret from login keyring: %s", + gkm_util_rv_stringize (rv)); + } + } + + (module->C_CloseSession) (session); + + return password; +} + +void +gkm_wrap_login_remove_secret (const gchar *first, ...) +{ + CK_FUNCTION_LIST_PTR module; + CK_SESSION_HANDLE session; + CK_OBJECT_HANDLE collection; + CK_OBJECT_HANDLE item; + GArray *template; + va_list va; + CK_RV rv; + + if (first == NULL) + return; + + if (!prepare_module_session_and_collection (&module, &session, &collection)) + return; + + template = gkm_template_new (NULL, 0); + if (!prepare_template_for_storage (module, session, collection, template)) { + gkm_template_free (template); + return; + } + + va_start(va, first); + string_fields_to_template_va (va, first, template); + va_end(va); + + item = find_login_keyring_item (module, session, template); + gkm_template_free (template); + + if (item != 0) { + rv = (module->C_DestroyObject) (session, item); + if (rv != CKR_OK && rv != CKR_OBJECT_HANDLE_INVALID) + g_warning ("couldn't remove stored secret from login keyring: %s", + gkm_util_rv_stringize (rv)); + } + + (module->C_CloseSession) (session); +} + +gboolean +gkm_wrap_login_is_usable (void) +{ + CK_FUNCTION_LIST_PTR module; + CK_OBJECT_HANDLE collection; + CK_SESSION_HANDLE session; + + if (!prepare_module_session_and_collection (&module, &session, &collection)) + return FALSE; + + (module->C_CloseSession) (session); + return TRUE; +} diff --git a/pkcs11/wrap-layer/gkm-wrap-login.h b/pkcs11/wrap-layer/gkm-wrap-login.h new file mode 100644 index 00000000..148a1b93 --- /dev/null +++ b/pkcs11/wrap-layer/gkm-wrap-login.h @@ -0,0 +1,42 @@ +/* + * gnome-keyring + * + * Copyright (C) 2010 Stefan Walter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __GKM_WRAP_LOGIN_H__ +#define __GKM_WRAP_LOGIN_H__ + +#include <glib.h> + +gboolean gkm_wrap_login_is_usable (void); + +gboolean gkm_wrap_login_did_unlock_fail (void); + +void gkm_wrap_login_attach_secret (const gchar *label, + const gchar *secret, + const gchar *first, + ...); + +gchar* gkm_wrap_login_lookup_secret (const gchar *first, + ...); + +void gkm_wrap_login_remove_secret (const gchar *first, + ...); + +#endif /* __GKM_WRAP_LOGIN_H__ */ diff --git a/pkcs11/wrap-layer/gkm-wrap-prompt.c b/pkcs11/wrap-layer/gkm-wrap-prompt.c index db2ffa3a..f788ac00 100644 --- a/pkcs11/wrap-layer/gkm-wrap-prompt.c +++ b/pkcs11/wrap-layer/gkm-wrap-prompt.c @@ -21,6 +21,7 @@ #include "config.h" +#include "gkm-wrap-login.h" #include "gkm-wrap-prompt.h" #include "egg/egg-secure-memory.h" @@ -47,6 +48,7 @@ struct _GkmWrapPrompt { gpointer prompt_data; GDestroyNotify destroy_data; + guint iteration; GQueue pool; }; @@ -54,122 +56,308 @@ typedef struct _CredentialPrompt { GArray *template; CK_ULONG n_template; gchar *password; - guint iteration; } CredentialPrompt; G_DEFINE_TYPE (GkmWrapPrompt, gkm_wrap_prompt, GKU_TYPE_PROMPT); /* ----------------------------------------------------------------------------- - * INTERNAL + * UTILITIES + */ + +static gpointer +pool_alloc (GkmWrapPrompt *self, gsize length) +{ + gpointer memory = g_malloc0 (length); + + g_assert (GKM_WRAP_IS_PROMPT (self)); + g_queue_push_tail (&self->pool, memory); + return memory; +} + +static gpointer +pool_dup (GkmWrapPrompt *self, gconstpointer original, gsize length) +{ + gpointer memory = pool_alloc (self, length); + memcpy (memory, original, length); + return memory; +} + +/* ----------------------------------------------------------------------------- + * AUTO UNLOCK */ #if 0 +static void +set_warning_wrong (GkdSecretUnlock *self) +{ + g_assert (GKD_SECRET_IS_UNLOCK (self)); + gku_prompt_set_warning (GKU_PROMPT (self), _("The unlock password was incorrect")); +} +#endif + static gchar* -location_string_for_attributes (GP11Attributes *attrs) +auto_unlock_keyring_location (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs) { - gchar *identifier; - gchar *location; + CK_ATTRIBUTE_PTR attr; + gboolean is_login = FALSE; + + if (gkm_attributes_find_boolean (attrs, n_attrs, CKA_G_LOGIN_COLLECTION, &is_login) && is_login) + return NULL; - identifier = identifier_string_for_attributes (attrs); - if (identifier == NULL) + attr = gkm_attributes_find (attrs, n_attrs, CKA_ID); + if (attr == NULL) return NULL; /* * COMPAT: Format it into a string. This is done this way for compatibility * with old gnome-keyring releases. In the future this may change. - * - * FYI: gp11_object_get_data() null terminates */ - location = g_strdup_printf ("LOCAL:/keyrings/%s.keyring", (gchar*)identifier); - g_free (identifier); - return location; + + return g_strdup_printf ("LOCAL:/keyrings/%s.keyring", (gchar*)attr->pValue); } -static void -set_warning_wrong (GkdSecretUnlock *self) +static gchar* +auto_unlock_object_unique (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs) { - g_assert (GKD_SECRET_IS_UNLOCK (self)); - gku_prompt_set_warning (GKU_PROMPT (self), _("The unlock password was incorrect")); + CK_ATTRIBUTE_PTR attr; + + attr = gkm_attributes_find (attrs, n_attrs, CKA_GNOME_UNIQUE); + if (attr == NULL) + return NULL; + + return g_strndup (attr->pValue, attr->ulValueLen); +} + +static gchar* +auto_unlock_lookup_keyring (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs) +{ + gchar *location; + gchar *password; + + location = auto_unlock_keyring_location (attrs, n_attrs); + if (location == NULL) + return NULL; + + password = gkm_wrap_login_lookup_secret ("keyring", location, NULL); + g_free (location); + return password; +} + + +static gchar* +auto_unlock_lookup_object (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs) +{ + CK_OBJECT_CLASS klass; + gchar *unique; + gchar *password; + + if (!gkm_attributes_find_ulong (attrs, n_attrs, CKA_CLASS, &klass)) + return NULL; + + if (klass == CKO_G_COLLECTION) + return auto_unlock_lookup_keyring (attrs, n_attrs); + + unique = auto_unlock_object_unique (attrs, n_attrs); + if (unique == NULL) + return NULL; + + password = gkm_wrap_login_lookup_secret ("unique", unique, NULL); + g_free (unique); + return password; +} + +static gchar* +auto_unlock_lookup_token (CK_TOKEN_INFO_PTR info) +{ + gchar *password = NULL; + gchar *manufacturer; + gchar *serial; + + g_assert (info); + + manufacturer = g_strndup ((gchar*)info->manufacturerID, sizeof (info->manufacturerID)); + g_strchomp (manufacturer); + + serial = g_strndup ((gchar*)info->serialNumber, sizeof (info->serialNumber)); + g_strchomp (serial); + + if (!g_str_equal (manufacturer, "") && !g_str_equal (serial, "")) + password = gkm_wrap_login_lookup_secret ("manufacturer", manufacturer, + "serial-number", serial, + NULL); + + g_free (manufacturer); + g_free (serial); + + return password; +} + +static gboolean +auto_unlock_should_attach (GkmWrapPrompt *self) +{ + GkuPrompt *prompt = GKU_PROMPT (self); + gint value = 0; + return gku_prompt_has_response (prompt) && + gku_prompt_get_unlock_option (prompt, GKU_UNLOCK_AUTO, &value) && + value != 0; } static void -attach_unlock_to_login (GP11Object *collection, GkdSecretSecret *master) +auto_unlock_attach_keyring (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs, const gchar *password) { - DBusError derr = DBUS_ERROR_INIT; - GP11Attributes *attrs; - GP11Object *cred; gchar *location; gchar *label; - g_assert (GP11_IS_OBJECT (collection)); + if (!password) + return; + + location = auto_unlock_keyring_location (attrs, n_attrs); + if (location == NULL) + return; - /* Relevant information for the unlock item */ - attrs = attributes_for_collection (collection); - g_return_if_fail (attrs); - location = location_string_for_attributes (attrs); - label = label_string_for_attributes (attrs); - gp11_attributes_unref (attrs); + if (!gkm_attributes_find_string (attrs, n_attrs, CKA_LABEL, &label)) + if (!gkm_attributes_find_string (attrs, n_attrs, CKA_ID, &label)) + label = g_strdup (location); - attrs = gkd_login_attach_make_attributes (label, "keyring", location, NULL); + gkm_wrap_login_attach_secret (label, password, "keyring", location, NULL); g_free (location); g_free (label); +} - cred = gkd_secret_session_create_credential (master->session, NULL, attrs, master, &derr); - gp11_attributes_unref (attrs); - g_object_unref (cred); +static void +auto_unlock_attach_object (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs, const gchar *password) +{ + CK_OBJECT_CLASS klass; + gchar *label; + gchar *unique; + + if (!password) + return; - if (!cred) { - g_warning ("couldn't save unlock password in login collection: %s", derr.message); - dbus_error_free (&derr); + if (!gkm_attributes_find_ulong (attrs, n_attrs, CKA_CLASS, &klass)) + return; + + if (klass == CKO_G_COLLECTION) { + auto_unlock_attach_keyring (attrs, n_attrs, password); + return; } + + unique = auto_unlock_object_unique (attrs, n_attrs); + if (unique == NULL) + return; + + if (!gkm_attributes_find_string (attrs, n_attrs, CKA_LABEL, &label)) + label = g_strdup (unique); + + gkm_wrap_login_attach_secret (label, password, "unique", unique, NULL); + g_free (unique); + g_free (label); } -/* Save it to the login keyring */ -if (!transient) - attach_unlock_to_login (collection, master); +static void +auto_unlock_attach_token (CK_TOKEN_INFO_PTR info, const gchar *password) +{ + gchar *manufacturer; + gchar *serial; + gchar *label; -/* Or try to use login keyring's passwords */ -} else { - attrs = attributes_for_collection (coll); - location = location_string_for_attributes (attrs); - gp11_attributes_unref (attrs); + g_assert (info); - if (location) { - password = gkd_login_lookup_secret ("keyring", location, NULL); - g_free (location); + if (!password) + return; - if (password) { - if (gkd_secret_unlock_with_password (coll, NULL, 0, NULL)) - locked = FALSE; - egg_secure_strfree (password); - } + manufacturer = g_strndup ((gchar*)info->manufacturerID, sizeof (info->manufacturerID)); + g_strchomp (manufacturer); + + serial = g_strndup ((gchar*)info->serialNumber, sizeof (info->serialNumber)); + g_strchomp (serial); + + label = g_strndup ((gchar*)info->label, sizeof (info->label)); + g_strchomp (label); + + if (g_str_equal (label, "")) { + g_free (label); + label = g_strdup (manufacturer); } -#endif + if (!g_str_equal (manufacturer, "") && !g_str_equal (serial, "")) + gkm_wrap_login_attach_secret (label, password, + "manufacturer", manufacturer, + "serial-number", serial, + NULL); -static GkuPrompt* -on_prompt_attention (gpointer user_data) + g_free (manufacturer); + g_free (serial); + g_free (label); +} + +static void +auto_unlock_remove_keyring (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs) { - /* We passed the prompt as the argument */ - return g_object_ref (user_data); + gchar *location; + + location = auto_unlock_keyring_location (attrs, n_attrs); + if (location == NULL) + return; + + gkm_wrap_login_remove_secret ("keyring", location, NULL); + g_free (location); } -static gpointer -pool_alloc (GkmWrapPrompt *self, gsize length) +static void +auto_unlock_remove_object (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs) { - gpointer memory = g_malloc0 (length); + CK_OBJECT_CLASS klass; + gchar *unique; - g_assert (GKM_WRAP_IS_PROMPT (self)); - g_queue_push_tail (&self->pool, memory); - return memory; + if (!gkm_attributes_find_ulong (attrs, n_attrs, CKA_CLASS, &klass)) + return; + + if (klass == CKO_G_COLLECTION) { + auto_unlock_remove_keyring (attrs, n_attrs); + return; + } + + unique = auto_unlock_object_unique (attrs, n_attrs); + if (unique == NULL) + return; + + gkm_wrap_login_remove_secret ("unique", unique, NULL); + g_free (unique); } -static gpointer -pool_dup (GkmWrapPrompt *self, gconstpointer original, gsize length) +static void +auto_unlock_remove_token (CK_TOKEN_INFO_PTR info) { - gpointer memory = pool_alloc (self, length); - memcpy (memory, original, length); - return memory; + gchar *manufacturer; + gchar *serial; + + g_assert (info); + + manufacturer = g_strndup ((gchar*)info->manufacturerID, sizeof (info->manufacturerID)); + g_strchomp (manufacturer); + + serial = g_strndup ((gchar*)info->serialNumber, sizeof (info->serialNumber)); + g_strchomp (serial); + + if (!g_str_equal (manufacturer, "") && !g_str_equal (serial, "")) + gkm_wrap_login_remove_secret ("manufacturer", manufacturer, + "serial-number", serial, + NULL); + + g_free (manufacturer); + g_free (serial); +} + +/* ----------------------------------------------------------------------------------------- + * PROMPTING + */ + +static GkuPrompt* +on_prompt_attention (gpointer user_data) +{ + /* We passed the prompt as the argument */ + return g_object_ref (user_data); } static CK_ATTRIBUTE_PTR @@ -260,6 +448,9 @@ get_unlock_options_from_prompt (GkmWrapPrompt *self, CK_ULONG_PTR n_options) g_assert (GKM_WRAP_IS_PROMPT (self)); g_assert (n_options); + if (!gku_prompt_has_response (GKU_PROMPT (self))) + return NULL; + *n_options = 4; options = pool_alloc (self, sizeof (CK_ATTRIBUTE) * (*n_options)); @@ -316,8 +507,8 @@ set_unlock_options_on_prompt (GkmWrapPrompt *self, CK_ATTRIBUTE_PTR options, CK_ static CK_ATTRIBUTE_PTR get_attributes_from_object (GkmWrapPrompt *self, CK_ULONG *n_attrs) { - CK_ATTRIBUTE attrs[3]; - CK_ULONG i, count = 3; + CK_ATTRIBUTE attrs[5]; + CK_ULONG i; CK_RV rv; g_assert (GKM_WRAP_IS_PROMPT (self)); @@ -328,8 +519,10 @@ get_attributes_from_object (GkmWrapPrompt *self, CK_ULONG *n_attrs) attrs[0].type = CKA_LABEL; attrs[1].type = CKA_ID; attrs[2].type = CKA_CLASS; + attrs[3].type = CKA_G_LOGIN_COLLECTION; + attrs[4].type = CKA_GNOME_UNIQUE; - rv = (self->module->C_GetAttributeValue) (self->session, self->object, attrs, count); + rv = (self->module->C_GetAttributeValue) (self->session, self->object, attrs, G_N_ELEMENTS (attrs)); if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID) { g_warning ("Couldn't retrieve information about object to unlock: %s", gkm_util_rv_to_string (rv)); @@ -337,24 +530,33 @@ get_attributes_from_object (GkmWrapPrompt *self, CK_ULONG *n_attrs) } /* Allocate for each value, note we're null terminating values */ - for (i = 0; i < count; ++i) { + for (i = 0; i < G_N_ELEMENTS (attrs); ++i) { if (attrs[i].ulValueLen != (CK_ULONG)-1) attrs[i].pValue = pool_alloc (self, attrs[i].ulValueLen + 1); } /* Now get the actual values */ - rv = (self->module->C_GetAttributeValue) (self->session, self->object, attrs, count); + rv = (self->module->C_GetAttributeValue) (self->session, self->object, attrs, G_N_ELEMENTS (attrs)); if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID) { g_warning ("couldn't retrieve credential template for prompt: %s", gkm_util_rv_to_string (rv)); return NULL; } - *n_attrs = count; + *n_attrs = G_N_ELEMENTS (attrs); return pool_dup (self, attrs, sizeof (attrs)); } +static gboolean +get_info_for_token (GkmWrapPrompt *self, CK_TOKEN_INFO_PTR tinfo) +{ + CK_SESSION_INFO sinfo; + + return (self->module->C_GetSessionInfo) (self->session, &sinfo) == CKR_OK && + (self->module->C_GetTokenInfo) (sinfo.slotID, tinfo) == CKR_OK; +} + static void prepare_unlock_keyring_login (GkmWrapPrompt *self) { @@ -370,12 +572,9 @@ prepare_unlock_keyring_login (GkmWrapPrompt *self) text = _("Enter password for to unlock your login keyring"); gku_prompt_set_primary_text (prompt, text); -#if 0 - /* TODO: Reimplement this */ - if (gkd_login_did_unlock_fail ()) + if (gkm_wrap_login_did_unlock_fail ()) text = _("The password you use to log in to your computer no longer matches that of your login keyring."); else -#endif text = _("The login keyring did not get unlocked when you logged into your computer."); gku_prompt_set_secondary_text (prompt, text); @@ -384,8 +583,6 @@ prepare_unlock_keyring_login (GkmWrapPrompt *self) gku_prompt_show_widget (prompt, "password_area"); } - - static void prepare_unlock_keyring_other (GkmWrapPrompt *self, const gchar *label) { @@ -413,11 +610,9 @@ prepare_unlock_keyring_other (GkmWrapPrompt *self, const gchar *label) gku_prompt_show_widget (prompt, "lock_area"); gku_prompt_show_widget (prompt, "options_area"); -#if 0 /* TODO: Implement */ - if (gkd_login_is_usable ()) + if (gkm_wrap_login_is_usable ()) gku_prompt_show_widget (prompt, "auto_unlock_check"); else -#endif gku_prompt_hide_widget (prompt, "auto_unlock_check"); } @@ -498,14 +693,14 @@ prepare_unlock_object (GkmWrapPrompt *self, const gchar *label, CK_OBJECT_CLASS } static void -prepare_unlock_prompt (GkmWrapPrompt *self, gboolean first) +prepare_unlock_prompt (GkmWrapPrompt *self, CK_ATTRIBUTE_PTR attrs, + CK_ULONG n_attrs, gboolean first) { - CK_ATTRIBUTE_PTR attrs; CK_ATTRIBUTE_PTR attr; - CK_ULONG n_attrs; GkuPrompt *prompt; const gchar *label = NULL; CK_OBJECT_CLASS klass; + gboolean is_login = FALSE; g_assert (GKM_WRAP_IS_PROMPT (self)); @@ -514,10 +709,6 @@ prepare_unlock_prompt (GkmWrapPrompt *self, gboolean first) /* Hard reset on first prompt, soft on later */ gku_prompt_reset (GKU_PROMPT (prompt), first); - /* Load up all the values, note they're null terminated */ - attrs = get_attributes_from_object (self, &n_attrs); - g_return_if_fail (attrs); - /* Load up the object class */ if (!gkm_attributes_find_ulong (attrs, n_attrs, CKA_CLASS, &klass)) klass = (CK_ULONG)-1; @@ -536,12 +727,10 @@ prepare_unlock_prompt (GkmWrapPrompt *self, gboolean first) label = _("Unnamed"); if (klass == CKO_G_COLLECTION) { - if (attr && attr->pValue && attr->ulValueLen == 5 && - memcmp (attr->pValue, "login", 5)) { + if (gkm_attributes_find_boolean (attrs, n_attrs, CKA_G_LOGIN_COLLECTION, &is_login) && is_login) prepare_unlock_keyring_login (self); - } else { + else prepare_unlock_keyring_other (self, label); - } } else { prepare_unlock_object (self, label, klass); } @@ -576,13 +765,11 @@ prepare_unlock_token (GkmWrapPrompt *self, CK_TOKEN_INFO_PTR tinfo) gku_prompt_set_secondary_text (prompt, text); g_free (text); -#if 0 - if (gkd_login_is_usable ()) { + if (gkm_wrap_login_is_usable ()) { gku_prompt_show_widget (prompt, "details_area"); gku_prompt_show_widget (prompt, "lock_area"); gku_prompt_hide_widget (prompt, "options_area"); } -#endif g_free (label); } @@ -693,8 +880,9 @@ gkm_wrap_prompt_do_credential (GkmWrapPrompt *self, CK_ATTRIBUTE_PTR *template, CK_ULONG *n_template) { CK_ATTRIBUTE_PTR options; + CK_ATTRIBUTE_PTR attrs; CK_ATTRIBUTE_PTR attr; - CK_ULONG n_options, i; + CK_ULONG n_attrs, n_options, i; CredentialPrompt *data; g_return_val_if_fail (GKM_WRAP_IS_PROMPT (self), FALSE); @@ -704,18 +892,33 @@ gkm_wrap_prompt_do_credential (GkmWrapPrompt *self, CK_ATTRIBUTE_PTR *template, g_assert (self->destroy_data == credential_prompt_free); data = self->prompt_data; - prepare_unlock_prompt (self, data->iteration == 0); - ++(data->iteration); + attrs = get_attributes_from_object (self, &n_attrs); + g_return_val_if_fail (attrs, FALSE); - gku_prompt_request_attention_sync (NULL, on_prompt_attention, - g_object_ref (self), g_object_unref); + egg_secure_free (data->password); + data->password = NULL; - if (gku_prompt_get_response (GKU_PROMPT (self)) != GKU_RESPONSE_OK) - return FALSE; + if (self->iteration == 0) { + ++(self->iteration); + data->password = auto_unlock_lookup_object (attrs, n_attrs); - egg_secure_strfree (data->password); - data->password = gku_prompt_get_password (GKU_PROMPT (self), "password"); - g_return_val_if_fail (data->password, FALSE); + } else if (self->iteration == 1) { + auto_unlock_remove_object (attrs, n_attrs); + } + + if (!data->password) { + prepare_unlock_prompt (self, attrs, n_attrs, self->iteration == 1); + ++(self->iteration); + + gku_prompt_request_attention_sync (NULL, on_prompt_attention, + g_object_ref (self), g_object_unref); + + if (gku_prompt_get_response (GKU_PROMPT (self)) != GKU_RESPONSE_OK) + return FALSE; + + data->password = gku_prompt_get_password (GKU_PROMPT (self), "password"); + g_return_val_if_fail (data->password, FALSE); + } /* Truncate any extra options off the end of template */ g_assert (data->n_template > 0); @@ -742,24 +945,25 @@ void gkm_wrap_prompt_done_credential (GkmWrapPrompt *self, CK_RV call_result) { CK_ATTRIBUTE_PTR options; - CK_ULONG n_options; - gint value = 0; + CK_ATTRIBUTE_PTR attrs; + CK_ULONG n_options, n_attrs; + CredentialPrompt *data; g_return_if_fail (GKM_WRAP_IS_PROMPT (self)); + g_assert (self->destroy_data == credential_prompt_free); + data = self->prompt_data; + /* Save the options, and possibly auto unlock */ if (call_result == CKR_OK) { options = get_unlock_options_from_prompt (self, &n_options); if (options != NULL) set_unlock_options_on_object (self, options, n_options); - if (gku_prompt_get_unlock_option (GKU_PROMPT (self), GKU_UNLOCK_AUTO, &value) && value) { - g_assert_not_reached (); /* TODO: Need to implement */ + if (auto_unlock_should_attach (self)) { + attrs = get_attributes_from_object (self, &n_attrs); + auto_unlock_attach_object (attrs, n_attrs, data->password); } - - /* Make sure to remove any auto-unlock when we fail */ - } else if (call_result == CKR_PIN_INCORRECT) { - /* TODO: Implement removal from login keyring */ } } @@ -853,29 +1057,62 @@ static gboolean login_prompt_do_specific (GkmWrapPrompt *self, CK_RV last_result, CK_UTF8CHAR_PTR *pin, CK_ULONG *n_pin) { + CK_ATTRIBUTE_PTR attrs; + CK_ULONG n_attrs; + g_assert (GKM_WRAP_IS_PROMPT (self)); g_assert (pin); g_assert (n_pin); - prepare_unlock_prompt (self, *pin == NULL); + g_assert (self->destroy_data == (GDestroyNotify)egg_secure_strfree); + egg_secure_strfree (self->prompt_data); + self->prompt_data = NULL; - gku_prompt_request_attention_sync (NULL, on_prompt_attention, - g_object_ref (self), g_object_unref); + attrs = get_attributes_from_object (self, &n_attrs); + g_return_val_if_fail (attrs, FALSE); - if (gku_prompt_get_response (GKU_PROMPT (self)) != GKU_RESPONSE_OK) - return FALSE; + if (self->iteration == 0) { + ++(self->iteration); + self->prompt_data = auto_unlock_lookup_object (attrs, n_attrs); - g_assert (self->destroy_data == (GDestroyNotify)egg_secure_strfree); - egg_secure_strfree (self->prompt_data); + } else if (self->iteration == 1 && last_result == CKR_PIN_INCORRECT) { + auto_unlock_remove_object (attrs, n_attrs); + } + + if (!self->prompt_data) { + prepare_unlock_prompt (self, attrs, n_attrs, self->iteration == 1); - self->prompt_data = gku_prompt_get_password (GKU_PROMPT (self), "password"); - g_return_val_if_fail (self->prompt_data, FALSE); + gku_prompt_request_attention_sync (NULL, on_prompt_attention, + g_object_ref (self), g_object_unref); + + if (gku_prompt_get_response (GKU_PROMPT (self)) != GKU_RESPONSE_OK) + return FALSE; + + self->prompt_data = gku_prompt_get_password (GKU_PROMPT (self), "password"); + g_return_val_if_fail (self->prompt_data, FALSE); + } *pin = self->prompt_data; *n_pin = strlen (self->prompt_data); return TRUE; } +static void +login_prompt_done_specific (GkmWrapPrompt *self, CK_RV call_result) +{ + CK_ATTRIBUTE_PTR attrs; + CK_ULONG n_attrs; + + g_assert (GKM_WRAP_IS_PROMPT (self)); + g_assert (self->destroy_data == (GDestroyNotify)egg_secure_strfree); + + /* Possibly save away auto unlock */ + if (call_result == CKR_OK && auto_unlock_should_attach (self)) { + attrs = get_attributes_from_object (self, &n_attrs); + auto_unlock_attach_object (attrs, n_attrs, self->prompt_data); + } +} + static GkmWrapPrompt* login_prompt_for_user (CK_FUNCTION_LIST_PTR module, CK_SESSION_HANDLE session) { @@ -896,45 +1133,65 @@ static gboolean login_prompt_do_user (GkmWrapPrompt *self, CK_RV last_result, CK_UTF8CHAR_PTR *pin, CK_ULONG *n_pin) { - CK_SESSION_INFO sinfo; CK_TOKEN_INFO tinfo; - CK_RV rv; g_assert (GKM_WRAP_IS_PROMPT (self)); g_assert (self->module); g_assert (pin); g_assert (n_pin); - rv = (self->module->C_GetSessionInfo) (self->session, &sinfo); - if (rv != CKR_OK) - return FALSE; + g_assert (self->destroy_data == (GDestroyNotify)egg_secure_strfree); + egg_secure_strfree (self->prompt_data); + self->prompt_data = NULL; - rv = (self->module->C_GetTokenInfo) (sinfo.slotID, &tinfo); - if (rv != CKR_OK) + if (!get_info_for_token (self, &tinfo)) return FALSE; - /* Hard reset on first prompt, soft on later */ - gku_prompt_reset (GKU_PROMPT (self), last_result == CKR_OK); + if (self->iteration == 0) { + ++(self->iteration); + self->prompt_data = auto_unlock_lookup_token (&tinfo); - prepare_unlock_token (self, &tinfo); + } else if (self->iteration == 1 && last_result == CKR_PIN_INCORRECT) { + auto_unlock_remove_token (&tinfo); + } - gku_prompt_request_attention_sync (NULL, on_prompt_attention, - g_object_ref (self), g_object_unref); + if (!self->prompt_data) { + /* Hard reset on first prompt, soft on later */ + gku_prompt_reset (GKU_PROMPT (self), last_result == CKR_OK); - if (gku_prompt_get_response (GKU_PROMPT (self)) != GKU_RESPONSE_OK) - return FALSE; + prepare_unlock_token (self, &tinfo); - g_assert (self->destroy_data == (GDestroyNotify)egg_secure_strfree); - egg_secure_strfree (self->prompt_data); + gku_prompt_request_attention_sync (NULL, on_prompt_attention, + g_object_ref (self), g_object_unref); + + if (gku_prompt_get_response (GKU_PROMPT (self)) != GKU_RESPONSE_OK) + return FALSE; - self->prompt_data = gku_prompt_get_password (GKU_PROMPT (self), "password"); - g_return_val_if_fail (self->prompt_data, FALSE); + self->prompt_data = gku_prompt_get_password (GKU_PROMPT (self), "password"); + g_return_val_if_fail (self->prompt_data, FALSE); + } *pin = self->prompt_data; *n_pin = strlen (self->prompt_data); return TRUE; } +static void +login_prompt_done_user (GkmWrapPrompt *self, CK_RV call_result) +{ + CK_TOKEN_INFO tinfo; + + g_assert (GKM_WRAP_IS_PROMPT (self)); + g_assert (self->destroy_data == (GDestroyNotify)egg_secure_strfree); + + /* Save the options, and possibly auto unlock */ + if (call_result == CKR_OK && auto_unlock_should_attach (self)) { + if (get_info_for_token (self, &tinfo)) + auto_unlock_attach_token (&tinfo, self->prompt_data); + } +} + + GkmWrapPrompt* gkm_wrap_prompt_for_login (CK_FUNCTION_LIST_PTR module, CK_USER_TYPE user_type, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, @@ -978,7 +1235,6 @@ gkm_wrap_prompt_done_login (GkmWrapPrompt *self, CK_USER_TYPE user_type, CK_RV c { g_return_if_fail (GKM_WRAP_IS_PROMPT (self)); -#if 0 switch (user_type) { case CKU_CONTEXT_SPECIFIC: login_prompt_done_specific (self, call_result); @@ -987,5 +1243,4 @@ gkm_wrap_prompt_done_login (GkmWrapPrompt *self, CK_USER_TYPE user_type, CK_RV c login_prompt_done_user (self, call_result); break; } -#endif } diff --git a/pkcs11/wrap-layer/tests/Makefile.am b/pkcs11/wrap-layer/tests/Makefile.am index ae531a65..bcc73cc4 100644 --- a/pkcs11/wrap-layer/tests/Makefile.am +++ b/pkcs11/wrap-layer/tests/Makefile.am @@ -1,10 +1,12 @@ TESTING_FILES = \ + mock-secret-store.c \ test-create-credential.c \ - test-login-user.c \ - test-login-specific.c - -UNIT_PROMPT = + test-login-auto.c \ + test-login-hints.c \ + test-login-keyring.c \ + test-login-specific.c \ + test-login-user.c TESTING_LIBS = \ $(top_builddir)/pkcs11/wrap-layer/libgkm-wrap-layer.la \ diff --git a/pkcs11/wrap-layer/tests/mock-secret-store.c b/pkcs11/wrap-layer/tests/mock-secret-store.c new file mode 100644 index 00000000..d724919f --- /dev/null +++ b/pkcs11/wrap-layer/tests/mock-secret-store.c @@ -0,0 +1,286 @@ +/* + * gnome-keyring + * + * Copyright (C) 2010 Stefan Walter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "config.h" + +#include "test-suite.h" + +#include "gkm/gkm-attributes.h" +#include "gkm/gkm-mock.h" +#include "gkm/gkm-test.h" + +#include "wrap-layer/gkm-wrap-layer.h" + +#include "ui/gku-prompt.h" + +static guint secret_identifier = 8800; + +static CK_RV +mock_secret_C_Initialize (CK_VOID_PTR pInitArgs) +{ + GArray *attrs; + CK_RV rv; + + rv = gkm_mock_C_Initialize (pInitArgs); + if (rv != CKR_OK) + return rv; + + attrs = gkm_template_new (NULL, 0); + gkm_template_set_ulong (attrs, CKA_CLASS, CKO_G_COLLECTION); + gkm_template_set_boolean (attrs, CKA_G_LOGIN_COLLECTION, CK_TRUE); + gkm_template_set_string (attrs, CKA_ID, "login"); + gkm_template_set_string (attrs, CKA_LABEL, "Login Keyring"); + gkm_template_set_boolean (attrs, CKA_TRUSTED, CK_TRUE); + gkm_template_set_boolean (attrs, CKA_G_LOCKED, CK_FALSE); + gkm_template_set_boolean (attrs, CKA_TOKEN, CK_TRUE); + gkm_mock_module_take_object (attrs); + + attrs = gkm_template_new (NULL, 0); + gkm_template_set_ulong (attrs, CKA_CLASS, CKO_G_COLLECTION); + gkm_template_set_boolean (attrs, CKA_G_LOGIN_COLLECTION, CK_FALSE); + gkm_template_set_string (attrs, CKA_ID, "other"); + gkm_template_set_string (attrs, CKA_LABEL, "Other Keyring"); + gkm_template_set_boolean (attrs, CKA_TRUSTED, CK_TRUE); + gkm_template_set_boolean (attrs, CKA_G_LOCKED, CK_TRUE); + gkm_template_set_boolean (attrs, CKA_TOKEN, CK_TRUE); + gkm_mock_module_take_object (attrs); + + attrs = gkm_template_new (NULL, 0); + gkm_template_set_string (attrs, CKA_G_COLLECTION, "login"); + gkm_template_set_string (attrs, CKA_ID, "23"); + gkm_template_set_string (attrs, CKA_LABEL, "Unlock password for: Mock"); + gkm_template_set_ulong (attrs, CKA_CLASS, CKO_SECRET_KEY); + gkm_template_set_value (attrs, CKA_G_FIELDS, "one\0" "1\0" "two\0" "2\0", 12); + gkm_template_set_string (attrs, CKA_VALUE, "mock"); + gkm_template_set_boolean (attrs, CKA_TOKEN, CK_TRUE); + gkm_template_set_boolean (attrs, CKA_G_LOCKED, CK_FALSE); + gkm_mock_module_take_object (attrs); + + return CKR_OK; +} + +typedef struct { + GArray *template; + GArray *objects; +} FieldSearch; + +static gboolean +match_fields (gconstpointer fields, gsize n_fields, gconstpointer all, gsize n_all) +{ + const guchar *field; + gsize n_field; + const guchar *ptr; + const guchar *last; + + g_assert (all); + g_assert (fields); + + ptr = fields; + last = ptr + n_fields; + + g_assert (ptr || last == ptr); + while (ptr && ptr != last) { + g_assert (ptr < last); + + field = ptr; + ptr = memchr (ptr, 0, last - ptr); + g_assert (ptr); + ++ptr; + + ptr = memchr (ptr, 0, last - ptr); + g_assert (ptr); + ++ptr; + + n_field = ptr - field; + if (!memmem (all, n_all, field, n_field)) + return FALSE; + } + + return TRUE; +} + +static gboolean +enumerate_field_search (CK_OBJECT_HANDLE handle, GArray *attrs, gpointer user_data) +{ + FieldSearch *ctx = user_data; + CK_ATTRIBUTE_PTR tattr; + CK_ATTRIBUTE_PTR oattr; + + tattr = gkm_template_find (ctx->template, CKA_G_FIELDS); + g_assert (tattr); + + oattr = gkm_template_find (attrs, CKA_G_FIELDS); + if (!oattr) + return TRUE; /* Continue */ + if (!match_fields (tattr->pValue, tattr->ulValueLen, oattr->pValue, oattr->ulValueLen)) + return TRUE; /* Continue */ + + tattr = gkm_template_find (ctx->template, CKA_G_COLLECTION); + if (tattr) { + oattr = gkm_template_find (attrs, CKA_G_COLLECTION); + if (!oattr || oattr->ulValueLen != tattr->ulValueLen) + return TRUE; /* Continue */ + if (memcmp (oattr->pValue, tattr->pValue, oattr->ulValueLen) != 0) + return TRUE; /* Continue */ + } + + /* Add it to the set */ + g_array_append_val (ctx->objects, handle); + return TRUE; /* Continue */ +} + +static CK_RV +mock_secret_C_CreateObject (CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject) +{ + GArray *template = NULL; + CK_OBJECT_CLASS klass; + CK_ATTRIBUTE_PTR attr; + gchar *text; + CK_RV rv; + + g_return_val_if_fail (phObject, CKR_ARGUMENTS_BAD); + + if (!gkm_attributes_find_ulong (pTemplate, ulCount, CKA_CLASS, &klass)) + klass = (CK_ULONG)-1; + + /* Is it a search object? */ + if (klass == CKO_G_SEARCH) { + + FieldSearch ctx; + ctx.template = template = gkm_template_new (pTemplate, ulCount); + ctx.objects = g_array_new (FALSE, FALSE, sizeof (CK_OBJECT_HANDLE)); + + /* Find all the fields */ + gkm_mock_module_enumerate_objects (hSession, enumerate_field_search, &ctx); + + gkm_template_set_value (template, CKA_G_MATCHED, ctx.objects->data, + ctx.objects->len * sizeof (CK_OBJECT_HANDLE)); + g_array_free (ctx.objects, TRUE); + + } else if (klass == CKO_SECRET_KEY) { + + /* If it's a secret key and id set, just overwrite */ + attr = gkm_attributes_find (pTemplate, ulCount, CKA_ID); + if (attr) { + CK_ATTRIBUTE attrs[2]; + + memcpy (attrs, attr, sizeof (CK_ATTRIBUTE)); + attrs[1].type = CKA_CLASS; + attrs[1].ulValueLen = sizeof (klass); + attrs[1].pValue = &klass; + + *phObject = gkm_mock_module_find_object (hSession, attrs, 2); + g_return_val_if_fail (*phObject, CKR_TEMPLATE_INCONSISTENT); + return gkm_mock_C_SetAttributeValue (hSession, *phObject, pTemplate, ulCount); + } + + /* Otherwise add a unique identifier */ + template = gkm_template_new (pTemplate, ulCount); + text = g_strdup_printf ("%d", ++secret_identifier); + gkm_template_set_string (template, CKA_ID, text); + g_free (text); + } + + if (template) { + pTemplate = (CK_ATTRIBUTE_PTR)template->data; + ulCount = template->len; + } + + rv = gkm_mock_C_CreateObject (hSession, pTemplate, ulCount, phObject); + + if (template) + g_array_free (template, TRUE); + + return rv; +} + +CK_FUNCTION_LIST mock_secret_store = { + { 2, 11 }, /* version */ + mock_secret_C_Initialize, + gkm_mock_C_Finalize, + gkm_mock_C_GetInfo, + gkm_mock_C_GetFunctionList, + gkm_mock_C_GetSlotList, + gkm_mock_C_GetSlotInfo, + gkm_mock_C_GetTokenInfo, + gkm_mock_C_GetMechanismList, + gkm_mock_C_GetMechanismInfo, + gkm_mock_C_InitToken, + gkm_mock_C_InitPIN, + gkm_mock_C_SetPIN, + gkm_mock_C_OpenSession, + gkm_mock_C_CloseSession, + gkm_mock_C_CloseAllSessions, + gkm_mock_C_GetSessionInfo, + gkm_mock_unsupported_C_GetOperationState, + gkm_mock_unsupported_C_SetOperationState, + gkm_mock_C_Login, + gkm_mock_C_Logout, + mock_secret_C_CreateObject, + gkm_mock_unsupported_C_CopyObject, + gkm_mock_C_DestroyObject, + gkm_mock_unsupported_C_GetObjectSize, + gkm_mock_C_GetAttributeValue, + gkm_mock_C_SetAttributeValue, + gkm_mock_C_FindObjectsInit, + gkm_mock_C_FindObjects, + gkm_mock_C_FindObjectsFinal, + gkm_mock_C_EncryptInit, + gkm_mock_C_Encrypt, + gkm_mock_unsupported_C_EncryptUpdate, + gkm_mock_unsupported_C_EncryptFinal, + gkm_mock_C_DecryptInit, + gkm_mock_C_Decrypt, + gkm_mock_unsupported_C_DecryptUpdate, + gkm_mock_unsupported_C_DecryptFinal, + gkm_mock_unsupported_C_DigestInit, + gkm_mock_unsupported_C_Digest, + gkm_mock_unsupported_C_DigestUpdate, + gkm_mock_unsupported_C_DigestKey, + gkm_mock_unsupported_C_DigestFinal, + gkm_mock_C_SignInit, + gkm_mock_C_Sign, + gkm_mock_unsupported_C_SignUpdate, + gkm_mock_unsupported_C_SignFinal, + gkm_mock_unsupported_C_SignRecoverInit, + gkm_mock_unsupported_C_SignRecover, + gkm_mock_C_VerifyInit, + gkm_mock_C_Verify, + gkm_mock_unsupported_C_VerifyUpdate, + gkm_mock_unsupported_C_VerifyFinal, + gkm_mock_unsupported_C_VerifyRecoverInit, + gkm_mock_unsupported_C_VerifyRecover, + gkm_mock_unsupported_C_DigestEncryptUpdate, + gkm_mock_unsupported_C_DecryptDigestUpdate, + gkm_mock_unsupported_C_SignEncryptUpdate, + gkm_mock_unsupported_C_DecryptVerifyUpdate, + gkm_mock_unsupported_C_GenerateKey, + gkm_mock_unsupported_C_GenerateKeyPair, + gkm_mock_unsupported_C_WrapKey, + gkm_mock_unsupported_C_UnwrapKey, + gkm_mock_unsupported_C_DeriveKey, + gkm_mock_unsupported_C_SeedRandom, + gkm_mock_unsupported_C_GenerateRandom, + gkm_mock_C_GetFunctionStatus, + gkm_mock_C_CancelFunction, + gkm_mock_unsupported_C_WaitForSlotEvent +}; diff --git a/pkcs11/wrap-layer/tests/test-create-credential.c b/pkcs11/wrap-layer/tests/test-create-credential.c index 2b89bb4c..17a0309f 100644 --- a/pkcs11/wrap-layer/tests/test-create-credential.c +++ b/pkcs11/wrap-layer/tests/test-create-credential.c @@ -86,6 +86,8 @@ DEFINE_TEARDOWN (create_credential) { CK_RV rv; + g_assert (!gku_prompt_dummy_have_response ()); + object = 0; rv = (module->C_CloseSession) (session); diff --git a/pkcs11/wrap-layer/tests/test-login-auto.c b/pkcs11/wrap-layer/tests/test-login-auto.c new file mode 100644 index 00000000..9b21649f --- /dev/null +++ b/pkcs11/wrap-layer/tests/test-login-auto.c @@ -0,0 +1,210 @@ +/* + * gnome-keyring + * + * Copyright (C) 2010 Stefan Walter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "config.h" + +#include "test-suite.h" + +#include "gkm/gkm-mock.h" +#include "gkm/gkm-test.h" + +#include "wrap-layer/gkm-wrap-layer.h" + +#include "ui/gku-prompt.h" + +extern CK_FUNCTION_LIST mock_secret_store; +static CK_FUNCTION_LIST functions; +static CK_FUNCTION_LIST_PTR module = NULL; +static CK_SESSION_HANDLE session = 0; +static CK_OBJECT_HANDLE key = 0; +static CK_OBJECT_HANDLE collection = 0; +static CK_MECHANISM mech = { CKM_MOCK_PREFIX, NULL, 0 }; + +DEFINE_SETUP (login_auto) +{ + CK_SLOT_ID slot_id; + CK_ULONG n_slots = 1; + CK_ULONG count; + CK_RV rv; + + CK_BBOOL always = TRUE; + CK_ATTRIBUTE kattrs[] = { + { CKA_ALWAYS_AUTHENTICATE, &always, sizeof (always) } + }; + + CK_OBJECT_CLASS fklass = CKO_G_COLLECTION; + CK_ATTRIBUTE fattrs[] = { + { CKA_CLASS, &fklass, sizeof (fklass) }, + { CKA_ID, "other", 5 }, + }; + + /* Always start off with test functions */ + memcpy (&functions, &mock_secret_store, sizeof (functions)); + + gkm_wrap_layer_reset_modules (); + gkm_wrap_layer_add_module (&functions); + module = gkm_wrap_layer_get_functions (); + + gku_prompt_dummy_prepare_response (); + + /* Open a session */ + rv = (module->C_Initialize) (NULL); + gkm_assert_cmprv (rv, ==, CKR_OK); + + rv = (module->C_GetSlotList) (CK_TRUE, &slot_id, &n_slots); + gkm_assert_cmprv (rv, ==, CKR_OK); + + rv = (module->C_OpenSession) (slot_id, CKF_SERIAL_SESSION, NULL, NULL, &session); + gkm_assert_cmprv (rv, ==, CKR_OK); + + /* Find keyring object */ + rv = (module->C_FindObjectsInit) (session, fattrs, 1); + gkm_assert_cmprv (rv, ==, CKR_OK); + rv = (module->C_FindObjects) (session, &collection, 1, &count); + gkm_assert_cmprv (rv, ==, CKR_OK); + gkm_assert_cmpulong (count, ==, 1); + gkm_assert_cmpulong (collection, !=, 0); + rv = (module->C_FindObjectsFinal) (session); + gkm_assert_cmprv (rv, ==, CKR_OK); + + /* Find the key object */ + rv = (module->C_FindObjectsInit) (session, kattrs, 1); + gkm_assert_cmprv (rv, ==, CKR_OK); + rv = (module->C_FindObjects) (session, &key, 1, &count); + gkm_assert_cmprv (rv, ==, CKR_OK); + gkm_assert_cmpulong (count, ==, 1); + gkm_assert_cmpulong (key, !=, 0); + rv = (module->C_FindObjectsFinal) (session); + gkm_assert_cmprv (rv, ==, CKR_OK); + + /* Start a signing operation, that needs to be authenticated */ + rv = (module->C_SignInit) (session, &mech, key); + gkm_assert_cmprv (rv, ==, CKR_OK); +} + +DEFINE_TEARDOWN (login_auto) +{ + CK_RV rv; + + g_assert (!gku_prompt_dummy_have_response ()); + + key = 0; + collection = 0; + + rv = (module->C_CloseSession) (session); + gkm_assert_cmprv (rv, ==, CKR_OK); + session = 0; + + rv = (module->C_Finalize) (NULL); + gkm_assert_cmprv (rv, ==, CKR_OK); + module = NULL; +} + +DEFINE_TEST (login_auto_specific) +{ + CK_RV rv; + + /* Login with prompt */ + gku_prompt_dummy_queue_auto_password ("booo"); + rv = (module->C_Login) (session, CKU_CONTEXT_SPECIFIC, NULL, 0); + gkm_assert_cmprv (rv, ==, CKR_OK); + + /* Start a signing operation, that needs to be authenticated */ + rv = (module->C_SignInit) (session, &mech, key); + gkm_assert_cmprv (rv, ==, CKR_OK); + + /* No further prompting should be shown, uses stored password */ + gku_prompt_dummy_prepare_response (); + rv = (module->C_Login) (session, CKU_CONTEXT_SPECIFIC, NULL, 0); + gkm_assert_cmprv (rv, ==, CKR_OK); + + /* Change the password */ + gkm_mock_module_set_pin ("other"); + + /* Start a signing operation, that needs to be authenticated */ + rv = (module->C_SignInit) (session, &mech, key); + gkm_assert_cmprv (rv, ==, CKR_OK); + + /* This should prompt again, as stored password is now wrong */ + gku_prompt_dummy_queue_ok_password ("other"); + rv = (module->C_Login) (session, CKU_CONTEXT_SPECIFIC, NULL, 0); + gkm_assert_cmprv (rv, ==, CKR_OK); +} + +DEFINE_TEST (login_auto_user_token) +{ + CK_RV rv; + + /* Login with prompt */ + gku_prompt_dummy_queue_auto_password ("booo"); + rv = (module->C_Login) (session, CKU_USER, NULL, 0); + gkm_assert_cmprv (rv, ==, CKR_OK); + rv = (module->C_Logout) (session); + gkm_assert_cmprv (rv, ==, CKR_OK); + + /* No further prompting should be shown, uses stored password */ + gku_prompt_dummy_prepare_response (); + rv = (module->C_Login) (session, CKU_USER, NULL, 0); + gkm_assert_cmprv (rv, ==, CKR_OK); + rv = (module->C_Logout) (session); + gkm_assert_cmprv (rv, ==, CKR_OK); + + /* Change the password */ + gkm_mock_module_set_pin ("other"); + + /* This should prompt again, as stored password is now wrong */ + gku_prompt_dummy_queue_ok_password ("other"); + rv = (module->C_Login) (session, CKU_USER, NULL, 0); + gkm_assert_cmprv (rv, ==, CKR_OK); +} + +DEFINE_TEST (login_auto_unlock_keyring) +{ + CK_OBJECT_HANDLE credential; + CK_RV rv; + + CK_OBJECT_CLASS klass = CKO_G_CREDENTIAL; + CK_ATTRIBUTE attrs[] = { + { CKA_CLASS, &klass, sizeof (klass) }, + { CKA_VALUE, NULL, 0 }, + { CKA_G_OBJECT, &collection, sizeof (collection) }, + }; + + /* Create credential with prompt */ + gku_prompt_dummy_queue_auto_password ("booo"); + rv = (module->C_CreateObject) (session, attrs, G_N_ELEMENTS (attrs), &credential); + gkm_assert_cmprv (rv, ==, CKR_OK); + rv = (module->C_DestroyObject) (session, credential); + gkm_assert_cmprv (rv, ==, CKR_OK); + + /* No further prompting should be shown, uses stored password */ + gku_prompt_dummy_prepare_response (); + rv = (module->C_CreateObject) (session, attrs, G_N_ELEMENTS (attrs), &credential); + gkm_assert_cmprv (rv, ==, CKR_OK); + + /* Change the password */ + gkm_mock_module_set_pin ("other"); + + /* This should prompt again, as stored password is now wrong */ + gku_prompt_dummy_queue_ok_password ("other"); + rv = (module->C_CreateObject) (session, attrs, G_N_ELEMENTS (attrs), &credential); + gkm_assert_cmprv (rv, ==, CKR_OK); +} diff --git a/pkcs11/wrap-layer/tests/test-login-hints.c b/pkcs11/wrap-layer/tests/test-login-hints.c new file mode 100644 index 00000000..d649c21c --- /dev/null +++ b/pkcs11/wrap-layer/tests/test-login-hints.c @@ -0,0 +1,44 @@ +/* + * gnome-keyring + * + * Copyright (C) 2010 Stefan Walter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "config.h" + +#include "test-suite.h" + +#include "wrap-layer/gkm-wrap-layer.h" +#include "wrap-layer/gkm-wrap-login.h" + +DEFINE_TEST (login_did_unlock_fail) +{ + gboolean ret; + + gkm_wrap_layer_hint_login_unlock_failure (); + + ret = gkm_wrap_login_did_unlock_fail (); + g_assert (ret == TRUE); + + gkm_wrap_layer_hint_login_unlock_failure (); + gkm_wrap_layer_hint_login_unlock_failure (); + gkm_wrap_layer_hint_login_unlock_success (); + + ret = gkm_wrap_login_did_unlock_fail (); + g_assert (ret == FALSE); +} diff --git a/pkcs11/wrap-layer/tests/test-login-keyring.c b/pkcs11/wrap-layer/tests/test-login-keyring.c new file mode 100644 index 00000000..43c4f43c --- /dev/null +++ b/pkcs11/wrap-layer/tests/test-login-keyring.c @@ -0,0 +1,306 @@ +/* + * gnome-keyring + * + * Copyright (C) 2010 Stefan Walter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "config.h" + +#include "test-suite.h" + +#include "egg/egg-secure-memory.h" + +#include "gkm/gkm-attributes.h" +#include "gkm/gkm-mock.h" +#include "gkm/gkm-test.h" + +#include "wrap-layer/gkm-wrap-layer.h" +#include "wrap-layer/gkm-wrap-login.h" + +extern CK_FUNCTION_LIST mock_secret_store; +static CK_FUNCTION_LIST functions; +static CK_FUNCTION_LIST_PTR module = NULL; + +DEFINE_SETUP (login_keyring) +{ + CK_RV rv; + + /* Always start off with test functions */ + memcpy (&functions, &mock_secret_store, sizeof (functions)); + gkm_wrap_layer_reset_modules (); + gkm_wrap_layer_add_module (&functions); + module = gkm_wrap_layer_get_functions (); + + /* Initialize */ + rv = (module->C_Initialize) (NULL); + gkm_assert_cmprv (rv, ==, CKR_OK); +} + +DEFINE_TEARDOWN (login_keyring) +{ + CK_RV rv; + + rv = (module->C_Finalize) (NULL); + gkm_assert_cmprv (rv, ==, CKR_OK); + module = NULL; +} + +DEFINE_TEST (login_is_usable) +{ + gboolean ret; + + ret = gkm_wrap_login_is_usable (); + g_assert (ret == TRUE); +} + +DEFINE_TEST (login_usable_fail_open_session) +{ + gboolean ret; + + functions.C_OpenSession = gkm_mock_fail_C_OpenSession; + ret = gkm_wrap_login_is_usable (); + g_assert (ret == FALSE); +} + +DEFINE_TEST (login_usable_fail_not_trusted) +{ + CK_OBJECT_HANDLE object; + CK_ATTRIBUTE attr; + CK_BBOOL bval; + gboolean ret; + + ret = gkm_wrap_login_is_usable (); + g_assert (ret == TRUE); + + bval = CK_TRUE; + attr.type = CKA_G_LOGIN_COLLECTION; + attr.pValue = &bval; + attr.ulValueLen = sizeof (bval); + + object = gkm_mock_module_find_object (0, &attr, 1); + gkm_assert_cmpulong (object, !=, 0); + + bval = CK_FALSE; + attr.type = CKA_TRUSTED; + attr.pValue = &bval; + attr.ulValueLen = sizeof (bval); + + gkm_mock_module_set_object (object, &attr, 1); + + /* Not trusted, so no longer usable */ + ret = gkm_wrap_login_is_usable (); + g_assert (ret == FALSE); +} + +DEFINE_TEST (login_usable_fail_locked) +{ + CK_OBJECT_HANDLE object; + CK_ATTRIBUTE attr; + CK_BBOOL bval; + gboolean ret; + + ret = gkm_wrap_login_is_usable (); + g_assert (ret == TRUE); + + bval = CK_TRUE; + attr.type = CKA_G_LOGIN_COLLECTION; + attr.pValue = &bval; + attr.ulValueLen = sizeof (bval); + + object = gkm_mock_module_find_object (0, &attr, 1); + gkm_assert_cmpulong (object, !=, 0); + + bval = CK_TRUE; + attr.type = CKA_G_LOCKED; + attr.pValue = &bval; + attr.ulValueLen = sizeof (bval); + + gkm_mock_module_set_object (object, &attr, 1); + + /* Not unlocked, so no longer usable */ + ret = gkm_wrap_login_is_usable (); + g_assert (ret == FALSE); +} + +DEFINE_TEST (login_lookup_secret_no_match) +{ + gchar *password; + + password = gkm_wrap_login_lookup_secret ("invalid", "attribute", + "second", "attribute", NULL); + g_assert_cmpstr (password, ==, NULL); +} + +DEFINE_TEST (login_lookup_secret_and_match) +{ + gchar *password; + + /* Secret stored in mock-secret-store.c */ + password = gkm_wrap_login_lookup_secret ("one", "1", + "two", "2", NULL); + g_assert_cmpstr (password, ==, "mock"); + + egg_secure_free (password); +} + +DEFINE_TEST (login_lookup_store_secret) +{ + CK_OBJECT_CLASS klass = CKO_SECRET_KEY; + CK_BBOOL tval = CK_TRUE; + CK_ATTRIBUTE attrs[] = { + { CKA_LABEL, "Unlock password for: The label", 30 }, + { CKA_CLASS, &klass, sizeof (klass) }, + { CKA_VALUE, "the password", 12 }, + { CKA_TOKEN, &tval, sizeof (tval) }, + { CKA_G_COLLECTION, "login", 5 }, + { CKA_G_FIELDS, "one\0" "1\0" "three\0" "3\0", 14 }, + }; + + CK_OBJECT_HANDLE object; + + /* Secret stored in mock-secret-store.c */ + gkm_wrap_login_attach_secret ("The label", "the password", + "one", "1", + "three", "3", NULL); + + object = gkm_mock_module_find_object (0, attrs, G_N_ELEMENTS (attrs)); + gkm_assert_cmpulong (object, !=, 0); +} + +DEFINE_TEST (login_lookup_store_secret_overwrite) +{ + CK_OBJECT_CLASS klass = CKO_SECRET_KEY; + CK_BBOOL tval = CK_TRUE; + CK_ATTRIBUTE attrs[] = { + { CKA_VALUE, "the password", 12 }, + { CKA_LABEL, "Unlock password for: The label", 30 }, + { CKA_CLASS, &klass, sizeof (klass) }, + { CKA_TOKEN, &tval, sizeof (tval) }, + { CKA_G_COLLECTION, "login", 5 }, + { CKA_G_FIELDS, "one\0" "1\0" "three\0" "3\0", 14 }, + }; + + CK_OBJECT_HANDLE object1, object2; + + /* Secret stored in mock-secret-store.c */ + gkm_wrap_login_attach_secret ("The label", "the password", + "one", "1", + "three", "3", NULL); + + object1 = gkm_mock_module_find_object (0, attrs, G_N_ELEMENTS (attrs)); + gkm_assert_cmpulong (object1, !=, 0); + + /* Secret stored in mock-secret-store.c */ + gkm_wrap_login_attach_secret ("The label", "other", + "one", "1", + "three", "3", NULL); + + attrs[0].pValue = "other"; + attrs[0].ulValueLen = 5; + + object2 = gkm_mock_module_find_object (0, attrs, G_N_ELEMENTS (attrs)); + gkm_assert_cmpulong (object2, !=, 0); + + /* Should have been stored on same object */ + gkm_assert_cmpulong (object1, ==, object2); +} + +DEFINE_TEST (login_lookup_store_null_secret) +{ + CK_OBJECT_CLASS klass = CKO_SECRET_KEY; + CK_BBOOL tval = CK_TRUE; + CK_ATTRIBUTE attrs[] = { + { CKA_LABEL, "Unlock password for: The label", 30 }, + { CKA_CLASS, &klass, sizeof (klass) }, + { CKA_VALUE, "", 0 }, + { CKA_TOKEN, &tval, sizeof (tval) }, + { CKA_G_COLLECTION, "login", 5 }, + { CKA_G_FIELDS, "one\0" "1\0" "three\0" "3\0", 14 }, + }; + + CK_OBJECT_HANDLE object; + + gkm_wrap_login_attach_secret ("The label", NULL, + "one", "1", + "three", "3", NULL); + + object = gkm_mock_module_find_object (0, attrs, G_N_ELEMENTS (attrs)); + gkm_assert_cmpulong (object, !=, 0); +} + +DEFINE_TEST (login_lookup_store_no_attributes_not_stored) +{ + CK_OBJECT_CLASS klass = CKO_SECRET_KEY; + CK_BBOOL tval = CK_TRUE; + CK_ATTRIBUTE attrs[] = { + { CKA_LABEL, "Unlock password for: The label", 30 }, + { CKA_CLASS, &klass, sizeof (klass) }, + { CKA_VALUE, "the password", 0 }, + { CKA_TOKEN, &tval, sizeof (tval) }, + { CKA_G_COLLECTION, "login", 5 }, + { CKA_G_FIELDS, "", 0 }, + }; + + CK_OBJECT_HANDLE object; + + gkm_wrap_login_attach_secret ("The label", "the password", NULL); + + object = gkm_mock_module_find_object (0, attrs, G_N_ELEMENTS (attrs)); + gkm_assert_cmpulong (object, ==, 0); +} + + +DEFINE_TEST (login_lookup_remove_present) +{ + CK_OBJECT_CLASS klass = CKO_SECRET_KEY; + CK_BBOOL tval = CK_TRUE; + CK_ATTRIBUTE attrs[] = { + { CKA_LABEL, "Unlock password for: Mock", 25 }, + { CKA_CLASS, &klass, sizeof (klass) }, + { CKA_VALUE, "mock", 4 }, + { CKA_TOKEN, &tval, sizeof (tval) }, + { CKA_G_COLLECTION, "login", 5 }, + { CKA_G_FIELDS, "one\0" "1\0" "two\0" "2\0", 12 }, + }; + + CK_OBJECT_HANDLE object; + + /* This object is created in mock-secret-store.c */ + object = gkm_mock_module_find_object (0, attrs, G_N_ELEMENTS (attrs)); + gkm_assert_cmpulong (object, !=, 0); + + gkm_wrap_login_remove_secret ("one", "1", "two", "2", NULL); + + /* No longer there */ + object = gkm_mock_module_find_object (0, attrs, G_N_ELEMENTS (attrs)); + gkm_assert_cmpulong (object, ==, 0); +} + +DEFINE_TEST (login_lookup_remove_no_attributes) +{ + guint n_objects, check; + + n_objects = gkm_mock_module_count_objects (0); + g_assert_cmpuint (n_objects, >, 0); + + /* Shouldn't remove anything if no attributes */ + gkm_wrap_login_remove_secret (NULL); + + check = gkm_mock_module_count_objects (0); + g_assert_cmpuint (check, ==, n_objects); +} diff --git a/pkcs11/wrap-layer/tests/test-login-specific.c b/pkcs11/wrap-layer/tests/test-login-specific.c index 2637c5bb..234076de 100644 --- a/pkcs11/wrap-layer/tests/test-login-specific.c +++ b/pkcs11/wrap-layer/tests/test-login-specific.c @@ -92,6 +92,8 @@ DEFINE_TEARDOWN (login_specific) { CK_RV rv; + g_assert (!gku_prompt_dummy_have_response ()); + rv = (module->C_CloseSession) (session); gkm_assert_cmprv (rv, ==, CKR_OK); session = 0; diff --git a/pkcs11/wrap-layer/tests/test-login-user.c b/pkcs11/wrap-layer/tests/test-login-user.c index 3fde9c68..f709d0a1 100644 --- a/pkcs11/wrap-layer/tests/test-login-user.c +++ b/pkcs11/wrap-layer/tests/test-login-user.c @@ -67,6 +67,8 @@ DEFINE_TEARDOWN (login_user) { CK_RV rv; + g_assert (!gku_prompt_dummy_have_response ()); + rv = (module->C_CloseSession) (session); gkm_assert_cmprv (rv, ==, CKR_OK); session = 0; diff --git a/po/POTFILES.in b/po/POTFILES.in index c90f557e..5c2cd7c3 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -24,6 +24,7 @@ gp11/gp11-misc.c pkcs11/gkm/gkm-certificate.c pkcs11/ssh-store/gkm-ssh-private-key.c pkcs11/secret-store/gkm-secret-collection.c +pkcs11/wrap-layer/gkm-wrap-login.c pkcs11/wrap-layer/gkm-wrap-prompt.c ui/gku-prompt-tool.c [type: gettext/glade]ui/gku-prompt.ui diff --git a/ui/gku-prompt.c b/ui/gku-prompt.c index ce473cf5..dc93acbf 100644 --- a/ui/gku-prompt.c +++ b/ui/gku-prompt.c @@ -39,7 +39,7 @@ #include <sys/wait.h> -#define DEBUG_PROMPT 1 +#define DEBUG_PROMPT 0 #define DEBUG_STDERR 0 enum { @@ -86,9 +86,9 @@ static void display_async_prompt (GkuPrompt *); /* User choices we transfer over during a soft prompt reset */ const struct { const gchar *section; const gchar *name; } SOFT_RESET[] = { - { "unlock-options", "unlock-auto"}, - { "unlock-options", "unlock-idle"}, - { "unlock-options", "unlock-timeout"}, + { "unlock-options", "unlock-auto" }, + { "unlock-options", "unlock-idle" }, + { "unlock-options", "unlock-timeout" }, { "details", "expanded" }, }; @@ -1172,6 +1172,16 @@ gku_prompt_dummy_prepare_response (void) g_static_mutex_unlock (&attention_mutex); } +gboolean +gku_prompt_dummy_have_response (void) +{ + gboolean ret; + g_static_mutex_lock (&attention_mutex); + ret = !g_queue_is_empty (&queued_responses); + g_static_mutex_unlock (&attention_mutex); + return ret; +} + static void queue_dummy_response (gchar *response) { @@ -1202,6 +1212,19 @@ gku_prompt_dummy_queue_ok_password (const gchar *password) } void +gku_prompt_dummy_queue_auto_password (const gchar *password) +{ + const static gchar *RESPONSE = "[password]\nparameter=\nvalue=%s\n[prompt]\nresponse=ok\n" + "[unlock-options]\nunlock-auto=1\n"; + gchar *value; + + g_return_if_fail (password); + value = egg_hex_encode ((const guchar*)password, strlen (password)); + queue_dummy_response (g_strdup_printf (RESPONSE, value)); + g_free (value); +} + +void gku_prompt_dummy_queue_no (void) { const static gchar *RESPONSE = "[prompt]\nresponse=no\n"; diff --git a/ui/gku-prompt.h b/ui/gku-prompt.h index 075a2092..1f82eb54 100644 --- a/ui/gku-prompt.h +++ b/ui/gku-prompt.h @@ -143,10 +143,14 @@ void gku_prompt_request_attention_sync (const gchar *window_i void gku_prompt_dummy_prepare_response (void); +gboolean gku_prompt_dummy_have_response (void); + void gku_prompt_dummy_queue_response (const gchar *response); void gku_prompt_dummy_queue_ok_password (const gchar *password); +void gku_prompt_dummy_queue_auto_password (const gchar *password); + void gku_prompt_dummy_queue_no (void); #endif |