[gnome-keyring] [wrap-layer] Fix login keyring password doesn't match login.



commit f3ab3e877cff32e01bad926bf7cd70b28c9d8873
Author: Stef Walter <stef memberwebs com>
Date:   Mon Oct 18 02:12:30 2010 +0000

    [wrap-layer] Fix login keyring password doesn't match login.
    
    When the user's unix login password doesn't match their login keyring
    (due to perhaps a change by an administrator) we need to prompt for
    the password to unlock. Now we keep track of the old one that failed
    and change their login keyring password to match their unix login.

 daemon/gkd-main.c                          |    2 +-
 daemon/login/gkd-login.c                   |    5 +-
 pkcs11/wrap-layer/gkm-wrap-layer.h         |    6 +-
 pkcs11/wrap-layer/gkm-wrap-login.c         |   38 ++++++++++--
 pkcs11/wrap-layer/gkm-wrap-login.h         |    2 +
 pkcs11/wrap-layer/gkm-wrap-prompt.c        |   89 +++++++++++++++++++++++++--
 pkcs11/wrap-layer/tests/test-login-hints.c |   15 ++++-
 7 files changed, 134 insertions(+), 23 deletions(-)
---
diff --git a/daemon/gkd-main.c b/daemon/gkd-main.c
index a19ceef..f78100c 100644
--- a/daemon/gkd-main.c
+++ b/daemon/gkd-main.c
@@ -684,7 +684,7 @@ gkr_daemon_initialize_steps (const gchar *components)
 		 */
 		if (login_password) {
 			if (!gkd_login_unlock (login_password))
-				g_message ("Failed to unlock login on startup");
+				g_message ("failed to unlock login keyring on startup");
 			egg_secure_strclear (login_password);
 		}
 
diff --git a/daemon/login/gkd-login.c b/daemon/login/gkd-login.c
index a4ef506..2fbbde8 100644
--- a/daemon/login/gkd-login.c
+++ b/daemon/login/gkd-login.c
@@ -201,7 +201,7 @@ unlock_or_create_login (GList *modules, const gchar *master)
 	/* Failure, bad password? */
 	if (cred == NULL) {
 		if (login && g_error_matches (error, GCK_ERROR, CKR_PIN_INCORRECT))
-			gkm_wrap_layer_hint_login_unlock_failure ();
+			gkm_wrap_layer_mark_login_unlock_failure (master);
 		else
 			g_warning ("couldn't create login credential: %s", egg_error_message (error));
 		g_clear_error (&error);
@@ -216,7 +216,7 @@ unlock_or_create_login (GList *modules, const gchar *master)
 
 	/* The unlock succeeded yay */
 	} else {
-		gkm_wrap_layer_hint_login_unlock_success ();
+		gkm_wrap_layer_mark_login_unlock_success ();
 	}
 
 	if (cred)
@@ -316,7 +316,6 @@ change_or_create_login (GList *modules, const gchar *original, const gchar *mast
 				g_message ("couldn't change login master password, "
 				           "original password was wrong: %s",
 				           egg_error_message (error));
-				gkm_wrap_layer_hint_login_unlock_failure ();
 			} else {
 				g_warning ("couldn't create original login credential: %s",
 				           egg_error_message (error));
diff --git a/pkcs11/wrap-layer/gkm-wrap-layer.h b/pkcs11/wrap-layer/gkm-wrap-layer.h
index 531e7b7..df5306a 100644
--- a/pkcs11/wrap-layer/gkm-wrap-layer.h
+++ b/pkcs11/wrap-layer/gkm-wrap-layer.h
@@ -24,6 +24,8 @@
 
 #include "pkcs11/pkcs11.h"
 
+#include <glib.h>
+
 CK_FUNCTION_LIST_PTR    gkm_wrap_layer_get_functions               (void);
 
 CK_FUNCTION_LIST_PTR    gkm_wrap_layer_get_functions_no_prompts    (void);
@@ -32,8 +34,8 @@ void                    gkm_wrap_layer_reset_modules               (void);
 
 void                    gkm_wrap_layer_add_module                  (CK_FUNCTION_LIST_PTR funcs);
 
-void                    gkm_wrap_layer_hint_login_unlock_success   (void);
+void                    gkm_wrap_layer_mark_login_unlock_success   (void);
 
-void                    gkm_wrap_layer_hint_login_unlock_failure   (void);
+void                    gkm_wrap_layer_mark_login_unlock_failure   (const gchar *failed_password);
 
 #endif /* __GKM_WRAP_LAYER_H__ */
diff --git a/pkcs11/wrap-layer/gkm-wrap-login.c b/pkcs11/wrap-layer/gkm-wrap-login.c
index 7bef442..d17b7ff 100644
--- a/pkcs11/wrap-layer/gkm-wrap-login.c
+++ b/pkcs11/wrap-layer/gkm-wrap-login.c
@@ -36,24 +36,50 @@
 
 #include <string.h>
 
-static gint unlock_failures = 0;
+/* Holds failed unlock password, accessed atomically */
+static gpointer unlock_failure = NULL;
 
 void
-gkm_wrap_layer_hint_login_unlock_success (void)
+gkm_wrap_layer_mark_login_unlock_success (void)
 {
-	g_atomic_int_set (&unlock_failures, 0);
+	gpointer oldval = g_atomic_pointer_get (&unlock_failure);
+	if (g_atomic_pointer_compare_and_exchange (&unlock_failure, oldval, NULL))
+		egg_secure_strfree (oldval);
 }
 
 void
-gkm_wrap_layer_hint_login_unlock_failure (void)
+gkm_wrap_layer_mark_login_unlock_failure (const gchar *failed_password)
 {
-	g_atomic_int_inc (&unlock_failures);
+	gpointer oldval;
+	gpointer newval;
+
+	g_return_if_fail (failed_password);
+
+	oldval = g_atomic_pointer_get (&unlock_failure);
+	newval = egg_secure_strdup (failed_password);
+
+	if (g_atomic_pointer_compare_and_exchange (&unlock_failure, oldval, newval))
+		egg_secure_strfree (oldval);
+	else
+		egg_secure_strfree (newval);
 }
 
 gboolean
 gkm_wrap_login_did_unlock_fail (void)
 {
-	return g_atomic_int_get (&unlock_failures) ? TRUE : FALSE;
+	return g_atomic_pointer_get (&unlock_failure) ? TRUE : FALSE;
+}
+
+gchar*
+gkm_wrap_login_steal_failed_password (void)
+{
+	gpointer oldval;
+
+	oldval = g_atomic_pointer_get (&unlock_failure);
+	if (!g_atomic_pointer_compare_and_exchange (&unlock_failure, oldval, NULL))
+		oldval = NULL;
+
+	return oldval;
 }
 
 static gboolean
diff --git a/pkcs11/wrap-layer/gkm-wrap-login.h b/pkcs11/wrap-layer/gkm-wrap-login.h
index 148a1b9..bbdc3a1 100644
--- a/pkcs11/wrap-layer/gkm-wrap-login.h
+++ b/pkcs11/wrap-layer/gkm-wrap-login.h
@@ -28,6 +28,8 @@ gboolean      gkm_wrap_login_is_usable                (void);
 
 gboolean      gkm_wrap_login_did_unlock_fail          (void);
 
+gchar*        gkm_wrap_login_steal_failed_password    (void);
+
 void          gkm_wrap_login_attach_secret            (const gchar *label,
                                                        const gchar *secret,
                                                        const gchar *first,
diff --git a/pkcs11/wrap-layer/gkm-wrap-prompt.c b/pkcs11/wrap-layer/gkm-wrap-prompt.c
index 8c4a1c2..7fcc3ac 100644
--- a/pkcs11/wrap-layer/gkm-wrap-prompt.c
+++ b/pkcs11/wrap-layer/gkm-wrap-prompt.c
@@ -91,13 +91,21 @@ set_warning_wrong (GkdSecretUnlock *self)
 }
 #endif
 
+static gboolean
+is_login_keyring (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs)
+{
+	gboolean is_login = FALSE;
+	if (!gkm_attributes_find_boolean (attrs, n_attrs, CKA_G_LOGIN_COLLECTION, &is_login))
+		return FALSE;
+	return is_login;
+}
+
 static gchar*
 auto_unlock_keyring_location (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs)
 {
 	CK_ATTRIBUTE_PTR attr;
-	gboolean is_login = FALSE;
 
-	if (gkm_attributes_find_boolean (attrs, n_attrs, CKA_G_LOGIN_COLLECTION, &is_login) && is_login)
+	if (is_login_keyring (attrs, n_attrs))
 		return NULL;
 
 	attr = gkm_attributes_find (attrs, n_attrs, CKA_ID);
@@ -755,7 +763,6 @@ prepare_unlock_prompt (GkmWrapPrompt *self, CK_ATTRIBUTE_PTR attrs,
 	GkuPrompt *prompt;
 	const gchar *label = NULL;
 	CK_OBJECT_CLASS klass;
-	gboolean is_login = FALSE;
 
 	g_assert (GKM_WRAP_IS_PROMPT (self));
 
@@ -782,7 +789,7 @@ prepare_unlock_prompt (GkmWrapPrompt *self, CK_ATTRIBUTE_PTR attrs,
 		label = _("Unnamed");
 
 	if (klass == CKO_G_COLLECTION) {
-		if (gkm_attributes_find_boolean (attrs, n_attrs, CKA_G_LOGIN_COLLECTION, &is_login) && is_login)
+		if (is_login_keyring (attrs, n_attrs))
 			prepare_unlock_keyring_login (self);
 		else
 			prepare_unlock_keyring_other (self, label);
@@ -830,6 +837,65 @@ prepare_unlock_token (GkmWrapPrompt *self, CK_TOKEN_INFO_PTR tinfo)
 	g_free (label);
 }
 
+static void
+fix_login_keyring_if_unlock_failed (GkmWrapPrompt *self, const gchar *password)
+{
+	CK_OBJECT_CLASS klass = CKO_G_CREDENTIAL;
+	CK_OBJECT_HANDLE cred;
+	CK_BBOOL tval = CK_TRUE;
+	CK_ATTRIBUTE attrs[4];
+	gchar *failed;
+	CK_RV rv;
+
+	failed = gkm_wrap_login_steal_failed_password ();
+
+	/* Do we have a failed unlock password? */
+	if (!failed || !failed[0]) {
+		egg_secure_strfree (failed);
+		return;
+	}
+
+	attrs[0].type = CKA_CLASS;
+	attrs[0].pValue = &klass;
+	attrs[0].ulValueLen = sizeof (klass);
+
+	attrs[1].type = CKA_VALUE;
+	attrs[1].pValue = failed;
+	attrs[1].ulValueLen = strlen (failed);
+
+	attrs[2].type = CKA_GNOME_TRANSIENT;
+	attrs[2].pValue = &tval;
+	attrs[2].ulValueLen = sizeof (tval);
+
+	attrs[3].type = CKA_TOKEN;
+	attrs[3].pValue = &tval;
+	attrs[3].ulValueLen = sizeof (tval);
+
+	/* Create a credential object for the failed password */
+	rv = (self->module->C_CreateObject) (self->session, attrs, G_N_ELEMENTS (attrs), &cred);
+	egg_secure_strfree (failed);
+
+	if (rv != CKR_OK) {
+		g_warning ("couldn't create credential to fix login password: %s",
+		           gkm_util_rv_to_string (rv));
+		return;
+	}
+
+	attrs[0].type = CKA_G_CREDENTIAL;
+	attrs[0].pValue = &cred;
+	attrs[0].ulValueLen = sizeof (cred);
+
+	/* Set the credential on the object */
+	rv = (self->module->C_SetAttributeValue) (self->session, self->object, attrs, 1);
+	if (rv != CKR_OK) {
+		g_warning ("couldn't change credential to fix login keyring password: %s",
+		           gkm_util_rv_to_string (rv));
+		return;
+	}
+
+	g_message ("fixed login keyring password to match login password");
+}
+
 /* -----------------------------------------------------------------------------
  * OBJECT
  */
@@ -1020,14 +1086,23 @@ gkm_wrap_prompt_done_credential (GkmWrapPrompt *self, CK_RV call_result)
 
 	/* Save the options, and possibly auto unlock */
 	if (call_result == CKR_OK) {
+
+		attrs = get_attributes_from_object (self, &n_attrs);
+
+		/*
+		 * For the login keyring, we check for a previous unlock failure,
+		 * that would have come from PAM, and try to change the password to
+		 * the one that failed earlier.
+		 */
+		if (is_login_keyring (attrs, n_attrs))
+			fix_login_keyring_if_unlock_failed (self, data->password);
+
 		options = get_unlock_options_from_prompt (self, &n_options);
 		if (options != NULL)
 			set_unlock_options_on_object (self, options, n_options);
 
-		if (auto_unlock_should_attach (self)) {
-			attrs = get_attributes_from_object (self, &n_attrs);
+		if (auto_unlock_should_attach (self))
 			auto_unlock_attach_object (attrs, n_attrs, data->password);
-		}
 	}
 }
 
diff --git a/pkcs11/wrap-layer/tests/test-login-hints.c b/pkcs11/wrap-layer/tests/test-login-hints.c
index d649c21..1959352 100644
--- a/pkcs11/wrap-layer/tests/test-login-hints.c
+++ b/pkcs11/wrap-layer/tests/test-login-hints.c
@@ -23,21 +23,28 @@
 
 #include "test-suite.h"
 
+#include "egg/egg-secure-memory.h"
+
 #include "wrap-layer/gkm-wrap-layer.h"
 #include "wrap-layer/gkm-wrap-login.h"
 
 DEFINE_TEST (login_did_unlock_fail)
 {
+	gchar *password;
 	gboolean ret;
 
-	gkm_wrap_layer_hint_login_unlock_failure ();
+	gkm_wrap_layer_mark_login_unlock_failure ("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 ();
+	password = gkm_wrap_login_steal_failed_password ();
+	g_assert_cmpstr (password, ==, "failure");
+	egg_secure_strfree (password);
+
+	gkm_wrap_layer_mark_login_unlock_failure ("failed password");
+	gkm_wrap_layer_mark_login_unlock_failure ("failed password");
+	gkm_wrap_layer_mark_login_unlock_success ();
 
 	ret = gkm_wrap_login_did_unlock_fail ();
 	g_assert (ret == FALSE);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]