summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2010-06-12 17:38:27 +0000
committerStef Walter <stef@memberwebs.com>2010-06-12 21:22:07 +0000
commit92852897d420071033557bf77123a447c45b0ea1 (patch)
treee2c553b3a2661ac0a9aca11f6a2076082072f239
parent2954ca8774c95bd2a033b3d62a64ce78c1a2fe93 (diff)
downloadgnome-keyring-92852897d420071033557bf77123a447c45b0ea1.tar.gz
Implement auto unlock in wrap-layer.
* Including tests and necessary infrastructure.
-rw-r--r--Makefile.am2
-rw-r--r--pkcs11/gkm/gkm-mock.c254
-rw-r--r--pkcs11/gkm/gkm-mock.h26
-rw-r--r--pkcs11/gkm/gkm-secret.c7
-rw-r--r--pkcs11/gkm/gkm-secret.h2
-rw-r--r--pkcs11/pkcs11i.h2
-rw-r--r--pkcs11/secret-store/gkm-secret-collection.c11
-rw-r--r--pkcs11/wrap-layer/Makefile.am1
-rw-r--r--pkcs11/wrap-layer/gkm-wrap-layer.h10
-rw-r--r--pkcs11/wrap-layer/gkm-wrap-login.c465
-rw-r--r--pkcs11/wrap-layer/gkm-wrap-login.h42
-rw-r--r--pkcs11/wrap-layer/gkm-wrap-prompt.c539
-rw-r--r--pkcs11/wrap-layer/tests/Makefile.am10
-rw-r--r--pkcs11/wrap-layer/tests/mock-secret-store.c286
-rw-r--r--pkcs11/wrap-layer/tests/test-create-credential.c2
-rw-r--r--pkcs11/wrap-layer/tests/test-login-auto.c210
-rw-r--r--pkcs11/wrap-layer/tests/test-login-hints.c44
-rw-r--r--pkcs11/wrap-layer/tests/test-login-keyring.c306
-rw-r--r--pkcs11/wrap-layer/tests/test-login-specific.c2
-rw-r--r--pkcs11/wrap-layer/tests/test-login-user.c2
-rw-r--r--po/POTFILES.in1
-rw-r--r--ui/gku-prompt.c31
-rw-r--r--ui/gku-prompt.h4
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