[gnome-keyring/ssh-wip: 2/4] WIP more work on this



commit b117fbc678c961a4e3b32ccccf1234c65b195985
Author: Stef Walter <stefw redhat com>
Date:   Fri Aug 29 15:04:38 2014 +0200

    WIP more work on this

 daemon/ssh-agent/Makefile.am                |    1 +
 daemon/ssh-agent/gkd-ssh-agent-client.h     |   46 +
 daemon/ssh-agent/gkd-ssh-agent-ops.c        | 1444 +++------------------------
 daemon/ssh-agent/gkd-ssh-agent-private.h    |   98 +--
 daemon/ssh-agent/gkd-ssh-agent-proto.c      |  502 ----------
 daemon/ssh-agent/gkd-ssh-agent-standalone.c |  126 ---
 daemon/ssh-agent/gkd-ssh-agent.c            |  209 +---
 daemon/ssh-agent/gkm-ssh-agent-connection.h |   72 --
 8 files changed, 230 insertions(+), 2268 deletions(-)
---
diff --git a/daemon/ssh-agent/Makefile.am b/daemon/ssh-agent/Makefile.am
index 01d0907..f84cc8a 100644
--- a/daemon/ssh-agent/Makefile.am
+++ b/daemon/ssh-agent/Makefile.am
@@ -8,6 +8,7 @@ noinst_LTLIBRARIES += \
 libgkd_ssh_agent_la_SOURCES = \
        daemon/ssh-agent/gkd-ssh-agent.c \
        daemon/ssh-agent/gkd-ssh-agent.h \
+       daemon/ssh-agent/gkd-ssh-agent-client.h \
        daemon/ssh-agent/gkd-ssh-agent-private.h \
        daemon/ssh-agent/gkd-ssh-agent-ops.c \
        daemon/ssh-agent/gkd-ssh-agent-proto.c
diff --git a/daemon/ssh-agent/gkd-ssh-agent-client.h b/daemon/ssh-agent/gkd-ssh-agent-client.h
new file mode 100644
index 0000000..9feacc0
--- /dev/null
+++ b/daemon/ssh-agent/gkd-ssh-agent-client.h
@@ -0,0 +1,46 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2014 Stef 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.
+ *
+ * Author: Stef Walter <stef thewalter net>
+ */
+
+#ifndef __GKD_SSH_AGENT_CLIENT_H__
+#define __GKD_SSH_AGENT_CLIENT_H__
+
+#include <glib-object.h>
+
+#define GKD_TYPE_SSH_AGENT_CLIENT               (gkm_ssh_agent_client_get_type ())
+#define GKD_SSH_AGENT_CLIENT(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GKD_TYPE_SSH_AGENT_CLIENT, GkdSshAgentClient))
+#define GKD_SSH_AGENT_CLIENT_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), 
GKD_TYPE_SSH_AGENT_CLIENT, GkdSshAgentClientClass))
+#define GKD_IS_SSH_AGENT_CLIENT(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GKD_TYPE_SSH_AGENT_CLIENT))
+#define GKD_IS_SSH_AGENT_CLIENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GKD_TYPE_SSH_AGENT_CLIENT))
+#define GKD_SSH_AGENT_CLIENT_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GKD_TYPE_SSH_AGENT_CLIENT, GkdSshAgentClientClass))
+
+typedef struct _GkdSshAgentClient GkdSshAgentClient;
+typedef struct _GkdSshAgentClientClass GkdSshAgentClientClass;
+
+GType                gkd_ssh_agent_client_get_type            (void);
+
+GList *              gkd_ssh_agent_client_get_preload_keys    (GkdSshAgentClient *self);
+
+void                 gkd_ssh_agent_client_clear_preload       (GkdSshAgentClient *self,
+                                                               GBytes *key);
+
+#endif /* __GKD_SSH_AGENT_CLIENT_H__ */
diff --git a/daemon/ssh-agent/gkd-ssh-agent-ops.c b/daemon/ssh-agent/gkd-ssh-agent-ops.c
index 5927d9a..47c6560 100644
--- a/daemon/ssh-agent/gkd-ssh-agent-ops.c
+++ b/daemon/ssh-agent/gkd-ssh-agent-ops.c
@@ -2,6 +2,7 @@
 /* gkd-ssh-agent-ops.h - SSH agent operations
 
    Copyright (C) 2007 Stefan Walter
+   Copyright (C) 2014 Stefan Walter
 
    Gnome keyring is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
@@ -17,18 +18,14 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-   Author: Stef Walter <stef memberwebs com>
+   Author: Stef Walter <stef thewalter net>
 */
 
 #include "config.h"
 
+#include "gkd-ssh-agent-client.h"
 #include "gkd-ssh-agent-private.h"
 
-#include <gck/gck.h>
-
-#include "pkcs11/pkcs11.h"
-#include "pkcs11/pkcs11i.h"
-
 #include "egg/egg-error.h"
 #include "egg/egg-secure-memory.h"
 
@@ -39,1382 +36,201 @@
 #include <string.h>
 #include <stdio.h>
 
-#define V1_LABEL "SSH1 RSA Key"
-
-typedef gboolean (*ObjectForeachFunc) (GckObject *object, gpointer user_data);
-
 EGG_SECURE_DECLARE (ssh_agent_ops);
 
 /* ---------------------------------------------------------------------------- */
 
-
-static void
-copy_attribute (GckAttributes *original,
-                CK_ATTRIBUTE_TYPE type,
-                GckBuilder *dest)
-{
-       const GckAttribute *attr;
-
-       g_assert (original);
-       g_assert (dest);
-
-       attr = gck_attributes_find (original, type);
-       if (attr)
-               gck_builder_add_attribute (dest, attr);
-}
-
-static gboolean
-login_session (GckSession *session)
-{
-       gulong state;
-       GError *error = NULL;
-       gboolean ret = TRUE;
-
-       state = gck_session_get_state (session);
-
-       /* Log in the session if necessary */
-       if (state == CKS_RO_PUBLIC_SESSION || state == CKS_RW_PUBLIC_SESSION) {
-               if (!gck_session_login (session, CKU_USER, NULL, 0, NULL, &error)) {
-                       g_message ("couldn't log in to session: %s", egg_error_message (error));
-                       ret = FALSE;
-               }
-       }
-
-       return ret;
-}
-
-static GckAttributes*
-build_like_attributes (GckAttributes *attrs, CK_OBJECT_CLASS klass)
-{
-       GckBuilder builder = GCK_BUILDER_INIT;
-       gulong key_type;
-
-       g_assert (attrs);
-
-       /* Determine the key type */
-       if (!gck_attributes_find_ulong (attrs, CKA_KEY_TYPE, &key_type))
-               g_return_val_if_reached (NULL);
-
-       gck_builder_add_ulong (&builder, CKA_CLASS, klass);
-       copy_attribute (attrs, CKA_KEY_TYPE, &builder);
-       copy_attribute (attrs, CKA_TOKEN, &builder);
-
-       switch (key_type) {
-       case CKK_RSA:
-               copy_attribute (attrs, CKA_MODULUS, &builder);
-               copy_attribute (attrs, CKA_PUBLIC_EXPONENT, &builder);
-               break;
-
-       case CKK_DSA:
-               copy_attribute (attrs, CKA_PRIME, &builder);
-               copy_attribute (attrs, CKA_SUBPRIME, &builder);
-               copy_attribute (attrs, CKA_BASE, &builder);
-               copy_attribute (attrs, CKA_VALUE, &builder);
-               break;
-
-       default:
-               g_return_val_if_reached (NULL);
-               break;
-       }
-
-       return gck_attributes_ref_sink (gck_builder_end (&builder));
-}
-
-static void
-search_keys_like_attributes (GList *modules, GckSession *session, GckAttributes *attrs,
-                             CK_OBJECT_CLASS klass, ObjectForeachFunc func, gpointer user_data)
-{
-       GckAttributes *search;
-       GckEnumerator *en;
-       GError *error = NULL;
-       GList *keys, *l;
-       GckObject *object;
-
-       g_assert (modules || session);
-
-       search = build_like_attributes (attrs, klass);
-
-       /* In all slots */
-       if (modules) {
-               en = gck_modules_enumerate_objects (modules, search, GCK_SESSION_AUTHENTICATE | 
GCK_SESSION_READ_WRITE);
-
-               for (;;) {
-                       object = gck_enumerator_next (en, NULL, &error);
-                       if (!object) {
-                               if (error) {
-                                       g_warning ("couldn't enumerate matching keys: %s", egg_error_message 
(error));
-                                       g_clear_error (&error);
-                               }
-                               break;
-                       }
-
-                       if (!(func) (object, user_data))
-                               break;
-               }
-
-               g_object_unref (en);
-
-       }
-
-       /* Search in the session */
-       if (session){
-               keys = gck_session_find_objects (session, search, NULL, &error);
-
-               if (error) {
-                       g_warning ("couldn't find matching keys: %s", egg_error_message (error));
-                       g_clear_error (&error);
-
-               } else {
-                       for (l = keys; l; l = g_list_next (l)) {
-                               if (!(func) (l->data, user_data))
-                                       break;
-                       }
-
-                       gck_list_unref_free (keys);
-               }
-       }
-
-       gck_attributes_unref (search);
-}
-
-static gboolean
-list_all_matching (GckObject *object, gpointer user_data)
-{
-       GList** list = (GList**)user_data;
-       g_return_val_if_fail (GCK_IS_OBJECT (object), FALSE);
-       *list = g_list_prepend (*list, g_object_ref (object));
-
-       /* Keep going */
-       return TRUE;
-}
-
-static gboolean
-return_first_matching (GckObject *object, gpointer user_data)
-{
-       GckObject **result = (GckObject**)user_data;
-
-       g_return_val_if_fail (GCK_IS_OBJECT (object), FALSE);
-       g_return_val_if_fail (result != NULL, FALSE);
-       g_return_val_if_fail (*result == NULL, FALSE);
-       *result = g_object_ref (object);
-
-       /* We've seen enough */
-       return FALSE;
-}
-
-static gboolean
-return_private_matching (GckObject *object, gpointer user_data)
-{
-       GckObject **result = (GckObject**)user_data;
-       GckBuilder builder = GCK_BUILDER_INIT;
-       GckSession *session;
-       GckAttributes *attrs;
-       const GckAttribute *attr;
-       gboolean token;
-       GList *objects;
-       GError *error = NULL;
-
-       g_return_val_if_fail (GCK_IS_OBJECT (object), FALSE);
-       g_return_val_if_fail (result != NULL, FALSE);
-       g_return_val_if_fail (*result == NULL, FALSE);
-
-       /* Get the key identifier and token */
-       attrs = gck_object_get (object, NULL, &error, CKA_ID, CKA_TOKEN, GCK_INVALID);
-       if (error) {
-               g_warning ("error retrieving attributes for public key: %s", egg_error_message (error));
-               g_clear_error (&error);
-               return TRUE;
-       }
-
-       /* Dig out the key identifier and token */
-       attr = gck_attributes_find (attrs, CKA_ID);
-       g_return_val_if_fail (attr, FALSE);
-
-       if (!gck_attributes_find_boolean (attrs, CKA_TOKEN, &token))
-               token = FALSE;
-
-       session = gck_object_get_session (object);
-       g_return_val_if_fail (GCK_IS_SESSION (session), FALSE);
-
-       if (!login_session (session))
-               return FALSE;
-
-       gck_builder_add_attribute (&builder, attr);
-       gck_builder_add_ulong (&builder, CKA_CLASS, CKO_PRIVATE_KEY);
-       gck_builder_add_boolean (&builder, CKA_TOKEN, token);
-
-       /* Search for the matching private key */
-       objects = gck_session_find_objects (session, gck_builder_end (&builder), NULL, NULL);
-       gck_attributes_unref (attrs);
-
-       /* Keep searching, not found */
-       if (objects) {
-               *result = g_object_ref (objects->data);
-               gck_list_unref_free (objects);
-       }
-
-       g_object_unref (session);
-
-       /* Stop once we have a key */
-       return (*result == NULL);
-}
-
-static gboolean
-load_identity_v1_attributes (GckObject *object, gpointer user_data)
-{
-       GckAttributes *attrs;
-       GError *error = NULL;
-       GList **all_attrs;
-
-       g_return_val_if_fail (GCK_IS_OBJECT (object), FALSE);
-       g_return_val_if_fail (user_data, FALSE);
-
-       /*
-        * The encompassing search should have limited to the right label.
-        * In addition V1 keys are only RSA.
-        */
-
-       attrs = gck_object_get (object, NULL, &error, CKA_ID, CKA_LABEL, CKA_KEY_TYPE, CKA_MODULUS,
-                               CKA_PUBLIC_EXPONENT, CKA_CLASS, CKA_MODULUS_BITS, GCK_INVALID);
-       if (error) {
-               g_warning ("error retrieving attributes for public key: %s", egg_error_message (error));
-               g_clear_error (&error);
-               return TRUE;
-       }
-
-       all_attrs = (GList**)user_data;
-       *all_attrs = g_list_prepend (*all_attrs, attrs);
-
-       /* Note that we haven't reffed the object or session */
-
-       /* Keep going */
-       return TRUE;
-}
-
-static gboolean
-load_identity_v2_attributes (GckObject *object, gpointer user_data)
-{
-       GckAttributes *attrs;
-       const GckAttribute *attr;
-       GError *error = NULL;
-       gboolean valid = TRUE;
-       gboolean token;
-       GList **all_attrs;
-
-       g_return_val_if_fail (GCK_IS_OBJECT (object), FALSE);
-       g_return_val_if_fail (user_data, FALSE);
-
-       attrs = gck_object_get (object, NULL, &error, CKA_ID, CKA_LABEL, CKA_KEY_TYPE, CKA_MODULUS,
-                               CKA_PUBLIC_EXPONENT, CKA_PRIME, CKA_SUBPRIME, CKA_BASE,
-                               CKA_VALUE, CKA_CLASS, CKA_MODULUS_BITS, CKA_TOKEN, GCK_INVALID);
-       if (error) {
-               g_warning ("error retrieving attributes for public key: %s", egg_error_message (error));
-               g_clear_error (&error);
-               return TRUE;
-       }
-
-       /* Dig out the label, and see if it's not v1, skip if so */
-       attr = gck_attributes_find (attrs, CKA_LABEL);
-       if (attr != NULL) {
-               if (attr->length == strlen (V1_LABEL) &&
-                   strncmp ((gchar*)attr->value, V1_LABEL, attr->length) == 0)
-                       valid = FALSE;
-       }
-
-       /* Figure out if it's a token object or not */
-       if (!gck_attributes_find_boolean (attrs, CKA_TOKEN, &token))
-               token = FALSE;
-
-       all_attrs = (GList**)user_data;
-       if (valid == TRUE)
-               *all_attrs = g_list_prepend (*all_attrs, attrs);
-       else
-               gck_attributes_unref (attrs);
-
-       /* Note that we haven't reffed the object or session */
-
-       /* Keep going */
-       return TRUE;
-}
-
-static void
-remove_key_pair (GckSession *session, GckObject *priv, GckObject *pub)
-{
-       GError *error = NULL;
-
-       g_assert (GCK_IS_SESSION (session));
-
-       if (!login_session (session))
-               return;
-
-       if (priv != NULL) {
-               gck_object_destroy (priv, NULL, &error);
-
-               if (error) {
-                       if (!g_error_matches (error, GCK_ERROR, CKR_OBJECT_HANDLE_INVALID))
-                               g_warning ("couldn't remove ssh private key: %s", egg_error_message (error));
-                       g_clear_error (&error);
-               }
-       }
-
-       if (pub != NULL) {
-               gck_object_destroy (pub, NULL, &error);
-
-               if (error) {
-                       if (!g_error_matches (error, GCK_ERROR, CKR_OBJECT_HANDLE_INVALID))
-                               g_warning ("couldn't remove ssh public key: %s", egg_error_message (error));
-                       g_clear_error (&error);
-               }
-       }
-}
-
-static void
-lock_key_pair (GckSession *session, GckObject *priv, GckObject *pub)
-{
-       GckBuilder builder = GCK_BUILDER_INIT;
-       GError *error = NULL;
-       GList *objects, *l;
-
-       g_assert (GCK_IS_SESSION (session));
-       g_assert (GCK_IS_OBJECT (priv));
-       g_assert (GCK_IS_OBJECT (pub));
-
-       if (!login_session (session))
-               return;
-
-       gck_builder_add_ulong (&builder, CKA_CLASS, CKO_G_CREDENTIAL);
-       gck_builder_add_ulong (&builder, CKA_G_OBJECT, gck_object_get_handle (priv));
-
-       /* Delete any authenticator objects */
-       objects = gck_session_find_objects (session, gck_builder_end (&builder), NULL, &error);
-
-       if (error) {
-               g_warning ("couldn't search for authenticator objects: %s", egg_error_message (error));
-               g_clear_error (&error);
-               return;
-       }
-
-       /* Delete them all */
-       for (l = objects; l; l = g_list_next (l)) {
-               gck_object_destroy (l->data, NULL, &error);
-               if (error) {
-                       g_warning ("couldn't delete authenticator object: %s", egg_error_message (error));
-                       g_clear_error (&error);
-               }
-       }
-}
-
-static void
-remove_by_public_key (GckSession *session, GckObject *pub, gboolean exclude_v1)
-{
-       GckBuilder builder = GCK_BUILDER_INIT;
-       GckAttributes *attrs;
-       GError *error = NULL;
-       GList *objects;
-       gboolean token;
-       gchar *label;
-
-       g_assert (GCK_IS_SESSION (session));
-       g_assert (GCK_IS_OBJECT (pub));
-
-       if (!login_session (session))
-               return;
-
-       attrs = gck_object_get (pub, NULL, &error, CKA_LABEL, CKA_ID, CKA_TOKEN, GCK_INVALID);
-
-       if (error) {
-               g_warning ("couldn't lookup attributes for key: %s", egg_error_message (error));
-               g_clear_error (&error);
-               return;
-       }
-
-       /* Skip over SSH V1 keys */
-       if (exclude_v1 && gck_attributes_find_string (attrs, CKA_LABEL, &label)) {
-               if (label && strcmp (label, V1_LABEL) == 0) {
-                       gck_attributes_unref (attrs);
-                       g_free (label);
-                       return;
-               }
-       }
-
-       /* Lock token objects, remove session objects */
-       if (!gck_attributes_find_boolean (attrs, CKA_TOKEN, &token))
-               token = FALSE;
-
-       /* Search for exactly the same attributes but with a private key class */
-       gck_builder_add_all (&builder, attrs);
-       gck_builder_add_ulong (&builder, CKA_CLASS, CKO_PRIVATE_KEY);
-       gck_attributes_unref (attrs);
-
-       objects = gck_session_find_objects (session, gck_builder_end (&builder), NULL, &error);
-
-       if (error) {
-               g_warning ("couldn't search for related key: %s", egg_error_message (error));
-               g_clear_error (&error);
-               return;
-       }
-
-       /* Lock the token objects */
-       if (token && objects) {
-               lock_key_pair (session, objects->data, pub);
-       } else if (!token) {
-               remove_key_pair (session, objects->data, pub);
-       }
-
-       gck_list_unref_free (objects);
-}
-
-static gboolean
-create_key_pair (GckSession *session, GckAttributes *priv, GckAttributes *pub)
-{
-       GckObject *priv_key, *pub_key;
-       GError *error = NULL;
-
-       g_assert (GCK_IS_SESSION (session));
-       g_assert (priv);
-       g_assert (pub);
-
-       if (!login_session (session))
-               return FALSE;
-
-       priv_key = gck_session_create_object (session, priv, NULL, &error);
-       if (error) {
-               g_warning ("couldn't create session private key: %s", egg_error_message (error));
-               g_clear_error (&error);
-               return FALSE;
-       }
-
-       pub_key = gck_session_create_object (session, pub, NULL, &error);
-       if (error) {
-               g_warning ("couldn't create session public key: %s", egg_error_message (error));
-               g_clear_error (&error);
-
-               /* Failed, so remove private as well */
-               gck_object_destroy (priv_key, NULL, NULL);
-               g_object_unref (priv_key);
-
-               return FALSE;
-       }
-
-       g_object_unref (pub_key);
-       g_object_unref (priv_key);
-
-       return TRUE;
-}
-
-static void
-destroy_replaced_keys (GckSession *session, GList *keys)
-{
-       GError *error = NULL;
-       GList *l;
-
-       g_assert (GCK_IS_SESSION (session));
-
-       for (l = keys; l; l = g_list_next (l)) {
-               if (!gck_object_destroy (l->data, NULL, &error)) {
-                       if (!g_error_matches (error, GCK_ERROR, CKR_OBJECT_HANDLE_INVALID))
-                               g_warning ("couldn't delete a SSH key we replaced: %s",
-                                          egg_error_message (error));
-                       g_clear_error (&error);
-               }
-       }
-}
-
-static gboolean
-replace_key_pair (GckSession *session,
-                  GckBuilder *priv,
-                  GckBuilder *pub)
-{
-       GList *priv_prev, *pub_prev;
-       GckAttributes *priv_atts, *pub_atts;
-
-       g_assert (GCK_IS_SESSION (session));
-       g_assert (priv != NULL);
-       g_assert (pub != NULL);
-
-       if (!login_session (session))
-               return FALSE;
-
-       gck_builder_set_boolean (priv, CKA_TOKEN, FALSE);
-       priv_atts = gck_attributes_ref_sink (gck_builder_end (priv));
-
-       gck_builder_set_boolean (pub, CKA_TOKEN, FALSE);
-       pub_atts = gck_attributes_ref_sink (gck_builder_end (pub));
-
-       /* Find the previous keys that match the same description */
-       priv_prev = pub_prev = NULL;
-       search_keys_like_attributes (NULL, session, priv_atts, CKO_PRIVATE_KEY, list_all_matching, 
&priv_prev);
-       search_keys_like_attributes (NULL, session, pub_atts, CKO_PUBLIC_KEY, list_all_matching, &pub_prev);
-
-       /* Now try and create the new keys */
-       if (create_key_pair (session, priv_atts, pub_atts)) {
-
-               /* Delete the old keys */
-               destroy_replaced_keys (session, priv_prev);
-               destroy_replaced_keys (session, pub_prev);
-       }
-
-       gck_attributes_unref (priv_atts);
-       gck_attributes_unref (pub_atts);
-       gck_list_unref_free (priv_prev);
-       gck_list_unref_free (pub_prev);
-
-       return TRUE;
-}
-
-static gboolean
-load_contraints (EggBuffer *buffer,
-                 gsize offset,
-                 gsize *next_offset,
-                 GckBuilder *priv,
-                 GckBuilder *pub)
-{
-       guchar constraint;
-       guint32 lifetime;
-
-       /*
-        * Constraints are a byte flag, and optional data depending
-        * on the constraint.
-        */
-
-       while (offset < egg_buffer_length (buffer)) {
-               if (!egg_buffer_get_byte (buffer, offset, &offset, &constraint))
-                       return FALSE;
-
-               switch (constraint) {
-               case GKD_SSH_FLAG_CONSTRAIN_LIFETIME:
-                       if (!egg_buffer_get_uint32 (buffer, offset, &offset, &lifetime))
-                               return FALSE;
-
-                       gck_builder_add_ulong (pub, CKA_G_DESTRUCT_AFTER, lifetime);
-                       gck_builder_add_ulong (priv, CKA_G_DESTRUCT_AFTER, lifetime);
-                       break;
-
-               case GKD_SSH_FLAG_CONSTRAIN_CONFIRM:
-                       /* We can't use prompting as access control on an insecure X desktop */
-                       g_message ("prompt constraints are not supported.");
-                       return FALSE;
-
-               default:
-                       g_message ("unsupported constraint or other unsupported data");
-                       return FALSE;
-               }
-       }
-
-       *next_offset = offset;
-       return TRUE;
-}
-
-/* -----------------------------------------------------------------------------
- * OPERATIONS
- */
-
 static gboolean
 op_add_identity (GkdSshAgentCall *call)
 {
-       GckBuilder pub;
-       GckBuilder priv;
-       GckSession *session;
-       gchar *stype = NULL;
-       gchar *comment = NULL;
-       gboolean ret;
-       gulong algo;
+       GList *keys;
        gsize offset;
-
-       if (!egg_buffer_get_string (call->req, 5, &offset, &stype, (EggBufferAllocator)g_realloc))
-               return FALSE;
-
-       algo = gkd_ssh_agent_proto_keytype_to_algo (stype);
-       if (algo == G_MAXULONG) {
-               g_warning ("unsupported algorithm from SSH: %s", stype);
-               g_free (stype);
-               return FALSE;
-       }
-
-       g_free (stype);
-       gck_builder_init_full (&pub, GCK_BUILDER_SECURE_MEMORY);
-       gck_builder_init_full (&priv, GCK_BUILDER_NONE);
-
-       switch (algo) {
-       case CKK_RSA:
-               ret = gkd_ssh_agent_proto_read_pair_rsa (call->req, &offset, &priv, &pub);
-               break;
-       case CKK_DSA:
-               ret = gkd_ssh_agent_proto_read_pair_dsa (call->req, &offset, &priv, &pub);
-               break;
-       default:
-               g_assert_not_reached ();
-               return FALSE;
-       }
-
-       if (!ret) {
-               g_warning ("couldn't read incoming SSH private key");
-               gck_builder_clear (&pub);
-               gck_builder_clear (&priv);
-               return FALSE;
-       }
-
-       /* Get the comment */
-       if (!egg_buffer_get_string (call->req, offset, &offset, &comment, (EggBufferAllocator)g_realloc)) {
-               gck_builder_clear (&pub);
-               gck_builder_clear (&priv);
-               return FALSE;
-       }
-
-       gck_builder_add_string (&pub, CKA_LABEL, comment);
-       gck_builder_add_string (&priv, CKA_LABEL, comment);
-       g_free (comment);
-
-       /* Any constraints on loading the key */
-       if (!load_contraints (call->req, offset, &offset, &priv, &pub)) {
-               gck_builder_clear (&pub);
-               gck_builder_clear (&priv);
-               return FALSE;
-       }
-
-       /*
-        * This is the session that owns these objects. Only
-        * one thread can use it at a time.
-        */
-
-       session = gkd_ssh_agent_checkout_main_session ();
-       g_return_val_if_fail (session, FALSE);
-
-       ret = replace_key_pair (session, &priv, &pub);
-
-       gkd_ssh_agent_checkin_main_session (session);
-
-       gck_builder_clear (&priv);
-       gck_builder_clear (&pub);
-
-       egg_buffer_add_byte (call->resp, ret ? GKD_SSH_RES_SUCCESS : GKD_SSH_RES_FAILURE);
-       return TRUE;
-}
-
-static gboolean
-op_v1_add_identity (GkdSshAgentCall *call)
-{
-       GckBuilder pub, priv;
-       GckSession *session;
-       gchar *comment = NULL;
-       gboolean ret;
-       gsize offset = 5;
-       guint32 unused;
-
-       if (!egg_buffer_get_uint32 (call->req, offset, &offset, &unused))
-               return FALSE;
-
-       gck_builder_init_full (&priv, GCK_BUILDER_SECURE_MEMORY);
-       gck_builder_init_full (&pub, GCK_BUILDER_NONE);
-
-       if (!gkd_ssh_agent_proto_read_pair_v1 (call->req, &offset, &priv, &pub)) {
-               g_warning ("couldn't read incoming SSH private key");
-               gck_builder_clear (&pub);
-               gck_builder_clear (&priv);
-               return FALSE;
-       }
-
-       /* Get the comment */
-       if (!egg_buffer_get_string (call->req, offset, &offset, &comment, (EggBufferAllocator)g_realloc)) {
-               gck_builder_clear (&pub);
-               gck_builder_clear (&priv);
-               return FALSE;
-       }
-
-       g_free (comment);
-
-       gck_builder_add_string (&priv, CKA_LABEL, V1_LABEL);
-       gck_builder_add_string (&pub, CKA_LABEL, V1_LABEL);
-
-       /* Any constraints on loading the key */
-       if (!load_contraints (call->req, offset, &offset, &priv, &pub)) {
-               gck_builder_clear (&pub);
-               gck_builder_clear (&priv);
-               return FALSE;
-       }
+       gconstpointer data;
+       gsize size;
+       GList *l;
 
        /*
-        * This is the session that owns these objects. Only
-        * one thread can use it at a time.
+        * Here we want to remove the preload key from our list, so that we
+        * don't accidentally add it automatically again later once the user
+        * has taken over manual management of this key.
+        *
+        * Compare the incoming key to the public keys in our list. This works
+        * because for RSA and DSA the private key pair coming in has the same
+        * initial bytes as the public key we've loaded.
         */
 
-       session = gkd_ssh_agent_checkout_main_session ();
-       g_return_val_if_fail (session, FALSE);
-
-       ret = replace_key_pair (session, &priv, &pub);
-
-       gkd_ssh_agent_checkin_main_session (session);
-
-       gck_builder_clear (&pub);
-       gck_builder_clear (&priv);
-
-       egg_buffer_add_byte (call->resp, ret ? GKD_SSH_RES_SUCCESS : GKD_SSH_RES_FAILURE);
-       return TRUE;
-}
-
-static gboolean
-op_request_identities (GkdSshAgentCall *call)
-{
-       GckBuilder builder = GCK_BUILDER_INIT;
-       GckEnumerator *en;
-       GckObject *obj;
-       GError *error = NULL;
-       GList *all_attrs, *l;
-       GckAttributes *attrs;
-       gsize blobpos;
-       gchar *comment;
-
-       /* TODO: Check SSH purpose */
-       gck_builder_add_ulong (&builder, CKA_CLASS, CKO_PUBLIC_KEY);
-
-       /* Find all the keys (we filter out v1 later) */
-       en = gck_modules_enumerate_objects (call->modules, gck_builder_end (&builder),
-                                           GCK_SESSION_AUTHENTICATE | GCK_SESSION_READ_WRITE);
-       g_return_val_if_fail (en, FALSE);
-
-       all_attrs = NULL;
-       do {
-               obj = gck_enumerator_next (en, NULL, &error);
-       } while (obj && load_identity_v2_attributes (obj, &all_attrs));
-
-       g_object_unref (en);
-
-       if (error) {
-               g_warning ("couldn't enumerate ssh keys: %s", egg_error_message (error));
-               egg_buffer_add_byte (call->resp, GKD_SSH_RES_FAILURE);
-               g_clear_error (&error);
-               return TRUE;
-       }
-
-       egg_buffer_add_byte (call->resp, GKD_SSH_RES_IDENTITIES_ANSWER);
-       egg_buffer_add_uint32 (call->resp, g_list_length (all_attrs));
-
-       for (l = all_attrs; l; l = g_list_next (l)) {
-
-               attrs = l->data;
-
-               /* Dig out the label */
-               if (!gck_attributes_find_string (attrs, CKA_LABEL, &comment))
-                       comment = NULL;
-
-               /* Add a space for the key blob length */
-               blobpos = call->resp->len;
-               egg_buffer_add_uint32 (call->resp, 0);
-
-               /* Write out the key */
-               gkd_ssh_agent_proto_write_public (call->resp, attrs);
-
-               /* Write back the blob length */
-               egg_buffer_set_uint32 (call->resp, blobpos, (call->resp->len - blobpos) - 4);
-
-               /* And now a per key comment */
-               egg_buffer_add_string (call->resp, comment ? comment : "");
-
-               g_free (comment);
-               gck_attributes_unref (attrs);
-       }
-
-       g_list_free (all_attrs);
-
-       return TRUE;
-}
-
-static gboolean
-op_v1_request_identities (GkdSshAgentCall *call)
-{
-       GckBuilder builder = GCK_BUILDER_INIT;
-       GList *all_attrs, *l;
-       GckAttributes *attrs;
-       GError *error = NULL;
-       GckEnumerator *en;
-       GckObject *obj;
-
-       /* TODO: Check SSH purpose */
-       gck_builder_add_ulong (&builder, CKA_CLASS, CKO_PUBLIC_KEY);
-       gck_builder_add_boolean (&builder, CKA_TOKEN, FALSE);
-       gck_builder_add_string (&builder, CKA_LABEL, V1_LABEL);
-
-       /* Find all the keys not on token, and are V1 */
-       en = gck_modules_enumerate_objects (call->modules, gck_builder_end (&builder),
-                                           GCK_SESSION_AUTHENTICATE | GCK_SESSION_READ_WRITE);
-
-       all_attrs = NULL;
-       do {
-               obj = gck_enumerator_next (en, NULL, &error);
-       } while (obj && load_identity_v1_attributes (obj, &all_attrs));
-
-       g_object_unref (en);
-
-       if (error) {
-               g_warning ("couldn't enumerate ssh keys: %s", egg_error_message (error));
-               egg_buffer_add_byte (call->resp, GKD_SSH_RES_FAILURE);
-               g_clear_error (&error);
-               return TRUE;
-       }
-
-       egg_buffer_add_byte (call->resp, GKD_SSH_RES_RSA_IDENTITIES_ANSWER);
-       egg_buffer_add_uint32 (call->resp, g_list_length (all_attrs));
-
-       for (l = all_attrs; l; l = g_list_next (l)) {
-
-               attrs = l->data;
-
-               /* Write out the key */
-               gkd_ssh_agent_proto_write_public_v1 (call->resp, attrs);
-
-               /* And now a per key comment */
-               egg_buffer_add_string (call->resp, "Public Key");
-
-               gck_attributes_unref (attrs);
-       }
-
-       g_list_free (all_attrs);
+       keys = gkd_ssh_agent_client_get_preload_keys (call->agent);
 
-       return TRUE;
-}
-
-static const guchar SHA1_ASN[15] = /* Object ID is 1.3.14.3.2.26 */
-       { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
-         0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
-
-static const guchar MD5_ASN[18] = /* Object ID is 1.2.840.113549.2.5 */
-       { 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86,0x48,
-         0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 };
-
-static guchar*
-make_pkcs1_sign_hash (GChecksumType algo, const guchar *data, gsize n_data,
-                      gsize *n_result)
-{
-       gsize n_algo, n_asn, n_hash;
-       GChecksum *checksum;
-       const guchar *asn;
-       guchar *hash;
-
-       g_assert (data);
-       g_assert (n_result);
-
-       n_algo = g_checksum_type_get_length (algo);
-       g_return_val_if_fail (n_algo > 0, FALSE);
-
-       if (algo == G_CHECKSUM_SHA1) {
-               asn = SHA1_ASN;
-               n_asn = sizeof (SHA1_ASN);
-       } else if (algo == G_CHECKSUM_MD5) {
-               asn = MD5_ASN;
-               n_asn = sizeof (MD5_ASN);
+       offset = 5;
+       for (l = keys; l != NULL; l = g_list_next (l)) {
+               data = g_bytes_get_data (l->data, &size);
+               if (call->req->len >= size + offset &&
+                   memcmp (call->req->buf, data, size) == 0) {
+                       gkd_ssh_agent_client_clear_preload (call->agent, l->data);
+                       break;
+               }:snp
        }
 
-       n_hash = n_algo + n_asn;
-       hash = g_malloc0 (n_hash);
-       memcpy (hash, asn, n_asn);
-
-       checksum = g_checksum_new (algo);
-       g_checksum_update (checksum, data, n_data);
-       g_checksum_get_digest (checksum, hash + n_asn, &n_algo);
-       g_checksum_free (checksum);
+       g_list_free_full (keys, g_bytes_unref);
 
-       *n_result = n_hash;
-       return hash;
+       return gkd_ssh_agent_relay (call);
 }
 
-static guchar*
-make_raw_sign_hash (GChecksumType algo, const guchar *data, gsize n_data,
-                    gsize *n_result)
+static GHashTable *
+parse_identities_answer (EggBuffer *resp)
 {
-       gsize n_hash;
-       GChecksum *checksum;
-       guchar *hash;
-
-       g_assert (data);
-       g_assert (n_result);
-
-       n_hash = g_checksum_type_get_length (algo);
-       g_return_val_if_fail (n_hash > 0, FALSE);
-
-       hash = g_malloc0 (n_hash);
+       gsize offset = 4;
+       guint32 count;
+       guchar op;
+       guint32 i;
 
-       checksum = g_checksum_new (algo);
-       g_checksum_update (checksum, data, n_data);
-       g_checksum_get_digest (checksum, hash, &n_hash);
-       g_checksum_free (checksum);
-
-       *n_result = n_hash;
-       return hash;
-}
-
-static guchar*
-unlock_and_sign (GckSession *session, GckObject *key, gulong mech_type, const guchar *input,
-                 gsize n_input, gsize *n_result, GError **err)
-{
-       GckBuilder builder = GCK_BUILDER_INIT;
-       GckAttributes *attrs;
-       GckObject *cred;
-       gboolean always;
-
-       /* First check if we should authenticate the key */
-       attrs = gck_object_get (key, NULL, err, CKA_ALWAYS_AUTHENTICATE, GCK_INVALID);
-       if (!attrs)
+       if (!egg_buffer_get_byte (resp, offset, &offset, &op) ||
+           op != GKD_SSH_RES_IDENTITIES_ANSWER ||
+           !egg_buffer_get_uint32 (resp, offset, &offset, &count)) {
+               g_warning ("got unexpected response back from ssh-agent when requesting identities");
                return NULL;
+       }
 
-       /* Authenticate the key if necessary, this allows long term */
-       if (!gck_attributes_find_boolean (attrs, CKA_ALWAYS_AUTHENTICATE, &always))
-               g_return_val_if_reached (NULL);
-
-       gck_attributes_unref (attrs);
-
-       if (always == TRUE) {
-               gck_builder_add_ulong (&builder, CKA_CLASS, CKO_G_CREDENTIAL);
-               gck_builder_add_boolean (&builder, CKA_TOKEN, FALSE);
-               gck_builder_add_empty (&builder, CKA_VALUE);
-               gck_builder_add_ulong (&builder, CKA_G_OBJECT, gck_object_get_handle (key));
-
-               cred = gck_session_create_object (session, gck_builder_end (&builder), NULL, err);
+       keys = g_hash_table_new_full (g_bytes_hash, g_bytes_equal, g_bytes_unref, g_free);
 
-               if (cred == NULL)
+       for (i = 0; i < count; i++) {
+               if (!egg_buffer_get_byte_array (resp, offset, &offset, &blob, &length) ||
+                   !egg_buffer_get_string (resp, offset, &offset, &comment, g_realloc)) {
+                       g_warning ("got unparseable response back from ssh-agent when requesting identities");
+                       g_hash_table_unref (keys);
                        return NULL;
-
-               g_object_unref (cred);
+               }
+               g_hash_table_insert (keys, g_bytes_new (blob, length), comment);
        }
 
-       /* Do the magic */
-       return gck_session_sign (session, key, mech_type, input, n_input, n_result, NULL, err);
+       return keys;
 }
 
 static gboolean
-op_sign_request (GkdSshAgentCall *call)
+op_request_identities (GkdSshAgentCall *call)
 {
-       GckBuilder builder = GCK_BUILDER_INIT;
-       GckAttributes *attrs;
-       GError *error = NULL;
-       GckObject *key = NULL;
-       const guchar *data;
-       const gchar *salgo;
-       GckSession *session;
-       guchar *result;
-       gsize n_data, n_result;
-       guint32 flags;
-       gsize offset;
-       gboolean ret = FALSE;
-       guint blobpos, sz;
-       guint8 *hash;
-       gulong algo, mech;
-       GChecksumType halgo;
-       gsize n_hash = 0;
+       GHashTable *answer;
+       guint32 added;
 
-       offset = 5;
-
-       /* The key packet size */
-       if (!egg_buffer_get_uint32 (call->req, offset, &offset, &sz))
-               return FALSE;
-
-       /* The key itself */
-       if (!gkd_ssh_agent_proto_read_public (call->req, &offset, &builder, &algo)) {
-               gck_builder_clear (&builder);
-               return FALSE;
-       }
-
-       attrs = gck_attributes_ref_sink (gck_builder_end (&builder));
-
-       /* Validate the key type / mechanism */
-       if (algo == CKK_RSA)
-               mech = CKM_RSA_PKCS;
-       else if (algo == CKK_DSA)
-               mech = CKM_DSA;
-       else
-               g_return_val_if_reached (FALSE);
-
-       if (!egg_buffer_get_byte_array (call->req, offset, &offset, &data, &n_data) ||
-           !egg_buffer_get_uint32 (call->req, offset, &offset, &flags)) {
-               gck_attributes_unref (attrs);
+       if (!gkd_ssh_agent_relay (call))
                return FALSE;
-       }
-
-       /* Lookup the key */
-       search_keys_like_attributes (call->modules, NULL, attrs, CKO_PUBLIC_KEY, return_private_matching, 
&key);
-       gck_attributes_unref (attrs);
-
-       if (!key) {
-               egg_buffer_add_byte (call->resp, GKD_SSH_RES_FAILURE);
-               return TRUE;
-       }
-
-       /* Usually we hash the data with SHA1 */
-       if (flags & GKD_SSH_FLAG_OLD_SIGNATURE)
-               halgo = G_CHECKSUM_MD5;
-       else
-               halgo = G_CHECKSUM_SHA1;
 
-       /* Build the hash */
-       if (mech == CKM_RSA_PKCS)
-               hash = make_pkcs1_sign_hash (halgo, data, n_data, &n_hash);
-       else
-               hash = make_raw_sign_hash (halgo, data, n_data, &n_hash);
-
-       session = gck_object_get_session (key);
-       g_return_val_if_fail (session, FALSE);
-
-       result = unlock_and_sign (session, key, mech, hash, n_hash, &n_result, &error);
-
-       g_object_unref (session);
-       g_object_unref (key);
-       g_free (hash);
-
-       if (error) {
-               if (!g_error_matches (error, GCK_ERROR, CKR_FUNCTION_CANCELED) &&
-                   !g_error_matches (error, GCK_ERROR, CKR_PIN_INCORRECT))
-                       g_message ("signing of the data failed: %s", egg_error_message (error));
-               g_clear_error (&error);
-               egg_buffer_add_byte (call->resp, GKD_SSH_RES_FAILURE);
+       /* Parse all the keys, and if it fails, just fall through */
+       answer = parse_identities_answer (call->resp);
+       if (!answer)
                return TRUE;
-       }
-
-       egg_buffer_add_byte (call->resp, GKD_SSH_RES_SIGN_RESPONSE);
-
-       /* Add a space for the sig blob length */
-       blobpos = call->resp->len;
-       egg_buffer_add_uint32 (call->resp, 0);
-
-       salgo = gkd_ssh_agent_proto_algo_to_keytype (algo);
-       g_assert (salgo);
-       egg_buffer_add_string (call->resp, salgo);
 
-       switch (algo) {
-       case CKK_RSA:
-               ret = gkd_ssh_agent_proto_write_signature_rsa (call->resp, result, n_result);
-               break;
+       added = 0;
 
-       case CKK_DSA:
-               ret = gkd_ssh_agent_proto_write_signature_dsa (call->resp, result, n_result);
-               break;
-
-       default:
-               g_assert_not_reached ();
+       /* Add any preloaded keys not already in answer */
+       keys = gkd_ssh_agent_get_preload_keys (call->agent);
+       for (l = keys; l != NULL; l = g_list_next (l)) {
+               if (g_hash_table_lookup (answer, l->data))
+                       continue;
+               blob = g_bytes_get_data (l->data, &length);
+               egg_buffer_add_byte_array (call->resp, blob, length);
+               comment = gkd_ssh_agent_get_preload_comment (l->data);
+               egg_buffer_add_string (comment ? comment : "");
+               added++;
        }
 
-       g_free (result);
-       g_return_val_if_fail (ret, FALSE);
+       g_list_free_full (keys, g_bytes_unref);
 
-       /* Write back the blob length */
-       egg_buffer_set_uint32 (call->resp, blobpos, (call->resp->len - blobpos) - 4);
+       /* Set the correct amount of keys including the ones we added */
+       egg_buffer_set_uint32 (call->resp, 5, added + g_hash_table_get_size (answer));
+       g_hash_table_unref (answer);
 
        return TRUE;
 }
 
 static gboolean
-op_v1_challenge (GkdSshAgentCall *call)
+op_sign_request (GkdSshAgentCall *call)
 {
-       GckBuilder builder = GCK_BUILDER_INIT;
-       gsize offset, n_data, n_result, n_hash;
-       GckSession *session;
-       GckAttributes *attrs;
-       guchar session_id[16];
-       guint8 hash[16];
-       const guchar *data;
-       guchar *result = NULL;
-       GChecksum *checksum;
-       GckObject *key = NULL;
-       guint32 resp_type;
-       GError *error = NULL;
-       guint i;
-       guchar b;
-
-       offset = 5;
-
-       if (!gkd_ssh_agent_proto_read_public_v1 (call->req, &offset, &builder)) {
-               gck_builder_clear (&builder);
-               return FALSE;
-       }
-
-       attrs = gck_attributes_ref_sink (gck_builder_end (&builder));
-
-       /* Read the entire challenge */
-       data = gkd_ssh_agent_proto_read_challenge_v1 (call->req, &offset, &n_data);
+       EggBuffer buf;
+       gsize offset = 5;
+       GBytes *key;
 
-       /* Only protocol 1.1 is supported */
-       if (call->req->len <= offset) {
-               gck_attributes_unref (attrs);
-               egg_buffer_add_byte (call->resp, GKD_SSH_RES_FAILURE);
-               return TRUE;
+       /* If parsing the request fails, just pass through */
+       if (!egg_buffer_get_byte_array (call->resp, offset, &offset, &blob, &length)) {
+               g_warning ("got unparseable sign request for ssh-agent");
+               return gkd_ssh_agent_relay (call);
        }
 
-       /* Read out the session id, raw, unbounded */
-       for (i = 0; i < 16; ++i) {
-               egg_buffer_get_byte (call->req, offset, &offset, &b);
-               session_id[i] = b;
-       }
+       key = g_bytes_new (blob, length);
+       priv = gkd_ssh_agent_get_preload_private (call->agent, key);
 
-       /* And the response type */
-       egg_buffer_get_uint32 (call->req, offset, &offset, &resp_type);
+       if (priv) {
+               egg_buffer_init_full (&buf, xxxx);
+               egg_buffer_add_uint32 (&buf, 0); /* length */
+               egg_buffer_add_byte (&buf, GKR_ ADD_IDENTITY);
+               blob = g_bytes_get_data (&buf, priv, &length);
+               egg_buffer_add_byte_array (&buf, blob, length);
 
-       /* Did parsing fail? */
-       if (egg_buffer_has_error (call->req) || data == NULL) {
-               gck_attributes_unref (attrs);
-               return FALSE;
-       }
+               xxxx gkd_ssh_agent_call (call->agent, buf);
 
-       /* Not supported request type */
-       if (resp_type != 1) {
-               gck_attributes_unref (attrs);
-               egg_buffer_add_byte (call->resp, GKD_SSH_RES_FAILURE);
-               return TRUE;
-       }
+               egg_buffer_get_byte (call->resp, what's the right code  GKD_SSH_RES_FAILURE);
 
-       /* Lookup the key */
-       search_keys_like_attributes (call->modules, NULL, attrs, CKO_PUBLIC_KEY, return_private_matching, 
&key);
-       gck_attributes_unref (attrs);
+               gkd_ssh_agent_clear ();
 
-       /* Didn't find a key? */
-       if (key == NULL) {
-               egg_buffer_add_byte (call->resp, GKD_SSH_RES_FAILURE);
-               return TRUE;
+               xxxx parse xxxx;
        }
 
-       session = gck_object_get_session (key);
-       g_return_val_if_fail (session, FALSE);
-
-       result = gck_session_decrypt (session, key, CKM_RSA_PKCS, data, n_data, &n_result, NULL, &error);
-
-       g_object_unref (session);
-       g_object_unref (key);
-
-       if (error) {
-               if (!g_error_matches (error, GCK_ERROR, CKR_FUNCTION_CANCELED))
-                       g_message ("decryption of the data failed: %s", egg_error_message (error));
-               g_clear_error (&error);
-               egg_buffer_add_byte (call->resp, GKD_SSH_RES_FAILURE);
-               return TRUE;
-       }
-
-       /* Now build up a hash of this and the session_id */
-       checksum = g_checksum_new (G_CHECKSUM_MD5);
-       g_checksum_update (checksum, result, n_result);
-       g_checksum_update (checksum, session_id, sizeof (session_id));
-       n_hash = sizeof (hash);
-       g_checksum_get_digest (checksum, hash, &n_hash);
-
-       egg_buffer_add_byte (call->resp, GKD_SSH_RES_RSA_RESPONSE);
-       egg_buffer_append (call->resp, hash, n_hash);
-
-       g_free (result);
-       return TRUE;
+       xxxx separate function xxxx
+out:
+       if (key)
+               g_bytes_unref (key);
+       return gkd_ssh_agent_relay (call);
 }
 
 static gboolean
 op_remove_identity (GkdSshAgentCall *call)
 {
-       GckBuilder builder = GCK_BUILDER_INIT;
-       GckAttributes *attrs;
-       GckSession *session;
-       GckObject *key = NULL;
-       gsize offset;
-       guint sz;
-
-       offset = 5;
-
-       /* The key packet size */
-       if (!egg_buffer_get_uint32 (call->req, offset, &offset, &sz))
-               return FALSE;
-
-       /* The public key itself */
-       if (!gkd_ssh_agent_proto_read_public (call->req, &offset, &builder, NULL)) {
-               gck_builder_clear (&builder);
-               return FALSE;
-       }
-
-       attrs = gck_attributes_ref_sink (gck_builder_end (&builder));
-
-       /*
-        * This is the session that owns these objects. Only
-        * one thread can use it at a time.
-        */
-
-       session = gkd_ssh_agent_checkout_main_session ();
-       g_return_val_if_fail (session, FALSE);
-
-       search_keys_like_attributes (NULL, session, attrs, CKO_PUBLIC_KEY, return_first_matching, &key);
-       gck_attributes_unref (attrs);
-
-       if (key != NULL) {
-               remove_by_public_key (session, key, TRUE);
-               g_object_unref (key);
-       }
-
-       gkd_ssh_agent_checkin_main_session (session);
-
-       egg_buffer_add_byte (call->resp, GKD_SSH_RES_SUCCESS);
-
-       return TRUE;
-}
-
-static gboolean
-op_v1_remove_identity (GkdSshAgentCall *call)
-{
-       GckBuilder builder = GCK_BUILDER_INIT;
-       GckSession *session;
-       GckAttributes *attrs;
-       GckObject *key = NULL;
-       gsize offset;
-
-       offset = 5;
-
-       if (!gkd_ssh_agent_proto_read_public_v1 (call->req, &offset, &builder)) {
-               gck_builder_clear (&builder);
-               return FALSE;
-       }
-
-       attrs = gck_attributes_ref_sink (gck_builder_end (&builder));
-
-       /*
-        * This is the session that owns these objects. Only
-        * one thread can use it at a time.
-        */
-
-       session = gkd_ssh_agent_checkout_main_session ();
-       g_return_val_if_fail (session, FALSE);
-
-       search_keys_like_attributes (NULL, session, attrs, CKO_PUBLIC_KEY, return_first_matching, &key);
-       gck_attributes_unref (attrs);
-
-       if (key != NULL) {
-               remove_by_public_key (session, key, FALSE);
-               g_object_unref (key);
-       }
-
-       gkd_ssh_agent_checkin_main_session (session);
-
-       egg_buffer_add_byte (call->resp, GKD_SSH_RES_SUCCESS);
-       return TRUE;
-}
-
-static gboolean
-op_remove_all_identities (GkdSshAgentCall *call)
-{
-       GckBuilder builder = GCK_BUILDER_INIT;
-       GckSession *session;
-       GList *objects, *l;
-       GError *error = NULL;
-       GckAttributes *attrs;
-
-       /*
-        * This is the session that owns these objects. Only
-        * one thread can use it at a time.
-        */
-
-       session = gkd_ssh_agent_checkout_main_session ();
-       g_return_val_if_fail (session, FALSE);
-
-       /* Find all session SSH public keys */
-       gck_builder_add_ulong (&builder, CKA_CLASS, CKO_PUBLIC_KEY);
-       attrs = gck_attributes_ref_sink (gck_builder_end (&builder));
-
-       objects = gck_session_find_objects (session, attrs, NULL, &error);
-       gck_attributes_unref (attrs);
-
-       if (error) {
-               g_warning ("couldn't search for keys to remove: %s", egg_error_message (error));
-               g_clear_error (&error);
-
+       /* If parsing the request fails, just pass through */
+       if (egg_buffer_get_byte_array (call->resp, offset, &offset, &blob, &length)) {
+               key = g_bytes_new (blob, length);
+               gkd_ssh_agent_clear_preload (call->agent, key);
+               g_bytes_unref (key);
        } else {
-               for (l = objects; l; l = g_list_next (l))
-                       remove_by_public_key (session, l->data, TRUE);
-               gck_list_unref_free (objects);
+               g_warning ("got unparseable remove request for ssh-agent");
        }
 
-       gkd_ssh_agent_checkin_main_session (session);
-
-       egg_buffer_add_byte (call->resp, GKD_SSH_RES_SUCCESS);
-       return TRUE;
+       /* If the key doesn't exist what happens here? */
+       return gkd_ssh_agent_relay (call);
 }
 
 static gboolean
-op_v1_remove_all_identities (GkdSshAgentCall *call)
-{
-       GckBuilder builder = GCK_BUILDER_INIT;
-       GckSession *session;
-       GList *objects, *l;
-       GError *error = NULL;
-       GckAttributes *attrs;
-
-       /*
-        * This is the session that owns these objects. Only
-        * one thread can use it at a time.
-        */
-
-       session = gkd_ssh_agent_checkout_main_session ();
-       g_return_val_if_fail (session, FALSE);
-
-       /* Find all session SSH v1 public keys */
-       gck_builder_add_ulong (&builder, CKA_CLASS, CKO_PUBLIC_KEY);
-       gck_builder_add_boolean (&builder, CKA_TOKEN, FALSE);
-       gck_builder_add_string (&builder, CKA_LABEL, V1_LABEL);
-       attrs = gck_attributes_ref_sink (gck_builder_end (&builder));
-
-       objects = gck_session_find_objects (session, attrs, NULL, &error);
-       gck_attributes_unref (attrs);
-
-       if (error) {
-               g_warning ("couldn't search for keys to remove: %s", egg_error_message (error));
-               g_clear_error (&error);
-
-       } else {
-               for (l = objects; l; l = g_list_next (l))
-                       remove_by_public_key (session, l->data, FALSE);
-               gck_list_unref_free (objects);
-       }
-
-       gkd_ssh_agent_checkin_main_session (session);
-
-       egg_buffer_add_byte (call->resp, GKD_SSH_RES_SUCCESS);
-       return TRUE;
-}
-
-static gboolean
-op_not_implemented_success (GkdSshAgentCall *call)
-{
-       egg_buffer_add_byte (call->resp, GKD_SSH_RES_SUCCESS);
-       return TRUE;
-}
-
-static gboolean
-op_not_implemented_failure (GkdSshAgentCall *call)
-{
-       egg_buffer_add_byte (call->resp, GKD_SSH_RES_FAILURE);
-       return TRUE;
-}
-
-static gboolean
-op_invalid (GkdSshAgentCall *call)
+op_remove_all_identities (GkdSshAgentCall *call)
 {
-       /* Invalid request, disconnect immediately */
-       return FALSE;
+       gkd_ssh_agent_clear_all (call->agent);
+       return gkd_ssh_agent_relay (call);
 }
 
 const GkdSshAgentOperation gkd_ssh_agent_operations[GKD_SSH_OP_MAX] = {
-     op_invalid,                                 /* 0 */
-     op_v1_request_identities,                   /* GKR_SSH_OP_REQUEST_RSA_IDENTITIES */
-     op_invalid,                                 /* 2 */
-     op_v1_challenge,                            /* GKR_SSH_OP_RSA_CHALLENGE */
-     op_invalid,                                 /* 4 */
-     op_invalid,                                 /* 5 */
-     op_invalid,                                 /* 6 */
-     op_v1_add_identity,                         /* GKR_SSH_OP_ADD_RSA_IDENTITY */
-     op_v1_remove_identity,                      /* GKR_SSH_OP_REMOVE_RSA_IDENTITY */
-     op_v1_remove_all_identities,                /* GKR_SSH_OP_REMOVE_ALL_RSA_IDENTITIES */
-     op_invalid,                                 /* 10 */
-     op_request_identities,                      /* GKR_SSH_OP_REQUEST_IDENTITIES */
-     op_invalid,                                 /* 12 */
-     op_sign_request,                            /* GKR_SSH_OP_SIGN_REQUEST */
-     op_invalid,                                 /* 14 */
-     op_invalid,                                 /* 15 */
-     op_invalid,                                 /* 16 */
-     op_add_identity,                            /* GKR_SSH_OP_ADD_IDENTITY */
-     op_remove_identity,                         /* GKR_SSH_OP_REMOVE_IDENTITY */
-     op_remove_all_identities,                   /* GKR_SSH_OP_REMOVE_ALL_IDENTITIES */
-     op_not_implemented_failure,                 /* GKR_SSH_OP_ADD_SMARTCARD_KEY */
-     op_not_implemented_failure,                 /* GKR_SSH_OP_REMOVE_SMARTCARD_KEY */
-     op_not_implemented_success,                 /* GKR_SSH_OP_LOCK */
-     op_not_implemented_success,                 /* GKR_SSH_OP_UNLOCK */
-     op_v1_add_identity,                         /* GKR_SSH_OP_ADD_RSA_ID_CONSTRAINED */
-     op_add_identity,                            /* GKR_SSH_OP_ADD_ID_CONSTRAINED */
-     op_not_implemented_failure,                 /* GKR_SSH_OP_ADD_SMARTCARD_KEY_CONSTRAINED */
+       NULL,                                 /* 0 */
+       NULL,                                 /* GKR_SSH_OP_REQUEST_RSA_IDENTITIES */
+       NULL,                                 /* 2 */
+       NULL,                                 /* GKR_SSH_OP_RSA_CHALLENGE */
+       NULL,                                 /* 4 */
+       NULL,                                 /* 5 */
+       NULL,                                 /* 6 */
+       NULL,                                 /* GKR_SSH_OP_ADD_RSA_IDENTITY */
+       NULL,                                 /* GKR_SSH_OP_REMOVE_RSA_IDENTITY */
+       NULL,                                 /* GKR_SSH_OP_REMOVE_ALL_RSA_IDENTITIES */
+       NULL,                                 /* 10 */
+       op_request_identities,                /* GKR_SSH_OP_REQUEST_IDENTITIES */
+       NULL,                                 /* 12 */
+       op_sign_request,                      /* GKR_SSH_OP_SIGN_REQUEST */
+       NULL,                                 /* 14 */
+       NULL,                                 /* 15 */
+       NULL,                                 /* 16 */
+       op_add_identity,                      /* GKR_SSH_OP_ADD_IDENTITY */
+       op_remove_identity,                   /* GKR_SSH_OP_REMOVE_IDENTITY */
+       op_remove_all_identities,             /* GKR_SSH_OP_REMOVE_ALL_IDENTITIES */
+       NULL,                                 /* GKR_SSH_OP_ADD_SMARTCARD_KEY */
+       NULL,                                 /* GKR_SSH_OP_REMOVE_SMARTCARD_KEY */
+       NULL,                                 /* GKR_SSH_OP_LOCK */
+       NULL,                                 /* GKR_SSH_OP_UNLOCK */
+       NULL,                                 /* GKR_SSH_OP_ADD_RSA_ID_CONSTRAINED */
+       op_add_identity,                      /* GKR_SSH_OP_ADD_ID_CONSTRAINED */
+       NULL,                                 /* GKR_SSH_OP_ADD_SMARTCARD_KEY_CONSTRAINED */
 };
diff --git a/daemon/ssh-agent/gkd-ssh-agent-private.h b/daemon/ssh-agent/gkd-ssh-agent-private.h
index 90a4aa4..6ee7bcf 100644
--- a/daemon/ssh-agent/gkd-ssh-agent-private.h
+++ b/daemon/ssh-agent/gkd-ssh-agent-private.h
@@ -23,21 +23,17 @@
 #ifndef GKDSSHPRIVATE_H_
 #define GKDSSHPRIVATE_H_
 
-#include "egg/egg-buffer.h"
-
-#include "pkcs11/pkcs11.h"
+#include "gkd-ssh-agent-client.h"
 
-#include <gck/gck.h>
+#include "egg/egg-buffer.h"
 
 #include <glib.h>
 
-gkd_ssh_agent_process_new
-
 typedef struct _GkdSshAgentCall {
        int sock;
-       GList *modules;
        EggBuffer *req;
        EggBuffer *resp;
+       GkdSshAgentClient *agent;
 } GkdSshAgentCall;
 
 /* -----------------------------------------------------------------------------
@@ -93,90 +89,10 @@ extern const GkdSshAgentOperation gkd_ssh_agent_operations[GKD_SSH_OP_MAX];
  * gkd-ssh-agent.c
  */
 
-gboolean              gkd_ssh_agent_initialize_with_module          (GckModule *module);
-
-GckSession*           gkd_ssh_agent_checkout_main_session           (void);
-
-void                  gkd_ssh_agent_checkin_main_session            (GckSession* session);
-
-/* -----------------------------------------------------------------------------
- * gkd-ssh-agent-proto.c
- */
-
-gulong                gkd_ssh_agent_proto_keytype_to_algo           (const gchar *salgo);
-
-const gchar*          gkd_ssh_agent_proto_algo_to_keytype           (gulong algo);
-
-gboolean              gkd_ssh_agent_proto_read_mpi                  (EggBuffer *req,
-                                                                     gsize *offset,
-                                                                     GckBuilder *attrs,
-                                                                     CK_ATTRIBUTE_TYPE type);
-
-gboolean              gkd_ssh_agent_proto_read_mpi_v1               (EggBuffer *req,
-                                                                     gsize *offset,
-                                                                     GckBuilder *attrs,
-                                                                     CK_ATTRIBUTE_TYPE type);
-
-const guchar*         gkd_ssh_agent_proto_read_challenge_v1         (EggBuffer *req,
-                                                                     gsize *offset,
-                                                                     gsize *n_challenge);
-
-gboolean              gkd_ssh_agent_proto_write_mpi                 (EggBuffer *resp,
-                                                                     const GckAttribute *attr);
-
-gboolean              gkd_ssh_agent_proto_write_mpi_v1              (EggBuffer *resp,
-                                                                     const GckAttribute *attr);
-
-gboolean              gkd_ssh_agent_proto_read_public               (EggBuffer *req,
-                                                                     gsize *offset,
-                                                                     GckBuilder *attrs,
-                                                                     gulong *algo);
-
-gboolean              gkd_ssh_agent_proto_read_public_rsa           (EggBuffer *req,
-                                                                     gsize *offset,
-                                                                     GckBuilder *attrs);
-
-gboolean              gkd_ssh_agent_proto_read_public_dsa           (EggBuffer *req,
-                                                                     gsize *offset,
-                                                                     GckBuilder *attrs);
-
-gboolean              gkd_ssh_agent_proto_read_public_v1            (EggBuffer *req,
-                                                                     gsize *offset,
-                                                                     GckBuilder *attrs);
-
-gboolean              gkd_ssh_agent_proto_read_pair_rsa             (EggBuffer *req,
-                                                                     gsize *offset,
-                                                                     GckBuilder *priv,
-                                                                     GckBuilder *pub);
-
-gboolean              gkd_ssh_agent_proto_read_pair_dsa             (EggBuffer *req,
-                                                                     gsize *offset,
-                                                                     GckBuilder *priv,
-                                                                     GckBuilder *pub);
-
-gboolean              gkd_ssh_agent_proto_read_pair_v1              (EggBuffer *req,
-                                                                     gsize *offset,
-                                                                     GckBuilder *priv,
-                                                                     GckBuilder *pub);
-
-gboolean              gkd_ssh_agent_proto_write_public              (EggBuffer *resp,
-                                                                     GckAttributes *attrs);
-
-gboolean              gkd_ssh_agent_proto_write_public_rsa          (EggBuffer *resp,
-                                                                     GckAttributes *attrs);
-
-gboolean              gkd_ssh_agent_proto_write_public_dsa          (EggBuffer *resp,
-                                                                     GckAttributes *attrs);
-
-gboolean              gkd_ssh_agent_proto_write_public_v1           (EggBuffer *resp,
-                                                                     GckAttributes *attrs);
-
-gboolean              gkd_ssh_agent_proto_write_signature_rsa       (EggBuffer *resp,
-                                                                     CK_BYTE_PTR signature,
-                                                                     CK_ULONG n_signature);
+gboolean              gkd_ssh_agent_read_packet                     (gint fd,
+                                                                     EggBuffer *buffer);
 
-gboolean              gkd_ssh_agent_proto_write_signature_dsa       (EggBuffer *resp,
-                                                                     CK_BYTE_PTR signature,
-                                                                     CK_ULONG n_signature);
+gboolean              gkd_ssh_agent_write_packet                    (gint fd,
+                                                                     EggBuffer *buffer);
 
 #endif /*GKDSSHPRIVATE_H_*/
diff --git a/daemon/ssh-agent/gkd-ssh-agent-proto.c b/daemon/ssh-agent/gkd-ssh-agent-proto.c
index f288374..d5a33e2 100644
--- a/daemon/ssh-agent/gkd-ssh-agent-proto.c
+++ b/daemon/ssh-agent/gkd-ssh-agent-proto.c
@@ -26,509 +26,7 @@
 
 #include "egg/egg-buffer.h"
 
-#include <gck/gck.h>
-
 #include <glib.h>
 
 #include <string.h>
 
-gulong
-gkd_ssh_agent_proto_keytype_to_algo (const gchar *salgo)
-{
-       g_return_val_if_fail (salgo, G_MAXULONG);
-       if (strcmp (salgo, "ssh-rsa") == 0)
-               return CKK_RSA;
-       else if (strcmp (salgo, "ssh-dss") == 0)
-               return CKK_DSA;
-       return G_MAXULONG;
-}
-
-const gchar*
-gkd_ssh_agent_proto_algo_to_keytype (gulong algo)
-{
-       if (algo == CKK_RSA)
-               return "ssh-rsa";
-       else if (algo == CKK_DSA)
-               return "ssh-dss";
-       return NULL;
-}
-
-gboolean
-gkd_ssh_agent_proto_read_mpi (EggBuffer *req, gsize *offset,
-                              GckBuilder *builder,
-                              CK_ATTRIBUTE_TYPE type)
-{
-       const guchar *data;
-       gsize len;
-
-       if (!egg_buffer_get_byte_array (req, *offset, offset, &data, &len))
-               return FALSE;
-
-       /* Convert to unsigned format */
-       if (len >= 2 && data[0] == 0 && (data[1] & 0x80)) {
-               ++data;
-               --len;
-       }
-
-       gck_builder_add_data (builder, type, data, len);
-       return TRUE;
-}
-
-gboolean
-gkd_ssh_agent_proto_read_mpi_v1 (EggBuffer *req,
-                                 gsize *offset,
-                                 GckBuilder *attrs,
-                                 CK_ATTRIBUTE_TYPE type)
-{
-       const guchar *data;
-       gsize bytes;
-       guint16 bits;
-
-       /* Get the number of bits */
-       if (!egg_buffer_get_uint16 (req, *offset, offset, &bits))
-               return FALSE;
-
-       /* Figure out the number of binary bytes following */
-       bytes = (bits + 7) / 8;
-       if (bytes > 8 * 1024)
-               return FALSE;
-
-       /* Pull these out directly */
-       if (req->len < *offset + bytes)
-               return FALSE;
-       data = req->buf + *offset;
-       *offset += bytes;
-
-       gck_builder_add_data (attrs, type, data, bytes);
-       return TRUE;
-}
-
-gboolean
-gkd_ssh_agent_proto_write_mpi (EggBuffer *resp,
-                               const GckAttribute *attr)
-{
-       const guchar *value;
-       guchar *data;
-       gsize n_extra;
-
-       g_assert (resp);
-       g_assert (attr);
-
-       /* Convert from unsigned format */
-       n_extra = 0;
-       value = attr->value;
-       if (attr->length && (value[0] & 0x80))
-               ++n_extra;
-
-       data = egg_buffer_add_byte_array_empty (resp, attr->length + n_extra);
-       if (data == NULL)
-               return FALSE;
-
-       memset (data, 0, n_extra);
-       memcpy (data + n_extra, attr->value, attr->length);
-       return TRUE;
-}
-
-gboolean
-gkd_ssh_agent_proto_write_mpi_v1 (EggBuffer *resp,
-                                  const GckAttribute *attr)
-{
-       guchar *data;
-
-       g_return_val_if_fail (attr->length * 8 < G_MAXUSHORT, FALSE);
-
-       if (!egg_buffer_add_uint16 (resp, attr->length * 8))
-               return FALSE;
-
-       data = egg_buffer_add_empty (resp, attr->length);
-       if (data == NULL)
-               return FALSE;
-       memcpy (data, attr->value, attr->length);
-       return TRUE;
-}
-
-const guchar*
-gkd_ssh_agent_proto_read_challenge_v1 (EggBuffer *req, gsize *offset, gsize *n_challenge)
-{
-       const guchar *data;
-       gsize bytes;
-       guint16 bits;
-
-       /* Get the number of bits */
-       if (!egg_buffer_get_uint16 (req, *offset, offset, &bits))
-               return FALSE;
-
-       /* Figure out the number of binary bytes following */
-       bytes = (bits + 7) / 8;
-       if (bytes > 8 * 1024)
-               return FALSE;
-
-       /* Pull these out directly */
-       if (req->len < *offset + bytes)
-               return FALSE;
-       data = req->buf + *offset;
-       *offset += bytes;
-       *n_challenge = bytes;
-       return data;
-}
-
-gboolean
-gkd_ssh_agent_proto_read_public (EggBuffer *req,
-                                 gsize *offset,
-                                 GckBuilder *attrs,
-                                 gulong *algo)
-{
-       gboolean ret;
-       gchar *stype;
-       gulong alg;
-
-       g_assert (req);
-       g_assert (offset);
-
-       /* The string algorithm */
-       if (!egg_buffer_get_string (req, *offset, offset, &stype, (EggBufferAllocator)g_realloc))
-               return FALSE;
-
-       alg = gkd_ssh_agent_proto_keytype_to_algo (stype);
-       if (alg == G_MAXULONG) {
-               g_warning ("unsupported algorithm from SSH: %s", stype);
-               g_free (stype);
-               return FALSE;
-       }
-
-       g_free (stype);
-       switch (alg) {
-       case CKK_RSA:
-               ret = gkd_ssh_agent_proto_read_public_rsa (req, offset, attrs);
-               break;
-       case CKK_DSA:
-               ret = gkd_ssh_agent_proto_read_public_dsa (req, offset, attrs);
-               break;
-       default:
-               g_assert_not_reached ();
-               return FALSE;
-       }
-
-       if (!ret) {
-               g_warning ("couldn't read incoming SSH private key");
-               return FALSE;
-       }
-
-       if (algo)
-               *algo = alg;
-       return ret;
-}
-
-gboolean
-gkd_ssh_agent_proto_read_pair_rsa (EggBuffer *req,
-                                   gsize *offset,
-                                   GckBuilder *priv_attrs,
-                                   GckBuilder *pub_attrs)
-{
-       const GckAttribute *attr;
-
-       g_assert (req);
-       g_assert (offset);
-       g_assert (priv_attrs);
-       g_assert (pub_attrs);
-
-       if (!gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_MODULUS) ||
-           !gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_PUBLIC_EXPONENT) ||
-           !gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_PRIVATE_EXPONENT) ||
-           !gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_COEFFICIENT) ||
-           !gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_PRIME_1) ||
-           !gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_PRIME_2))
-               return FALSE;
-
-       /* Copy attributes to the public key */
-       attr = gck_builder_find (priv_attrs, CKA_MODULUS);
-       gck_builder_add_attribute (pub_attrs, attr);
-       attr = gck_builder_find (priv_attrs, CKA_PUBLIC_EXPONENT);
-       gck_builder_add_attribute (pub_attrs, attr);
-
-       /* Add in your basic other required attributes */
-       gck_builder_add_ulong (priv_attrs, CKA_CLASS, CKO_PRIVATE_KEY);
-       gck_builder_add_ulong (priv_attrs, CKA_KEY_TYPE, CKK_RSA);
-       gck_builder_add_ulong (pub_attrs, CKA_CLASS, CKO_PUBLIC_KEY);
-       gck_builder_add_ulong (pub_attrs, CKA_KEY_TYPE, CKK_RSA);
-
-       return TRUE;
-}
-
-gboolean
-gkd_ssh_agent_proto_read_pair_v1 (EggBuffer *req,
-                                  gsize *offset,
-                                  GckBuilder *priv_attrs,
-                                  GckBuilder *pub_attrs)
-{
-       const GckAttribute *attr;
-
-       g_assert (req);
-       g_assert (offset);
-       g_assert (priv_attrs);
-       g_assert (pub_attrs);
-
-       if (!gkd_ssh_agent_proto_read_mpi_v1 (req, offset, priv_attrs, CKA_MODULUS) ||
-           !gkd_ssh_agent_proto_read_mpi_v1 (req, offset, priv_attrs, CKA_PUBLIC_EXPONENT) ||
-           !gkd_ssh_agent_proto_read_mpi_v1 (req, offset, priv_attrs, CKA_PRIVATE_EXPONENT) ||
-           !gkd_ssh_agent_proto_read_mpi_v1 (req, offset, priv_attrs, CKA_COEFFICIENT) ||
-           !gkd_ssh_agent_proto_read_mpi_v1 (req, offset, priv_attrs, CKA_PRIME_1) ||
-           !gkd_ssh_agent_proto_read_mpi_v1 (req, offset, priv_attrs, CKA_PRIME_2))
-               return FALSE;
-
-       /* Copy attributes to the public key */
-       attr = gck_builder_find (priv_attrs, CKA_MODULUS);
-       gck_builder_add_attribute (pub_attrs, attr);
-       attr = gck_builder_find (priv_attrs, CKA_PUBLIC_EXPONENT);
-       gck_builder_add_attribute (pub_attrs, attr);
-
-       /* Add in your basic other required attributes */
-       gck_builder_add_ulong (priv_attrs, CKA_CLASS, CKO_PRIVATE_KEY);
-       gck_builder_add_ulong (priv_attrs, CKA_KEY_TYPE, CKK_RSA);
-       gck_builder_add_ulong (pub_attrs, CKA_CLASS, CKO_PUBLIC_KEY);
-       gck_builder_add_ulong (pub_attrs, CKA_KEY_TYPE, CKK_RSA);
-
-       return TRUE;
-}
-
-gboolean
-gkd_ssh_agent_proto_read_public_rsa (EggBuffer *req,
-                                     gsize *offset,
-                                     GckBuilder *attrs)
-{
-       g_assert (req);
-       g_assert (offset);
-       g_assert (attrs);
-
-       if (!gkd_ssh_agent_proto_read_mpi (req, offset, attrs, CKA_PUBLIC_EXPONENT) ||
-           !gkd_ssh_agent_proto_read_mpi (req, offset, attrs, CKA_MODULUS))
-               return FALSE;
-
-       /* Add in your basic other required attributes */
-       gck_builder_add_ulong (attrs, CKA_CLASS, CKO_PUBLIC_KEY);
-       gck_builder_add_ulong (attrs, CKA_KEY_TYPE, CKK_RSA);
-
-       return TRUE;
-}
-
-gboolean
-gkd_ssh_agent_proto_read_public_v1 (EggBuffer *req,
-                                    gsize *offset,
-                                    GckBuilder *attrs)
-{
-       guint32 bits;
-
-       g_assert (req);
-       g_assert (offset);
-       g_assert (attrs);
-
-       if (!egg_buffer_get_uint32 (req, *offset, offset, &bits))
-               return FALSE;
-
-       if (!gkd_ssh_agent_proto_read_mpi_v1 (req, offset, attrs, CKA_PUBLIC_EXPONENT) ||
-           !gkd_ssh_agent_proto_read_mpi_v1 (req, offset, attrs, CKA_MODULUS))
-               return FALSE;
-
-       /* Add in your basic other required attributes */
-       gck_builder_add_ulong (attrs, CKA_CLASS, CKO_PUBLIC_KEY);
-       gck_builder_add_ulong (attrs, CKA_KEY_TYPE, CKK_RSA);
-
-       return TRUE;
-}
-
-gboolean
-gkd_ssh_agent_proto_read_pair_dsa (EggBuffer *req,
-                                   gsize *offset,
-                                   GckBuilder *priv_attrs,
-                                   GckBuilder *pub_attrs)
-{
-       const GckAttribute *attr;
-
-       g_assert (req);
-       g_assert (offset);
-       g_assert (priv_attrs);
-       g_assert (pub_attrs);
-
-       if (!gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_PRIME) ||
-           !gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_SUBPRIME) ||
-           !gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_BASE) ||
-           !gkd_ssh_agent_proto_read_mpi (req, offset, pub_attrs, CKA_VALUE) ||
-           !gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_VALUE))
-               return FALSE;
-
-       /* Copy attributes to the public key */
-       attr = gck_builder_find (priv_attrs, CKA_PRIME);
-       gck_builder_add_attribute (pub_attrs, attr);
-       attr = gck_builder_find (priv_attrs, CKA_SUBPRIME);
-       gck_builder_add_attribute (pub_attrs, attr);
-       attr = gck_builder_find (priv_attrs, CKA_BASE);
-       gck_builder_add_attribute (pub_attrs, attr);
-
-       /* Add in your basic other required attributes */
-       gck_builder_add_ulong (priv_attrs, CKA_CLASS, CKO_PRIVATE_KEY);
-       gck_builder_add_ulong (priv_attrs, CKA_KEY_TYPE, CKK_DSA);
-       gck_builder_add_ulong (pub_attrs, CKA_CLASS, CKO_PUBLIC_KEY);
-       gck_builder_add_ulong (pub_attrs, CKA_KEY_TYPE, CKK_DSA);
-
-       return TRUE;
-}
-
-gboolean
-gkd_ssh_agent_proto_read_public_dsa (EggBuffer *req,
-                                     gsize *offset,
-                                     GckBuilder *attrs)
-{
-       g_assert (req);
-       g_assert (offset);
-       g_assert (attrs);
-
-       if (!gkd_ssh_agent_proto_read_mpi (req, offset, attrs, CKA_PRIME) ||
-           !gkd_ssh_agent_proto_read_mpi (req, offset, attrs, CKA_SUBPRIME) ||
-           !gkd_ssh_agent_proto_read_mpi (req, offset, attrs, CKA_BASE) ||
-           !gkd_ssh_agent_proto_read_mpi (req, offset, attrs, CKA_VALUE))
-               return FALSE;
-
-       /* Add in your basic other required attributes */
-       gck_builder_add_ulong (attrs, CKA_CLASS, CKO_PUBLIC_KEY);
-       gck_builder_add_ulong (attrs, CKA_KEY_TYPE, CKK_DSA);
-
-       return TRUE;
-}
-
-gboolean
-gkd_ssh_agent_proto_write_public (EggBuffer *resp, GckAttributes *attrs)
-{
-       gboolean ret = FALSE;
-       const gchar *salgo;
-       gulong algo;
-
-       g_assert (resp);
-       g_assert (attrs);
-
-       if (!gck_attributes_find_ulong (attrs, CKA_KEY_TYPE, &algo))
-               g_return_val_if_reached (FALSE);
-
-       salgo = gkd_ssh_agent_proto_algo_to_keytype (algo);
-       g_assert (salgo);
-       egg_buffer_add_string (resp, salgo);
-
-       switch (algo) {
-       case CKK_RSA:
-               ret = gkd_ssh_agent_proto_write_public_rsa (resp, attrs);
-               break;
-
-       case CKK_DSA:
-               ret = gkd_ssh_agent_proto_write_public_dsa (resp, attrs);
-               break;
-
-       default:
-               g_return_val_if_reached (FALSE);
-               break;
-       }
-
-       return ret;
-}
-
-gboolean
-gkd_ssh_agent_proto_write_public_rsa (EggBuffer *resp, GckAttributes *attrs)
-{
-       const GckAttribute *attr;
-
-       g_assert (resp);
-       g_assert (attrs);
-
-       attr = gck_attributes_find (attrs, CKA_PUBLIC_EXPONENT);
-       g_return_val_if_fail (attr, FALSE);
-
-       if (!gkd_ssh_agent_proto_write_mpi (resp, attr))
-               return FALSE;
-
-       attr = gck_attributes_find (attrs, CKA_MODULUS);
-       g_return_val_if_fail (attr, FALSE);
-
-       if (!gkd_ssh_agent_proto_write_mpi (resp, attr))
-               return FALSE;
-
-       return TRUE;
-}
-
-gboolean
-gkd_ssh_agent_proto_write_public_dsa (EggBuffer *resp, GckAttributes *attrs)
-{
-       const GckAttribute *attr;
-
-       g_assert (resp);
-       g_assert (attrs);
-
-       attr = gck_attributes_find (attrs, CKA_PRIME);
-       g_return_val_if_fail (attr, FALSE);
-
-       if (!gkd_ssh_agent_proto_write_mpi (resp, attr))
-               return FALSE;
-
-       attr = gck_attributes_find (attrs, CKA_SUBPRIME);
-       g_return_val_if_fail (attr, FALSE);
-
-       if (!gkd_ssh_agent_proto_write_mpi (resp, attr))
-               return FALSE;
-
-       attr = gck_attributes_find (attrs, CKA_BASE);
-       g_return_val_if_fail (attr, FALSE);
-
-       if (!gkd_ssh_agent_proto_write_mpi (resp, attr))
-               return FALSE;
-
-       attr = gck_attributes_find (attrs, CKA_VALUE);
-       g_return_val_if_fail (attr, FALSE);
-
-       if (!gkd_ssh_agent_proto_write_mpi (resp, attr))
-               return FALSE;
-
-       return TRUE;
-}
-
-gboolean
-gkd_ssh_agent_proto_write_public_v1 (EggBuffer *resp, GckAttributes *attrs)
-{
-       const GckAttribute *attr;
-       gulong bits;
-
-       g_assert (resp);
-       g_assert (attrs);
-
-       /* This is always an RSA key. */
-
-       /* Write out the number of bits of the key */
-       if (!gck_attributes_find_ulong (attrs, CKA_MODULUS_BITS, &bits))
-               g_return_val_if_reached (FALSE);
-       egg_buffer_add_uint32 (resp, bits);
-
-       /* Write out the exponent */
-       attr = gck_attributes_find (attrs, CKA_PUBLIC_EXPONENT);
-       g_return_val_if_fail (attr, FALSE);
-
-       if (!gkd_ssh_agent_proto_write_mpi_v1 (resp, attr))
-               return FALSE;
-
-       /* Write out the modulus */
-       attr = gck_attributes_find (attrs, CKA_MODULUS);
-       g_return_val_if_fail (attr, FALSE);
-
-       if (!gkd_ssh_agent_proto_write_mpi_v1 (resp, attr))
-               return FALSE;
-
-       return TRUE;
-}
-
-gboolean
-gkd_ssh_agent_proto_write_signature_rsa (EggBuffer *resp, CK_BYTE_PTR signature, CK_ULONG n_signature)
-{
-       return egg_buffer_add_byte_array (resp, signature, n_signature);
-}
-
-gboolean
-gkd_ssh_agent_proto_write_signature_dsa (EggBuffer *resp, CK_BYTE_PTR signature, CK_ULONG n_signature)
-{
-       g_return_val_if_fail (n_signature == 40, FALSE);
-       return egg_buffer_add_byte_array (resp, signature, n_signature);
-}
diff --git a/daemon/ssh-agent/gkd-ssh-agent.c b/daemon/ssh-agent/gkd-ssh-agent.c
index 2ef7f3b..b831408 100644
--- a/daemon/ssh-agent/gkd-ssh-agent.c
+++ b/daemon/ssh-agent/gkd-ssh-agent.c
@@ -104,36 +104,46 @@ write_all (int fd, const guchar *buf, int len)
        return TRUE;
 }
 
-static gboolean
-read_packet_with_size (GkdSshAgentCall *call)
+gboolean
+gkd_ssh_agent_read_packet (gint fd,
+                           EggBuffer *buffer)
 {
-       int fd;
        guint32 packet_size;
 
-       fd = call->sock;
-
-       egg_buffer_resize (call->req, 4);
-       if (!read_all (fd, call->req->buf, 4))
+       egg_buffer_reset (buffer);
+       egg_buffer_resize (buffer, 4);
+       if (!read_all (fd, buffer->buf, 4))
                return FALSE;
 
-       if (!egg_buffer_get_uint32 (call->req, 0, NULL, &packet_size) ||
+       if (!egg_buffer_get_uint32 (buffer, 0, NULL, &packet_size) ||
            packet_size < 1) {
                g_warning ("invalid packet size from client");
                return FALSE;
        }
 
-       egg_buffer_resize (call->req, packet_size + 4);
-       if (!read_all (fd, call->req->buf + 4, packet_size))
+       egg_buffer_resize (buffer, packet_size + 4);
+       if (!read_all (fd, buffer->buf + 4, packet_size))
                return FALSE;
 
        return TRUE;
 }
 
+gboolean
+gkd_ssh_agent_write_packet (gint fd,
+                            EggBuffer *buffer)
+{
+       if (!egg_buffer_set_uint32 (buffer, 0, buffer->len - 4))
+               g_return_val_if_reached (FALSE);
+       return write_all (fd, buffer->buf, buffer->len);
+}
+
 static gpointer
 run_client_thread (gpointer data)
 {
-       gint *socket = data;
+       gint *socket = xxxx;
+       gint *agent = xxxx;
        GkdSshAgentCall call;
+       GkdSshAgentOperation func;
        EggBuffer req;
        EggBuffer resp;
        guchar op;
@@ -146,40 +156,39 @@ run_client_thread (gpointer data)
        egg_buffer_init_full (&resp, 128, (EggBufferAllocator)g_realloc);
        call.req = &req;
        call.resp = &resp;
-       call.modules = gck_list_ref_copy (pkcs11_modules);
 
-       for (;;) {
+       call.agent = gkd_ssh_agent_client_connect ();
+       if (!call.agent)
+               goto out;
 
-               egg_buffer_reset (call.req);
+       for (;;) {
 
                /* 1. Read in the request */
-               if (!read_packet_with_size (&call))
+               if (!gkd_ssh_agent_read_packet (call.sock, &call.req))
                        break;
 
                /* 2. Now decode the operation */
                if (!egg_buffer_get_byte (call.req, 4, NULL, &op))
                        break;
-               if (op >= GKD_SSH_OP_MAX)
-                       break;
-               g_assert (gkd_ssh_agent_operations[op]);
 
                /* 3. Execute the right operation */
                egg_buffer_reset (call.resp);
                egg_buffer_add_uint32 (call.resp, 0);
-               if (!(gkd_ssh_agent_operations[op]) (&call))
-                       break;
-               if (!egg_buffer_set_uint32 (call.resp, 0, call.resp->len - 4))
+               if (op >= GKD_SSH_OP_MAX || gkd_ssh_agent_operations[op])
+                       func = gkd_ssh_agent_operations[op];
+               else
+                       func = gkd_ssh_agent_relay;
+               if (!func (&call))
                        break;
 
                /* 4. Write the reply back out */
-               if (!write_all (call.sock, call.resp->buf, call.resp->len))
+               if (!gkd_ssh_agent_write_packet (call.sock, call.resp))
                        break;
        }
 
+out:
        egg_buffer_uninit (&req);
        egg_buffer_uninit (&resp);
-       gck_list_unref_free (call.modules);
-       call.modules = NULL;
 
        close (call.sock);
        g_atomic_int_set (socket, -1);
@@ -188,57 +197,13 @@ run_client_thread (gpointer data)
 }
 
 /* --------------------------------------------------------------------------------------
- * SESSION MANAGEMENT
- */
-
-/* The main PKCS#11 session that owns objects, and the mutex/cond for waiting on it */
-static GckSession *pkcs11_main_session = NULL;
-static gboolean pkcs11_main_checked = FALSE;
-static GMutex *pkcs11_main_mutex = NULL;
-static GCond *pkcs11_main_cond = NULL;
-
-GckSession*
-gkd_ssh_agent_checkout_main_session (void)
-{
-       GckSession *result;
-
-       g_mutex_lock (pkcs11_main_mutex);
-
-               g_assert (GCK_IS_SESSION (pkcs11_main_session));
-               while (pkcs11_main_checked)
-                       g_cond_wait (pkcs11_main_cond, pkcs11_main_mutex);
-               pkcs11_main_checked = TRUE;
-               result = g_object_ref (pkcs11_main_session);
-
-       g_mutex_unlock (pkcs11_main_mutex);
-
-       return result;
-}
-
-void
-gkd_ssh_agent_checkin_main_session (GckSession *session)
-{
-       g_assert (GCK_IS_SESSION (session));
-
-       g_mutex_lock (pkcs11_main_mutex);
-
-               g_assert (session == pkcs11_main_session);
-               g_assert (pkcs11_main_checked);
-
-               g_object_unref (session);
-               pkcs11_main_checked = FALSE;
-               g_cond_signal (pkcs11_main_cond);
-
-       g_mutex_unlock (pkcs11_main_mutex);
-}
-
-/* --------------------------------------------------------------------------------------
  * MAIN THREAD
  */
 
 typedef struct _Client {
        GThread *thread;
        gint sock;
+       gint agent;
 } Client;
 
 /* Each client thread in this list */
@@ -280,18 +245,19 @@ gkd_ssh_agent_accept (void)
                return;
        }
 
+       real_agent = gkd_ssh_agent_process_connect ();
+       if (real_agent < 0) {
+               /* Warning already printed */
+               close (new_fd);
+               return;
+       }
+
        client = g_slice_new0 (Client);
        client->sock = new_fd;
+       client->agent = real_agent;
 
        /* And create a new thread/process */
        client->thread = g_thread_new ("ssh-agent", run_client_thread, &client->sock);
-       if (!client->thread) {
-               g_warning ("couldn't create thread SSH agent connection: %s",
-                          egg_error_message (error));
-               g_slice_free (Client, client);
-               return;
-       }
-
        socket_clients = g_list_append (socket_clients, client);
 }
 
@@ -312,8 +278,10 @@ gkd_ssh_agent_shutdown (void)
                client = l->data;
 
                /* Forcibly shutdown the connection */
-               if (client->sock != -1)
+               if (client->sock != -1) {
                        shutdown (client->sock, SHUT_RDWR);
+                       shutdown (client->agent, SHUT_RDWR);
+               }
                g_thread_join (client->thread);
 
                /* This is always closed by client thread */
@@ -323,93 +291,8 @@ gkd_ssh_agent_shutdown (void)
 
        g_list_free (socket_clients);
        socket_clients = NULL;
-}
-
-void
-gkd_ssh_agent_uninitialize (void)
-{
-       gboolean ret;
-
-       g_assert (pkcs11_main_mutex);
-       ret = g_mutex_trylock (pkcs11_main_mutex);
-       g_assert (ret);
-
-               g_assert (GCK_IS_SESSION (pkcs11_main_session));
-               g_assert (!pkcs11_main_checked);
-               g_object_unref (pkcs11_main_session);
-               pkcs11_main_session = NULL;
-
-       g_mutex_unlock (pkcs11_main_mutex);
-       g_mutex_clear (pkcs11_main_mutex);
-       g_free (pkcs11_main_mutex);
-       g_cond_clear (pkcs11_main_cond);
-       g_free (pkcs11_main_cond);
 
-       gck_list_unref_free (pkcs11_modules);
-       pkcs11_modules = NULL;
-}
-
-int
-gkd_ssh_agent_initialize (CK_FUNCTION_LIST_PTR funcs)
-{
-       GckModule *module;
-       gboolean ret;
-
-       g_return_val_if_fail (funcs, -1);
-
-       module = gck_module_new (funcs);
-       ret = gkd_ssh_agent_initialize_with_module (module);
-       g_object_unref (module);
-       return ret;
-}
-
-gboolean
-gkd_ssh_agent_initialize_with_module (GckModule *module)
-{
-       GckSession *session = NULL;
-       GList *slots, *l;
-       GArray *mechs;
-       GError *error = NULL;
-
-       g_assert (GCK_IS_MODULE (module));
-
-       /* Find a good slot for our session keys */
-       slots = gck_module_get_slots (module, TRUE);
-       for (l = slots; session == NULL && l; l = g_list_next (l)) {
-
-               /* Check that it has the mechanisms we need */
-               mechs = gck_slot_get_mechanisms (l->data);
-               if (gck_mechanisms_check (mechs, CKM_RSA_PKCS, CKM_DSA, GCK_INVALID)) {
-
-                       /* Try and open a session */
-                       session = gck_slot_open_session (l->data, GCK_SESSION_AUTHENTICATE, NULL, &error);
-                       if (!session) {
-                               g_warning ("couldn't create pkcs#11 session: %s", egg_error_message (error));
-                               g_clear_error (&error);
-                       }
-               }
-
-               g_array_unref (mechs);
-       }
-
-       gck_list_unref_free (slots);
-
-       if (!session) {
-               g_warning ("couldn't select a usable pkcs#11 slot for the ssh agent to use");
-               return FALSE;
-       }
-
-       g_assert (!pkcs11_modules);
-       pkcs11_modules = g_list_append (NULL, g_object_ref (module));
-
-       pkcs11_main_mutex = g_new0 (GMutex, 1);
-       g_mutex_init (pkcs11_main_mutex);
-       pkcs11_main_cond = g_new0 (GCond, 1);
-       g_cond_init (pkcs11_main_cond);
-       pkcs11_main_checked = FALSE;
-       pkcs11_main_session = session;
-
-       return TRUE;
+       gkd_ssh_agent_process_cleanup ();
 }
 
 int


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