[gnome-keyring] [daemon] Rework control, and implement login keyring support.



commit e206707ce7d57ee60cd950e2e667bbb0ec2064ed
Author: Stef Walter <stef memberwebs com>
Date:   Thu Dec 17 03:32:53 2009 +0000

    [daemon] Rework control, and implement login keyring support.
    
    Implement support for unlocking and changing and storing secrets
    in the login keyring, based on the secret-store.
    
    Refactor how the control protocol hangs out, and implement some
    simple test utilities for it.

 configure.in                               |    2 +
 daemon/Makefile.am                         |    2 +
 daemon/control/Makefile.am                 |   17 +-
 daemon/control/gkd-control-client.c        |  280 ++++++++++++
 daemon/control/gkd-control-private.h       |   71 +++
 daemon/control/gkd-control-server.c        |  414 +++++++++++++++++
 daemon/control/gkd-control.c               |  649 --------------------------
 daemon/control/gkd-control.h               |   10 +-
 daemon/control/tests/Makefile.am           |   37 ++
 daemon/control/tests/test-control-change   |  Bin 0 -> 96222 bytes
 daemon/control/tests/test-control-change.c |   30 ++
 daemon/control/tests/test-control-unlock   |  Bin 0 -> 96149 bytes
 daemon/control/tests/test-control-unlock.c |   24 +
 daemon/gkd-main.c                          |   22 +-
 daemon/gkd-util.c                          |   10 +-
 daemon/login/Makefile.am                   |   24 +
 daemon/login/gkd-login.c                   |  678 ++++++++++++++++++++++++++++
 daemon/login/gkd-login.h                   |   50 ++
 daemon/pkcs11/gkr-pkcs11-auth.c            |   91 ++---
 daemon/pkcs11/gkr-pkcs11-daemon.c          |   16 +-
 daemon/pkcs11/gkr-pkcs11-daemon.h          |    2 +
 tests/gtest-helpers.c                      |   16 +-
 tests/prep-gtest.sh                        |   10 +
 23 files changed, 1712 insertions(+), 743 deletions(-)
---
diff --git a/configure.in b/configure.in
index 0121c4e..afaa402 100644
--- a/configure.in
+++ b/configure.in
@@ -551,10 +551,12 @@ Makefile
 daemon/Makefile
 daemon/gnome-keyring-daemon.desktop.in
 daemon/control/Makefile
+daemon/control/tests/Makefile
 daemon/data/Makefile
 daemon/dbus/Makefile
 daemon/keyrings/Makefile
 daemon/keyrings/tests/Makefile
+daemon/login/Makefile
 daemon/pkcs11/Makefile
 daemon/prompt/Makefile
 daemon/prompt/tests/Makefile
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index cc818f0..a0ce816 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -3,6 +3,7 @@ SUBDIRS = \
 	ui \
 	prompt \
 	keyrings \
+	login \
 	control \
 	pkcs11 \
 	dbus \
@@ -35,6 +36,7 @@ gnome_keyring_daemon_LDADD = \
 	$(top_builddir)/daemon/pkcs11/libgkr-pkcs11.la \
 	$(top_builddir)/daemon/dbus/libgkr-dbus.la \
 	$(top_builddir)/daemon/keyrings/libgkr-keyrings.la \
+	$(top_builddir)/daemon/login/libgkd-login.la \
 	$(top_builddir)/daemon/ui/libgkr-ui.la \
 	$(top_builddir)/daemon/control/libgkd-control.la \
 	$(top_builddir)/daemon/prompt/libgkd-prompt.la \
diff --git a/daemon/control/Makefile.am b/daemon/control/Makefile.am
index e189c5a..2fdcf78 100644
--- a/daemon/control/Makefile.am
+++ b/daemon/control/Makefile.am
@@ -1,3 +1,10 @@
+if WITH_TESTS
+TESTS_DIR = tests
+else
+TESTS_DIR =
+endif
+
+SUBDIRS = . $(TESTS_DIR)
 
 INCLUDES= \
 	-DPREFIX=\""$(prefix)"\" \
@@ -12,10 +19,12 @@ INCLUDES= \
 # ------------------------------------------------------------------
 # DAEMON CODE
 
-noinst_LTLIBRARIES = libgkd-control.la
+noinst_LTLIBRARIES = \
+	libgkd-control.la \
+	libgkd-control-client.la
 
 libgkd_control_la_SOURCES = \
-	gkd-control.c gkd-control.h
+	gkd-control-server.c gkd-control-client.c gkd-control.h
 
-libgkd_control_la_LIBADD = \
-	$(GLIB_LIBS)
+libgkd_control_client_la_SOURCES = \
+	gkd-control-client.c gkd-control.h
diff --git a/daemon/control/gkd-control-client.c b/daemon/control/gkd-control-client.c
new file mode 100644
index 0000000..dedf6e0
--- /dev/null
+++ b/daemon/control/gkd-control-client.c
@@ -0,0 +1,280 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+
+#include "gkd-control.h"
+#include "gkd-control-private.h"
+
+#include "egg/egg-buffer.h"
+#include "egg/egg-secure-memory.h"
+#include "egg/egg-unix-credentials.h"
+
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+static int
+control_connect (const gchar *path)
+{
+	struct sockaddr_un addr;
+	struct stat st;
+	int sock;
+
+	/* First a bunch of checks to make sure nothing funny is going on */
+	if (lstat (path, &st) < 0) {
+		g_message ("couldn't access conrol socket: %s: %s", path, g_strerror (errno));
+		return -1;
+
+	} else if (st.st_uid != geteuid ()) {
+		g_message("The control socket is not owned with the same "
+		          "credentials as the user login: %s", path);
+		return -1;
+
+	} else if (S_ISLNK (st.st_mode) || !S_ISSOCK (st.st_mode)) {
+		g_message ("The control socket is not a valid simple non-linked socket");
+		return -1;
+	}
+
+	addr.sun_family = AF_UNIX;
+	g_strlcpy (addr.sun_path, path, sizeof (addr.sun_path));
+
+	/* Now we connect */
+	sock = socket (AF_UNIX, SOCK_STREAM, 0);
+	if (sock < 0) {
+		g_warning ("couldn't create control socket: %s", g_strerror (errno));
+		return -1;
+	}
+
+	/* Close on exec */
+	fcntl (sock, F_SETFD, 1);
+
+	if (connect (sock, (struct sockaddr*) &addr, sizeof (addr)) < 0) {
+		g_message ("couldn't connect to control socket at: %s: %s",
+		           addr.sun_path, g_strerror (errno));
+		close (sock);
+		return -1;
+	}
+
+	/* This lets the server verify us */
+	for (;;) {
+		if (egg_unix_credentials_write (sock) < 0) {
+			if (errno == EINTR || errno == EAGAIN)
+				continue;
+			g_message ("couldn't send credentials to control socket: %s",
+			           g_strerror (errno));
+			close (sock);
+			return -1;
+		}
+
+		return sock;
+	}
+}
+
+static gboolean
+control_write (int fd, EggBuffer *buf)
+{
+	gsize bytes = 0;
+	gssize res;
+
+	while (bytes < buf->len) {
+		res = write (fd, buf->buf + bytes, buf->len - bytes);
+		if (res < 0) {
+			if (errno != EINTR && errno != EAGAIN) {
+				g_warning ("couldn't write all bytes to control socket: %s",
+				           g_strerror (errno));
+				return FALSE;
+			}
+		} else {
+			bytes += res;
+		}
+	}
+
+	return TRUE;
+}
+
+static gsize
+control_read_raw (int fd, guchar *buf, size_t len)
+{
+	gsize bytes;
+	gssize res;
+
+	bytes = 0;
+	while (bytes < len) {
+		res = read (fd, buf + bytes, len - bytes);
+		if (res <= 0) {
+			if (res == 0)
+				res = -1;
+			else if (errno == EAGAIN)
+				continue;
+			else
+				g_warning ("couldn't read %u bytes from control socket: %s",
+					   (unsigned int)len, g_strerror (errno));
+			return res;
+		}
+		bytes += res;
+	}
+	return bytes;
+}
+
+
+static gboolean
+control_read (int fd, EggBuffer *buffer)
+{
+	guint32 packet_size;
+
+	egg_buffer_resize (buffer, 4);
+	if (control_read_raw (fd, buffer->buf, 4) != 4)
+		return FALSE;
+
+	if (!egg_buffer_get_uint32 (buffer, 0, NULL, &packet_size) ||
+	    packet_size < 4)
+		return FALSE;
+
+	egg_buffer_resize (buffer, packet_size);
+	if (control_read_raw (fd, buffer->buf + 4, packet_size - 4) != packet_size - 4)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+control_chat (const gchar *directory, EggBuffer *buffer)
+{
+	gboolean ret;
+	gchar *path;
+	int sock;
+
+	path = g_strdup_printf ("%s/control", directory);
+	sock = control_connect (path);
+	g_free (path);
+
+	if (sock < 0)
+		return FALSE;
+
+	ret = control_write (sock, buffer) && control_read (sock, buffer);
+	close (sock);
+	return ret;
+}
+
+
+gchar**
+gkd_control_initialize (const gchar *directory, const gchar **envp)
+{
+	gchar **env = NULL;
+	EggBuffer buffer;
+	gsize offset = 4;
+	gboolean ret;
+	guint32 res;
+
+	egg_buffer_init_full (&buffer, 128, g_realloc);
+	egg_buffer_add_uint32 (&buffer, 0);
+	egg_buffer_add_uint32 (&buffer, GNOME_KEYRING_OP_PREPARE_ENVIRONMENT);
+	egg_buffer_add_stringv (&buffer, (const char**)envp);
+	egg_buffer_set_uint32 (&buffer, 0, buffer.len);
+
+	g_return_val_if_fail (!egg_buffer_has_error (&buffer), FALSE);
+
+	ret = control_chat (directory, &buffer);
+
+	if (ret)
+		ret = egg_buffer_get_uint32 (&buffer, offset, &offset, &res);
+	if (ret && res == GNOME_KEYRING_RESULT_OK)
+	      ret = egg_buffer_get_stringv (&buffer, offset, &offset, &env, g_realloc);
+
+	egg_buffer_uninit (&buffer);
+
+	if (!ret || res != GNOME_KEYRING_RESULT_OK) {
+		g_message ("couldn't initialize running daemon");
+		return NULL;
+	}
+
+	return env;
+}
+
+gboolean
+gkd_control_unlock (const gchar *directory, const gchar *password)
+{
+	EggBuffer buffer;
+	gsize offset = 4;
+	gboolean ret;
+	guint32 res;
+
+	egg_buffer_init_full (&buffer, 128, egg_secure_realloc);
+	egg_buffer_add_uint32 (&buffer, 0);
+	egg_buffer_add_uint32 (&buffer, GNOME_KEYRING_OP_UNLOCK_KEYRING);
+	egg_buffer_add_string (&buffer, "login");
+	egg_buffer_add_string (&buffer, password);
+	egg_buffer_set_uint32 (&buffer, 0, buffer.len);
+
+	g_return_val_if_fail (!egg_buffer_has_error (&buffer), FALSE);
+
+	ret = control_chat (directory, &buffer);
+
+	if (ret)
+		ret = egg_buffer_get_uint32 (&buffer, offset, &offset, &res);
+
+	egg_buffer_uninit (&buffer);
+
+	if (!ret || res != GNOME_KEYRING_RESULT_OK) {
+		g_message ("couldn't unlock login keyring");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+gboolean
+gkd_control_change_lock (const gchar *directory, const gchar *original,
+                         const gchar *password)
+{
+	EggBuffer buffer;
+	gsize offset = 4;
+	gboolean ret;
+	guint32 res;
+
+	egg_buffer_init_full (&buffer, 128, egg_secure_realloc);
+	egg_buffer_add_uint32 (&buffer, 0);
+	egg_buffer_add_uint32 (&buffer, GNOME_KEYRING_OP_CHANGE_KEYRING_PASSWORD);
+	egg_buffer_add_string (&buffer, "login");
+	egg_buffer_add_string (&buffer, original);
+	egg_buffer_add_string (&buffer, password);
+	egg_buffer_set_uint32 (&buffer, 0, buffer.len);
+
+	g_return_val_if_fail (!egg_buffer_has_error (&buffer), FALSE);
+
+	ret = control_chat (directory, &buffer);
+
+	if (ret)
+		ret = egg_buffer_get_uint32 (&buffer, offset, &offset, &res);
+
+	egg_buffer_uninit (&buffer);
+
+	if (!ret || res != GNOME_KEYRING_RESULT_OK) {
+		g_message ("couldn't change lock on login keyring");
+		return FALSE;
+	}
+
+	return TRUE;
+}
diff --git a/daemon/control/gkd-control-private.h b/daemon/control/gkd-control-private.h
new file mode 100644
index 0000000..a982c14
--- /dev/null
+++ b/daemon/control/gkd-control-private.h
@@ -0,0 +1,71 @@
+/*
+ * 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_CONTROL_PRIVATE_H__
+#define __GKD_CONTROL_PRIVATE_H__
+
+/* All the old op codes, most are no longer used */
+enum {
+	GNOME_KEYRING_OP_LOCK_ALL,
+	GNOME_KEYRING_OP_SET_DEFAULT_KEYRING,
+	GNOME_KEYRING_OP_GET_DEFAULT_KEYRING,
+	GNOME_KEYRING_OP_LIST_KEYRINGS,
+	GNOME_KEYRING_OP_CREATE_KEYRING,
+	GNOME_KEYRING_OP_LOCK_KEYRING,
+	GNOME_KEYRING_OP_UNLOCK_KEYRING,
+	GNOME_KEYRING_OP_DELETE_KEYRING,
+	GNOME_KEYRING_OP_GET_KEYRING_INFO,
+	GNOME_KEYRING_OP_SET_KEYRING_INFO,
+	GNOME_KEYRING_OP_LIST_ITEMS,
+	GNOME_KEYRING_OP_FIND,
+	GNOME_KEYRING_OP_CREATE_ITEM,
+	GNOME_KEYRING_OP_DELETE_ITEM,
+	GNOME_KEYRING_OP_GET_ITEM_INFO,
+	GNOME_KEYRING_OP_SET_ITEM_INFO,
+	GNOME_KEYRING_OP_GET_ITEM_ATTRIBUTES,
+	GNOME_KEYRING_OP_SET_ITEM_ATTRIBUTES,
+	GNOME_KEYRING_OP_GET_ITEM_ACL,
+	GNOME_KEYRING_OP_SET_ITEM_ACL,
+	GNOME_KEYRING_OP_CHANGE_KEYRING_PASSWORD,
+	GNOME_KEYRING_OP_SET_DAEMON_DISPLAY,
+	GNOME_KEYRING_OP_GET_ITEM_INFO_FULL,
+	GNOME_KEYRING_OP_PREPARE_ENVIRONMENT,
+
+	/* Add new ops here */
+
+	GNOME_KEYRING_NUM_OPS
+};
+
+/* All the old result codes */
+enum {
+	GNOME_KEYRING_RESULT_OK,
+	GNOME_KEYRING_RESULT_DENIED,
+	GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON,
+	GNOME_KEYRING_RESULT_ALREADY_UNLOCKED,
+	GNOME_KEYRING_RESULT_NO_SUCH_KEYRING,
+	GNOME_KEYRING_RESULT_BAD_ARGUMENTS,
+	GNOME_KEYRING_RESULT_IO_ERROR,
+	GNOME_KEYRING_RESULT_CANCELLED,
+	GNOME_KEYRING_RESULT_KEYRING_ALREADY_EXISTS,
+	GNOME_KEYRING_RESULT_NO_MATCH
+};
+
+#endif /* __GKD_CONTROL_PRIVATE_H__ */
diff --git a/daemon/control/gkd-control-server.c b/daemon/control/gkd-control-server.c
new file mode 100644
index 0000000..87a50d7
--- /dev/null
+++ b/daemon/control/gkd-control-server.c
@@ -0,0 +1,414 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+
+#include "gkd-control.h"
+#include "gkd-control-private.h"
+
+#include "gkd-main.h"
+#include "gkd-util.h"
+
+#include "egg/egg-buffer.h"
+#include "egg/egg-cleanup.h"
+#include "egg/egg-secure-memory.h"
+#include "egg/egg-unix-credentials.h"
+
+#include "login/gkd-login.h"
+
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+typedef struct _ControlData {
+	EggBuffer buffer;
+	gsize position;
+} ControlData;
+
+/* -----------------------------------------------------------------------------------
+ * CONTROL SERVER
+ */
+
+static ControlData*
+control_data_new (void)
+{
+	ControlData *cdata = g_slice_new0 (ControlData);
+	egg_buffer_init_full (&cdata->buffer, 128, egg_secure_realloc);
+	cdata->position = 0;
+	return cdata;
+}
+
+static void
+control_data_free (gpointer data)
+{
+	ControlData *cdata = data;
+	egg_buffer_uninit (&cdata->buffer);
+	g_slice_free (ControlData, cdata);
+}
+
+static guint32
+control_unlock_keyring (EggBuffer *buffer)
+{
+	gchar *name;
+	gchar *master;
+	gsize offset = 8;
+	guint32 res;
+
+	if (!egg_buffer_get_string (buffer, offset, &offset, &name, g_realloc))
+		return GNOME_KEYRING_RESULT_BAD_ARGUMENTS;
+
+	if (!egg_buffer_get_string (buffer, offset, &offset, &master, egg_secure_realloc)) {
+		g_free (name);
+		return GNOME_KEYRING_RESULT_BAD_ARGUMENTS;
+	}
+
+	if (!name || g_str_equal (name, "login")) {
+		if (gkd_login_unlock (master))
+			res = GNOME_KEYRING_RESULT_OK;
+		else
+			res = GNOME_KEYRING_RESULT_DENIED;
+	} else {
+		g_message ("keyring request not supported");
+		res = GNOME_KEYRING_RESULT_NO_SUCH_KEYRING;
+	}
+
+	egg_secure_strfree (master);
+	g_free (name);
+	return res;
+}
+
+static guint32
+control_change_keyring_password (EggBuffer *buffer)
+{
+	gsize offset = 8;
+	guint32 res;
+	gchar *name;
+	gchar *master;
+	gchar *original;
+
+	if (!egg_buffer_get_string (buffer, offset, &offset, &name, g_realloc))
+		return GNOME_KEYRING_RESULT_BAD_ARGUMENTS;
+
+	if (!egg_buffer_get_string (buffer, offset, &offset, &original, egg_secure_realloc)) {
+		g_free (name);
+		return GNOME_KEYRING_RESULT_BAD_ARGUMENTS;
+	}
+
+	if (!egg_buffer_get_string (buffer, offset, &offset, &master, egg_secure_realloc)) {
+		egg_secure_strfree (original);
+		g_free (name);
+		return GNOME_KEYRING_RESULT_BAD_ARGUMENTS;
+	}
+
+	if (!name || g_str_equal (name, "login")) {
+		if (gkd_login_change_lock (original, master))
+			res = GNOME_KEYRING_RESULT_OK;
+		else
+			res = GNOME_KEYRING_RESULT_DENIED;
+	} else {
+		g_message ("keyring request not supported");
+		res = GNOME_KEYRING_RESULT_NO_SUCH_KEYRING;
+	}
+
+	egg_secure_strfree (master);
+	egg_secure_strfree (original);
+	g_free (name);
+	return res;
+}
+
+static guint32
+control_prepare_environment (EggBuffer *buffer)
+{
+	gchar **environment, **e;
+	gsize offset = 8;
+	gchar *x;
+	int i;
+
+	if (!egg_buffer_get_stringv (buffer, offset, &offset, &environment, g_realloc))
+		return GNOME_KEYRING_RESULT_BAD_ARGUMENTS;
+
+	/* Accept environment from outside */
+	for (e = environment; *e; ++e) {
+		x = strchr (*e, '=');
+		if (x) {
+			*(x++) = 0;
+			/* We're only interested in these environment variables */
+			for (i = 0; GKD_UTIL_IN_ENVIRONMENT[i] != NULL; ++i) {
+				if (g_str_equal (*e, GKD_UTIL_IN_ENVIRONMENT[i])) {
+					g_setenv (*e, x, FALSE);
+					break;
+				}
+			}
+		}
+	}
+
+	g_strfreev (environment);
+
+	/*
+	 * We've now definitely received everything we need to run. Ask
+	 * the daemon to complete the initialization.
+	 */
+	gkd_main_complete_initialization ();
+	return GNOME_KEYRING_RESULT_OK;
+}
+
+static gboolean
+control_output (GIOChannel *channel, GIOCondition cond, gpointer user_data)
+{
+	ControlData *cdata = user_data;
+	EggBuffer *buffer = &cdata->buffer;
+	int fd, res;
+
+	fd = g_io_channel_unix_get_fd (channel);
+	g_assert (cdata->position < buffer->len);
+
+	if (cond & G_IO_OUT) {
+		res = write (fd, buffer->buf + cdata->position, buffer->len - cdata->position);
+		if (res <= 0) {
+			if (errno != EAGAIN && errno != EINTR)
+				cdata->position = buffer->len;
+		} else {
+			cdata->position += res;
+			g_assert (cdata->position <= buffer->len);
+		}
+	}
+
+	if (cdata->position == buffer->len)
+		cond |= G_IO_HUP;
+
+	return (cond & G_IO_HUP) == 0;
+}
+
+static void
+control_process (EggBuffer *req, GIOChannel *channel)
+{
+	ControlData *cdata = NULL;
+	guint32 res;
+	guint32 op;
+
+	if (!egg_buffer_get_uint32 (req, 4, NULL, &op)) {
+		g_message ("invalid operation sent to control socket");
+		return;
+	}
+
+	switch (op) {
+	case GNOME_KEYRING_OP_CREATE_KEYRING:
+	case GNOME_KEYRING_OP_UNLOCK_KEYRING:
+		res = control_unlock_keyring (req);
+		cdata = control_data_new ();
+		egg_buffer_add_uint32 (&cdata->buffer, 0);
+		egg_buffer_add_uint32 (&cdata->buffer, res);
+		break;
+	case GNOME_KEYRING_OP_CHANGE_KEYRING_PASSWORD:
+		res = control_change_keyring_password (req);
+		cdata = control_data_new ();
+		egg_buffer_add_uint32 (&cdata->buffer, 0);
+		egg_buffer_add_uint32 (&cdata->buffer, res);
+		break;
+	case GNOME_KEYRING_OP_PREPARE_ENVIRONMENT:
+		res = control_prepare_environment (req);
+		cdata = control_data_new ();
+		egg_buffer_add_uint32 (&cdata->buffer, 0);
+		egg_buffer_add_uint32 (&cdata->buffer, res);
+		egg_buffer_add_stringv (&cdata->buffer, gkd_util_get_environment ());
+		break;
+	default:
+		g_message ("received unsupported request operation on control socket: %d", (int)op);
+		break;
+	}
+
+	if (cdata) {
+		g_return_if_fail (!egg_buffer_has_error (&cdata->buffer));
+		egg_buffer_set_uint32 (&cdata->buffer, 0, cdata->buffer.len);
+		g_io_add_watch_full (channel, G_PRIORITY_DEFAULT, G_IO_OUT | G_IO_HUP,
+		                     control_output, cdata, control_data_free);
+	}
+}
+
+static gboolean
+control_input (GIOChannel *channel, GIOCondition cond, gpointer user_data)
+{
+	ControlData *cdata = user_data;
+	EggBuffer *buffer = &cdata->buffer;
+	guint32 packet_size = 0;
+	gboolean finished = FALSE;
+	int fd, res;
+	pid_t pid;
+	uid_t uid;
+
+	fd = g_io_channel_unix_get_fd (channel);
+
+	if (cond & G_IO_IN) {
+
+		/* Time for reading credentials */
+		if (cdata->position == 0) {
+			if (egg_unix_credentials_read (fd, &pid, &uid) < 0) {
+				if (errno != EAGAIN || errno != EINTR)
+					finished = TRUE;
+			} else if (getuid () != uid) {
+				g_message ("control request from bad uid: %u, should be %u\n", uid, getuid ());
+				finished = TRUE;
+			} else {
+				cdata->position = 1;
+			}
+
+		/* Time for reading a packet size */
+		} else if (egg_buffer_length (buffer) < 4) {
+			egg_buffer_reserve (buffer, 4);
+			res = read (fd, buffer->buf + buffer->len, 4 - buffer->len);
+			if (res <= 0) {
+				if (errno != EAGAIN || errno != EINTR)
+					finished = TRUE;
+			} else {
+				buffer->len += res;
+			}
+
+		/* Time for reading the packet */
+		} else {
+			if (!egg_buffer_get_uint32 (buffer, 0, NULL, &packet_size) || packet_size < 4) {
+				g_message ("invalid packet size in control request");
+				finished = TRUE;
+			} else {
+				g_assert (buffer->len < packet_size);
+				egg_buffer_reserve (buffer, packet_size);
+				res = read (fd, buffer->buf + buffer->len, packet_size - buffer->len);
+				if (res <= 0) {
+					if (errno != EAGAIN && errno != EINTR)
+						finished = TRUE;
+				} else {
+					buffer->len += res;
+					g_assert (buffer->len <= packet_size);
+				}
+			}
+		}
+
+		/* Received a full packet, process */
+		if (packet_size && buffer->len == packet_size) {
+			control_process (buffer, channel);
+			finished = TRUE;
+		}
+	}
+
+	if (finished)
+		cond |= G_IO_HUP;
+
+	return (cond & G_IO_HUP) == 0;
+}
+
+static gboolean
+control_accept (GIOChannel *channel, GIOCondition cond, gpointer callback_data)
+{
+	struct sockaddr_un addr;
+	socklen_t addrlen;
+	ControlData *cdata;
+	GIOChannel *new_channel;
+	int fd, new_fd;
+	int val;
+
+	fd = g_io_channel_unix_get_fd (channel);
+
+	addrlen = sizeof (addr);
+	new_fd = accept (fd, (struct sockaddr *) &addr, &addrlen);
+	if (new_fd < 0) {
+		g_warning ("couldn't accept new control request: %s", g_strerror (errno));
+		return TRUE;
+	}
+
+	val = fcntl (new_fd, F_GETFL, 0);
+	if (val < 0) {
+		g_warning ("can't get control request fd flags: %s", g_strerror (errno));
+		close (new_fd);
+		return TRUE;
+	}
+
+	if (fcntl (new_fd, F_SETFL, val | O_NONBLOCK) < 0) {
+		g_warning ("can't set control request to non-blocking io: %s", g_strerror (errno));
+		close (new_fd);
+		return TRUE;
+	}
+
+	cdata = control_data_new ();
+	new_channel = g_io_channel_unix_new (new_fd);
+	g_io_channel_set_close_on_unref (new_channel, TRUE);
+	g_io_add_watch_full (new_channel, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP,
+	                     control_input, cdata, control_data_free);
+	g_io_channel_unref (new_channel);
+
+	return TRUE;
+}
+
+static void
+control_cleanup_channel (gpointer user_data)
+{
+	gchar *path = user_data;
+	unlink (path);
+	g_free (path);
+}
+
+gboolean
+gkd_control_listen (void)
+{
+	struct sockaddr_un addr;
+	GIOChannel *channel;
+	gchar *path;
+	int sock;
+
+	path = g_strdup_printf ("%s/control", gkd_util_get_master_directory ());
+	egg_cleanup_register (control_cleanup_channel, path);
+
+	unlink (path);
+
+	sock = socket (AF_UNIX, SOCK_STREAM, 0);
+	if (sock < 0) {
+		g_warning ("couldn't open socket: %s", g_strerror (errno));
+		return FALSE;
+	}
+
+	memset (&addr, 0, sizeof (addr));
+	addr.sun_family = AF_UNIX;
+	g_strlcpy (addr.sun_path, path, sizeof (addr.sun_path));
+	if (bind (sock, (struct sockaddr*) &addr, sizeof (addr)) < 0) {
+		g_warning ("couldn't bind to control socket: %s: %s", path, g_strerror (errno));
+		close (sock);
+		return FALSE;
+	}
+
+	if (listen (sock, 128) < 0) {
+		g_warning ("couldn't listen on control socket: %s: %s", path, g_strerror (errno));
+		close (sock);
+		return FALSE;
+	}
+
+	if (!egg_unix_credentials_setup (sock) < 0) {
+		close (sock);
+		return FALSE;
+	}
+
+	channel = g_io_channel_unix_new (sock);
+	g_io_add_watch (channel, G_IO_IN | G_IO_HUP, control_accept, NULL);
+	g_io_channel_set_close_on_unref (channel, TRUE);
+	egg_cleanup_register ((GDestroyNotify)g_io_channel_unref, channel);
+
+	return TRUE;
+}
diff --git a/daemon/control/gkd-control.h b/daemon/control/gkd-control.h
index 84249db..796dd6b 100644
--- a/daemon/control/gkd-control.h
+++ b/daemon/control/gkd-control.h
@@ -26,6 +26,14 @@
 
 gboolean          gkd_control_listen        (void);
 
-gboolean          gkd_control_initialize    (const gchar *directory);
+gchar**           gkd_control_initialize    (const gchar *directory,
+                                             const gchar **env);
+
+gboolean          gkd_control_unlock        (const gchar *directory,
+                                             const gchar *password);
+
+gboolean          gkd_control_change_lock   (const gchar *directory,
+                                             const gchar *original,
+                                             const gchar *password);
 
 #endif /* __GKD_CONTROL_H__ */
diff --git a/daemon/control/tests/Makefile.am b/daemon/control/tests/Makefile.am
new file mode 100644
index 0000000..36f9e24
--- /dev/null
+++ b/daemon/control/tests/Makefile.am
@@ -0,0 +1,37 @@
+INCLUDES= \
+	-I$(top_srcdir) \
+	-I$(top_builddir) \
+	-I$(top_srcdir)/daemon \
+	$(GTK_CFLAGS) \
+	$(GLIB_CFLAGS)
+
+LIBS = \
+	$(GLIB_LIBS) \
+	$(GTK_LIBS) \
+	$(GTHREAD_LIBS) \
+	$(P11_TESTS_LIBS)
+
+noinst_PROGRAMS= \
+	test-control-unlock \
+	test-control-change
+
+# ------------------------------------------------------------------------------
+# Test unlocking the login keyring
+
+test_control_unlock_SOURCES = \
+	test-control-unlock.c
+
+test_control_unlock_LDADD = \
+	$(top_builddir)/daemon/control/libgkd-control-client.la \
+	$(top_builddir)/egg/libegg-buffer.la \
+	$(top_builddir)/egg/libegg-creds.la \
+	$(top_builddir)/egg/libegg-secure.la
+
+test_control_change_SOURCES = \
+	test-control-change.c
+
+test_control_change_LDADD = \
+	$(top_builddir)/daemon/control/libgkd-control-client.la \
+	$(top_builddir)/egg/libegg-buffer.la \
+	$(top_builddir)/egg/libegg-creds.la \
+	$(top_builddir)/egg/libegg-secure.la
\ No newline at end of file
diff --git a/daemon/control/tests/test-control-change b/daemon/control/tests/test-control-change
new file mode 100755
index 0000000..e2236f4
Binary files /dev/null and b/daemon/control/tests/test-control-change differ
diff --git a/daemon/control/tests/test-control-change.c b/daemon/control/tests/test-control-change.c
new file mode 100644
index 0000000..ee5d995
--- /dev/null
+++ b/daemon/control/tests/test-control-change.c
@@ -0,0 +1,30 @@
+
+#include "control/gkd-control.h"
+#include "tests/gtest-helpers.h"
+
+#include <pwd.h>
+#include <unistd.h>
+
+static int
+run (void)
+{
+	gchar *original;
+	const char *password;
+	const char *directory;
+
+	directory = g_getenv ("GNOME_KEYRING_CONTROL");
+	g_return_val_if_fail (directory, 1);
+
+	original = g_strdup (getpass ("Original: "));
+	g_return_val_if_fail (original, 1);
+
+	password = getpass ("New Password: ");
+	g_return_val_if_fail (password, 1);
+
+	gkd_control_change_lock (directory, original, password);
+
+	g_free (original);
+	return 0;
+}
+
+#include "tests/gtest-helpers.c"
diff --git a/daemon/control/tests/test-control-unlock b/daemon/control/tests/test-control-unlock
new file mode 100755
index 0000000..d71dd09
Binary files /dev/null and b/daemon/control/tests/test-control-unlock differ
diff --git a/daemon/control/tests/test-control-unlock.c b/daemon/control/tests/test-control-unlock.c
new file mode 100644
index 0000000..d77901b
--- /dev/null
+++ b/daemon/control/tests/test-control-unlock.c
@@ -0,0 +1,24 @@
+
+#include "control/gkd-control.h"
+#include "tests/gtest-helpers.h"
+
+#include <pwd.h>
+#include <unistd.h>
+
+static int
+run (void)
+{
+	const char *password;
+	const char *directory;
+
+	password = getpass ("Unlock: ");
+	g_return_val_if_fail (password, 1);
+
+	directory = g_getenv ("GNOME_KEYRING_CONTROL");
+	g_return_val_if_fail (directory, 1);
+
+	gkd_control_unlock (directory, password);
+	return 0;
+}
+
+#include "tests/gtest-helpers.c"
diff --git a/daemon/gkd-main.c b/daemon/gkd-main.c
index 1d17458..b7d56b1 100644
--- a/daemon/gkd-main.c
+++ b/daemon/gkd-main.c
@@ -36,9 +36,7 @@
 #include "egg/egg-secure-memory.h"
 #include "egg/egg-unix-credentials.h"
 
-#include "keyrings/gkr-keyring-login.h"
-
-#include "library/gnome-keyring.h"
+#include "login/gkd-login.h"
 
 #include "pkcs11/gkr-pkcs11-daemon.h"
 
@@ -527,13 +525,25 @@ print_environment (pid_t pid)
 static gboolean
 start_or_initialize_daemon (const gchar *directory)
 {
+	gchar **ourenv, **daemonenv, **e;
+
 	if (!directory)
 		return FALSE;
 
+	/* Exchange environment variables, and try to initialize daemon */
+	ourenv = gkd_util_build_environment (GKD_UTIL_IN_ENVIRONMENT);
+	daemonenv = gkd_control_initialize (directory, (const gchar**)ourenv);
+	g_strfreev (ourenv);
+
 	/* Initialization failed, start this process up as a daemon */
-	if (!gkd_control_initialize (directory))
+	if (!daemonenv)
 		return FALSE;
 
+	/* Setup all the environment variables we were passed */
+	for (e = daemonenv; *e; ++e)
+		gkd_util_push_environment_full (*e);
+	g_strfreev (daemonenv);
+
 	/*
 	 * Now we've initialized the daemon, we need to print out
 	 * the daemon's environment for any callers, and possibly
@@ -660,7 +670,7 @@ gkr_daemon_initialize_steps (void)
 	 * If it does not exist. We create it.
 	 */
 	if (login_password) {
-		if (!gkr_keyring_login_unlock (login_password))
+		if (!gkd_login_unlock (login_password))
 			g_message ("Failed to unlock login on startup");
 		egg_secure_strclear (login_password);
 	}
@@ -792,7 +802,7 @@ main (int argc, char *argv[])
 	 * If it does not exist. We create it.
 	 */
 	if (login_password) {
-		if (!gkr_keyring_login_unlock (login_password))
+		if (!gkd_login_unlock (login_password))
 			g_message ("Failed to unlock login on startup");
 		egg_secure_strclear (login_password);
 	}
diff --git a/daemon/gkd-util.c b/daemon/gkd-util.c
index 06e4dbf..16a0730 100644
--- a/daemon/gkd-util.c
+++ b/daemon/gkd-util.c
@@ -92,14 +92,16 @@ gkd_util_init_master_directory (const gchar *replace)
 	if (replace) {
 		exists = TRUE;
 		if (lstat (replace, &st) < 0) {
-			if (errno != ENOTDIR && errno != ENOENT)
+			if (errno == ENOTDIR || errno == ENOENT) {
 				exists = FALSE;
+				valid = TRUE;
+			}
 		} else if (st.st_uid != geteuid ()) {
 			g_message ("The gnome-keyring control directory is not owned with the same "
 			           "credentials as the user login: %s", replace);
-		} else if (st.st_mode != (S_IRUSR | S_IWUSR | S_IXUSR)) {
+		} else if ((st.st_mode & 0777) != 0700) {
 			g_message ("The gnome-keyring control directory has invalid permissions. It "
-			           "must be only be accessible by its owner (ie: 0600): %s", replace);
+			           "must be only be accessible by its owner (ie: 0700): %s", replace);
 		} else {
 			valid = TRUE;
 		}
@@ -115,7 +117,7 @@ gkd_util_init_master_directory (const gchar *replace)
 	} else if (!exists) {
 		g_assert (replace);
 		master_directory = g_strdup (replace);
-		if (g_mkdir_with_parents (master_directory, S_IRUSR | S_IWUSR | S_IXUSR) < 0)
+		if (g_mkdir_with_parents (master_directory, 0700) < 0)
 			g_warning ("couldn't create socket directory: %s", g_strerror (errno));
 
 	/* A valid existing directory was supplied */
diff --git a/daemon/login/Makefile.am b/daemon/login/Makefile.am
new file mode 100644
index 0000000..b48f519
--- /dev/null
+++ b/daemon/login/Makefile.am
@@ -0,0 +1,24 @@
+
+INCLUDES= \
+	-DPREFIX=\""$(prefix)"\" \
+	-DBINDIR=\""$(bindir)"\" \
+	-DLIBEXECDIR=\""$(libexecdir)"\" \
+	-DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/daemon \
+	-I$(top_builddir) \
+	$(GOBJECT_CFLAGS) \
+	$(GLIB_CFLAGS)
+
+# ------------------------------------------------------------------
+# DAEMON CODE
+
+noinst_LTLIBRARIES = libgkd-login.la
+
+libgkd_login_la_SOURCES = \
+	gkd-login.c gkd-login.h
+
+libgkd_login_la_LIBADD = \
+	$(top_builddir)/gp11/libgp11.la \
+	$(GOBJECT_LIBS) \
+	$(GLIB_LIBS)
diff --git a/daemon/login/gkd-login.c b/daemon/login/gkd-login.c
new file mode 100644
index 0000000..f327e34
--- /dev/null
+++ b/daemon/login/gkd-login.c
@@ -0,0 +1,678 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+
+#include "gkd-login.h"
+
+#include "egg/egg-secure-memory.h"
+
+#include "pkcs11/gkr-pkcs11-daemon.h"
+#include "pkcs11/pkcs11i.h"
+
+#include <string.h>
+
+static gint unlock_failures = 0;
+
+static void
+note_that_unlock_failed (void)
+{
+	g_atomic_int_inc (&unlock_failures);
+}
+
+static GP11Module*
+module_instance (void)
+{
+	GP11Module *module = gp11_module_new (gkr_pkcs11_daemon_get_base_functions ());
+	gp11_module_set_pool_sessions (module, FALSE);
+	gp11_module_set_auto_authenticate (module, FALSE);
+	g_return_val_if_fail (module, NULL);
+	return module;
+}
+
+static GP11Session*
+open_and_login_session (GP11Slot *slot, CK_USER_TYPE user_type, GError **error)
+{
+	GP11Session *session;
+
+	g_return_val_if_fail (GP11_IS_SLOT (slot), NULL);
+
+	session = gp11_slot_open_session (slot, CKF_RW_SESSION, error);
+	if (session != NULL) {
+		if (!gp11_session_login (session, user_type, NULL, 0, error)) {
+			g_object_unref (session);
+			session = NULL;
+		}
+	}
+
+	return session;
+}
+
+static GP11Session*
+lookup_login_session (GP11Module *module)
+{
+	GP11Slot *slot = NULL;
+	GError *error = NULL;
+	GP11Session *session;
+	GP11SlotInfo *info;
+	GList *slots;
+	GList *l;
+
+	g_assert (GP11_IS_MODULE (module));
+
+	/*
+	 * Find the right slot.
+	 *
+	 * TODO: This isn't necessarily the best way to do this.
+	 * A good function could be added to gp11 library.
+	 * But needs more thought on how to do this.
+	 */
+	slots = gp11_module_get_slots (module, TRUE);
+	for (l = slots; !slot && l; l = g_list_next (l)) {
+		info = gp11_slot_get_info (l->data);
+		if (g_ascii_strcasecmp ("Secret Store", info->slot_description) == 0)
+			slot = g_object_ref (l->data);
+		gp11_slot_info_free (info);
+	}
+	gp11_list_unref_free (slots);
+
+	g_return_val_if_fail (slot, NULL);
+
+	session = open_and_login_session (slot, CKU_USER, &error);
+	if (session == NULL) {
+		g_warning ("couldn't open pkcs11 session for login: %s", error->message);
+		g_clear_error (&error);
+	}
+
+	g_object_unref (slot);
+
+	return session;
+}
+
+static GP11Object*
+lookup_login_keyring (GP11Session *session)
+{
+	GError *error = NULL;
+	GP11Object *login = NULL;
+	GList *objects;
+	guint length;
+
+	g_return_val_if_fail (GP11_IS_SESSION (session), NULL);
+
+	objects = gp11_session_find_objects (session, &error,
+	                                     CKA_CLASS, GP11_ULONG, CKO_G_COLLECTION,
+	                                     CKA_TOKEN, GP11_BOOLEAN, TRUE,
+	                                     CKA_ID, 5, "login",
+	                                     GP11_INVALID);
+
+	if (error) {
+		g_warning ("couldn't search for login keyring: %s", error->message);
+		g_clear_error (&error);
+		return NULL;
+	}
+
+	length = g_list_length (objects);
+	if (length == 1) {
+		login = g_object_ref (objects->data);
+		gp11_object_set_session (login, session);
+	} else if (length > 1) {
+		g_warning ("more than one login keyring exists");
+	}
+
+	gp11_list_unref_free (objects);
+	return login;
+}
+
+static GP11Object*
+create_login_keyring (GP11Session *session, GP11Object *cred, GError **error)
+{
+	GP11Object *login;
+
+	g_return_val_if_fail (GP11_IS_SESSION (session), NULL);
+	g_return_val_if_fail (GP11_IS_OBJECT (cred), NULL);
+
+	login = gp11_session_create_object (session, error,
+	                                    CKA_CLASS, GP11_ULONG, CKO_G_COLLECTION,
+	                                    CKA_ID, 5, "login",
+	                                    CKA_G_CREDENTIAL, GP11_ULONG, gp11_object_get_handle (cred),
+	                                    CKA_TOKEN, GP11_BOOLEAN, TRUE,
+	                                    GP11_INVALID);
+
+	if (login != NULL)
+		gp11_object_set_session (login, session);
+	return login;
+}
+
+static GP11Object*
+create_credential (GP11Session *session, GP11Object *object,
+                   const gchar *secret, GError **error)
+{
+	GP11Attributes *attrs;
+	GP11Object *cred;
+
+	g_return_val_if_fail (GP11_IS_SESSION (session), NULL);
+	g_return_val_if_fail (!object || GP11_IS_OBJECT (object), NULL);
+
+	if (!secret)
+		secret = "";
+
+	attrs = gp11_attributes_newv (CKA_CLASS, GP11_ULONG, CKO_G_CREDENTIAL,
+	                              CKA_VALUE, strlen (secret), secret,
+	                              CKA_GNOME_TRANSIENT, GP11_BOOLEAN, TRUE,
+	                              CKA_TOKEN, GP11_BOOLEAN, TRUE,
+	                              GP11_INVALID);
+
+	if (object)
+		gp11_attributes_add_ulong (attrs, CKA_G_OBJECT,
+		                           gp11_object_get_handle (object));
+
+	cred = gp11_session_create_object_full (session, attrs, NULL, error);
+	gp11_attributes_unref (attrs);
+
+	if (cred != NULL)
+		gp11_object_set_session (cred, session);
+
+	return cred;
+}
+
+static gboolean
+unlock_or_create_login (GP11Module *module, const gchar *master)
+{
+	GError *error = NULL;
+	GP11Session *session;
+	GP11Object *login;
+	GP11Object *cred;
+
+	g_return_val_if_fail (GP11_IS_MODULE (module), FALSE);
+	g_return_val_if_fail (master, FALSE);
+
+	/* Find the login object */
+	session = lookup_login_session (module);
+	login = lookup_login_keyring (session);
+
+	/* Create credentials for login object */
+	cred = create_credential (session, login, master, &error);
+
+	/* Failure, bad password? */
+	if (cred == NULL) {
+		if (login && error->code == CKR_PIN_INCORRECT)
+			note_that_unlock_failed ();
+		else
+			g_warning ("couldn't create login credential: %s", error->message);
+		g_clear_error (&error);
+
+	/* Non login keyring, create it */
+	} else if (!login) {
+		login = create_login_keyring (session, cred, &error);
+		if (login == NULL) {
+			g_warning ("couldn't create login keyring: %s", error->message);
+			g_clear_error (&error);
+		}
+	}
+
+	if (cred)
+		g_object_unref (cred);
+	if (login)
+		g_object_unref (login);
+	if (session)
+		g_object_unref (session);
+
+	return cred && login;
+}
+
+static gboolean
+init_pin_for_uninitialized_slots (GP11Module *module, const gchar *master)
+{
+	GError *error = NULL;
+	GList *slots, *l;
+	gboolean initialize;
+	GP11TokenInfo *info;
+	GP11Session *session;
+
+	g_return_val_if_fail (GP11_IS_MODULE (module), FALSE);
+	g_return_val_if_fail (master, FALSE);
+
+	slots = gp11_module_get_slots (module, TRUE);
+	for (l = slots; l; l = g_list_next (l)) {
+		info = gp11_slot_get_token_info (l->data);
+		initialize = (info && !(info->flags & CKF_USER_PIN_INITIALIZED));
+
+		if (initialize) {
+			session = open_and_login_session (l->data, CKU_SO, NULL);
+			if (session != NULL) {
+				if (gp11_session_init_pin (session, (const guchar*)master, strlen (master), &error)) {
+					gkd_login_attach_secret (info->label, master,
+					                         "manufacturer", info->manufacturer_id,
+					                         "serial-number", info->serial_number,
+					                         NULL);
+				} else {
+					if (error->code != CKR_FUNCTION_NOT_SUPPORTED)
+						g_warning ("couldn't initialize slot with master password: %s", error->message);
+					g_clear_error (&error);
+				}
+				g_object_unref (session);
+			}
+		}
+
+		gp11_token_info_free (info);
+	}
+	gp11_list_unref_free (slots);
+	return TRUE;
+}
+
+gboolean
+gkd_login_unlock (const gchar *master)
+{
+	GP11Module *module;
+	gboolean result;
+
+	/* We don't support null or empty master passwords */
+	if (!master || !master[0])
+		return FALSE;
+
+	module = module_instance ();
+
+	result = unlock_or_create_login (module, master);
+	if (result == TRUE)
+		init_pin_for_uninitialized_slots (module, master);
+
+	g_object_unref (module);
+	return result;
+}
+
+static gboolean
+change_or_create_login (GP11Module *module, const gchar *original, const gchar *master)
+{
+	GError *error = NULL;
+	GP11Session *session;
+	GP11Object *login = NULL;
+	GP11Object *ocred = NULL;
+	GP11Object *mcred = NULL;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (GP11_IS_MODULE (module), FALSE);
+	g_return_val_if_fail (original, FALSE);
+	g_return_val_if_fail (master, FALSE);
+
+	/* Find the login object */
+	session = lookup_login_session (module);
+	login = lookup_login_keyring (session);
+
+	/* Create the new credential we'll be changing to */
+	mcred = create_credential (session, NULL, master, &error);
+	if (mcred == NULL) {
+		g_warning ("couldn't create new login credential: %s", error->message);
+		g_clear_error (&error);
+
+	/* Create original credentials */
+	} else if (login) {
+		ocred = create_credential (session, login, original, &error);
+		if (ocred == NULL) {
+			if (error->code == CKR_PIN_INCORRECT) {
+				g_message ("couldn't change login master password, "
+				           "original password was wrong: %s", error->message);
+				note_that_unlock_failed ();
+			} else {
+				g_warning ("couldn't create original login credential: %s", error->message);
+			}
+			g_clear_error (&error);
+		}
+	}
+
+	/* No keyring? try to create */
+	if (!login && mcred) {
+		login = create_login_keyring (session, mcred, &error);
+		if (login == NULL) {
+			g_warning ("couldn't create login keyring: %s", error->message);
+			g_clear_error (&error);
+		} else {
+			success = TRUE;
+		}
+
+	/* Change the master password */
+	} else if (login && ocred && mcred) {
+		if (!gp11_object_set (login, &error,
+		                      CKA_G_CREDENTIAL, GP11_ULONG, gp11_object_get_handle (mcred),
+		                      GP11_INVALID)) {
+			g_warning ("couldn't change login master password: %s", error->message);
+			g_clear_error (&error);
+		} else {
+			success = TRUE;
+		}
+	}
+
+	if (ocred) {
+		gp11_object_destroy (ocred, NULL);
+		g_object_unref (ocred);
+	}
+	if (mcred)
+		g_object_unref (mcred);
+	if (login)
+		g_object_unref (login);
+	if (session)
+		g_object_unref (session);
+
+	return success;
+}
+
+static gboolean
+set_pin_for_any_slots (GP11Module *module, const gchar *original, const gchar *master)
+{
+	GError *error = NULL;
+	GList *slots, *l;
+	gboolean initialize;
+	GP11TokenInfo *info;
+	GP11Session *session;
+
+	g_return_val_if_fail (GP11_IS_MODULE (module), FALSE);
+	g_return_val_if_fail (original, FALSE);
+	g_return_val_if_fail (master, FALSE);
+
+	slots = gp11_module_get_slots (module, TRUE);
+	for (l = slots; l; l = g_list_next (l)) {
+
+		/* Set pin for any that are initialized, and not pap */
+		info = gp11_slot_get_token_info (l->data);
+		initialize = (info && (info->flags & CKF_USER_PIN_INITIALIZED));
+
+		if (initialize) {
+			session = open_and_login_session (l->data, CKU_USER, NULL);
+			if (session != NULL) {
+				if (gp11_session_set_pin (session, (const guchar*)original, strlen (original),
+				                          (const guchar*)master, strlen (master), &error)) {
+					gkd_login_attach_secret (info->label, master,
+					                         "manufacturer", info->manufacturer_id,
+					                         "serial-number", info->serial_number,
+					                         NULL);
+				} else {
+					if (error->code != CKR_PIN_INCORRECT && error->code != CKR_FUNCTION_NOT_SUPPORTED)
+						g_warning ("couldn't change slot master password: %s", error->message);
+					g_clear_error (&error);
+				}
+				g_object_unref (session);
+			}
+		}
+
+		gp11_token_info_free (info);
+	}
+	gp11_list_unref_free (slots);
+	return TRUE;
+}
+
+gboolean
+gkd_login_change_lock (const gchar *original, const gchar *master)
+{
+	GP11Module *module;
+	gboolean result;
+
+	/* We don't support null or empty master passwords */
+	if (!master || !master[0])
+		return FALSE;
+	if (original == NULL)
+		original = "";
+
+	module = module_instance ();
+
+	result = change_or_create_login (module, original, master);
+	if (result == TRUE)
+		set_pin_for_any_slots (module, original, master);
+
+	g_object_unref (module);
+	return result;
+}
+
+gboolean
+gkd_login_is_usable (void)
+{
+	GP11Module *module;
+	GP11Session *session;
+	GP11Object *login;
+	gboolean usable = FALSE;
+	gpointer data;
+	gsize n_data;
+
+	module = module_instance ();
+	if (!module)
+		return FALSE;
+
+	session = lookup_login_session (module);
+	if (session) {
+		login = lookup_login_keyring (session);
+		if (login) {
+			data = gp11_object_get_data (login, CKA_G_LOCKED, &n_data, NULL);
+			usable = (data && n_data == sizeof (CK_BBOOL) && *((CK_BBOOL*)data));
+			g_free (data);
+			g_object_unref (login);
+		}
+		g_object_unref (session);
+	}
+
+	g_object_unref (module);
+	return usable;
+}
+
+static void
+string_attribute_list_va (va_list args, const gchar *name, GP11Attribute *attr)
+{
+	GString *fields = g_string_sized_new(128);
+	gsize length;
+
+	while (name != NULL) {
+		g_string_append (fields, name);
+		g_string_append_c (fields, '\0');
+		g_string_append (fields, va_arg (args, const gchar*));
+		g_string_append_c (fields, '\0');
+		name = va_arg (args, const gchar*);
+	}
+
+	length = fields->len;
+	gp11_attribute_init (attr, CKA_G_FIELDS, g_string_free (fields, FALSE), length);
+}
+
+static GP11Object*
+find_login_keyring_item (GP11Session *session, GP11Attribute *fields)
+{
+	GP11Object *search;
+	GP11Object *item;
+	GList *objects;
+	GError *error = NULL;
+	gpointer data;
+	gsize n_data;
+
+	g_return_val_if_fail (GP11_IS_SESSION (session), FALSE);
+
+	/* Create a search object */
+	search = gp11_session_create_object (session, &error,
+	                                     CKA_CLASS, GP11_ULONG, CKO_G_SEARCH,
+	                                     CKA_G_COLLECTION, 5, "login",
+	                                     CKA_G_FIELDS, fields->length, fields->value,
+	                                     GP11_INVALID);
+
+	if (!search) {
+		g_warning ("couldn't create search for login keyring: %s", error->message);
+		g_clear_error (&error);
+		return NULL;
+	}
+
+	/* Get the data from the search */
+	gp11_object_set_session (search, session);
+	data = gp11_object_get_data (search, CKA_G_MATCHED, &n_data, &error);
+	gp11_object_destroy (search, NULL);
+	g_object_unref (search);
+
+	if (data == NULL) {
+		g_warning ("couldn't read search in login keyring: %s", error->message);
+		g_clear_error (&error);
+		return NULL;
+	}
+
+	objects = gp11_objects_from_handle_array (gp11_session_get_slot (session), data,
+	                                          MIN (sizeof (CK_OBJECT_HANDLE), n_data));
+	g_free (data);
+
+	if (objects) {
+		item = g_object_ref (objects->data);
+		gp11_object_set_session (item, session);
+	}
+
+	gp11_list_unref_free (objects);
+	return item;
+}
+
+void
+gkd_login_attach_secret (const gchar *display_name, const gchar *secret,
+                         const gchar *first, ...)
+{
+	GError *error = NULL;
+	GP11Attribute fields;
+	GP11Session *session;
+	GP11Module *module;
+	GP11Object* item;
+	va_list va;
+
+	if (display_name == NULL)
+		display_name = "";
+	if (secret == NULL)
+		secret = "";
+
+	module = module_instance ();
+	session = lookup_login_session (module);
+
+	va_start(va, first);
+	gp11_attribute_init_empty (&fields, CKA_G_FIELDS);
+	string_attribute_list_va (va, first, &fields);
+	va_end(va);
+
+	item = find_login_keyring_item (session, &fields);
+	if (item) {
+		gp11_object_set (item, &error,
+		                 CKA_LABEL, strlen (display_name), display_name,
+		                 CKA_VALUE, strlen (secret), secret,
+		                 GP11_INVALID);
+	} else {
+		item = gp11_session_create_object (session, &error,
+		                                   CKA_CLASS, GP11_ULONG, CKO_SECRET_KEY,
+		                                   CKA_LABEL, strlen (display_name), display_name,
+		                                   CKA_VALUE, strlen (secret), secret,
+		                                   CKA_G_COLLECTION, 5, "login",
+		                                   CKA_G_FIELDS, fields.length, fields.value,
+		                                   GP11_INVALID);
+	}
+
+	if (error != NULL) {
+		g_warning ("couldn't store secret in login keyring: %s", error->message);
+		g_clear_error (&error);
+	}
+
+	if (item)
+		g_object_unref (item);
+	g_object_unref (session);
+	g_object_unref (module);
+}
+
+gchar*
+gkd_login_lookup_secret (const gchar *first, ...)
+{
+	GError *error = NULL;
+	GP11Attribute fields;
+	GP11Session *session;
+	GP11Module *module;
+	GP11Object* item;
+	gpointer data;
+	gsize n_data;
+	va_list va;
+
+	module = module_instance ();
+	session = lookup_login_session (module);
+
+	va_start(va, first);
+	gp11_attribute_init_empty (&fields, CKA_G_FIELDS);
+	string_attribute_list_va (va, first, &fields);
+	va_end(va);
+
+	item = find_login_keyring_item (session, &fields);
+	if (item == NULL) {
+		data = gp11_object_get_data_full (item, CKA_VALUE, egg_secure_realloc, NULL, &n_data, &error);
+		if (!g_utf8_validate (data, n_data, NULL)) {
+			g_warning ("expected string, but found binary secret in login keyring");
+			egg_secure_clear (data, n_data);
+			egg_secure_free (data);
+			data = NULL;
+		}
+		g_object_unref (item);
+	}
+
+	g_object_unref (session);
+	g_object_unref (module);
+
+	/* Memory returned from gp11_object_get_data is null terminated */
+	return data;
+}
+
+void
+gkd_login_remove_secret (const gchar *first, ...)
+{
+	GError *error = NULL;
+	GP11Attribute fields;
+	GP11Session *session;
+	GP11Module *module;
+	GP11Object* item;
+	va_list va;
+
+	module = module_instance ();
+	session = lookup_login_session (module);
+
+	va_start(va, first);
+	gp11_attribute_init_empty (&fields, CKA_G_FIELDS);
+	string_attribute_list_va (va, first, &fields);
+	va_end(va);
+
+	item = find_login_keyring_item (session, &fields);
+	if (item == NULL) {
+		if (!gp11_object_destroy (item, &error)) {
+			g_warning ("couldn't remove stored secret from login keyring: %s", error->message);
+			g_clear_error (&error);
+		}
+		g_object_unref (item);
+	}
+
+	g_object_unref (session);
+	g_object_unref (module);
+}
+
+GP11Attributes*
+gkd_login_attributes_for_secret (const gchar *first, ...)
+{
+	GP11Attributes *attrs;
+	GP11Attribute *fields;
+	va_list va;
+
+	attrs = gp11_attributes_newv (CKA_CLASS, GP11_ULONG, CKO_SECRET_KEY,
+	                              CKA_G_COLLECTION, 5, "login",
+	                              GP11_INVALID);
+
+	va_start(va, first);
+	fields = gp11_attributes_add_empty (attrs, CKA_G_FIELDS);
+	string_attribute_list_va (va, first, fields);
+	va_end(va);
+
+	return attrs;
+}
diff --git a/daemon/login/gkd-login.h b/daemon/login/gkd-login.h
new file mode 100644
index 0000000..d6ee6ef
--- /dev/null
+++ b/daemon/login/gkd-login.h
@@ -0,0 +1,50 @@
+/*
+ * 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_LOGIN_H__
+#define __GKD_LOGIN_H__
+
+#include <glib.h>
+
+#include "gp11/gp11.h"
+
+gboolean          gkd_login_unlock                   (const gchar *master);
+
+gboolean          gkd_login_change_lock              (const gchar *original,
+                                                      const gchar *master);
+
+gboolean          gkd_login_is_usable                (void);
+
+void              gkd_login_attach_secret            (const gchar *display_name,
+                                                      const gchar *secret,
+                                                      const gchar *first,
+                                                      ...);
+
+gchar*            gkd_login_lookup_secret            (const gchar *first,
+                                                      ...);
+
+void              gkd_login_remove_secret            (const gchar *first,
+                                                      ...);
+
+GP11Attributes*   gkd_login_attributes_for_secret    (const gchar *first,
+                                                      ...);
+
+#endif /* __GKD_LOGIN_H__ */
diff --git a/daemon/pkcs11/gkr-pkcs11-auth.c b/daemon/pkcs11/gkr-pkcs11-auth.c
index dca070a..421d282 100644
--- a/daemon/pkcs11/gkr-pkcs11-auth.c
+++ b/daemon/pkcs11/gkr-pkcs11-auth.c
@@ -26,13 +26,13 @@
 #include "egg/egg-cleanup.h"
 #include "egg/egg-secure-memory.h"
 
-#include "keyrings/gkr-keyring-login.h"
+#include "login/gkd-login.h"
+
+#include "pkcs11/pkcs11.h"
 
 #include "ui/gkr-ask-request.h"
 #include "ui/gkr-ask-daemon.h"
 
-#include "pkcs11/pkcs11.h"
-
 #include <glib.h>
 #include <glib/gi18n.h>
 
@@ -85,7 +85,7 @@ password_to_pin (const gchar *password, CK_UTF8CHAR_PTR *pin, CK_ULONG *pin_len)
 		*pin = NULL;
 		*pin_len = 0;
 	} else {
-		*pin = (CK_UTF8CHAR_PTR)egg_secure_strdup (password);
+		*pin = (CK_UTF8CHAR_PTR)password;
 		*pin_len = strlen (password);
 	}
 }
@@ -214,8 +214,7 @@ gkr_pkcs11_auth_login_specific_prompt (CK_SESSION_HANDLE handle, CK_SESSION_INFO
 
 	/* See if we can just use the login keyring password for this */
 	if (object->unique && object->token) {
-		password = gkr_keyring_login_lookup_secret (GNOME_KEYRING_ITEM_ENCRYPTION_KEY_PASSWORD,
-		                                            "unique", object->unique, NULL);
+		password = gkd_login_lookup_secret ("unique", object->unique, NULL);
 		if (password != NULL) { 
 			password_to_pin (password, pin, pin_len);
 			return TRUE;
@@ -225,13 +224,11 @@ gkr_pkcs11_auth_login_specific_prompt (CK_SESSION_HANDLE handle, CK_SESSION_INFO
 	/* COMPAT: Check old method of storing secrets for objects in login keyring */
 	if (object->digest) {
 		convert_upper_case (object->digest);
-		password = gkr_keyring_login_lookup_secret (GNOME_KEYRING_ITEM_PK_STORAGE,
-		                                            "object-digest", object->digest, NULL);
+		password = gkd_login_lookup_secret ("object-digest", object->digest, NULL);
 		if (password != NULL) {
 			if (object->unique)
-				gkr_keyring_login_attach_secret (GNOME_KEYRING_ITEM_ENCRYPTION_KEY_PASSWORD, 
-				                                 object->label, password, 
-		                                                 "unique", object->unique, NULL);
+				gkd_login_attach_secret (object->label, password,
+				                         "unique", object->unique, NULL);
 			password_to_pin (password, pin, pin_len);
 			return TRUE;
 		}
@@ -246,7 +243,7 @@ gkr_pkcs11_auth_login_specific_prompt (CK_SESSION_HANDLE handle, CK_SESSION_INFO
 	gkr_ask_request_set_secondary (ask, secondary);
 	g_free (secondary);
 
-	if (object->unique && gkr_keyring_login_is_usable ())
+	if (object->unique && gkd_login_is_usable ())
 		gkr_ask_request_set_check_option (ask, prepare_specific_check (object->klass));
 
 	/* Prompt the user */
@@ -267,9 +264,8 @@ gkr_pkcs11_auth_login_specific_prompt (CK_SESSION_HANDLE handle, CK_SESSION_INFO
 		
 		/* Store forever */
 		if (ask->checked && object->unique && object->token) {
-			gkr_keyring_login_attach_secret (GNOME_KEYRING_ITEM_ENCRYPTION_KEY_PASSWORD, 
-			                                 object->label, ask->typed_password, 
-			                                 "unique", object->unique, NULL);
+			gkd_login_attach_secret (object->label, ask->typed_password,
+			                         "unique", object->unique, NULL);
 		}
 	}
 	
@@ -325,8 +321,7 @@ gkr_pkcs11_auth_login_specific_done (CK_SESSION_HANDLE handle, CK_SESSION_INFO *
 	case CKR_PIN_LEN_RANGE:
 	case CKR_PIN_LOCKED:
 		if (object->unique && object->token)
-			gkr_keyring_login_remove_secret (GNOME_KEYRING_ITEM_ENCRYPTION_KEY_PASSWORD,
-			                                 "unique", object->unique, NULL);
+			gkd_login_remove_secret ("unique", object->unique, NULL);
 		break;
 		
 	case CKR_OK:
@@ -374,12 +369,11 @@ gkr_pkcs11_auth_login_user_prompt (CK_SESSION_HANDLE handle, CK_TOKEN_INFO *info
 	label = g_strndup ((gchar*)info->label, sizeof (info->label));
 	g_strchomp (label);
 	
-	if (gkr_keyring_login_is_usable ()) {
+	if (gkd_login_is_usable ()) {
 
-		password = gkr_keyring_login_lookup_secret (GNOME_KEYRING_ITEM_CHAINED_KEYRING_PASSWORD, 
-		                                            "manufacturer", manufacturer,
-		                                            "serial-number", serial,
-		                                            NULL);
+		password = gkd_login_lookup_secret ("manufacturer", manufacturer,
+		                                    "serial-number", serial,
+		                                    NULL);
 		if (password != NULL) {
 			password_to_pin (password, pin, pin_len);
 			g_free (manufacturer);
@@ -399,7 +393,7 @@ gkr_pkcs11_auth_login_user_prompt (CK_SESSION_HANDLE handle, CK_TOKEN_INFO *info
 	gkr_ask_request_set_secondary (ask, secondary);
 	g_free (secondary);
 	
-	if (gkr_keyring_login_is_usable ())
+	if (gkd_login_is_usable ())
 		gkr_ask_request_set_check_option (ask, _("Automatically unlock secure storage when I log in."));
 
 	/* Prompt the user */
@@ -420,11 +414,10 @@ gkr_pkcs11_auth_login_user_prompt (CK_SESSION_HANDLE handle, CK_TOKEN_INFO *info
 		
 		/* Store forever */
 		if (ask->checked) {
-			gkr_keyring_login_attach_secret (GNOME_KEYRING_ITEM_CHAINED_KEYRING_PASSWORD, 
-			                                 label, ask->typed_password,
-			                                 "manufacturer", manufacturer, 
-			                                 "serial-number", serial,
-			                                 NULL);
+			gkd_login_attach_secret (label, ask->typed_password,
+			                         "manufacturer", manufacturer,
+			                         "serial-number", serial,
+			                         NULL);
 		}
 	}
 	
@@ -444,7 +437,7 @@ clear_user_login (CK_TOKEN_INFO *info)
 	
 	g_assert (info);
 	
-	if (gkr_keyring_login_is_usable ()) {
+	if (gkd_login_is_usable ()) {
 		/* 
 		 * The manufacturer and serial number together uniquely identify token 
 		 * They're stored with space padded in the token info structure.
@@ -456,10 +449,9 @@ clear_user_login (CK_TOKEN_INFO *info)
 		serial = g_strndup ((gchar*)info->serialNumber, sizeof (info->serialNumber));
 		g_strchomp (serial);
 
-		gkr_keyring_login_remove_secret (GNOME_KEYRING_ITEM_CHAINED_KEYRING_PASSWORD,
-						 "manufacturer", manufacturer, 
-						 "serial-number", serial, 
-						 NULL);
+		gkd_login_remove_secret ("manufacturer", manufacturer,
+		                         "serial-number", serial,
+		                         NULL);
 		
 		g_free (manufacturer);
 		g_free (serial);
@@ -498,7 +490,6 @@ gkr_pkcs11_auth_init_user_prompt (CK_SESSION_HANDLE handle, CK_TOKEN_INFO *info,
 	gchar *secondary;
 	gchar *manufacturer;
 	gchar *serial;
-	const gchar *password;
 	gboolean ret = TRUE;
 	guint flags;
 	
@@ -520,27 +511,6 @@ gkr_pkcs11_auth_init_user_prompt (CK_SESSION_HANDLE handle, CK_TOKEN_INFO *info,
 	label = g_strndup ((gchar*)info->label, sizeof (info->label));
 	g_strchomp (label);
 
-	/* We try to use the login keyring password if available */
-	password = gkr_keyring_login_master ();
-	if (password != NULL) {
-		password_to_pin (password, pin, pin_len);
-		
-		/* Save this away in case the main password changes without us being aware */
-		if (gkr_keyring_login_is_usable ())
-			gkr_keyring_login_attach_secret (GNOME_KEYRING_ITEM_CHAINED_KEYRING_PASSWORD, 
-			                                 label, password,
-			                                 "manufacturer", manufacturer, 
-			                                 "serial-number", serial,
-			                                 NULL);
-		
-		g_free (manufacturer);
-		g_free (serial);
-		g_free (label);
-		return TRUE;
-	}
-
-	/* Otherwise we have to prompt for it */
-	
 	/* Build up the prompt */
 	flags = GKR_ASK_REQUEST_NEW_PASSWORD;
 	ask = gkr_ask_request_new (_("New Password Required"), 
@@ -550,7 +520,7 @@ gkr_pkcs11_auth_init_user_prompt (CK_SESSION_HANDLE handle, CK_TOKEN_INFO *info,
 	gkr_ask_request_set_secondary (ask, secondary);
 	g_free (secondary);
 
-	if (gkr_keyring_login_is_usable ())
+	if (gkd_login_is_usable ())
 		gkr_ask_request_set_check_option (ask, _("Automatically unlock secure storage when I log in."));
 
 	/* Prompt the user */
@@ -569,11 +539,10 @@ gkr_pkcs11_auth_init_user_prompt (CK_SESSION_HANDLE handle, CK_TOKEN_INFO *info,
 		password_to_pin (ask->typed_password, pin, pin_len);
 		
 		if (ask->checked) {
-			gkr_keyring_login_attach_secret (GNOME_KEYRING_ITEM_CHAINED_KEYRING_PASSWORD, 
-			                                 label, ask->typed_password,
-			                                 "manufacturer", manufacturer, 
-			                                 "serial-number", serial,
-			                                 NULL);
+			gkd_login_attach_secret (label, ask->typed_password,
+			                         "manufacturer", manufacturer,
+			                         "serial-number", serial,
+			                         NULL);
 		}
 		
 		ret = TRUE;
diff --git a/daemon/pkcs11/gkr-pkcs11-daemon.c b/daemon/pkcs11/gkr-pkcs11-daemon.c
index 9fcaefc..4850fb7 100644
--- a/daemon/pkcs11/gkr-pkcs11-daemon.c
+++ b/daemon/pkcs11/gkr-pkcs11-daemon.c
@@ -51,6 +51,7 @@
 
 /* The top level of our internal PKCS#11 module stack */
 static CK_FUNCTION_LIST_PTR pkcs11_roof = NULL;
+static CK_FUNCTION_LIST_PTR pkcs11_base = NULL;
 
 static void
 pkcs11_daemon_cleanup (gpointer unused)
@@ -76,7 +77,6 @@ pkcs11_daemon_cleanup (gpointer unused)
 gboolean
 gkr_pkcs11_daemon_initialize (void)
 {
-	CK_FUNCTION_LIST_PTR plex_layer;
 	CK_FUNCTION_LIST_PTR roots_store;
 	CK_FUNCTION_LIST_PTR secret_store;
 	CK_FUNCTION_LIST_PTR ssh_store;
@@ -106,11 +106,11 @@ gkr_pkcs11_daemon_initialize (void)
 #endif
 		gck_plex_layer_add_module (secret_store);
 		gck_plex_layer_add_module (user_store);
-		
-		plex_layer = gck_plex_layer_get_functions (); 
-		
+
+		pkcs11_base = gck_plex_layer_get_functions ();
+
 		/* The auth component is the top component */
-		gkr_pkcs11_auth_chain_functions (plex_layer);
+		gkr_pkcs11_auth_chain_functions (pkcs11_base);
 		pkcs11_roof = gkr_pkcs11_auth_get_functions ();
 	
 		/* Initialize the whole caboodle */
@@ -245,3 +245,9 @@ gkr_pkcs11_daemon_get_functions (void)
 {
 	return pkcs11_roof;
 }
+
+CK_FUNCTION_LIST_PTR
+gkr_pkcs11_daemon_get_base_functions (void)
+{
+	return pkcs11_base;
+}
diff --git a/daemon/pkcs11/gkr-pkcs11-daemon.h b/daemon/pkcs11/gkr-pkcs11-daemon.h
index 52659e8..8f8f83e 100644
--- a/daemon/pkcs11/gkr-pkcs11-daemon.h
+++ b/daemon/pkcs11/gkr-pkcs11-daemon.h
@@ -34,4 +34,6 @@ gboolean      gkr_pkcs11_daemon_startup_ssh      (void);
 
 CK_FUNCTION_LIST_PTR   gkr_pkcs11_daemon_get_functions    (void);
 
+CK_FUNCTION_LIST_PTR   gkr_pkcs11_daemon_get_base_functions    (void);
+
 #endif /* GKRPKCS11DAEMON_H_ */
diff --git a/tests/gtest-helpers.c b/tests/gtest-helpers.c
index 7ca44e1..dd032f6 100644
--- a/tests/gtest-helpers.c
+++ b/tests/gtest-helpers.c
@@ -158,7 +158,7 @@ test_data_read (const gchar *basename, gsize *n_result)
 	return (guchar*)result;
 }
 
-#ifdef WITH_P11_TESTS
+#if WITH_P11_TESTS
 
 static void
 on_p11_tests_log (int level, const char *section, const char *message)
@@ -246,7 +246,6 @@ int
 main (int argc, char* argv[])
 {
 	GLogLevelFlags fatal_mask;
-	int ret;
 
 	g_thread_init (NULL);
 
@@ -268,15 +267,6 @@ main (int argc, char* argv[])
 	fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
 	g_log_set_always_fatal (fatal_mask);
 
-	initialize_tests ();
-
-	start_tests ();
-	ret = g_test_run ();
-
-	/* Any auxiliary suites */
-	run_externals ();
-
-	stop_tests();
-
-	return ret;
+	/* Must have been defined by the test including this file */
+	return run();
 }
diff --git a/tests/prep-gtest.sh b/tests/prep-gtest.sh
index de19a5d..87f6c64 100755
--- a/tests/prep-gtest.sh
+++ b/tests/prep-gtest.sh
@@ -95,6 +95,16 @@ build_source()
 	echo "}"
 	echo
 
+	echo "static int run(void) {"
+	echo "	int ret;"
+	echo "	initialize_tests ();"
+	echo "	start_tests ();"
+	echo "	ret = g_test_run ();"
+	echo "	run_externals ();"
+	echo "	stop_tests();"
+	echo "	return ret;"
+	echo "}"
+
 	echo "#include \"tests/gtest-helpers.c\""
 }
 



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