[gnome-keyring/dbus-api] [dbus] Big rework of prompting, unlocking and change lock.



commit 0e7a6268410a88a3d392ab3124df9229ba7f07b6
Author: Stef Walter <stef memberwebs com>
Date:   Mon Dec 14 01:55:48 2009 +0000

    [dbus] Big rework of prompting, unlocking and change lock.
    
    Implement locking, unlocking, and changing of passwords.
    Rework how prompt secrets get sent to the pkcs11 layer.

 daemon/dbus/Makefile.am          |    2 +
 daemon/dbus/gkd-secret-change.c  |  313 ++++++++++++++++++++++++++++++++++++++
 daemon/dbus/gkd-secret-change.h  |   56 +++++++
 daemon/dbus/gkd-secret-create.c  |  142 +++++-------------
 daemon/dbus/gkd-secret-create.h  |    7 +-
 daemon/dbus/gkd-secret-lock.c    |   67 ++++++++
 daemon/dbus/gkd-secret-lock.h    |   34 ++++
 daemon/dbus/gkd-secret-objects.c |   39 ++----
 daemon/dbus/gkd-secret-prompt.c  |  120 ++++++++++++++-
 daemon/dbus/gkd-secret-prompt.h  |   10 ++
 daemon/dbus/gkd-secret-secret.c  |   55 +++++--
 daemon/dbus/gkd-secret-secret.h  |   13 +-
 daemon/dbus/gkd-secret-service.c |  291 +++++++++++++++++++++++++++++------
 daemon/dbus/gkd-secret-session.c |  118 ++++++++++----
 daemon/dbus/gkd-secret-session.h |   18 ++-
 daemon/dbus/gkd-secret-types.h   |    4 +-
 daemon/dbus/gkd-secret-unlock.c  |  135 ++++++++++-------
 daemon/dbus/gkd-secret-unlock.h  |    4 +
 daemon/prompt/gkd-prompt-tool.c  |    2 +-
 daemon/prompt/gkd-prompt-util.c  |   52 ++++---
 daemon/prompt/gkd-prompt-util.h  |    8 +-
 daemon/prompt/gkd-prompt.c       |  229 ++++++++++++++++++----------
 daemon/prompt/gkd-prompt.h       |   18 +++
 23 files changed, 1323 insertions(+), 414 deletions(-)
---
diff --git a/daemon/dbus/Makefile.am b/daemon/dbus/Makefile.am
index c4afb1f..6e864d4 100644
--- a/daemon/dbus/Makefile.am
+++ b/daemon/dbus/Makefile.am
@@ -18,8 +18,10 @@ libgkr_dbus_la_SOURCES = \
 	gkd-dbus-service.c \
 	gkd-dbus-session.c \
 	gkd-dbus-util.c gkd-dbus-util.h \
+	gkd-secret-change.c gkd-secret-change.h \
 	gkd-secret-create.c gkd-secret-create.h \
 	gkd-secret-error.c gkd-secret-error.h \
+	gkd-secret-lock.c gkd-secret-lock.h \
 	gkd-secret-objects.c gkd-secret-objects.h \
 	gkd-secret-property.c gkd-secret-property.h \
 	gkd-secret-prompt.c gkd-secret-prompt.h \
diff --git a/daemon/dbus/gkd-secret-change.c b/daemon/dbus/gkd-secret-change.c
new file mode 100644
index 0000000..9ca895d
--- /dev/null
+++ b/daemon/dbus/gkd-secret-change.c
@@ -0,0 +1,313 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 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 "gkd-secret-change.h"
+#include "gkd-secret-prompt.h"
+#include "gkd-secret-secret.h"
+#include "gkd-secret-service.h"
+#include "gkd-secret-session.h"
+#include "gkd-secret-types.h"
+#include "gkd-secret-util.h"
+
+#include "egg/egg-secure-memory.h"
+
+#include "pkcs11/pkcs11i.h"
+
+#include <glib/gi18n.h>
+
+#include <gp11/gp11.h>
+
+#include <string.h>
+
+enum {
+	PROP_0,
+	PROP_COLLECTION_PATH
+};
+
+struct _GkdSecretChange {
+	GkdSecretPrompt parent;
+	gchar *collection_path;
+};
+
+G_DEFINE_TYPE (GkdSecretChange, gkd_secret_change, GKD_SECRET_TYPE_PROMPT);
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+static void
+prepare_change_prompt (GkdSecretChange *self, GP11Object *collection)
+{
+	GError *error = NULL;
+	GkdPrompt *prompt;
+	gpointer data;
+	gsize n_data;
+	gchar *label;
+	gchar *text;
+
+	prompt = GKD_PROMPT (self);
+
+	data = gp11_object_get_data (collection, CKA_LABEL, &n_data, &error);
+	if (!data) {
+		g_warning ("couldn't get label for collection: %s", error->message);
+		g_clear_error (&error);
+	}
+
+	if (!data || !n_data)
+		label = g_strdup (_("Unnamed"));
+	else
+		label = g_strndup (data, n_data);
+	g_free (data);
+
+	gkd_prompt_reset (prompt);
+
+	gkd_prompt_set_title (prompt, _("Change Keyring Password"));
+	gkd_prompt_set_primary_text (prompt, _("Choose a new password for the '%s' keyring"));
+
+	text = g_markup_printf_escaped (_("An application wants to change the password for the '%s' keyring. "
+	                                  "Choose the new password you want to use for it."), label);
+	gkd_prompt_set_secondary_text (prompt, text);
+	g_free (text);
+
+	gkd_prompt_hide_widget (prompt, "name_area");
+	gkd_prompt_hide_widget (prompt, "details_area");
+
+	gkd_prompt_show_widget (prompt, "original_area");
+	gkd_prompt_show_widget (prompt, "confirm_area");
+
+	g_free (label);
+}
+
+static void
+set_warning_wrong (GkdSecretChange *self)
+{
+	g_assert (GKD_SECRET_IS_CHANGE (self));
+	gkd_prompt_set_warning (GKD_PROMPT (self), _("The original password was incorrect"));
+}
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static void
+gkd_secret_change_prompt_ready (GkdSecretPrompt *prompt)
+{
+	GkdSecretChange *self = GKD_SECRET_CHANGE (prompt);
+	GkdSecretSecret *original, *master;
+	DBusError derr = DBUS_ERROR_INIT;
+	GP11Object *collection;
+	gboolean result;
+
+	collection = gkd_secret_prompt_lookup_collection (prompt, self->collection_path);
+
+	/* No more prompt, just go away */
+	if (collection == NULL) {
+		gkd_secret_prompt_dismiss (prompt);
+		return;
+	}
+
+	if (!gkd_prompt_has_response (GKD_PROMPT (prompt))) {
+		prepare_change_prompt (self, collection);
+		return;
+	}
+
+	original = gkd_secret_prompt_get_secret (prompt, "original");
+	master = gkd_secret_prompt_get_secret (prompt, "password");
+
+	result = gkd_secret_change_with_secrets (collection, original, master, &derr);
+
+	gkd_secret_secret_free (original);
+	gkd_secret_secret_free (master);
+
+	/* The change succeeded, yay */
+	if (result) {
+		gkd_secret_prompt_complete (prompt);
+
+	/* The original password was incorrect */
+	} else if (dbus_error_has_name (&derr, INTERNAL_ERROR_DENIED)) {
+		prepare_change_prompt (self, collection);
+		set_warning_wrong (self);
+
+	/* Other failures */
+	} else {
+		gkd_secret_prompt_dismiss (prompt);
+	}
+
+	g_object_unref (collection);
+}
+
+static void
+gkd_secret_change_encode_result (GkdSecretPrompt *base, DBusMessageIter *iter)
+{
+	DBusMessageIter variant;
+	const gchar *string = "";
+
+	dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, "s", &variant);
+	dbus_message_iter_append_basic (&variant, DBUS_TYPE_STRING, &string);
+	dbus_message_iter_close_container (iter, &variant);
+}
+
+static void
+gkd_secret_change_init (GkdSecretChange *self)
+{
+
+}
+
+static void
+gkd_secret_change_finalize (GObject *obj)
+{
+	GkdSecretChange *self = GKD_SECRET_CHANGE (obj);
+
+	g_free (self->collection_path);
+	self->collection_path = NULL;
+
+	G_OBJECT_CLASS (gkd_secret_change_parent_class)->finalize (obj);
+}
+
+static void
+gkd_secret_change_set_property (GObject *obj, guint prop_id, const GValue *value,
+                                GParamSpec *pspec)
+{
+	GkdSecretChange *self = GKD_SECRET_CHANGE (obj);
+
+	switch (prop_id) {
+	case PROP_COLLECTION_PATH:
+		g_return_if_fail (!self->collection_path);
+		self->collection_path = g_value_dup_string (value);
+		g_return_if_fail (self->collection_path);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gkd_secret_change_get_property (GObject *obj, guint prop_id, GValue *value,
+                                GParamSpec *pspec)
+{
+	GkdSecretChange *self = GKD_SECRET_CHANGE (obj);
+
+	switch (prop_id) {
+	case PROP_COLLECTION_PATH:
+		g_value_set_string (value, self->collection_path);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gkd_secret_change_class_init (GkdSecretChangeClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GkdSecretPromptClass *prompt_class = GKD_SECRET_PROMPT_CLASS (klass);
+
+	gobject_class->finalize = gkd_secret_change_finalize;
+	gobject_class->get_property = gkd_secret_change_get_property;
+	gobject_class->set_property = gkd_secret_change_set_property;
+
+	prompt_class->prompt_ready = gkd_secret_change_prompt_ready;
+	prompt_class->encode_result = gkd_secret_change_encode_result;
+
+	g_object_class_install_property (gobject_class, PROP_COLLECTION_PATH,
+		g_param_spec_string ("collection-path", "Collection Path", "Collection Path",
+		                     "/", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+GkdSecretChange*
+gkd_secret_change_new (GkdSecretService *service, const gchar *caller,
+                       const gchar *path)
+{
+	g_return_val_if_fail (GKD_SECRET_IS_SERVICE (service), NULL);
+	g_return_val_if_fail (caller, NULL);
+	g_return_val_if_fail (path, NULL);
+
+	return g_object_new (GKD_SECRET_TYPE_CHANGE,
+	                     "service", service,
+	                     "caller", caller,
+	                     "collection-path", path,
+	                     NULL);
+}
+
+gboolean
+gkd_secret_change_with_secrets (GP11Object *collection, GkdSecretSecret *original,
+                                GkdSecretSecret *master, DBusError *derr)
+{
+	GError *error = NULL;
+	GP11Attributes *attrs = NULL;
+	gboolean result = FALSE;
+	GP11Object *ocred = NULL;
+	GP11Object *mcred = NULL;
+
+	/* Create the new credential */
+	attrs = gp11_attributes_newv (CKA_CLASS, GP11_ULONG, CKO_G_CREDENTIAL,
+	                              CKA_TOKEN, GP11_BOOLEAN, FALSE, GP11_INVALID);
+	mcred = gkd_secret_session_create_credential (master->session, NULL, attrs, master, derr);
+	if (mcred == NULL)
+		goto cleanup;
+
+	/* Create the original credential, in order to make sure we can the collection */
+	gp11_attributes_add_ulong (attrs, CKA_G_OBJECT, gp11_object_get_handle (collection));
+	ocred = gkd_secret_session_create_credential (original->session, NULL, attrs, original, derr);
+	if (ocred == NULL)
+		goto cleanup;
+
+	/* Now set the collection credentials to the first one */
+	result = gp11_object_set (collection, &error,
+	                          CKA_G_CREDENTIAL, GP11_ULONG, gp11_object_get_handle (mcred),
+	                          GP11_INVALID);
+
+cleanup:
+	if (ocred) {
+		/* Always destroy the original credential */
+		gp11_object_destroy (ocred, NULL);
+		g_object_unref (ocred);
+	}
+	if (mcred) {
+		/* Destroy the master credential if failed */
+		if (!result)
+			gp11_object_destroy (mcred, NULL);
+		g_object_unref (mcred);
+	}
+	if (attrs)
+		gp11_attributes_unref (attrs);
+
+	if (!result && error) {
+		if (error->code == CKR_USER_NOT_LOGGED_IN)
+			dbus_set_error (derr, INTERNAL_ERROR_DENIED, "The original password was invalid");
+		else
+			g_warning ("failure occurred while changing password: %s", error->message);
+	}
+
+	if (!result && !dbus_error_is_set (derr))
+		dbus_set_error (derr, DBUS_ERROR_FAILED, "Couldn't change master password");
+
+	g_clear_error (&error);
+	return result;
+}
diff --git a/daemon/dbus/gkd-secret-change.h b/daemon/dbus/gkd-secret-change.h
new file mode 100644
index 0000000..8c70232
--- /dev/null
+++ b/daemon/dbus/gkd-secret-change.h
@@ -0,0 +1,56 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2009 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 __GKD_SECRET_CHANGE_H__
+#define __GKD_SECRET_CHANGE_H__
+
+#include <glib-object.h>
+
+#include "gkd-secret-prompt.h"
+#include "gkd-secret-types.h"
+
+#include "gp11/gp11.h"
+
+#define GKD_SECRET_TYPE_CHANGE               (gkd_secret_change_get_type ())
+#define GKD_SECRET_CHANGE(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GKD_SECRET_TYPE_CHANGE, GkdSecretChange))
+#define GKD_SECRET_CHANGE_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GKD_SECRET_TYPE_CHANGE, GkdSecretChangeClass))
+#define GKD_SECRET_IS_CHANGE(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GKD_SECRET_TYPE_CHANGE))
+#define GKD_SECRET_IS_CHANGE_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GKD_SECRET_TYPE_CHANGE))
+#define GKD_SECRET_CHANGE_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GKD_SECRET_TYPE_CHANGE, GkdSecretChangeClass))
+
+typedef struct _GkdSecretChangeClass GkdSecretChangeClass;
+
+struct _GkdSecretChangeClass {
+	GkdSecretPromptClass parent_class;
+};
+
+GType               gkd_secret_change_get_type                (void);
+
+GkdSecretChange*    gkd_secret_change_new                     (GkdSecretService *service,
+                                                               const gchar *caller,
+                                                               const gchar *path);
+
+gboolean            gkd_secret_change_with_secrets            (GP11Object *collection,
+                                                               GkdSecretSecret *original,
+                                                               GkdSecretSecret *master,
+                                                               DBusError *derr);
+
+#endif /* __GKD_SECRET_CHANGE_H__ */
diff --git a/daemon/dbus/gkd-secret-create.c b/daemon/dbus/gkd-secret-create.c
index 0ad964e..f5f1c68 100644
--- a/daemon/dbus/gkd-secret-create.c
+++ b/daemon/dbus/gkd-secret-create.c
@@ -90,108 +90,50 @@ prepare_create_prompt (GkdSecretCreate *self)
 }
 
 static gboolean
-create_collection_with_credential (GkdSecretCreate *self, GP11Object *cred)
+create_collection_with_secret (GkdSecretCreate *self, GkdSecretSecret *master)
 {
-	GError *error = NULL;
-	GP11Object *collection;
-	GP11Session *session;
-	gpointer identifier;
-	gsize n_identifier;
+	DBusError derr = DBUS_ERROR_INIT;
 
 	g_assert (GKD_SECRET_IS_CREATE (self));
-	g_return_val_if_fail (self->pkcs11_attrs, FALSE);
-	g_return_val_if_fail (!self->result_path, FALSE);
-	g_return_val_if_fail (GP11_IS_OBJECT (cred), FALSE);
+	g_assert (master);
+	g_assert (!self->result_path);
 
-	session =  gkd_secret_prompt_get_pkcs11_session (GKD_SECRET_PROMPT (self));
-	g_return_val_if_fail (session, FALSE);
+	self->result_path = gkd_secret_create_with_secret (self->pkcs11_attrs, master, &derr);
 
-	collection = gkd_secret_create_with_credential (session, self->pkcs11_attrs, cred, &error);
-	if (!collection) {
-		g_warning ("couldn't create collection: %s", error->message);
-		g_clear_error (&error);
+	if (!self->result_path) {
+		g_warning ("couldn't create new collection: %s", derr.message);
+		dbus_error_free (&derr);
 		return FALSE;
 	}
 
-	identifier = gp11_object_get_data (collection, CKA_ID, &n_identifier, &error);
-	g_object_unref (collection);
-
-	if (!identifier) {
-		g_warning ("couldn't lookup new collection identifier: %s", error->message);
-		g_clear_error (&error);
-		return FALSE;
-	}
-
-	self->result_path = gkd_secret_util_build_path (SECRET_COLLECTION_PREFIX, identifier, n_identifier);
-	g_free (identifier);
-
 	return TRUE;
 }
 
-static gboolean
-create_collection_with_password (GkdSecretCreate *self, const gchar *password)
-{
-	GError *error = NULL;
-	GP11Session *session;
-	GP11Object *cred;
-	gsize n_password;
-	gboolean token;
-	gboolean result;
-
-	g_assert (GKD_SECRET_IS_CREATE (self));
-
-	if (!gp11_attributes_find_boolean (self->pkcs11_attrs, CKA_TOKEN, &token))
-		token = FALSE;
-	n_password = password ? strlen (password) : 0;
-
-	session =  gkd_secret_prompt_get_pkcs11_session (GKD_SECRET_PROMPT (self));
-	g_return_val_if_fail (session, FALSE);
-
-	cred = gp11_session_create_object (session, &error,
-	                                   CKA_CLASS, GP11_ULONG, CKO_G_CREDENTIAL,
-	                                   CKA_GNOME_TRANSIENT, GP11_BOOLEAN, TRUE,
-	                                   CKA_TOKEN, GP11_BOOLEAN, token,
-	                                   CKA_VALUE, n_password, password,
-	                                   GP11_INVALID);
-
-	if (!cred) {
-		g_warning ("couldn't create credential for new collection: %s", error->message);
-		g_clear_error (&error);
-		return FALSE;
-	}
-
-	result = create_collection_with_credential (self, cred);
-	g_object_unref (cred);
-
-	return result;
-}
-
 /* -----------------------------------------------------------------------------
  * OBJECT
  */
 
 static void
-gkd_secret_create_prompt_ready (GkdSecretPrompt *base)
+gkd_secret_create_prompt_ready (GkdSecretPrompt *prompt)
 {
-	GkdSecretCreate *self = GKD_SECRET_CREATE (base);
-	GkdPrompt *prompt = GKD_PROMPT (self);
-	gchar *password;
+	GkdSecretCreate *self = GKD_SECRET_CREATE (prompt);
+	GkdSecretSecret *master;
 
-	if (!gkd_prompt_has_response (prompt)) {
+	if (!gkd_prompt_has_response (GKD_PROMPT (prompt))) {
 		prepare_create_prompt (self);
 		return;
 	}
 
 	/* Already prompted, create collection */
-	g_return_if_fail (gkd_prompt_get_response (prompt) == GKD_RESPONSE_OK);
-	password = gkd_prompt_get_password (prompt, "password");
+	g_return_if_fail (gkd_prompt_get_response (GKD_PROMPT (prompt)) == GKD_RESPONSE_OK);
+	master = gkd_secret_prompt_get_secret (prompt, "password");
 
-	if (create_collection_with_password (self, password))
-		gkd_secret_prompt_complete (GKD_SECRET_PROMPT (self));
+	if (master && create_collection_with_secret (self, master))
+		gkd_secret_prompt_complete (prompt);
 	else
-		gkd_secret_prompt_dismiss (GKD_SECRET_PROMPT (self));
+		gkd_secret_prompt_dismiss (prompt);
 
-	egg_secure_strfree (password);
+	gkd_secret_secret_free (master);
 }
 
 static void
@@ -222,6 +164,9 @@ gkd_secret_create_finalize (GObject *obj)
 		gp11_attributes_unref (self->pkcs11_attrs);
 	self->pkcs11_attrs = NULL;
 
+	g_free (self->result_path);
+	self->result_path = NULL;
+
 	G_OBJECT_CLASS (gkd_secret_create_parent_class)->finalize (obj);
 }
 
@@ -321,28 +266,20 @@ gkd_secret_create_with_credential (GP11Session *session, GP11Attributes *attrs,
 	return collection;
 }
 
-DBusMessage*
-gkd_secret_create_without_prompting (GkdSecretService *service, DBusMessage *message,
-                                     GP11Attributes *attrs, GkdSecretSecret *master)
+gchar*
+gkd_secret_create_with_secret (GP11Attributes *attrs, GkdSecretSecret *master,
+                               DBusError *derr)
 {
-	DBusError derr = DBUS_ERROR_INIT;
-	GkdSecretSession *session;
 	GP11Attributes *atts;
-	DBusMessage *reply;
 	GP11Object *cred;
 	GP11Object *collection;
-	GP11Session *pkcs11_session;
+	GP11Session *session;
 	GError *error = NULL;
 	gpointer identifier;
 	gsize n_identifier;
 	gboolean token;
 	gchar *path;
 
-	/* Figure out the session */
-	session = gkd_secret_session_for_secret (service, master, &derr);
-	if (session == NULL)
-		return gkd_secret_error_to_reply (message, &derr);
-
 	if (!gp11_attributes_find_boolean (attrs, CKA_TOKEN, &token))
 		token = FALSE;
 
@@ -351,17 +288,17 @@ gkd_secret_create_without_prompting (GkdSecretService *service, DBusMessage *mes
 	                             CKA_TOKEN, GP11_BOOLEAN, token,
 	                             GP11_INVALID);
 
-	pkcs11_session = gkd_secret_service_get_pkcs11_session (service, dbus_message_get_sender (message));
-	g_return_val_if_fail (pkcs11_session, NULL);
+	session = gkd_secret_session_get_pkcs11_session (master->session);
+	g_return_val_if_fail (session, NULL);
 
 	/* Create ourselves some credentials */
-	cred = gkd_secret_session_create_credential (session, pkcs11_session, atts, master, &derr);
+	cred = gkd_secret_session_create_credential (master->session, session, atts, master, derr);
 	gp11_attributes_unref (atts);
 
 	if (cred == NULL)
-		return gkd_secret_error_to_reply (message, &derr);
+		return FALSE;
 
-	collection = gkd_secret_create_with_credential (pkcs11_session, attrs, cred, &error);
+	collection = gkd_secret_create_with_credential (session, attrs, cred, &error);
 
 	gp11_attributes_unref (atts);
 	g_object_unref (cred);
@@ -369,29 +306,22 @@ gkd_secret_create_without_prompting (GkdSecretService *service, DBusMessage *mes
 	if (collection == NULL) {
 		g_warning ("couldn't create collection: %s", error->message);
 		g_clear_error (&error);
-		return dbus_message_new_error (message, DBUS_ERROR_FAILED,
-		                               "Couldn't create new collection");
+		dbus_set_error (derr, DBUS_ERROR_FAILED, "Couldn't create new collection");
+		return FALSE;
 	}
 
-	gp11_object_set_session (collection, pkcs11_session);
+	gp11_object_set_session (collection, session);
 	identifier = gp11_object_get_data (collection, CKA_ID, &n_identifier, &error);
 	g_object_unref (collection);
 
 	if (!identifier) {
 		g_warning ("couldn't lookup new collection identifier: %s", error->message);
 		g_clear_error (&error);
-		return dbus_message_new_error (message, DBUS_ERROR_FAILED,
-		                               "Couldn't find new collection just created");
+		dbus_set_error (derr, DBUS_ERROR_FAILED, "Couldn't find new collection just created");
+		return FALSE;
 	}
 
 	path = gkd_secret_util_build_path (SECRET_COLLECTION_PREFIX, identifier, n_identifier);
 	g_free (identifier);
-
-	reply = dbus_message_new_method_return (message);
-	dbus_message_append_args (reply,
-	                          DBUS_TYPE_OBJECT_PATH, &path,
-	                          DBUS_TYPE_INVALID);
-	g_free (path);
-
-	return reply;
+	return path;
 }
diff --git a/daemon/dbus/gkd-secret-create.h b/daemon/dbus/gkd-secret-create.h
index 0d522f6..f9e6864 100644
--- a/daemon/dbus/gkd-secret-create.h
+++ b/daemon/dbus/gkd-secret-create.h
@@ -53,9 +53,8 @@ GP11Object*         gkd_secret_create_with_credential         (GP11Session *sess
                                                                GP11Object *cred,
                                                                GError **error);
 
-DBusMessage*        gkd_secret_create_without_prompting       (GkdSecretService *service,
-                                                               DBusMessage *message,
-                                                               GP11Attributes *attrs,
-                                                               GkdSecretSecret *master);
+gchar*              gkd_secret_create_with_secret             (GP11Attributes *attrs,
+                                                               GkdSecretSecret *master,
+                                                               DBusError *derr);
 
 #endif /* __GKD_SECRET_CREATE_H__ */
diff --git a/daemon/dbus/gkd-secret-lock.c b/daemon/dbus/gkd-secret-lock.c
new file mode 100644
index 0000000..abecf63
--- /dev/null
+++ b/daemon/dbus/gkd-secret-lock.c
@@ -0,0 +1,67 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 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 "gkd-secret-lock.h"
+#include "gkd-secret-service.h"
+
+#include "pkcs11/pkcs11i.h"
+
+#include <gp11/gp11.h>
+
+gboolean
+gkd_secret_lock (GP11Object *collection, DBusError *derr)
+{
+	GError *error = NULL;
+	GP11Session *session;
+	GP11Object *cred;
+	GList *objects, *l;
+
+	session = gp11_object_get_session (collection);
+	g_return_val_if_fail (session, FALSE);
+
+	objects = gp11_session_find_objects (session, &error,
+	                                     CKA_CLASS, GP11_ULONG, CKO_G_CREDENTIAL,
+	                                     CKA_G_OBJECT, GP11_ULONG, gp11_object_get_handle (collection),
+	                                     GP11_INVALID);
+
+	if (error != NULL) {
+		g_object_unref (session);
+		g_warning ("couldn't search for credential objects: %s", error->message);
+		dbus_set_error (derr, DBUS_ERROR_FAILED, "Couldn't lock collection");
+		g_clear_error (&error);
+		return FALSE;
+	}
+
+	for (l = objects; l; l = g_list_next (l)) {
+		cred = GP11_OBJECT (l->data);
+		gp11_object_set_session (cred, session);
+		if (!gp11_object_destroy (cred, &error)) {
+			g_warning ("couldn't destroy credential object: %s", error->message);
+			g_clear_error (&error);
+		}
+	}
+
+	gp11_list_unref_free (objects);
+	g_object_unref (session);
+	return TRUE;
+}
diff --git a/daemon/dbus/gkd-secret-lock.h b/daemon/dbus/gkd-secret-lock.h
new file mode 100644
index 0000000..d4a59a5
--- /dev/null
+++ b/daemon/dbus/gkd-secret-lock.h
@@ -0,0 +1,34 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2009 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 __GKD_SECRET_LOCK_H__
+#define __GKD_SECRET_LOCK_H__
+
+#include "gkd-secret-types.h"
+
+#include <gp11/gp11.h>
+
+#include <dbus/dbus.h>
+
+gboolean            gkd_secret_lock                (GP11Object *collection,
+                                                    DBusError *derr);
+
+#endif /* __GKD_SECRET_LOCK_H__ */
diff --git a/daemon/dbus/gkd-secret-objects.c b/daemon/dbus/gkd-secret-objects.c
index 25bd4dd..85ef17d 100644
--- a/daemon/dbus/gkd-secret-objects.c
+++ b/daemon/dbus/gkd-secret-objects.c
@@ -367,9 +367,9 @@ item_method_get_secret (GkdSecretObjects *self, GP11Object *item, DBusMessage *m
 	if (!dbus_message_get_args (message, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID))
 		return NULL;
 
-	session = gkd_secret_session_for_path (self->service, path, dbus_message_get_sender (message), &derr);
+	session = gkd_secret_service_lookup_session (self->service, path, dbus_message_get_sender (message));
 	if (session == NULL)
-		return gkd_secret_error_to_reply (message, &derr);
+		return dbus_message_new_error (message, SECRET_ERROR_NO_SESSION, "The session does not exist");
 
 	secret = gkd_secret_session_get_item_secret (session, item, &derr);
 	if (secret == NULL)
@@ -388,27 +388,19 @@ item_method_set_secret (GkdSecretObjects *self, GP11Object *item, DBusMessage *m
 	DBusError derr = DBUS_ERROR_INIT;
 	DBusMessageIter iter;
 	GkdSecretSecret *secret;
-	GkdSecretSession *session;
 	const char *caller;
 
 	if (!dbus_message_has_signature (message, "(oayay)"))
 		return NULL;
 	dbus_message_iter_init (message, &iter);
-	secret = gkd_secret_secret_parse (message, &iter);
+	secret = gkd_secret_secret_parse (self->service, message, &iter, &derr);
 	if (secret == NULL)
-		return dbus_message_new_error (message, DBUS_ERROR_INVALID_ARGS,
-		                               "The secret is invalid");
+		return gkd_secret_error_to_reply (message, &derr);
 
 	caller = dbus_message_get_sender (message);
 	g_return_val_if_fail (caller, NULL);
 
-	session = gkd_secret_session_for_secret (self->service, secret, &derr);
-	if (session == NULL) {
-		gkd_secret_secret_free (secret);
-		return gkd_secret_error_to_reply (message, &derr);
-	}
-
-	gkd_secret_session_set_item_secret (session, item, secret, &derr);
+	gkd_secret_session_set_item_secret (secret->session, item, secret, &derr);
 	gkd_secret_secret_free (secret);
 
 	if (dbus_error_is_set (&derr))
@@ -643,7 +635,6 @@ collection_method_create_item (GkdSecretObjects *self, GP11Object *object, DBusM
 	GP11Session *pkcs11_session = NULL;
 	DBusError derr = DBUS_ERROR_INIT;
 	GkdSecretSecret *secret = NULL;
-	GkdSecretSession *session;
 	dbus_bool_t replace = FALSE;
 	GP11Attributes *attrs = NULL;
 	GP11Attribute *fields;
@@ -670,22 +661,14 @@ collection_method_create_item (GkdSecretObjects *self, GP11Object *object, DBusM
 		goto cleanup;
 	}
 	dbus_message_iter_next (&iter);
-	secret = gkd_secret_secret_parse (message, &iter);
+	secret = gkd_secret_secret_parse (self->service, message, &iter, &derr);
 	if (secret == NULL) {
-		reply = dbus_message_new_error (message, DBUS_ERROR_INVALID_ARGS,
-		                                "Invalid secret argument");
+		reply = gkd_secret_error_to_reply (message, &derr);
 		goto cleanup;
 	}
 	dbus_message_iter_next (&iter);
 	dbus_message_iter_get_basic (&iter, &replace);
 
-	/* Figure out which session we're dealing with */
-	session = gkd_secret_session_for_secret (self->service, secret, &derr);
-	if (session == NULL) {
-		reply = gkd_secret_error_to_reply (message, &derr);
-		goto cleanup;
-	}
-
 	base = dbus_message_get_path (message);
 	if (!parse_object_path (self, base, &identifier, NULL))
 		g_return_val_if_reached (NULL);
@@ -717,7 +700,7 @@ collection_method_create_item (GkdSecretObjects *self, GP11Object *object, DBusM
 	}
 
 	/* Set the secret */
-	if (!gkd_secret_session_set_item_secret (session, item, secret, &derr)) {
+	if (!gkd_secret_session_set_item_secret (secret->session, item, secret, &derr)) {
 		if (created) /* If we created, then try to destroy on failure */
 			gp11_object_destroy (item, NULL);
 		goto cleanup;
@@ -1282,10 +1265,10 @@ gkd_secret_objects_handle_get_secrets (GkdSecretObjects *self, DBusMessage *mess
 	caller = dbus_message_get_sender (message);
 	g_return_val_if_fail (caller, NULL);
 
-	session = gkd_secret_session_for_path (self->service, session_path,
-	                                       dbus_message_get_sender (message), &derr);
+	session = gkd_secret_service_lookup_session (self->service, session_path,
+	                                             dbus_message_get_sender (message));
 	if (session == NULL)
-		return gkd_secret_error_to_reply (message, &derr);
+		return dbus_message_new_error (message, SECRET_ERROR_NO_SESSION, "The session does not exist");
 
 	reply = dbus_message_new_method_return (message);
 	dbus_message_iter_init_append (reply, &iter);
diff --git a/daemon/dbus/gkd-secret-prompt.c b/daemon/dbus/gkd-secret-prompt.c
index 79a45d6..bda65dc 100644
--- a/daemon/dbus/gkd-secret-prompt.c
+++ b/daemon/dbus/gkd-secret-prompt.c
@@ -25,11 +25,15 @@
 #include "gkd-secret-service.h"
 #include "gkd-secret-prompt.h"
 #include "gkd-secret-objects.h"
+#include "gkd-secret-secret.h"
+#include "gkd-secret-session.h"
 #include "gkd-secret-types.h"
 #include "gkd-secret-util.h"
 
 #include "prompt/gkd-prompt.h"
 
+#include "egg/egg-dh.h"
+
 #include <string.h>
 
 enum {
@@ -39,11 +43,15 @@ enum {
 	PROP_SERVICE
 };
 
+#define PROMPT_IKE_GROUP  "ietf-ike-grp-modp-1536"
+
 struct _GkdSecretPromptPrivate {
 	GkdPrompt parent;
 	gchar *object_path;
 	GkdSecretService *service;
+	GkdSecretSession *session;
 	gboolean prompted;
+	gboolean negotiated;
 	gboolean completed;
 	gchar *caller;
 	gchar *window_id;
@@ -58,6 +66,61 @@ static guint unique_prompt_number = 0;
  * INTERNAL
  */
 
+static void
+setup_transport_params (GkdSecretPrompt *self)
+{
+	GkdPrompt *prompt = GKD_PROMPT (self);
+	gsize n_public, n_prime, n_base;
+	gconstpointer prime, base;
+	gpointer public;
+
+	if (self->pv->session)
+		g_object_unref (self->pv->session);
+	self->pv->session = gkd_secret_session_new (self->pv->service, self->pv->caller);
+
+	public = gkd_secret_session_begin (self->pv->session, PROMPT_IKE_GROUP, &n_public);
+	g_return_if_fail (public);
+	self->pv->negotiated = FALSE;
+
+	gkd_prompt_set_transport_param (prompt, "public", public, n_public);
+	g_free (public);
+
+	/* Setup transport crypto */
+	if (!egg_dh_default_params_raw (PROMPT_IKE_GROUP, &prime, &n_prime, &base, &n_base))
+		g_return_if_reached ();
+
+	gkd_prompt_set_transport_param (prompt, "prime", prime, n_prime);
+	gkd_prompt_set_transport_param (prompt, "base", base, n_base);
+}
+
+static gboolean
+complete_transport_params (GkdSecretPrompt *self)
+{
+	GkdPrompt *prompt = GKD_PROMPT (self);
+	gboolean result;
+	gsize n_peer;
+	gpointer peer;
+
+	if (self->pv->negotiated)
+		return TRUE;
+
+	g_return_val_if_fail (self->pv->session, FALSE);
+
+	peer = gkd_prompt_get_transport_param (prompt, "public", &n_peer);
+	if (peer == NULL) {
+		g_warning ("prompt did not return a public dh key");
+		return FALSE;
+	}
+
+	result = gkd_secret_session_complete (self->pv->session, peer, n_peer);
+	g_free (peer);
+
+	if (!result)
+		g_warning ("negotiation of transport crypto with prompt failed");
+
+	return result;
+}
+
 static GkdPrompt*
 on_prompt_attention (gpointer user_data)
 {
@@ -69,6 +132,8 @@ on_prompt_attention (gpointer user_data)
 
 	if (self->pv->completed)
 		return NULL;
+
+	setup_transport_params (self);
 	return g_object_ref (self);
 }
 
@@ -165,7 +230,14 @@ gkd_secret_prompt_responded (GkdPrompt *base)
 	/* Check with the prompt ready guys */
 	g_return_val_if_fail (GKD_SECRET_PROMPT_GET_CLASS (self)->prompt_ready, TRUE);
 	GKD_SECRET_PROMPT_GET_CLASS (self)->prompt_ready (self);
-	return !self->pv->completed;
+
+	/* Not yet done, will display again */
+	if (!self->pv->completed) {
+		setup_transport_params (self);
+		return TRUE;
+	}
+
+	return FALSE;
 }
 
 static void
@@ -217,6 +289,10 @@ gkd_secret_prompt_dispose (GObject *obj)
 		self->pv->service = NULL;
 	}
 
+	if (self->pv->session)
+		g_object_unref (self->pv->session);
+	self->pv->session = NULL;
+
 	G_OBJECT_CLASS (gkd_secret_prompt_parent_class)->dispose (obj);
 }
 
@@ -227,6 +303,7 @@ gkd_secret_prompt_finalize (GObject *obj)
 
 	g_assert (!self->pv->object_path);
 	g_assert (!self->pv->service);
+	g_assert (!self->pv->session);
 
 	g_free (self->pv->caller);
 	self->pv->caller = NULL;
@@ -391,3 +468,44 @@ gkd_secret_prompt_dismiss (GkdSecretPrompt *self)
 	self->pv->completed = TRUE;
 	emit_completed (self, TRUE);
 }
+
+GkdSecretSession*
+gkd_secret_prompt_get_session (GkdSecretPrompt *self)
+{
+	g_return_val_if_fail (GKD_SECRET_IS_PROMPT (self), NULL);
+	g_return_val_if_fail (self->pv->service, NULL);
+	return self->pv->session;
+}
+
+GP11Object*
+gkd_secret_prompt_lookup_collection (GkdSecretPrompt *self, const gchar *path)
+{
+	GkdSecretObjects *objects;
+
+	g_return_val_if_fail (GKD_SECRET_IS_PROMPT (self), NULL);
+	g_return_val_if_fail (path, NULL);
+
+	objects = gkd_secret_prompt_get_objects (GKD_SECRET_PROMPT (self));
+	return gkd_secret_objects_lookup_collection (objects, self->pv->caller, path);
+}
+
+GkdSecretSecret*
+gkd_secret_prompt_get_secret (GkdSecretPrompt *self, const gchar *password_type)
+{
+	gpointer parameter, value;
+	gsize n_parameter, n_value;
+
+	g_return_val_if_fail (GKD_SECRET_IS_PROMPT (self), NULL);
+
+	if (!complete_transport_params (self))
+		return NULL;
+
+	if (!gkd_prompt_get_transport_password (GKD_PROMPT (self), password_type,
+	                                        &parameter, &n_parameter,
+	                                        &value, &n_value))
+		return NULL;
+
+	return gkd_secret_secret_new_take_memory (self->pv->session,
+	                                          parameter, n_parameter,
+	                                          value, n_value);
+}
diff --git a/daemon/dbus/gkd-secret-prompt.h b/daemon/dbus/gkd-secret-prompt.h
index 2a4f0a7..4226b84 100644
--- a/daemon/dbus/gkd-secret-prompt.h
+++ b/daemon/dbus/gkd-secret-prompt.h
@@ -66,8 +66,18 @@ const gchar*        gkd_secret_prompt_get_object_path         (GkdSecretPrompt *
 
 GP11Session*        gkd_secret_prompt_get_pkcs11_session      (GkdSecretPrompt *self);
 
+GkdSecretService*   gkd_secret_prompt_get_service             (GkdSecretPrompt *self);
+
 GkdSecretObjects*   gkd_secret_prompt_get_objects             (GkdSecretPrompt *self);
 
+GkdSecretSession*   gkd_secret_prompt_get_session             (GkdSecretPrompt *self);
+
+GkdSecretSecret*    gkd_secret_prompt_get_secret              (GkdSecretPrompt *self,
+                                                               const gchar *password_type);
+
+GP11Object*         gkd_secret_prompt_lookup_collection       (GkdSecretPrompt *self,
+                                                               const gchar *path);
+
 void                gkd_secret_prompt_complete                (GkdSecretPrompt *self);
 
 void                gkd_secret_prompt_dismiss                 (GkdSecretPrompt *self);
diff --git a/daemon/dbus/gkd-secret-secret.c b/daemon/dbus/gkd-secret-secret.c
index 8473b0c..85bbbff 100644
--- a/daemon/dbus/gkd-secret-secret.c
+++ b/daemon/dbus/gkd-secret-secret.c
@@ -22,34 +22,34 @@
 #include "config.h"
 
 #include "gkd-secret-secret.h"
+#include "gkd-secret-service.h"
+#include "gkd-secret-session.h"
 
 #include "egg/egg-secure-memory.h"
 
+#include <glib-object.h>
+
 #include <string.h>
 
 static void
 destroy_with_owned_memory (gpointer data)
 {
 	GkdSecretSecret *secret = data;
-	g_free (secret->path);
-	g_free (secret->caller);
 	g_free (secret->parameter);
 	g_free (secret->value);
 }
 
 GkdSecretSecret*
-gkd_secret_secret_new_take_memory (const gchar *path, const gchar *caller,
+gkd_secret_secret_new_take_memory (GkdSecretSession *session,
                                    gpointer parameter, gsize n_parameter,
                                    gpointer value, gsize n_value)
 {
 	GkdSecretSecret *secret;
 
-	g_return_val_if_fail (path, NULL);
-	g_return_val_if_fail (caller, NULL);
+	g_return_val_if_fail (GKD_SECRET_IS_SESSION (session), NULL);
 
 	secret = g_slice_new0 (GkdSecretSecret);
-	secret->path = g_strdup (path);
-	secret->caller = g_strdup (caller);
+	secret->session = g_object_ref (session);
 	secret->parameter = parameter;
 	secret->n_parameter = n_parameter;
 	secret->value = value;
@@ -62,41 +62,62 @@ gkd_secret_secret_new_take_memory (const gchar *path, const gchar *caller,
 }
 
 GkdSecretSecret*
-gkd_secret_secret_parse (DBusMessage *message, DBusMessageIter *iter)
+gkd_secret_secret_parse (GkdSecretService *service, DBusMessage *message,
+                         DBusMessageIter *iter, DBusError *derr)
 {
 	GkdSecretSecret *secret;
+	GkdSecretSession *session;
 	DBusMessageIter struc, array;
 	void *parameter, *value;
 	int n_value, n_parameter;
 	char *path;
 
+	g_return_val_if_fail (GKD_SECRET_IS_SERVICE (service), NULL);
+	g_return_val_if_fail (message, NULL);
+	g_return_val_if_fail (iter, NULL);
+
 	g_return_val_if_fail (dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_STRUCT, NULL);
 	dbus_message_iter_recurse (iter, &struc);
 
 	/* Get the path */
-	if (dbus_message_iter_get_arg_type (&struc) != DBUS_TYPE_OBJECT_PATH)
+	if (dbus_message_iter_get_arg_type (&struc) != DBUS_TYPE_OBJECT_PATH) {
+		dbus_set_error (derr, DBUS_ERROR_INVALID_ARGS, "Invalid secret argument");
 		return NULL;
+	}
 	dbus_message_iter_get_basic (&struc, &path);
 
 	/* Get the parameter */
 	if (!dbus_message_iter_next (&struc) ||
 	    dbus_message_iter_get_arg_type (&struc) != DBUS_TYPE_ARRAY ||
-	    dbus_message_iter_get_element_type(&struc) != DBUS_TYPE_BYTE)
+	    dbus_message_iter_get_element_type(&struc) != DBUS_TYPE_BYTE) {
+		dbus_set_error (derr, DBUS_ERROR_INVALID_ARGS, "Invalid secret argument");
 		return NULL;
+	}
 	dbus_message_iter_recurse (&struc, &array);
 	dbus_message_iter_get_fixed_array (&array, &parameter, &n_parameter);
 
 	/* Get the value */
 	if (!dbus_message_iter_next (&struc) ||
 	    dbus_message_iter_get_arg_type (&struc) != DBUS_TYPE_ARRAY ||
-	    dbus_message_iter_get_element_type(&struc) != DBUS_TYPE_BYTE)
+	    dbus_message_iter_get_element_type(&struc) != DBUS_TYPE_BYTE) {
+		dbus_set_error (derr, DBUS_ERROR_INVALID_ARGS, "Invalid secret argument");
 		return NULL;
+	}
 	dbus_message_iter_recurse (&struc, &array);
 	dbus_message_iter_get_fixed_array (&array, &value, &n_value);
 
+	/* Try to lookup the session */
+	session = gkd_secret_service_lookup_session (service, path,
+	                                             dbus_message_get_sender (message));
+
+	if (session == NULL) {
+		dbus_set_error (derr, SECRET_ERROR_NO_SESSION,
+		                "The session wrapping the secret does not exist");
+		return NULL;
+	}
+
 	secret = g_slice_new0 (GkdSecretSecret);
-	secret->path = path;
-	secret->caller = (char*)dbus_message_get_sender (message);
+	secret->session = g_object_ref (session);
 	secret->parameter = parameter;
 	secret->n_parameter = n_parameter;
 	secret->value = value;
@@ -112,10 +133,14 @@ void
 gkd_secret_secret_append (GkdSecretSecret *secret, DBusMessageIter *iter)
 {
 	DBusMessageIter struc, array;
+	const gchar *path;
 	int length;
 
+	path = gkd_secret_session_get_object_path (secret->session);
+	g_return_if_fail (path);
+
 	dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL, &struc);
-	dbus_message_iter_append_basic (&struc, DBUS_TYPE_OBJECT_PATH, &(secret->path));
+	dbus_message_iter_append_basic (&struc, DBUS_TYPE_OBJECT_PATH, &path);
 	dbus_message_iter_open_container (&struc, DBUS_TYPE_ARRAY, "y", &array);
 	length = secret->n_parameter;
 	dbus_message_iter_append_fixed_array (&array, DBUS_TYPE_BYTE, &(secret->parameter), length);
@@ -148,6 +173,8 @@ gkd_secret_secret_free (gpointer data)
 	egg_secure_clear (secret->parameter, secret->n_parameter);
 	egg_secure_clear (secret->value, secret->n_value);
 
+	g_object_unref (secret->session);
+
 	/* Call the destructor of memory */
 	if (secret->destroy_func)
 		(secret->destroy_func) (secret->destroy_data);
diff --git a/daemon/dbus/gkd-secret-secret.h b/daemon/dbus/gkd-secret-secret.h
index 810cc3c..c6047bc 100644
--- a/daemon/dbus/gkd-secret-secret.h
+++ b/daemon/dbus/gkd-secret-secret.h
@@ -29,8 +29,8 @@
 #include <dbus/dbus.h>
 
 struct _GkdSecretSecret {
-	gchar *path;
-	gchar *caller;
+	GkdSecretSession *session;
+
 	gpointer parameter;
 	gsize n_parameter;
 	gpointer value;
@@ -40,15 +40,16 @@ struct _GkdSecretSecret {
 	gpointer destroy_data;
 };
 
-GkdSecretSecret*       gkd_secret_secret_new_take_memory          (const gchar *path,
-                                                                   const gchar *caller,
+GkdSecretSecret*       gkd_secret_secret_new_take_memory          (GkdSecretSession *session,
                                                                    gpointer parameter,
                                                                    gsize n_parameter,
                                                                    gpointer value,
                                                                    gsize n_value);
 
-GkdSecretSecret*       gkd_secret_secret_parse                    (DBusMessage *message,
-                                                                   DBusMessageIter *iter);
+GkdSecretSecret*       gkd_secret_secret_parse                    (GkdSecretService *service,
+                                                                   DBusMessage *message,
+                                                                   DBusMessageIter *iter,
+                                                                   DBusError *derr);
 
 void                   gkd_secret_secret_append                   (GkdSecretSecret *secret,
                                                                    DBusMessageIter *iter);
diff --git a/daemon/dbus/gkd-secret-service.c b/daemon/dbus/gkd-secret-service.c
index 0149183..09fa9f8 100644
--- a/daemon/dbus/gkd-secret-service.c
+++ b/daemon/dbus/gkd-secret-service.c
@@ -22,8 +22,10 @@
 #include "config.h"
 
 #include "gkd-dbus-util.h"
+#include "gkd-secret-change.h"
 #include "gkd-secret-create.h"
 #include "gkd-secret-error.h"
+#include "gkd-secret-lock.h"
 #include "gkd-secret-objects.h"
 #include "gkd-secret-prompt.h"
 #include "gkd-secret-property.h"
@@ -376,11 +378,7 @@ service_method_open_session (GkdSecretService *self, DBusMessage *message)
 	caller = dbus_message_get_sender (message);
 
 	/* Now we can create a session with this information */
-	session = g_object_new (GKD_SECRET_TYPE_SESSION,
-	                        "caller", caller,
-	                        "service", self,
-	                        NULL);
-
+	session = gkd_secret_session_new (self, caller);
 	reply = gkd_secret_session_handle_open (session, message);
 
 	if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_METHOD_RETURN) {
@@ -446,46 +444,6 @@ service_method_create_collection (GkdSecretService *self, DBusMessage *message)
 }
 
 static DBusMessage*
-service_method_create_with_master_password (GkdSecretService *self, DBusMessage *message)
-{
-	DBusMessageIter iter, array;
-	DBusMessage *reply = NULL;
-	GkdSecretSecret *secret = NULL;
-	GP11Attributes *attrs = NULL;
-	const char *caller;
-
-	caller = dbus_message_get_sender (message);
-	g_return_val_if_fail (caller, NULL);
-
-	/* Parse the incoming message */
-	if (!dbus_message_has_signature (message, "a{sv}(oayay)"))
-		return NULL;
-	if (!dbus_message_iter_init (message, &iter))
-		g_return_val_if_reached (NULL);
-	attrs = gp11_attributes_new ();
-	dbus_message_iter_recurse (&iter, &array);
-	if (!gkd_secret_property_parse_all (&array, attrs)) {
-		gp11_attributes_unref (attrs);
-		return dbus_message_new_error (message, DBUS_ERROR_INVALID_ARGS,
-		                               "Invalid properties argument");
-	}
-	dbus_message_iter_next (&iter);
-	secret = gkd_secret_secret_parse (message, &iter);
-	if (secret == NULL) {
-		gp11_attributes_unref (attrs);
-		return dbus_message_new_error (message, DBUS_ERROR_INVALID_ARGS,
-		                               "Invalid secret argument");
-	}
-
-	gp11_attributes_add_boolean (attrs, CKA_TOKEN, TRUE);
-	reply = gkd_secret_create_without_prompting (self, message, attrs, secret);
-	gp11_attributes_unref (attrs);
-	gkd_secret_secret_free (secret);
-
-	return reply;
-}
-
-static DBusMessage*
 service_method_lock_service (GkdSecretService *self, DBusMessage *message)
 {
 	if (!dbus_message_get_args (message, NULL, DBUS_TYPE_INVALID))
@@ -543,6 +501,78 @@ service_method_unlock (GkdSecretService *self, DBusMessage *message)
 }
 
 static DBusMessage*
+service_method_lock (GkdSecretService *self, DBusMessage *message)
+{
+	DBusMessage *reply;
+	const char *caller;
+	const gchar *prompt;
+	GP11Object *collection;
+	int n_objpaths, i;
+	char **objpaths;
+	GPtrArray *array;
+
+	if (!dbus_message_get_args (message, NULL,
+	                            DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objpaths, &n_objpaths,
+	                            DBUS_TYPE_INVALID))
+		return NULL;
+
+	caller = dbus_message_get_sender (message);
+	array = g_ptr_array_new ();
+	for (i = 0; i < n_objpaths; ++i) {
+		collection = gkd_secret_objects_lookup_collection (self->objects, caller, objpaths[i]);
+		if (collection != NULL) {
+			if (gkd_secret_lock (collection, NULL))
+				g_ptr_array_add (array, objpaths[i]);
+			g_object_unref (collection);
+		}
+	}
+
+	prompt = "/";
+	reply = dbus_message_new_method_return (message);
+	dbus_message_append_args (reply,
+	                          DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &array->pdata, array->len,
+	                          DBUS_TYPE_OBJECT_PATH, &prompt,
+	                          DBUS_TYPE_INVALID);
+
+	dbus_free_string_array (objpaths);
+	return reply;
+}
+
+static DBusMessage*
+service_method_change_lock (GkdSecretService *self, DBusMessage *message)
+{
+	GkdSecretChange *change;
+	ServiceClient *client;
+	DBusMessage *reply;
+	const char *caller;
+	const gchar *path;
+	GP11Object *collection;
+
+	caller = dbus_message_get_sender (message);
+	if (!dbus_message_get_args (message, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID))
+		return NULL;
+
+	/* Make sure it exists */
+	collection = gkd_secret_objects_lookup_collection (self->objects, caller, path);
+	if (!collection)
+		return dbus_message_new_error (message, SECRET_ERROR_NO_SUCH_OBJECT,
+		                               "The collection does not exist");
+	g_object_unref (collection);
+
+	change = gkd_secret_change_new (self, caller, path);
+	client = g_hash_table_lookup (self->clients, caller);
+	g_return_val_if_fail (client, NULL);
+	path = gkd_secret_prompt_get_object_path (GKD_SECRET_PROMPT (change));
+	g_hash_table_replace (client->prompts, (gpointer)path, g_object_ref (change));
+
+	reply = dbus_message_new_method_return (message);
+	dbus_message_append_args (reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
+
+	g_object_unref (change);
+	return reply;
+}
+
+static DBusMessage*
 service_method_read_alias (GkdSecretService *self, DBusMessage *message)
 {
 	DBusMessage *reply;
@@ -621,6 +651,151 @@ service_method_set_alias (GkdSecretService *self, DBusMessage *message)
 }
 
 static DBusMessage*
+service_method_create_with_master_password (GkdSecretService *self, DBusMessage *message)
+{
+	DBusError derr = DBUS_ERROR_INIT;
+	DBusMessageIter iter, array;
+	DBusMessage *reply = NULL;
+	GkdSecretSecret *secret = NULL;
+	GP11Attributes *attrs = NULL;
+	gchar *path;
+
+	/* Parse the incoming message */
+	if (!dbus_message_has_signature (message, "a{sv}(oayay)"))
+		return NULL;
+	if (!dbus_message_iter_init (message, &iter))
+		g_return_val_if_reached (NULL);
+	attrs = gp11_attributes_new ();
+	dbus_message_iter_recurse (&iter, &array);
+	if (!gkd_secret_property_parse_all (&array, attrs)) {
+		gp11_attributes_unref (attrs);
+		return dbus_message_new_error (message, DBUS_ERROR_INVALID_ARGS,
+		                               "Invalid properties argument");
+	}
+	dbus_message_iter_next (&iter);
+	secret = gkd_secret_secret_parse (self, message, &iter, &derr);
+	if (secret == NULL) {
+		gp11_attributes_unref (attrs);
+		return gkd_secret_error_to_reply (message, &derr);
+	}
+
+	gp11_attributes_add_boolean (attrs, CKA_TOKEN, TRUE);
+	path = gkd_secret_create_with_secret (attrs, secret, &derr);
+	gp11_attributes_unref (attrs);
+	gkd_secret_secret_free (secret);
+
+	if (path == NULL)
+		return gkd_secret_error_to_reply (message, &derr);
+
+	reply = dbus_message_new_method_return (message);
+	dbus_message_append_args (reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
+	g_free (path);
+
+	return reply;
+}
+
+static DBusMessage*
+service_method_change_with_master_password (GkdSecretService *self, DBusMessage *message)
+{
+	DBusError derr = DBUS_ERROR_INIT;
+	GkdSecretSecret *original, *master;
+	GP11Object *collection;
+	DBusMessageIter iter;
+	DBusMessage *reply;
+	const gchar *path;
+
+	/* Parse the incoming message */
+	if (!dbus_message_has_signature (message, "o(oayay)(oayay)"))
+		return NULL;
+	if (!dbus_message_iter_init (message, &iter))
+		g_return_val_if_reached (NULL);
+	dbus_message_iter_get_basic (&iter, &path);
+	dbus_message_iter_next (&iter);
+	original = gkd_secret_secret_parse (self, message, &iter, &derr);
+	if (original == NULL)
+		return gkd_secret_error_to_reply (message, &derr);
+	dbus_message_iter_next (&iter);
+	master = gkd_secret_secret_parse (self, message, &iter, &derr);
+	if (master == NULL) {
+		gkd_secret_secret_free (original);
+		return gkd_secret_error_to_reply (message, &derr);
+	}
+
+	/* Make sure we have such a collection */
+	collection = gkd_secret_objects_lookup_collection (self->objects,
+	                                                   dbus_message_get_sender (message),
+	                                                   path);
+
+	/* No such collection */
+	if (collection == NULL)
+		reply = dbus_message_new_error (message, SECRET_ERROR_NO_SUCH_OBJECT,
+		                                "The collection does not exist");
+
+	/* Success */
+	else if (gkd_secret_change_with_secrets (collection, original, master, &derr))
+		reply = dbus_message_new_method_return (message);
+
+	/* Failure */
+	else
+		reply = gkd_secret_error_to_reply (message, &derr);
+
+	gkd_secret_secret_free (original);
+	gkd_secret_secret_free (master);
+
+	if (collection)
+		g_object_unref (collection);
+
+	return reply;
+}
+
+static DBusMessage*
+service_method_unlock_with_master_password (GkdSecretService *self, DBusMessage *message)
+{
+	DBusError derr = DBUS_ERROR_INIT;
+	GkdSecretSecret *master;
+	GP11Object *collection;
+	DBusMessageIter iter;
+	DBusMessage *reply;
+	const gchar *path;
+
+	/* Parse the incoming message */
+	if (!dbus_message_has_signature (message, "o(oayay)"))
+		return NULL;
+	if (!dbus_message_iter_init (message, &iter))
+		g_return_val_if_reached (NULL);
+	dbus_message_iter_get_basic (&iter, &path);
+	dbus_message_iter_next (&iter);
+	master = gkd_secret_secret_parse (self, message, &iter, &derr);
+	if (master == NULL)
+		return gkd_secret_error_to_reply (message, &derr);
+
+	/* Make sure we have such a collection */
+	collection = gkd_secret_objects_lookup_collection (self->objects,
+	                                                   dbus_message_get_sender (message),
+	                                                   path);
+
+	/* No such collection */
+	if (collection == NULL)
+		reply = dbus_message_new_error (message, SECRET_ERROR_NO_SUCH_OBJECT,
+		                                "The collection does not exist");
+
+	/* Success */
+	else if (gkd_secret_unlock_with_secret (collection, master, &derr))
+		reply = dbus_message_new_method_return (message);
+
+	/* Failure */
+	else
+		reply = gkd_secret_error_to_reply (message, &derr);
+
+	gkd_secret_secret_free (master);
+
+	if (collection)
+		g_object_unref (collection);
+
+	return reply;
+}
+
+static DBusMessage*
 service_message_handler (GkdSecretService *self, DBusMessage *message)
 {
 	g_return_val_if_fail (message, NULL);
@@ -650,18 +825,38 @@ service_message_handler (GkdSecretService *self, DBusMessage *message)
 	if (dbus_message_is_method_call (message, SECRET_SERVICE_INTERFACE, "Unlock"))
 		return service_method_unlock (self, message);
 
-	/* org.freedesktop.Secret.Service.ReadAlias */
+	/* org.freedesktop.Secret.Service.Lock() */
+	if (dbus_message_is_method_call (message, SECRET_SERVICE_INTERFACE, "Lock"))
+		return service_method_lock (self, message);
+
+	/* org.gnome.keyring.Service.ChangeLock() */
+	if (dbus_message_is_method_call (message, INTERNAL_SERVICE_INTERFACE, "ChangeLock"))
+		return service_method_change_lock (self, message);
+
+	/* org.freedesktop.Secret.Service.ReadAlias() */
 	if (dbus_message_is_method_call (message, SECRET_SERVICE_INTERFACE, "ReadAlias"))
 		return service_method_read_alias (self, message);
 
-	/* org.freedesktop.Secret.Service.SetAlias */
+	/* org.freedesktop.Secret.Service.SetAlias() */
 	if (dbus_message_is_method_call (message, SECRET_SERVICE_INTERFACE, "SetAlias"))
 		return service_method_set_alias (self, message);
 
-	/* org.gnome.keyring.InternalUnsupportedGuiltRiddenInterface.CreateCollectionWithPassword */
+	/* org.gnome.keyring.InternalUnsupportedGuiltRiddenInterface.ChangeLock() */
+	if (dbus_message_is_method_call (message, INTERNAL_SERVICE_INTERFACE, "ChangeLock"))
+		return service_method_change_lock (self, message);
+
+	/* org.gnome.keyring.InternalUnsupportedGuiltRiddenInterface.CreateWithMasterPassword */
 	if (dbus_message_is_method_call (message, INTERNAL_SERVICE_INTERFACE, "CreateWithMasterPassword"))
 		return service_method_create_with_master_password (self, message);
 
+	/* org.gnome.keyring.InternalUnsupportedGuiltRiddenInterface.ChangeWithMasterPassword() */
+	if (dbus_message_is_method_call (message, INTERNAL_SERVICE_INTERFACE, "ChangeWithMasterPassword"))
+		return service_method_change_with_master_password (self, message);
+
+	/* org.gnome.keyring.InternalUnsupportedGuiltRiddenInterface.UnlockWithMasterPassword() */
+	if (dbus_message_is_method_call (message, INTERNAL_SERVICE_INTERFACE, "UnlockWithMasterPassword"))
+		return service_method_unlock_with_master_password (self, message);
+
 	/* org.freedesktop.DBus.Properties.Get() */
 	if (dbus_message_is_method_call (message, PROPERTIES_INTERFACE, "Get"))
 		return service_property_get (self, message);
diff --git a/daemon/dbus/gkd-secret-session.c b/daemon/dbus/gkd-secret-session.c
index d996d94..7d6efb6 100644
--- a/daemon/dbus/gkd-secret-session.c
+++ b/daemon/dbus/gkd-secret-session.c
@@ -44,10 +44,17 @@ enum {
 
 struct _GkdSecretSession {
 	GObject parent;
+
+	/* Information about this object */
 	gchar *object_path;
 	GkdSecretService *service;
 	gchar *caller_exec;
 	gchar *caller;
+
+	/* While negotiating with a prompt, set to private key */
+	GP11Object *private;
+
+	/* Once negotiated set to key and mechanism */
 	GP11Object *key;
 	CK_MECHANISM_TYPE mech_type;
 };
@@ -79,8 +86,7 @@ aes_create_dh_keys (GP11Session *session, const gchar *group,
 	GError *error = NULL;
 	gboolean ret;
 
-	if (!egg_dh_default_params_raw ("ietf-ike-grp-modp-1024", &prime,
-	                                &n_prime, &base, &n_base)) {
+	if (!egg_dh_default_params_raw (group, &prime, &n_prime, &base, &n_base)) {
 		g_warning ("couldn't load dh parameter group: %s", group);
 		return FALSE;
 	}
@@ -104,6 +110,8 @@ aes_create_dh_keys (GP11Session *session, const gchar *group,
 		return FALSE;
 	}
 
+	gp11_object_set_session (*pub_key, session);
+	gp11_object_set_session (*priv_key, session);
 	return TRUE;
 }
 
@@ -132,6 +140,7 @@ aes_derive_key (GP11Session *session, GP11Object *priv_key,
 		return FALSE;
 	}
 
+	gp11_object_set_session (*aes_key, session);
 	return TRUE;
 }
 
@@ -155,7 +164,6 @@ aes_negotiate (GkdSecretSession *self, DBusMessage *message, gconstpointer input
 		                                       "Failed to create necessary crypto keys.");
 
 	/* Get the output data */
-	gp11_object_set_session (pub, session);
 	output = gp11_object_get_data (pub, CKA_VALUE, &n_output, &error);
 	gp11_object_destroy (pub, NULL);
 	g_object_unref (pub);
@@ -179,7 +187,6 @@ aes_negotiate (GkdSecretSession *self, DBusMessage *message, gconstpointer input
 		                                       "Failed to create necessary crypto key.");
 	}
 
-	gp11_object_set_session (key, session);
 	take_session_key (self, key, CKM_AES_CBC_PAD);
 
 	reply = dbus_message_new_method_return (message);
@@ -402,6 +409,68 @@ gkd_secret_session_class_init (GkdSecretSessionClass *klass)
  * PUBLIC
  */
 
+GkdSecretSession*
+gkd_secret_session_new (GkdSecretService *service, const gchar *caller)
+{
+	g_return_val_if_fail (GKD_SECRET_IS_SERVICE (service), NULL);
+	g_return_val_if_fail (caller, NULL);
+	return g_object_new (GKD_SECRET_TYPE_SESSION,
+	                     "caller", caller, "service", service, NULL);
+}
+
+gpointer
+gkd_secret_session_begin (GkdSecretSession *self, const gchar *group,
+                          gsize *n_output)
+{
+	GError *error = NULL;
+	GP11Session *session;
+	GP11Object *public;
+	gpointer output;
+
+	g_return_val_if_fail (GKD_SECRET_IS_SESSION (self), NULL);
+	g_return_val_if_fail (group, NULL);
+	g_return_val_if_fail (n_output, NULL);
+	g_return_val_if_fail (self->private == NULL, NULL);
+
+	session = gkd_secret_session_get_pkcs11_session (self);
+	g_return_val_if_fail (session, NULL);
+
+	if (!aes_create_dh_keys (session, group, &public, &self->private))
+		return NULL;
+
+	/* Get the output data */
+	output = gp11_object_get_data (public, CKA_VALUE, n_output, &error);
+	gp11_object_destroy (public, NULL);
+	g_object_unref (public);
+
+	if (output == NULL) {
+		g_warning ("couldn't get public key DH value: %s", error->message);
+		g_clear_error (&error);
+		return NULL;
+	}
+
+	return output;
+}
+
+gboolean
+gkd_secret_session_complete (GkdSecretSession *self, gconstpointer peer,
+                             gsize n_peer)
+{
+	GP11Session *session;
+
+	g_return_val_if_fail (GKD_SECRET_IS_SESSION (self), FALSE);
+	g_return_val_if_fail (self->key == NULL, FALSE);
+
+	session = gkd_secret_session_get_pkcs11_session (self);
+	g_return_val_if_fail (session, FALSE);
+
+	if (!aes_derive_key (session, self->private, peer, n_peer, &self->key))
+		return FALSE;
+
+	self->mech_type = CKM_AES_CBC_PAD;
+	return TRUE;
+}
+
 DBusMessage*
 gkd_secret_session_dispatch (GkdSecretSession *self, DBusMessage *message)
 {
@@ -490,6 +559,13 @@ gkd_secret_session_get_object_path (GkdSecretSession *self)
 	return self->object_path;
 }
 
+GP11Session*
+gkd_secret_session_get_pkcs11_session (GkdSecretSession *self)
+{
+	g_return_val_if_fail (GKD_SECRET_IS_SESSION (self), NULL);
+	return gkd_secret_service_get_pkcs11_session (self->service, self->caller);
+}
+
 GkdSecretSecret*
 gkd_secret_session_get_item_secret (GkdSecretSession *self, GP11Object *item,
                                     DBusError *derr)
@@ -534,8 +610,7 @@ gkd_secret_session_get_item_secret (GkdSecretSession *self, GP11Object *item,
 		return NULL;
 	}
 
-	return gkd_secret_secret_new_take_memory (self->object_path, self->caller,
-	                                          iv, n_iv, value, n_value);
+	return gkd_secret_secret_new_take_memory (self, iv, n_iv, value, n_value);
 }
 
 gboolean
@@ -608,32 +683,6 @@ gkd_secret_session_set_item_secret (GkdSecretSession *self, GP11Object *item,
 	return TRUE;
 }
 
-GkdSecretSession*
-gkd_secret_session_for_path (GkdSecretService *service, const gchar *path,
-                             const gchar *caller, DBusError *derr)
-{
-	GkdSecretSession *self;
-
-	self = gkd_secret_service_lookup_session (service, path, caller);
-	if (self == NULL)
-		dbus_set_error (derr, SECRET_ERROR_NO_SESSION, "The session does not exist");
-
-	return self;
-}
-
-GkdSecretSession*
-gkd_secret_session_for_secret (GkdSecretService *service, GkdSecretSecret *secret, DBusError *derr)
-{
-	GkdSecretSession *self;
-
-	self = gkd_secret_service_lookup_session (service, secret->path, secret->caller);
-	if (self == NULL)
-		dbus_set_error (derr, SECRET_ERROR_NO_SESSION,
-		                "The session wrapping the secret does not exist");
-
-	return self;
-}
-
 GP11Object*
 gkd_secret_session_create_credential (GkdSecretSession *self, GP11Session *session,
                                       GP11Attributes *attrs, GkdSecretSecret *secret,
@@ -645,7 +694,6 @@ gkd_secret_session_create_credential (GkdSecretSession *self, GP11Session *sessi
 	GError *error = NULL;
 
 	g_assert (GP11_IS_OBJECT (self->key));
-	g_assert (GP11_IS_SESSION (session));
 	g_assert (attrs);
 
 	if (session == NULL)
@@ -668,7 +716,9 @@ gkd_secret_session_create_credential (GkdSecretSession *self, GP11Session *sessi
 		gp11_attributes_unref (alloc);
 
 	if (object == NULL) {
-		if (error->code == CKR_WRAPPED_KEY_INVALID ||
+		if (error->code == CKR_PIN_INCORRECT) {
+			dbus_set_error_const (derr, INTERNAL_ERROR_DENIED, "The password was incorrect.");
+		} else if (error->code == CKR_WRAPPED_KEY_INVALID ||
 		    error->code == CKR_WRAPPED_KEY_LEN_RANGE ||
 		    error->code == CKR_MECHANISM_PARAM_INVALID) {
 			dbus_set_error_const (derr, DBUS_ERROR_INVALID_ARGS,
diff --git a/daemon/dbus/gkd-secret-session.h b/daemon/dbus/gkd-secret-session.h
index ae329a1..84f2d9d 100644
--- a/daemon/dbus/gkd-secret-session.h
+++ b/daemon/dbus/gkd-secret-session.h
@@ -41,14 +41,16 @@ struct _GkdSecretSessionClass {
 
 GType               gkd_secret_session_get_type                (void);
 
-GkdSecretSession*   gkd_secret_session_for_path                (GkdSecretService *service,
-                                                                const gchar *path,
-                                                                const gchar *caller,
-                                                                DBusError *derr);
+GkdSecretSession*   gkd_secret_session_new                     (GkdSecretService *service,
+                                                                const gchar *caller);
 
-GkdSecretSession*   gkd_secret_session_for_secret              (GkdSecretService *service,
-                                                                GkdSecretSecret *secret,
-                                                                DBusError *derr);
+gpointer            gkd_secret_session_begin                   (GkdSecretSession *self,
+                                                                const gchar *group,
+                                                                gsize *n_public);
+
+gboolean            gkd_secret_session_complete                (GkdSecretSession *self,
+                                                                gconstpointer peer,
+                                                                gsize n_peer);
 
 DBusMessage*        gkd_secret_session_dispatch                (GkdSecretSession *self,
                                                                 DBusMessage *message);
@@ -59,6 +61,8 @@ const gchar*        gkd_secret_session_get_caller_executable   (GkdSecretSession
 
 const gchar*        gkd_secret_session_get_object_path         (GkdSecretSession *self);
 
+GP11Session*        gkd_secret_session_get_pkcs11_session      (GkdSecretSession *self);
+
 GkdSecretSecret*    gkd_secret_session_get_item_secret         (GkdSecretSession *self,
                                                                 GP11Object *item,
                                                                 DBusError *derr);
diff --git a/daemon/dbus/gkd-secret-types.h b/daemon/dbus/gkd-secret-types.h
index 596a196..36903be 100644
--- a/daemon/dbus/gkd-secret-types.h
+++ b/daemon/dbus/gkd-secret-types.h
@@ -25,7 +25,8 @@
 #define BUS_INTERFACE                  "org.freedesktop.DBus"
 #define PROPERTIES_INTERFACE           "org.freedesktop.DBus.Properties"
 
-#define INTERNAL_SERVICE_INTERFACE      "org.gnome.keyring.InternalUnsupportedGuiltRiddenInterface"
+#define INTERNAL_SERVICE_INTERFACE     "org.gnome.keyring.InternalUnsupportedGuiltRiddenInterface"
+#define INTERNAL_ERROR_DENIED          "org.gnome.keyring.Error.Denied"
 
 #define SECRET_COLLECTION_INTERFACE    "org.freedesktop.Secret.Collection"
 #define SECRET_ITEM_INTERFACE          "org.freedesktop.Secret.Item"
@@ -49,6 +50,7 @@
 #define SECRET_ERROR_NO_SUCH_OBJECT    "org.freedesktop.Secret.Error.NoSuchObject"
 
 typedef struct _GkdSecretCollection GkdSecretCollection;
+typedef struct _GkdSecretChange GkdSecretChange;
 typedef struct _GkdSecretCreate GkdSecretCreate;
 typedef struct _GkdSecretIndex GkdSecretIndex;
 typedef struct _GkdSecretItem GkdSecretItem;
diff --git a/daemon/dbus/gkd-secret-unlock.c b/daemon/dbus/gkd-secret-unlock.c
index e4c84b9..388b08c 100644
--- a/daemon/dbus/gkd-secret-unlock.c
+++ b/daemon/dbus/gkd-secret-unlock.c
@@ -24,6 +24,8 @@
 #include "gkd-secret-objects.h"
 #include "gkd-secret-service.h"
 #include "gkd-secret-prompt.h"
+#include "gkd-secret-secret.h"
+#include "gkd-secret-session.h"
 #include "gkd-secret-types.h"
 #include "gkd-secret-unlock.h"
 #include "gkd-secret-util.h"
@@ -105,97 +107,84 @@ set_warning_wrong (GkdSecretUnlock *self)
 }
 
 static gboolean
-authenticate_collection (GkdSecretUnlock *self, GP11Object *coll, gboolean *locked)
+check_locked_collection (GP11Object *collection, gboolean *locked)
 {
 	GError *error = NULL;
-	GP11Attributes *attrs;
-	GP11Session *session;
-	GP11Object *cred;
-	gchar *password;
-	gsize n_password;
-
-	g_assert (GKD_SECRET_IS_UNLOCK (self));
-	g_assert (locked);
-	g_assert (coll);
+	gpointer value;
+	gsize n_value;
 
-	attrs = gp11_object_get (coll, &error, CKA_G_LOCKED, GP11_INVALID);
-	if (!attrs) {
+	value = gp11_object_get_data (collection, CKA_G_LOCKED, &n_value, &error);
+	if (value == NULL) {
 		if (error->code != CKR_OBJECT_HANDLE_INVALID)
 			g_warning ("couldn't check locked status of collection: %s",
 			           error->message);
-		g_clear_error (&error);
 		return FALSE;
 	}
 
-	if (!gp11_attributes_find_boolean (attrs, CKA_G_LOCKED, locked))
-		g_return_val_if_reached (FALSE);
-	if (!locked)
-		return TRUE;
+	*locked = (value && n_value == sizeof (CK_BBOOL) && *(CK_BBOOL*)value);
+	g_free (value);
+	return TRUE;
+}
 
-	session = gp11_object_get_session (coll);
-	g_return_val_if_fail (session, FALSE);
-
-	if (!gkd_prompt_has_response (GKD_PROMPT (self)))
-		return TRUE; /* Bail out early, just checking locked status */
-
-	password = gkd_prompt_get_password (GKD_PROMPT (self), "password");
-	n_password = password ? strlen (password) : 0;
-	cred = gp11_session_create_object (session, &error,
-	                                   CKA_CLASS, GP11_ULONG, CKO_G_CREDENTIAL,
-	                                   CKA_G_OBJECT, GP11_ULONG, gp11_object_get_handle (coll),
-	                                   CKA_GNOME_TRANSIENT, GP11_BOOLEAN, TRUE,
-	                                   CKA_TOKEN, GP11_BOOLEAN, FALSE,
-	                                   CKA_VALUE, n_password, password,
-	                                   GP11_INVALID);
-	egg_secure_strfree (password);
-	g_object_unref (session);
-
-	if (cred) {
-		g_object_unref (cred);
+static gboolean
+authenticate_collection (GkdSecretUnlock *self, GP11Object *collection, gboolean *locked)
+{
+	DBusError derr = DBUS_ERROR_INIT;
+	GkdSecretSecret *master;
+	gboolean result;
+
+	g_assert (GKD_SECRET_IS_UNLOCK (self));
+	g_assert (GP11_IS_OBJECT (collection));
+	g_assert (locked);
+
+	/* Bail out early, just checking locked status */
+	if (!gkd_prompt_has_response (GKD_PROMPT (self))) {
+		return check_locked_collection (collection, locked);
+	}
+
+	master = gkd_secret_prompt_get_secret (GKD_SECRET_PROMPT (self), "password");
+	if (master == NULL) {
+		g_warning ("couldn't get password from prompt");
+		return FALSE;
+	}
+
+	result = gkd_secret_unlock_with_secret (collection, master, &derr);
+	gkd_secret_secret_free (master);
+
+	if (result) {
 		*locked = FALSE;
 		return TRUE; /* Operation succeeded, and unlocked */
 
 	} else {
-		if (error->code == (gint)CKR_PIN_INCORRECT) {
-			g_clear_error (&error);
+		if (dbus_error_has_name (&derr, INTERNAL_ERROR_DENIED)) {
+			dbus_error_free (&derr);
 			*locked = TRUE;
 			return TRUE; /* Operation succeded, although not unlocked*/
 
 		} else {
 			g_warning ("couldn't create credential for collection: %s",
-			           error->message);
-			g_clear_error (&error);
+			           derr.message);
+			dbus_error_free (&derr);
 			return FALSE; /* Operation failed */
 		}
 	}
 }
 
-static GP11Object*
-lookup_collection (GkdSecretUnlock *self, const gchar *path)
-{
-	GkdSecretObjects *objects;
-	const gchar *caller;
-
-	objects = gkd_secret_prompt_get_objects (GKD_SECRET_PROMPT (self));
-	caller = gkd_secret_prompt_get_caller (GKD_SECRET_PROMPT (self));
-	return gkd_secret_objects_lookup_collection (objects, caller, path);
-}
-
 /* -----------------------------------------------------------------------------
  * OBJECT
  */
 
 static void
-gkd_secret_unlock_prompt_ready (GkdSecretPrompt *base)
+gkd_secret_unlock_prompt_ready (GkdSecretPrompt *prompt)
 {
-	GkdSecretUnlock *self = GKD_SECRET_UNLOCK (base);
+	GkdSecretUnlock *self = GKD_SECRET_UNLOCK (prompt);
 	GP11Object *coll;
 	gboolean locked;
 	gchar *objpath;
 
 	/* Already prompted for an item */
 	if (self->current) {
-		coll = lookup_collection (self, self->current);
+		coll = gkd_secret_prompt_lookup_collection (prompt, self->current);
 
 		/* If the object or collection is gone, no need to unlock */
 		if (coll == NULL) {
@@ -229,12 +218,12 @@ gkd_secret_unlock_prompt_ready (GkdSecretPrompt *base)
 
 		/* Nothing more to prompt for? */
 		if (!objpath) {
-			gkd_secret_prompt_complete (base);
+			gkd_secret_prompt_complete (prompt);
 			break;
 		}
 
 		/* Find the collection, make sure it's still around */
-		coll = lookup_collection (self, objpath);
+		coll = gkd_secret_prompt_lookup_collection (prompt, objpath);
 		if (coll == NULL) {
 			g_free (objpath);
 			continue;
@@ -340,7 +329,7 @@ gkd_secret_unlock_queue (GkdSecretUnlock *self, const gchar *objpath)
 	g_return_if_fail (GKD_SECRET_IS_UNLOCK (self));
 	g_return_if_fail (objpath);
 
-	coll = lookup_collection (self, objpath);
+	coll = gkd_secret_prompt_lookup_collection (GKD_SECRET_PROMPT (self), objpath);
 	if (coll == NULL)
 		return;
 
@@ -382,3 +371,33 @@ gkd_secret_unlock_reset_results (GkdSecretUnlock *self)
 		g_free (g_array_index (self->results, gchar*, i));
 	g_array_set_size (self->results, 0);
 }
+
+gboolean
+gkd_secret_unlock_with_secret (GP11Object *collection, GkdSecretSecret *master,
+                               DBusError *derr)
+{
+	GP11Attributes *attrs;
+	GP11Object *cred;
+	gboolean locked;
+
+	g_return_val_if_fail (GP11_IS_OBJECT (collection), FALSE);
+	g_return_val_if_fail (master, FALSE);
+
+	/* Shortcut if already unlocked */
+	if (check_locked_collection (collection, &locked) && !locked)
+		return TRUE;
+
+	attrs = gp11_attributes_newv (CKA_CLASS, GP11_ULONG, CKO_G_CREDENTIAL,
+	                              CKA_G_OBJECT, GP11_ULONG, gp11_object_get_handle (collection),
+	                              CKA_GNOME_TRANSIENT, GP11_BOOLEAN, TRUE,
+	                              CKA_TOKEN, GP11_BOOLEAN, TRUE,
+	                              GP11_INVALID);
+
+	cred = gkd_secret_session_create_credential (master->session, NULL, attrs, master, derr);
+
+	gp11_attributes_unref (attrs);
+
+	if (cred != NULL)
+		g_object_unref (cred);
+	return (cred != NULL);
+}
diff --git a/daemon/dbus/gkd-secret-unlock.h b/daemon/dbus/gkd-secret-unlock.h
index bb345f6..f0f351c 100644
--- a/daemon/dbus/gkd-secret-unlock.h
+++ b/daemon/dbus/gkd-secret-unlock.h
@@ -54,4 +54,8 @@ gchar**             gkd_secret_unlock_get_results             (GkdSecretUnlock *
 
 void                gkd_secret_unlock_reset_results           (GkdSecretUnlock *self);
 
+gboolean            gkd_secret_unlock_with_secret             (GP11Object *collection,
+                                                               GkdSecretSecret *master,
+                                                               DBusError *derr);
+
 #endif /* __GKD_SECRET_UNLOCK_H__ */
diff --git a/daemon/prompt/gkd-prompt-tool.c b/daemon/prompt/gkd-prompt-tool.c
index bd65dbf..1e360e6 100644
--- a/daemon/prompt/gkd-prompt-tool.c
+++ b/daemon/prompt/gkd-prompt-tool.c
@@ -292,7 +292,7 @@ gather_password (GtkBuilder *builder, const gchar *password_type)
 	                                     gtk_entry_get_text (entry), &n_data);
 	g_return_if_fail (data);
 
-	gkd_prompt_util_encode_hex (output_data, password_type, "iv", iv, sizeof (iv));
+	gkd_prompt_util_encode_hex (output_data, password_type, "parameter", iv, sizeof (iv));
 	gkd_prompt_util_encode_hex (output_data, password_type, "value", data, n_data);
 
 	g_free (data);
diff --git a/daemon/prompt/gkd-prompt-util.c b/daemon/prompt/gkd-prompt-util.c
index 66d8453..3f722ff 100644
--- a/daemon/prompt/gkd-prompt-util.c
+++ b/daemon/prompt/gkd-prompt-util.c
@@ -26,6 +26,7 @@
 
 #include "egg/egg-dh.h"
 #include "egg/egg-hex.h"
+#include "egg/egg-padding.h"
 #include "egg/egg-secure-memory.h"
 
 void
@@ -42,16 +43,16 @@ gkd_prompt_util_encode_mpi (GKeyFile *key_file, const gchar *section,
 	g_return_if_fail (mpi);
 
 	/* Get the size */
-	gcry = gcry_mpi_print (GCRYMPI_FMT_HEX, NULL, 0, &n_data, mpi);
+	gcry = gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, &n_data, mpi);
 	g_return_if_fail (gcry == 0);
 
-	data = g_malloc0 (n_data + 1);
+	data = g_malloc0 (n_data);
 
 	/* Write into buffer */
-	gcry = gcry_mpi_print (GCRYMPI_FMT_HEX, data, n_data, &n_data, mpi);
+	gcry = gcry_mpi_print (GCRYMPI_FMT_USG, data, n_data, &n_data, mpi);
 	g_return_if_fail (gcry == 0);
 
-	g_key_file_set_value (key_file, section, field, (gchar*)data);
+	gkd_prompt_util_encode_hex (key_file, section, field, data, n_data);
 	g_free (data);
 }
 
@@ -94,25 +95,27 @@ gkd_prompt_util_decode_mpi (GKeyFile *key_file, const gchar *section,
                             const gchar *field, gcry_mpi_t *mpi)
 {
 	gcry_error_t gcry;
-	gchar *data;
+	gpointer data;
+	gsize n_data;
 
 	g_return_val_if_fail (key_file, FALSE);
 	g_return_val_if_fail (section, FALSE);
 	g_return_val_if_fail (field, FALSE);
 	g_return_val_if_fail (mpi, FALSE);
 
-	data = g_key_file_get_value (key_file, section, field, NULL);
+	data = gkd_prompt_util_decode_hex (key_file, section, field, &n_data);
 	if (data == NULL)
 		return FALSE;
 
-	gcry = gcry_mpi_scan (mpi, GCRYMPI_FMT_HEX, data, 0, NULL);
+	gcry = gcry_mpi_scan (mpi, GCRYMPI_FMT_USG, data, n_data, NULL);
 	g_free (data);
 
 	return (gcry == 0);
 }
 
 gpointer
-gkd_prompt_util_encrypt_text (gpointer key, gsize n_key, gpointer iv, gsize n_iv,
+gkd_prompt_util_encrypt_text (gconstpointer key, gsize n_key,
+                              gconstpointer iv, gsize n_iv,
                               const gchar *text, gsize *n_result)
 {
 	gcry_cipher_hd_t cih;
@@ -141,16 +144,12 @@ gkd_prompt_util_encrypt_text (gpointer key, gsize n_key, gpointer iv, gsize n_iv
 	gcry = gcry_cipher_setiv (cih, iv, 16);
 	g_return_val_if_fail (gcry == 0, NULL);
 
-	/* Allocate memory for the operation */
-	n_text = strlen (text) + 1;
-	*n_result = ((n_text + 15) / 16) * 16;
-	padded = egg_secure_alloc (*n_result);
+	/* Pad the text properly */
+	n_text = strlen (text);
+	if (!egg_padding_pkcs7_pad (egg_secure_realloc, 16, text, n_text, (gpointer*)&padded, n_result))
+		g_return_val_if_reached (NULL);
 	result = g_malloc0 (*n_result);
 
-	/* Setup the padding */
-	memset (padded, 0, *n_result);
-	memcpy (padded, text, n_text);
-
 	for (pos = 0; pos < *n_result; pos += 16) {
 		gcry = gcry_cipher_encrypt (cih, result + pos, 16, padded + pos, 16);
 		g_return_val_if_fail (gcry == 0, NULL);
@@ -164,13 +163,13 @@ gkd_prompt_util_encrypt_text (gpointer key, gsize n_key, gpointer iv, gsize n_iv
 }
 
 gchar*
-gkd_prompt_util_decrypt_text (gpointer key, gsize n_key, gpointer iv, gsize n_iv,
-                              gpointer data, gsize n_data)
+gkd_prompt_util_decrypt_text (gconstpointer key, gsize n_key, gconstpointer iv, gsize n_iv,
+                              gconstpointer data, gsize n_data)
 {
 	gcry_cipher_hd_t cih;
 	gcry_error_t gcry;
-	gchar *result;
-	gsize pos;
+	gchar *result, *padded;
+	gsize pos, n_result;
 
 	g_return_val_if_fail (key, NULL);
 	g_return_val_if_fail (n_key == 16, NULL);
@@ -200,18 +199,23 @@ gkd_prompt_util_decrypt_text (gpointer key, gsize n_key, gpointer iv, gsize n_iv
 	g_return_val_if_fail (gcry == 0, NULL);
 
 	/* Allocate memory for the result */
-	result = egg_secure_alloc (n_data);
+	padded = egg_secure_alloc (n_data);
 
 	for (pos = 0; pos < n_data; pos += 16) {
-		gcry = gcry_cipher_decrypt (cih, result + pos, 16, (guchar*)data + pos, 16);
+		gcry = gcry_cipher_decrypt (cih, padded + pos, 16, (guchar*)data + pos, 16);
 		g_return_val_if_fail (gcry == 0, NULL);
 	}
 
 	gcry_cipher_close (cih);
 
-	if (!g_utf8_validate (result, -1, NULL)) {
+	if (!egg_padding_pkcs7_unpad (egg_secure_realloc, 16, padded, n_data, (gpointer*)&result, &n_result))
+		result = NULL;
+
+	egg_secure_free (padded);
+
+	if (result && !g_utf8_validate (result, n_result, NULL)) {
 		egg_secure_free (result);
-		return NULL;
+		result = NULL;
 	}
 
 	return result;
diff --git a/daemon/prompt/gkd-prompt-util.h b/daemon/prompt/gkd-prompt-util.h
index 56e3c17..e93dde3 100644
--- a/daemon/prompt/gkd-prompt-util.h
+++ b/daemon/prompt/gkd-prompt-util.h
@@ -38,10 +38,12 @@ gboolean    gkd_prompt_util_decode_mpi                   (GKeyFile *key_file, co
 gpointer    gkd_prompt_util_decode_hex                   (GKeyFile *key_file, const gchar *section,
                                                           const gchar *field, gsize *n_result);
 
-gpointer    gkd_prompt_util_encrypt_text                 (gpointer key, gsize n_key, gpointer iv, gsize n_iv,
+gpointer    gkd_prompt_util_encrypt_text                 (gconstpointer key, gsize n_key,
+                                                          gconstpointer iv, gsize n_iv,
                                                           const gchar *text, gsize *n_result);
 
-gchar*      gkd_prompt_util_decrypt_text                 (gpointer key, gsize n_key, gpointer iv, gsize n_iv,
-                                                          gpointer data, gsize n_data);
+gchar*      gkd_prompt_util_decrypt_text                 (gconstpointer key, gsize n_key,
+                                                          gconstpointer iv, gsize n_iv,
+                                                          gconstpointer data, gsize n_data);
 
 #endif /* __GKD_PROMPT_H__ */
diff --git a/daemon/prompt/gkd-prompt.c b/daemon/prompt/gkd-prompt.c
index a325f42..c92aae8 100644
--- a/daemon/prompt/gkd-prompt.c
+++ b/daemon/prompt/gkd-prompt.c
@@ -44,6 +44,13 @@ enum {
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
+typedef struct _TransportCrypto {
+	gcry_mpi_t private;
+	gcry_mpi_t prime;
+	gpointer key;
+	gsize n_key;
+} TransportCrypto;
+
 struct _GkdPromptPrivate {
 	GKeyFile *input;
 	GKeyFile *output;
@@ -52,10 +59,7 @@ struct _GkdPromptPrivate {
 	gboolean failure;
 
 	/* Transport crypto */
-	gcry_mpi_t secret;
-	gcry_mpi_t prime;
-	gpointer key;
-	gsize n_key;
+	TransportCrypto *transport;
 
 	/* Information about child */
 	GPid pid;
@@ -279,46 +283,70 @@ on_child_exited (GPid pid, gint status, gpointer user_data)
 static void
 prepare_transport_crypto (GkdPrompt *self)
 {
+	TransportCrypto *transport;
 	gcry_mpi_t pub, base;
 
-	g_assert (!self->pv->prime);
-	g_assert (!self->pv->secret);
+	if (!g_key_file_has_group (self->pv->input, "transport")) {
+		g_assert (!self->pv->transport);
+		transport = g_slice_new0 (TransportCrypto);
+
+		/* Figure out our prime, base, public and secret bits */
+		if (!egg_dh_default_params ("ietf-ike-grp-modp-1536", &transport->prime, &base) ||
+		    !egg_dh_gen_pair (transport->prime, base, 0, &pub, &transport->private))
+			g_return_if_reached ();
 
-	/* Figure out our prime, base, public and secret bits */
-	if (!egg_dh_default_params ("ietf-ike-grp-modp-1536", &self->pv->prime, &base) ||
-	    !egg_dh_gen_pair (self->pv->prime, base, 0, &pub, &self->pv->secret))
-		g_return_if_reached ();
+		/* Send over the prime, base, and public bits */
+		gkd_prompt_util_encode_mpi (self->pv->input, "transport", "prime", transport->prime);
+		gkd_prompt_util_encode_mpi (self->pv->input, "transport", "base", base);
+		gkd_prompt_util_encode_mpi (self->pv->input, "transport", "public", pub);
 
-	/* Send over the prime, base, and public bits */
-	gkd_prompt_util_encode_mpi (self->pv->input, "transport", "prime", self->pv->prime);
-	gkd_prompt_util_encode_mpi (self->pv->input, "transport", "base", base);
-	gkd_prompt_util_encode_mpi (self->pv->input, "transport", "public", pub);
+		gcry_mpi_release (base);
+		gcry_mpi_release (pub);
+
+		self->pv->transport = transport;
+	}
 
-	gcry_mpi_release (base);
-	gcry_mpi_release (pub);
+	if (self->pv->transport) {
+		egg_secure_free (self->pv->transport->key);
+		self->pv->transport->key = NULL;
+		self->pv->transport->n_key = 0;
+	}
 }
 
-static gboolean
-receive_transport_crypto (GkdPrompt *self)
+static gconstpointer
+calculate_transport_key (GkdPrompt *self, gsize *n_key)
 {
 	gcry_mpi_t peer;
 	gpointer value;
 
 	g_assert (self->pv->output);
+	g_assert (n_key);
 
-	if (!gkd_prompt_util_decode_mpi (self->pv->output, "transport", "public", &peer))
-		return FALSE;
+	if (!self->pv->transport) {
+		g_warning ("GkdPrompt did not negotiate crypto, but its caller is now asking"
+		           " it to do the decryption. This is an error in gnome-keyring");
+		return NULL;
+	}
 
-	value = egg_dh_gen_secret (peer, self->pv->secret, self->pv->prime, 16);
-	gcry_mpi_release (peer);
-	if (!value)
-		return FALSE;
+	if (!self->pv->transport->key) {
+		if (!gkd_prompt_util_decode_mpi (self->pv->output, "transport", "public", &peer))
+			return NULL;
 
-	egg_secure_free (self->pv->key);
-	self->pv->key = value;
-	self->pv->n_key = 16;
+		value = egg_dh_gen_secret (peer, self->pv->transport->private,
+		                           self->pv->transport->prime, 16);
 
-	return TRUE;
+		gcry_mpi_release (peer);
+
+		if (!value)
+			return NULL;
+
+		egg_secure_free (self->pv->transport->key);
+		self->pv->transport->key = value;
+		self->pv->transport->n_key = 16;
+	}
+
+	*n_key = self->pv->transport->n_key;
+	return self->pv->transport->key;
 }
 
 static gboolean
@@ -409,6 +437,8 @@ display_async_prompt (GkdPrompt *self)
 static void
 clear_prompt_data (GkdPrompt *self)
 {
+	TransportCrypto *transport;
+
 	if (self->pv->input)
 		g_key_file_free (self->pv->input);
 	self->pv->input = NULL;
@@ -436,19 +466,18 @@ clear_prompt_data (GkdPrompt *self)
 		g_source_remove (self->pv->io_tag);
 	self->pv->io_tag = 0;
 
-	if (self->pv->prime)
-		gcry_mpi_release (self->pv->prime);
-	self->pv->prime = NULL;
-
-	if (self->pv->secret)
-		gcry_mpi_release (self->pv->secret);
-	self->pv->secret = NULL;
-
-	if (self->pv->key) {
-		egg_secure_clear (self->pv->key, self->pv->n_key);
-		egg_secure_free (self->pv->key);
-		self->pv->key = NULL;
-		self->pv->n_key = 0;
+	if (self->pv->transport) {
+		transport = self->pv->transport;
+		if (transport->prime)
+			gcry_mpi_release (transport->prime);
+		if (transport->private)
+			gcry_mpi_release (transport->private);
+		if (transport->key) {
+			egg_secure_clear (transport->key, transport->n_key);
+			egg_secure_free (transport->key);
+		}
+		g_slice_free (TransportCrypto, transport);
+		self->pv->transport = NULL;
 	}
 }
 
@@ -511,9 +540,7 @@ gkd_prompt_finalize (GObject *obj)
 	g_assert (!self->pv->out_data);
 	g_assert (!self->pv->err_data);
 	g_assert (!self->pv->io_tag);
-	g_assert (!self->pv->prime);
-	g_assert (!self->pv->secret);
-	g_assert (!self->pv->key);
+	g_assert (!self->pv->transport);
 
 	g_free (self->pv->executable);
 	self->pv->executable = NULL;
@@ -638,51 +665,37 @@ gkd_prompt_get_response (GkdPrompt *self)
 gchar*
 gkd_prompt_get_password (GkdPrompt *self, const gchar *password_type)
 {
-	gboolean encrypted;
 	gchar *result;
-	guchar *data;
+	gpointer data;
 	gsize n_data;
-	guchar *iv;
-	gsize n_iv;
+	gconstpointer key;
+	gsize n_key;
+	gpointer parameter;
+	gsize n_parameter;
 
 	g_return_val_if_fail (GKD_IS_PROMPT (self), NULL);
-	g_return_val_if_fail (self->pv->output, NULL);
 
-	if (self->pv->failure)
+	if (!gkd_prompt_get_transport_password (self, password_type,
+	                                        &parameter, &n_parameter,
+	                                        &data, &n_data))
 		return NULL;
 
-	g_assert (self->pv->output);
-
-	if (!password_type)
-		password_type = "password";
-
-	encrypted = g_key_file_get_boolean (self->pv->output, password_type, "encrypted", NULL);
-	if (!encrypted)
-		return g_key_file_get_string (self->pv->output, password_type, "value", NULL);
-
 	/* Parse the encryption params and figure out a key */
-	if (!self->pv->key && !receive_transport_crypto (self))
-		g_return_val_if_reached (NULL);
-
-	/* Parse out an IV */
-	iv = gkd_prompt_util_decode_hex (self->pv->output, password_type, "iv", &n_iv);
-	if (iv == NULL) {
-		g_warning ("prompt response has encrypted password, but no iv set");
-		return NULL;
-	}
-
-	/* Parse out the password */
-	data = gkd_prompt_util_decode_hex (self->pv->output, password_type, "value", &n_data);
-	if (data == NULL) {
-		g_warning ("prompt response missing encrypted password value");
-		g_free (iv);
-		return NULL;
+	if (n_parameter) {
+		key = calculate_transport_key (self, &n_key);
+		g_return_val_if_fail (key, NULL);
+		result = gkd_prompt_util_decrypt_text (key, n_key,
+		                                       parameter, n_parameter,
+		                                       data, n_data);
+
+	/* A non-encrypted password */
+	} else {
+		result = egg_secure_alloc (n_data + 1);
+		memcpy (result, data, n_data);
 	}
 
-	result = gkd_prompt_util_decrypt_text (self->pv->key, self->pv->n_key, iv, n_iv, data, n_data);
+	g_free (parameter);
 	g_free (data);
-	g_free (iv);
-
 	return result;
 }
 
@@ -733,6 +746,65 @@ gkd_prompt_reset (GkdPrompt *self)
 	self->pv->input = g_key_file_new ();
 }
 
+
+void
+gkd_prompt_set_transport_param (GkdPrompt *self, const gchar *name,
+                                gconstpointer value, gsize n_value)
+{
+	g_return_if_fail (GKD_IS_PROMPT (self));
+	g_return_if_fail (self->pv->input);
+	g_return_if_fail (name);
+	gkd_prompt_util_encode_hex (self->pv->input, "transport", name, value, n_value);
+}
+
+gpointer
+gkd_prompt_get_transport_param (GkdPrompt *self, const gchar *name, gsize *n_value)
+{
+	g_return_val_if_fail (GKD_IS_PROMPT (self), NULL);
+	g_return_val_if_fail (name, NULL);
+	g_return_val_if_fail (*n_value, NULL);
+
+	if (self->pv->failure)
+		return NULL;
+
+	g_return_val_if_fail (self->pv->output, NULL);
+	return gkd_prompt_util_decode_hex (self->pv->output, "transport", name, n_value);
+
+}
+
+gboolean
+gkd_prompt_get_transport_password (GkdPrompt *self, const gchar *password_type,
+                                   gpointer *parameter, gsize *n_parameter,
+                                   gpointer *value, gsize *n_value)
+{
+	if (!password_type)
+		password_type = "password";
+
+	g_return_val_if_fail (parameter, FALSE);
+	g_return_val_if_fail (n_parameter, FALSE);
+	g_return_val_if_fail (value, FALSE);
+	g_return_val_if_fail (n_value, FALSE);
+
+	if (self->pv->failure)
+		return FALSE;
+
+	g_return_val_if_fail (self->pv->output, FALSE);
+
+	/* Parse out an IV */
+	*parameter = gkd_prompt_util_decode_hex (self->pv->output, password_type,
+	                                         "parameter", n_parameter);
+	if (*parameter == NULL)
+		*n_parameter = 0;
+
+	/* Parse out the password */
+	*value = gkd_prompt_util_decode_hex (self->pv->output, password_type,
+	                                     "value", n_value);
+	if (*value == NULL)
+		*n_value = 0;
+
+	return TRUE;
+}
+
 /* ----------------------------------------------------------------------------------
  * ATTENTION QUEUES
  */
@@ -925,4 +997,3 @@ gkd_prompt_request_attention_async (const gchar *window_id, GkdPromptAttentionFu
 
 	g_timeout_add (0, service_attention_req, att);
 }
-
diff --git a/daemon/prompt/gkd-prompt.h b/daemon/prompt/gkd-prompt.h
index 2f5265f..d3917c4 100644
--- a/daemon/prompt/gkd-prompt.h
+++ b/daemon/prompt/gkd-prompt.h
@@ -50,6 +50,8 @@ struct _GkdPrompt {
 
 struct _GkdPromptClass {
 	GObjectClass parent_class;
+
+	/* signals */
 	gboolean (*responded) (GkdPrompt *self);
 	void (*completed) (GkdPrompt *self);
 };
@@ -89,6 +91,22 @@ gint                gkd_prompt_get_response           (GkdPrompt *prompt);
 gchar*              gkd_prompt_get_password           (GkdPrompt *prompt,
                                                        const gchar *password_type);
 
+gpointer            gkd_prompt_get_transport_param    (GkdPrompt *prompt,
+                                                       const gchar *name,
+                                                       gsize *n_value);
+
+void                gkd_prompt_set_transport_param    (GkdPrompt *prompt,
+                                                       const gchar *name,
+                                                       gconstpointer value,
+                                                       gsize n_value);
+
+gboolean            gkd_prompt_get_transport_password (GkdPrompt *self,
+                                                       const gchar *password_type,
+                                                       gpointer *parameter,
+                                                       gsize *n_parameter,
+                                                       gpointer *value,
+                                                       gsize *n_value);
+
 gboolean            gkd_prompt_is_widget_selected     (GkdPrompt *prompt,
                                                        const gchar *widget);
 



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