[gnome-keyring/wip/dueno/ssh-agent: 9/24] More work on loading



commit c26929f96aa90666f011d9e72b23536652794cc8
Author: Stef Walter <stefw redhat com>
Date:   Sat Aug 30 07:11:49 2014 +0200

    More work on loading

 daemon/ssh-agent/Makefile.am             |    2 +
 daemon/ssh-agent/gkd-ssh-agent-client.c  |  179 ++++++--------
 daemon/ssh-agent/gkd-ssh-agent-client.h  |   37 +---
 daemon/ssh-agent/gkd-ssh-agent-ops.c     |   26 +-
 daemon/ssh-agent/gkd-ssh-agent-preload.c |  383 ++++++++++++++++++++++++++++
 daemon/ssh-agent/gkd-ssh-agent-preload.h |   42 +++
 daemon/ssh-agent/gkd-ssh-agent-private.h |    2 +-
 daemon/ssh-agent/gkd-ssh-agent.c         |    7 +-
 daemon/ssh-agent/gkd-ssh-openssh.h       |   29 ++
 daemon/ssh-agent/gkm-ssh-openssh.c       |  409 ++++++++++++++++++++++++++++++
 daemon/ssh-agent/test-ssh-openssh.c      |  138 ++++++++++
 11 files changed, 1098 insertions(+), 156 deletions(-)
---
diff --git a/daemon/ssh-agent/Makefile.am b/daemon/ssh-agent/Makefile.am
index 86b47ba..687db32 100644
--- a/daemon/ssh-agent/Makefile.am
+++ b/daemon/ssh-agent/Makefile.am
@@ -10,6 +10,8 @@ libgkd_ssh_agent_la_SOURCES = \
        daemon/ssh-agent/gkd-ssh-agent.h \
        daemon/ssh-agent/gkd-ssh-agent-client.h \
        daemon/ssh-agent/gkd-ssh-agent-client.c \
+       daemon/ssh-agent/gkd-ssh-agent-preload.h \
+       daemon/ssh-agent/gkd-ssh-agent-preload.c \
        daemon/ssh-agent/gkd-ssh-agent-private.h \
        daemon/ssh-agent/gkd-ssh-agent-ops.c
 libgkd_ssh_agent_la_CFLAGS = \
diff --git a/daemon/ssh-agent/gkd-ssh-agent-client.c b/daemon/ssh-agent/gkd-ssh-agent-client.c
index 3fa6d4d..9d5e8d5 100644
--- a/daemon/ssh-agent/gkd-ssh-agent-client.c
+++ b/daemon/ssh-agent/gkd-ssh-agent-client.c
@@ -22,141 +22,116 @@
 
 #include "gkd-ssh-agent-client.h"
 
-static gint
-agent_start (const char *socket)
+#include "daemon/gkd-util.h"
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <unistd.h>
+
+static gchar *ssh_agent_path = NULL;
+static GPid ssh_agent_pid;
+static GMutex ssh_agent_mutex;
+
+static void
+on_child_watch (GPid pid,
+                gint status,
+                gpointer user_data)
 {
-       const gchar *argv[] = { SSH_AGENT, "-d", "-a", socket, NULL };
-       gchar *standard_error = NULL;
-       gchar *standard_output = NULL;
        GError *error = NULL;
-       gint exit_status = 0;
-       gint ret = 0;
-       gchar *cmd;
-
-       if (!g_spawn_async ("/", argv, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_STDOUT_TO_DEV_NULL,
-                           NULL, NULL, 
-                           NULL, NULL, &standard_output,
-                           &standard_error, &exit_status, &error) ||
-           !g_spawn_check_exit_status (exit_status, NULL)) {
-               cmd = g_strjoinv (" ", argv);
-               if (error != NULL) {
-                       g_warning ("couldn't run: %s: %s", cmd, error->message);
-                       g_error_free (error);
-               } else {
-                       g_warning ("failed to run: %s", cmd);
-               }
-               g_free (cmd);
-
-       /* Sucessfully running, pull out the PID */
-       } else {
-               lines = g_strsplit (standard_output, "\n", -1);
-               for (i = 0; lines[i] != NULL; i++) {
-                       g_strstrip (lines[i]);
-                       if (g_str_has_prefix (lines[i], "SSH_AGENT_PID=")) {
-                               pid = lines[i] + 16;
-                               pos = strchr (pid, ';');
-                               if (pos != NULL)
-                                       pos[0] = '\0';
-                               ret = (int)strtol (pid, 10, &endptr);
-                               if (!endptr || endptr != '\0') {
-                                       g_warning ("invalid pid received from ssh-agent: %s", pid);
-                                       ret = 0;
-                               }
-                               break;
-                       }
-               }
-               g_strfreev (lines);
+
+       if (pid != ssh_agent_pid)
+               return;
+
+       g_mutex_lock (&ssh_agent_mutex);
+
+       ssh_agent_pid = 0;
+
+       if (!g_spawn_check_exit_status (status, &error)) {
+               g_message ("ssh-agent: %s", error->message);
+               g_error_free (error);
        }
 
-       if (standard_error) {
-               lines = g_strsplit (standard_error, "\n", -1);
-               for (i = 0; lines[i] != NULL; i++)
-                       g_warning ("%s", g_strchomp (lines[0]));
-               g_strfreev (lines);
+       g_mutex_unlock (&ssh_agent_mutex);
+}
+
+static gboolean
+agent_start_inlock (const char *socket)
+{
+       const gchar *argv[] = { SSH_AGENT, "-d", "-a", socket, NULL };
+       GError *error = NULL;
+       GPid pid;
+
+       if (!g_spawn_async ("/", (gchar **)argv, NULL,
+                           G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_STDOUT_TO_DEV_NULL,
+                           NULL, NULL, &pid, &error)) {
+               g_warning ("couldn't run %s: %s", SSH_AGENT, error->message);
+               g_error_free (error);
+               return FALSE;
        }
 
-       g_free (standard_error);
-       g_free (standard_output);
-       return ret;
+       ssh_agent_pid = pid;
+       g_child_watch_add (ssh_agent_pid, on_child_watch, NULL);
+       return TRUE;
 }
 
-gboolean
-agent_check (gint pid)
+static gboolean
+agent_check (GPid pid)
 {
        return pid && (kill (pid, 0) == 0);
 }
 
-void
+static void
 agent_terminate (gint pid)
 {
        kill (pid, SIGTERM);
 }
 
-static gchar *
-agent_make_path (void)
-{
-       const char *directory;
-
-       directory = gkd_util_master_directory ();
-}
-
-G_LOCK (ssh_agent_process);
-static gchar *ssh_agent_path = NULL;
-static gint ssh_agent_pid;
-
-GkdSshAgentClient *
+gint
 gkd_ssh_agent_client_connect (void)
 {
-       GSocketConnection *connection;
-       GSocketAddress *address;
+       struct sockaddr_un addr;
        const gchar *directory;
-       GError *error = NULL;
-       GSocket *sock;
        gboolean ready;
+       gint sock;
 
-       G_LOCK (ssh_agent_process);
+       g_mutex_lock (&ssh_agent_mutex);
 
-       if (ssh_agent_path) {
-               directory = gkd_util_master_directory ();
-               ssh_agent_path = g_build_filename (directory, "ssh-actual", NULL);
+       if (!ssh_agent_path) {
+               directory = gkd_util_get_master_directory ();
+               ssh_agent_path = g_build_filename (directory, "ssh-agent-real", NULL);
        }
 
        ready = agent_check (ssh_agent_pid);
-       if (!ready) {
-               ssh_agent_pid = agent_start (ssh_agent_path);
-               ready = (ssh_agent_pid != 0);
-       }
-
-       G_UNLOCK (ssh_agent_pid);
-
        if (!ready)
-               return NULL;
+               ready = agent_start_inlock (ssh_agent_path);
 
-       sock = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM,
-                            G_SOCKET_PROTOCOL_DEFAULT);
-       g_return_val_if_fail (sock != NULL, NULL);
+       addr.sun_family = AF_UNIX;
+       g_strlcpy (addr.sun_path, ssh_agent_path, sizeof (addr.sun_path));
 
-       connection = g_socket_connection_factory_create_connection (sock);
-       g_return_val_if_fail (connection != NULL, NULL);
-       g_object_unref (sock);
+       g_mutex_unlock (&ssh_agent_mutex);
+
+       if (!ready)
+               return -1;
 
-       address = g_unix_socket_address_new (ssh_agent_path);
-       g_return_val_if_fail (address != NULL, NULL);
+       sock = socket (AF_UNIX, SOCK_STREAM, 0);
+       g_return_val_if_fail (sock >= 0, -1);
 
-       if (!g_socket_connection_connect (connection, address, NULL, &error)) {
-               g_warning ("couldn't connect to ssh-agent: %s", error->message);
-               g_object_unref (connection);
-               connection = NULL;
+       if (connect (sock, (struct sockaddr*) &addr, sizeof (addr)) < 0) {
+               g_message ("couldn't connect to ssh-agent socket at: %s: %s",
+                          addr.sun_path, g_strerror (errno));
+               close (sock);
+               sock = -1;
        }
 
-       g_object_unref (address);
-       return connection;
+       return sock;
 }
 
 void
 gkd_ssh_agent_client_cleanup (void)
 {
-       G_LOCK (ssh_agent_process);
+       g_mutex_lock (&ssh_agent_mutex);
 
        if (ssh_agent_pid)
                agent_terminate (ssh_agent_pid);
@@ -165,13 +140,5 @@ gkd_ssh_agent_client_cleanup (void)
        g_free (ssh_agent_path);
        ssh_agent_path = NULL;
 
-       G_UNLOCK (ssh_agent_process);
-}
-
-gboolean
-gkd_ssh_agent_client_transact (GkdSshAgentClient *self,
-                               EggBuffer *req,
-                               EggBuffer *resp)
-{
-
+       g_mutex_unlock (&ssh_agent_mutex);
 }
diff --git a/daemon/ssh-agent/gkd-ssh-agent-client.h b/daemon/ssh-agent/gkd-ssh-agent-client.h
index cd5919d..c999073 100644
--- a/daemon/ssh-agent/gkd-ssh-agent-client.h
+++ b/daemon/ssh-agent/gkd-ssh-agent-client.h
@@ -24,41 +24,10 @@
 #ifndef __GKD_SSH_AGENT_CLIENT_H__
 #define __GKD_SSH_AGENT_CLIENT_H__
 
-#include <glib-object.h>
+#include <glib.h>
 
-#include "egg/egg-buffer.h"
+gint                 gkd_ssh_agent_client_connect             (void);
 
-#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);
-
-GkdSshAgentClient *  gkd_ssh_agent_client_connect             (void);
-
-gboolean             gkd_ssh_agent_client_transact            (GkdSshAgentClient *self,
-                                                               EggBuffer *req,
-                                                               EggBuffer *resp);
-
-GList *              gkd_ssh_agent_preload_keys               (GkdSshAgentClient *self);
-
-gchar *              gkd_ssh_agent_preload_comment            (GkdSshAgentClient *self,
-                                                               GBytes *key);
-
-GBytes *             gkd_ssh_agent_preload_unlock             (GkdSshAgentClient *self,
-                                                               GBytes *key);
-
-void                 gkd_ssh_agent_preload_clear              (GkdSshAgentClient *self,
-                                                               GBytes *key);
-
-void                 gkd_ssh_agent_preload_clear_all          (GkdSshAgentClient *self);
-
-void                 gkd_ssh_agent_preload_cleanup            (void);
+void                 gkd_ssh_agent_client_cleanup             (void);
 
 #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 3d9db37..73bf2ca 100644
--- a/daemon/ssh-agent/gkd-ssh-agent-ops.c
+++ b/daemon/ssh-agent/gkd-ssh-agent-ops.c
@@ -24,6 +24,7 @@
 #include "config.h"
 
 #include "gkd-ssh-agent-client.h"
+#include "gkd-ssh-agent-preload.h"
 #include "gkd-ssh-agent-private.h"
 
 #include "egg/egg-error.h"
@@ -59,14 +60,14 @@ op_add_identity (GkdSshAgentCall *call)
         * initial bytes as the public key we've loaded.
         */
 
-       keys = gkd_ssh_agent_client_preload_keys (call->agent);
+       keys = gkd_ssh_agent_preload_keys ();
 
        offset = 5;
        for (l = keys; l != NULL; l = g_list_next (l)) {
                blob = g_bytes_get_data (l->data, &length);
                if (call->req->len >= length + offset &&
                    memcmp (call->req->buf + offset, blob, length) == 0) {
-                       gkd_ssh_agent_client_preload_clear (call->agent, l->data);
+                       gkd_ssh_agent_preload_clear (l->data);
                        break;
                }
        }
@@ -133,13 +134,13 @@ op_request_identities (GkdSshAgentCall *call)
        added = 0;
 
        /* Add any preloaded keys not already in answer */
-       keys = gkd_ssh_agent_client_preload_keys (call->agent);
+       keys = gkd_ssh_agent_preload_keys ();
        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_client_preload_comment (call->agent, l->data);
+               comment = gkd_ssh_agent_preload_comment (l->data);
                egg_buffer_add_string (call->resp, comment ? comment : "");
                g_free (comment);
                added++;
@@ -155,7 +156,7 @@ op_request_identities (GkdSshAgentCall *call)
 }
 
 static void
-preload_key_if_necessary (GkdSshAgentClient *agent,
+preload_key_if_necessary (gint ssh_agent,
                           GBytes *key)
 {
        EggBuffer buf;
@@ -165,7 +166,7 @@ preload_key_if_necessary (GkdSshAgentClient *agent,
        GBytes *priv;
        guchar code;
 
-       priv = gkd_ssh_agent_client_preload_unlock (agent, key);
+       priv = gkd_ssh_agent_preload_private (key);
        if (!priv)
                return;
 
@@ -175,15 +176,16 @@ preload_key_if_necessary (GkdSshAgentClient *agent,
        blob = g_bytes_get_data (priv, &length);
        egg_buffer_add_byte_array (&buf, blob, length);
 
-       if (gkd_ssh_agent_client_transact (agent, &buf, &buf)) {
+       if (gkd_ssh_agent_write_packet (ssh_agent, &buf) &&
+           gkd_ssh_agent_read_packet (ssh_agent, &buf)) {
                if (!egg_buffer_get_byte (&buf, 4, NULL, &code) || code != GKD_SSH_RES_SUCCESS) {
-                       comment = gkd_ssh_agent_client_preload_comment (agent, key);
+                       comment = gkd_ssh_agent_preload_comment (key);
                        g_warning ("couldn't add private key '%s' to ssh-agent", comment);
                        g_free (comment);
                }
        }
 
-       gkd_ssh_agent_client_preload_clear (agent, key);
+       gkd_ssh_agent_preload_clear (key);
 }
 
 static gboolean
@@ -197,7 +199,7 @@ op_sign_request (GkdSshAgentCall *call)
        /* 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);
-               preload_key_if_necessary (call->agent, key);
+               preload_key_if_necessary (call->ssh_agent, key);
                g_bytes_unref (key);
        } else {
                g_warning ("got unparseable sign request for ssh-agent");
@@ -217,7 +219,7 @@ op_remove_identity (GkdSshAgentCall *call)
        /* 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_client_preload_clear (call->agent, key);
+               gkd_ssh_agent_preload_clear (key);
                g_bytes_unref (key);
        } else {
                g_warning ("got unparseable remove request for ssh-agent");
@@ -230,7 +232,7 @@ op_remove_identity (GkdSshAgentCall *call)
 static gboolean
 op_remove_all_identities (GkdSshAgentCall *call)
 {
-       gkd_ssh_agent_client_preload_clear_all (call->agent);
+       gkd_ssh_agent_preload_clear_all ();
        return gkd_ssh_agent_relay (call);
 }
 
diff --git a/daemon/ssh-agent/gkd-ssh-agent-preload.c b/daemon/ssh-agent/gkd-ssh-agent-preload.c
new file mode 100644
index 0000000..1eb63fa
--- /dev/null
+++ b/daemon/ssh-agent/gkd-ssh-agent-preload.c
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2014 Stef Walter
+ *
+ * Gnome keyring is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * Gnome keyring 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Stef Walter <stef thewalter net>
+ */
+
+#include "config.h"
+
+#include "gkd-ssh-agent-preload.h"
+#include "gkd-ssh-openssh.h"
+
+#include "egg/egg-file-tracker.h"
+
+#include <string.h>
+
+typedef struct {
+       gchar *filename;
+       GBytes *public_key;
+       GBytes *private_file;
+       gchar *comment;
+} Preload;
+
+static GMutex preload_mutex;
+static GHashTable *preloads_by_filename;
+static GHashTable *preloads_by_key;
+static EggFileTracker *file_tracker;
+
+static void
+preload_free (gpointer data)
+{
+       Preload *preload = data;
+       if (preload->private_file)
+               g_bytes_unref (preload->private_file);
+       g_bytes_unref (preload->public_key);
+       g_free (preload->comment);
+       g_free (preload->filename);
+       g_free (preload);
+}
+
+static gchar *
+private_path_for_public (const gchar *public_path)
+{
+       gsize length;
+
+       length = strlen (public_path);
+       if (length > 4 && g_str_equal (public_path + (length - 4), ".pub"))
+               return g_strndup (public_path, length - 4);
+
+       return NULL;
+}
+
+static GBytes *
+file_get_contents (const gchar *path,
+                   gboolean must_be_present)
+{
+       GError *error = NULL;
+       gchar *contents;
+       gsize length;
+
+       if (!g_file_get_contents (path, &contents, &length, &error)) {
+               if (must_be_present || error->code != G_FILE_ERROR_NOENT)
+                       g_message ("couldn't read file: %s: %s", path, error->message);
+               g_error_free (error);
+               return NULL;
+       }
+
+       return g_bytes_new_take (contents, length);
+}
+
+static void
+file_remove_inlock (EggFileTracker *tracker,
+                    const gchar *path,
+                    gpointer user_data)
+{
+       Preload *preload;
+
+       preload = g_hash_table_lookup (preloads_by_filename, path);
+       if (preload) {
+               g_hash_table_remove (preloads_by_filename, path);
+               g_hash_table_remove (preloads_by_key, preload->public_key);
+       }
+}
+
+static void
+file_load_inlock (EggFileTracker *tracker,
+                  const gchar *path,
+                  gpointer user_data)
+{
+       gchar *private_path;
+       GBytes *private_bytes;
+       GBytes *public_bytes;
+       GBytes *public_key;
+       Preload *preload;
+       gchar *comment;
+
+       file_remove_inlock (tracker, path, user_data);
+
+       private_path = private_path_for_public (path);
+
+       private_bytes = file_get_contents (private_path, FALSE);
+       if (!private_bytes) {
+               g_debug ("no private key present for public key: %s", path);
+               g_free (private_path);
+               return;
+       }
+
+       public_bytes = file_get_contents (path, TRUE);
+       if (public_bytes) {
+               public_key = gkd_ssh_openssh_parse_public_key (public_bytes, &comment);
+               if (public_key) {
+                       preload = g_new0 (Preload, 1);
+                       preload->filename = g_strdup (path);
+                       preload->public_key = public_key;
+                       preload->comment = comment;
+                       g_hash_table_replace (preloads_by_filename, preload->filename, preload);
+                       g_hash_table_replace (preloads_by_key, preload->public_key, preload);
+               } else {
+                       g_message ("failed to parse ssh public key: %s", path);
+               }
+
+               g_bytes_unref (public_bytes);
+       }
+
+       g_bytes_unref (private_bytes);
+       g_free (private_path);
+}
+
+static gboolean
+preload_lock_and_update (void)
+{
+       g_mutex_lock (&preload_mutex);
+
+       if (!preloads_by_filename)
+               preloads_by_filename = g_hash_table_new (g_str_hash, g_str_equal);
+
+       if (!preloads_by_key)
+               preloads_by_key = g_hash_table_new_full (g_bytes_hash, g_bytes_equal, NULL, preload_free);
+
+       if (!file_tracker) {
+               file_tracker = egg_file_tracker_new ("~/.ssh", "*.pub", NULL);
+               g_signal_connect (file_tracker, "file-added", G_CALLBACK (file_load_inlock), NULL);
+               g_signal_connect (file_tracker, "file-removed", G_CALLBACK (file_remove_inlock), NULL);
+       }
+
+       egg_file_tracker_refresh (file_tracker, FALSE);
+
+       return TRUE;
+}
+
+static void
+preload_unlock (void)
+{
+       g_mutex_unlock (&preload_mutex);
+}
+
+GList *
+gkd_ssh_agent_preload_keys (void)
+{
+       GList *keys = NULL;
+       GHashTableIter iter;
+       Preload *preload;
+
+       preload_lock_and_update ();
+
+       g_hash_table_iter_init (&iter, preloads_by_key);
+       while (g_hash_table_iter_next (&iter, NULL, (gpointer *)preload)) {
+               if (preload->private_file)
+                       keys = g_list_prepend (keys, g_bytes_ref (preload->public_key));
+       }
+
+       preload_unlock ();
+
+       return keys;
+}
+
+gchar *
+gkd_ssh_agent_preload_comment (GBytes *key)
+{
+       gchar *comment = NULL;
+       Preload *preload;
+
+       preload_lock_and_update ();
+
+       preload = g_hash_table_lookup (preloads_by_key, key);
+       if (preload)
+               comment = g_strdup (preload->comment);
+
+       preload_unlock ();
+
+       return comment;
+}
+
+gboolean
+gkd_ssh_agent_proto_write_pair_rsa (EggBuffer *req,
+                                    gsize *offset,
+                                    GckAttributes *priv_attrs,
+                                    GckAttributes *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_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;
+}
+
+static GBytes *
+encode_key_pair ()
+{
+
+}
+
+GBytes *
+gkd_ssh_agent_preload_private (GBytes *key)
+{
+       GcrParsed *parsed = NULL;
+       gchar *comment = NULL;
+       GBytes *contents = NULL;
+       GcrParser *parser;
+       Preload *preload;
+
+       preload_lock_and_update ();
+
+       preload = g_hash_table_lookup (preloads_by_key, key);
+       if (preload) {
+               if (preload->private_file)
+                       contents = g_bytes_ref (preload->private_file);
+               comments = g_strdup (preload->comment);
+       }
+
+       preload_unlock ();
+
+       if (!contents)
+               return NULL;
+
+       parser = gcr_parser_new ();
+       gcr_parser_format_disable (parser, GCR_FORMAT_ALL);
+       gcr_parser_format_enable (parser, GCR_FORMAT_PEM);
+       g_signal_connect (parser, "authenticate", G_CALLBACK (on_parser_authenticate), comment);
+       g_signal_connect (parser, "parsed", G_CALLBACK (on_parser_parsed), &parsed);
+
+       if (!gcr_parser_parse_bytes (parser, contents, &error))
+               g_message ("couldn't parse private key: %s: %s", comment, error->message);
+
+       g_free (comment);
+       g_bytes_unref (contents);
+
+       if (parsed) {
+               gcr_parsed_get_attributes ();
+       }
+
+
+       g_object_unref (parser);
+       /* TODO */
+}
+
+void
+gkd_ssh_agent_preload_clear (GBytes *key)
+{
+       Preload *preload;
+
+       preload_lock_and_update ();
+
+       preload = g_hash_table_lookup (preloads_by_key, key);
+       if (preload) {
+               g_bytes_unref (preload->private_file);
+               preload->private_file = NULL;
+       }
+
+       preload_unlock ();
+}
+
+void
+gkd_ssh_agent_preload_clear_all (void)
+{
+       GHashTableIter iter;
+       Preload *preload;
+
+       preload_lock_and_update ();
+
+       g_hash_table_iter_init (&iter, preloads_key_key);
+       while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&preload)) {
+               if (preload->private_file) {
+                       g_bytes_unref (preload->private_file);
+                       preload->private_file = NULL;
+               }
+       }
+
+       preload_unlock ();
+}
+
+void
+gkd_ssh_agent_preload_cleanup (void)
+{
+       g_mutex_lock (&preload_mutex);
+
+       if (preloads_by_key)
+               g_hash_table_destroy (prelodas_by_key);
+       preloads_by_key = NULL;
+
+       if (preloads_by_filename)
+               g_hash_table_destroy (preloads_by_filename);
+       preloads_by_filename = NULL;
+
+       if (file_tracker)
+               g_object_unref (file_tracker);
+       file_tracker = NULL;
+
+       g_mutex_unlock (&preload_mutex);
+}
diff --git a/daemon/ssh-agent/gkd-ssh-agent-preload.h b/daemon/ssh-agent/gkd-ssh-agent-preload.h
new file mode 100644
index 0000000..503b25e
--- /dev/null
+++ b/daemon/ssh-agent/gkd-ssh-agent-preload.h
@@ -0,0 +1,42 @@
+/*
+ * 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_PRELOAD_H__
+#define __GKD_SSH_AGENT_PRELOAD_H__
+
+#include <glib.h>
+
+GList *              gkd_ssh_agent_preload_keys               (void);
+
+gchar *              gkd_ssh_agent_preload_comment            (GBytes *key);
+
+GBytes *             gkd_ssh_agent_preload_private            (GBytes *key);
+
+void                 gkd_ssh_agent_preload_clear              (GBytes *key);
+
+void                 gkd_ssh_agent_preload_clear_all          (void);
+
+void                 gkd_ssh_agent_preload_cleanup            (void);
+
+#endif /* __GKD_SSH_AGENT_PRELOAD_H__ */
+
diff --git a/daemon/ssh-agent/gkd-ssh-agent-private.h b/daemon/ssh-agent/gkd-ssh-agent-private.h
index d6c79cd..d477bf6 100644
--- a/daemon/ssh-agent/gkd-ssh-agent-private.h
+++ b/daemon/ssh-agent/gkd-ssh-agent-private.h
@@ -33,7 +33,7 @@ typedef struct _GkdSshAgentCall {
        int sock;
        EggBuffer *req;
        EggBuffer *resp;
-       GkdSshAgentClient *agent;
+       gint ssh_agent;
 } GkdSshAgentCall;
 
 /* -----------------------------------------------------------------------------
diff --git a/daemon/ssh-agent/gkd-ssh-agent.c b/daemon/ssh-agent/gkd-ssh-agent.c
index ad84756..385cf97 100644
--- a/daemon/ssh-agent/gkd-ssh-agent.c
+++ b/daemon/ssh-agent/gkd-ssh-agent.c
@@ -137,7 +137,8 @@ gkd_ssh_agent_write_packet (gint fd,
 gboolean
 gkd_ssh_agent_relay (GkdSshAgentCall *call)
 {
-       return gkd_ssh_agent_client_transact (call->agent, call->req, call->resp);
+       return gkd_ssh_agent_write_packet (call->ssh_agent, call->req) &&
+              gkd_ssh_agent_read_packet (call->ssh_agent, call->resp);
 }
 
 static gpointer
@@ -159,8 +160,8 @@ run_client_thread (gpointer data)
        call.req = &req;
        call.resp = &resp;
 
-       call.agent = gkd_ssh_agent_client_connect ();
-       if (!call.agent)
+       call.ssh_agent = gkd_ssh_agent_client_connect ();
+       if (call.ssh_agent < 0)
                goto out;
 
        for (;;) {
diff --git a/daemon/ssh-agent/gkd-ssh-openssh.h b/daemon/ssh-agent/gkd-ssh-openssh.h
new file mode 100644
index 0000000..382e3c6
--- /dev/null
+++ b/daemon/ssh-agent/gkd-ssh-openssh.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 Stef Walter
+ *
+ * Gnome keyring is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * Gnome keyring 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Stef Walter <stef thewalter net>
+ */
+
+#ifndef GKD_SSH_OPENSSH_H_
+#define GKD_SSH_OPENSSH_H_
+
+#include <glib.h>
+
+GBytes *         gkd_ssh_openssh_parse_public_key      (GBytes *data,
+                                                        gchar **comment);
+
+#endif /* GKD_SSH_OPENSSH_H_ */
diff --git a/daemon/ssh-agent/gkm-ssh-openssh.c b/daemon/ssh-agent/gkm-ssh-openssh.c
new file mode 100644
index 0000000..c3a59a8
--- /dev/null
+++ b/daemon/ssh-agent/gkm-ssh-openssh.c
@@ -0,0 +1,409 @@
+
+#include "gkm-ssh-openssh.h"
+
+#include "gkm/gkm-data-asn1.h"
+#include "gkm/gkm-data-der.h"
+#include "gkm/gkm-data-types.h"
+
+#include "egg/egg-armor.h"
+#include "egg/egg-asn1x.h"
+#include "egg/egg-buffer.h"
+#include "egg/egg-openssl.h"
+#include "egg/egg-secure-memory.h"
+
+typedef struct _ParsePrivate {
+       gcry_sexp_t sexp;
+       gboolean seen;
+       GkmDataResult result;
+       const gchar *password;
+       gssize n_password;
+} ParsePrivate;
+
+/* ------------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+static int
+keytype_to_algo (const gchar *salgo)
+{
+       g_return_val_if_fail (salgo, 0);
+       if (strcmp (salgo, "ssh-rsa") == 0)
+               return GCRY_PK_RSA;
+       else if (strcmp (salgo, "ssh-dss") == 0)
+               return GCRY_PK_DSA;
+       return 0;
+}
+
+static gboolean
+read_mpi (EggBuffer *req, gsize *offset, gcry_mpi_t *mpi)
+{
+       const guchar *data;
+       gsize len;
+       gcry_error_t gcry;
+
+       if (!egg_buffer_get_byte_array (req, *offset, offset, &data, &len))
+               return FALSE;
+
+       gcry = gcry_mpi_scan (mpi, GCRYMPI_FMT_USG, data, len, NULL);
+       if (gcry)
+               return FALSE;
+
+       return TRUE;
+}
+
+#define SEXP_PUBLIC_DSA  \
+       "(public-key"    \
+       "  (dsa"         \
+       "    (p %m)"     \
+       "    (q %m)"     \
+       "    (g %m)"     \
+       "    (y %m)))"
+
+static gboolean
+read_public_dsa (EggBuffer *req, gsize *offset, gcry_sexp_t *sexp)
+{
+       gcry_mpi_t p, q, g, y;
+       int gcry;
+
+       if (!read_mpi (req, offset, &p) ||
+           !read_mpi (req, offset, &q) ||
+           !read_mpi (req, offset, &g) ||
+           !read_mpi (req, offset, &y))
+               return FALSE;
+
+       gcry = gcry_sexp_build (sexp, NULL, SEXP_PUBLIC_DSA, p, q, g, y);
+       if (gcry) {
+               g_warning ("couldn't parse incoming public DSA key: %s", gcry_strerror (gcry));
+               return FALSE;
+       }
+
+       gcry_mpi_release (p);
+       gcry_mpi_release (q);
+       gcry_mpi_release (g);
+       gcry_mpi_release (y);
+
+       return TRUE;
+}
+
+#define SEXP_PUBLIC_RSA  \
+       "(public-key"    \
+       "  (rsa"         \
+       "    (n %m)"     \
+       "    (e %m)))"
+
+static gboolean
+read_public_rsa (EggBuffer *req, gsize *offset, gcry_sexp_t *sexp)
+{
+       gcry_mpi_t n, e;
+       int gcry;
+
+       if (!read_mpi (req, offset, &e) ||
+           !read_mpi (req, offset, &n))
+               return FALSE;
+
+       gcry = gcry_sexp_build (sexp, NULL, SEXP_PUBLIC_RSA, n, e);
+       if (gcry) {
+               g_warning ("couldn't parse incoming public RSA key: %s", gcry_strerror (gcry));
+               return FALSE;
+       }
+
+       gcry_mpi_release (n);
+       gcry_mpi_release (e);
+
+       return TRUE;
+}
+
+static gboolean
+read_public (EggBuffer *req, gsize *offset, gcry_sexp_t *key, int *algo)
+{
+       gboolean ret;
+       gchar *stype;
+       int alg;
+
+       /* The string algorithm */
+       if (!egg_buffer_get_string (req, *offset, offset, &stype, (EggBufferAllocator)g_realloc))
+               return FALSE;
+
+       alg = keytype_to_algo (stype);
+       g_free (stype);
+
+       if (!alg) {
+               g_warning ("unsupported algorithm from SSH: %s", stype);
+               return FALSE;
+       }
+
+       switch (alg) {
+       case GCRY_PK_RSA:
+               ret = read_public_rsa (req, offset, key);
+               break;
+       case GCRY_PK_DSA:
+               ret = read_public_dsa (req, offset, key);
+               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 TRUE;
+}
+
+static GkmDataResult
+load_encrypted_key (GBytes *data,
+                    const gchar *dekinfo,
+                    const gchar *password,
+                    gssize n_password,
+                    gcry_sexp_t *skey)
+{
+       guchar *decrypted = NULL;
+       gsize n_decrypted = 0;
+       GBytes *bytes;
+       GkmDataResult ret;
+       gint length;
+
+       /* Decrypt, this will result in garble if invalid password */
+       decrypted = egg_openssl_decrypt_block (dekinfo, password, n_password,
+                                              data, &n_decrypted);
+       if (!decrypted)
+               return FALSE;
+
+       /* Unpad the DER data */
+       length = egg_asn1x_element_length (decrypted, n_decrypted);
+       if (length > 0)
+               n_decrypted = length;
+
+       bytes = g_bytes_new_with_free_func (decrypted, n_decrypted, egg_secure_free, decrypted);
+
+       /* Try to parse */
+       ret = gkm_data_der_read_private_key (bytes, skey);
+       g_bytes_unref (bytes);
+
+       if (ret != GKM_DATA_UNRECOGNIZED)
+               return ret;
+
+       return GKM_DATA_LOCKED;
+}
+
+static gboolean
+is_private_key_type (GQuark type)
+{
+       static GQuark PEM_RSA_PRIVATE_KEY;
+       static GQuark PEM_DSA_PRIVATE_KEY;
+       static gsize quarks_inited = 0;
+
+       /* Initialize the first time through */
+       if (g_once_init_enter (&quarks_inited)) {
+               PEM_RSA_PRIVATE_KEY = g_quark_from_static_string ("RSA PRIVATE KEY");
+               PEM_DSA_PRIVATE_KEY = g_quark_from_static_string ("DSA PRIVATE KEY");
+               g_once_init_leave (&quarks_inited, 1);
+       }
+
+       /* Only handle SSHv2 private keys */
+       return (type == PEM_RSA_PRIVATE_KEY || type == PEM_DSA_PRIVATE_KEY);
+}
+
+static void
+parsed_pem_block (GQuark type,
+                  GBytes *data,
+                  GBytes *outer,
+                  GHashTable *headers,
+                  gpointer user_data)
+{
+       ParsePrivate *ctx = (ParsePrivate*)user_data;
+       const gchar *dekinfo;
+
+       if (!is_private_key_type (type))
+               return;
+
+       ctx->seen = TRUE;
+
+       /* Only parse first key in the file */
+       if (ctx->sexp)
+               return;
+
+       /* If it's encrypted ... */
+       dekinfo = egg_openssl_get_dekinfo (headers);
+       if (dekinfo) {
+               ctx->result = load_encrypted_key (data, dekinfo, ctx->password,
+                                                 ctx->n_password, &ctx->sexp);
+
+       /* not encryted, just load the data */
+       } else {
+               ctx->result = gkm_data_der_read_private_key (data, &ctx->sexp);
+       }
+}
+
+static void
+digest_pem_block (GQuark type,
+                  GBytes *data,
+                  GBytes *outer,
+                  GHashTable *headers,
+                  gpointer user_data)
+{
+       gchar **result = (gchar**)user_data;
+
+       g_assert (result);
+
+       if (!is_private_key_type (type))
+               return;
+
+       /* Only digest the first key in the file */
+       if (*result != NULL)
+               return;
+
+       *result = g_compute_checksum_for_data (G_CHECKSUM_SHA1,
+                                              g_bytes_get_data (data, NULL),
+                                              g_bytes_get_size (data));
+}
+
+/* ------------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+GkmDataResult
+gkm_ssh_openssh_parse_public_key (gconstpointer input, gsize n_data,
+                                  gcry_sexp_t *sexp, gchar **comment)
+{
+       EggBuffer buf;
+       const guchar *at;
+       guchar *decoded;
+       gsize n_decoded;
+       gsize offset;
+       gchar *val;
+       gboolean ret;
+       gint state, algo;
+       guint save;
+       const guchar *data = input;
+
+       g_return_val_if_fail (data, FALSE);
+       g_return_val_if_fail (sexp, FALSE);
+
+       /* Look for a key line */
+       for (;;) {
+               /* Eat space at the front */
+               while (n_data > 0 && g_ascii_isspace (data[0])) {
+                       ++data;
+                       --n_data;
+               }
+
+               /* Not a comment or blank line? Then parse... */
+               if (data[0] != '#')
+                       break;
+
+               /* Skip to the next line */
+               at = memchr (data, '\n', n_data);
+               if (!at)
+                       return GKM_DATA_UNRECOGNIZED;
+               at += 1;
+               n_data -= (at - data);
+               data = at;
+       }
+
+       /* Limit to use only the first line */
+       at = memchr (data, '\n', n_data);
+       if (at != NULL)
+               n_data = at - data;
+
+       /* Find the first space */
+       at = memchr (data, ' ', n_data);
+       if (!at) {
+               g_message ("SSH public key missing space");
+               return GKM_DATA_UNRECOGNIZED;
+       }
+
+       /* Parse the key type */
+       val = g_strndup ((gchar*)data, at - data);
+       algo = keytype_to_algo (val);
+       if (!algo) {
+               /* A number usually means an SSH1 key, just quietly ignore */
+               if (atoi (val) == 0)
+                       g_message ("Unsupported or unknown SSH key algorithm: %s", val);
+       }
+       g_free (val);
+       if (!algo)
+               return GKM_DATA_UNRECOGNIZED;
+
+       /* Skip more whitespace */
+       n_data -= (at - data);
+       data = at;
+       while (n_data > 0 && (data[0] == ' ' || data[0] == '\t')) {
+               ++data;
+               --n_data;
+       }
+
+       /* Find the next whitespace, or the end */
+       at = memchr (data, ' ', n_data);
+       if (at == NULL)
+               at = data + n_data;
+
+       /* Decode the base64 key */
+       save = state = 0;
+       decoded = g_malloc (n_data * 3 / 4);
+       n_decoded = g_base64_decode_step ((gchar*)data, n_data, decoded, &state, &save);
+
+       /* Parse the actual key */
+       egg_buffer_init_static (&buf, decoded, n_decoded);
+       offset = 0;
+       ret = read_public (&buf, &offset, sexp, NULL);
+       g_free (decoded);
+       if (!ret) {
+               g_message ("failed to parse base64 part of SSH key");
+               return GKM_DATA_FAILURE;
+       }
+
+       /* Skip more whitespace */
+       n_data -= (at - data);
+       data = at;
+       while (n_data > 0 && (data[0] == ' ' || data[0] == '\t')) {
+               ++data;
+               --n_data;
+       }
+
+       /* If there's data left, its the comment */
+       if (comment)
+               *comment = n_data ? g_strndup ((gchar*)data, n_data) : NULL;
+
+       return GKM_DATA_SUCCESS;
+}
+
+GkmDataResult
+gkm_ssh_openssh_parse_private_key (GBytes *data,
+                                   const gchar *password,
+                                   gssize n_password,
+                                   gcry_sexp_t *sexp)
+{
+       ParsePrivate ctx;
+       guint num;
+
+       memset (&ctx, 0, sizeof (ctx));
+       ctx.result = FALSE;
+       ctx.seen = FALSE;
+       ctx.sexp = NULL;
+       ctx.password = password;
+       ctx.n_password = n_password;
+
+       num = egg_armor_parse (data, parsed_pem_block, &ctx);
+
+       /* Didn't find any private key there */
+       if (num == 0 || !ctx.seen) {
+               g_message ("no private keys found in file");
+               return GKM_DATA_UNRECOGNIZED;
+       }
+
+       *sexp = ctx.sexp;
+       return ctx.result;
+}
+
+gchar *
+gkm_ssh_openssh_digest_private_key (GBytes *data)
+{
+       gchar *result = NULL;
+       egg_armor_parse (data, digest_pem_block, &result);
+       return result;
+}
diff --git a/daemon/ssh-agent/test-ssh-openssh.c b/daemon/ssh-agent/test-ssh-openssh.c
new file mode 100644
index 0000000..e1c4fb7
--- /dev/null
+++ b/daemon/ssh-agent/test-ssh-openssh.c
@@ -0,0 +1,138 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+   Copyright (C) 2008 Stefan Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   <http://www.gnu.org/licenses/>.
+
+   Author: Stef Walter <stef memberwebs com>
+*/
+
+#include "config.h"
+
+#include "egg/egg-secure-memory.h"
+
+#include "ssh-store/gkm-ssh-openssh.h"
+
+#include "gkm/gkm-sexp.h"
+
+#include <glib.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+EGG_SECURE_DEFINE_GLIB_GLOBALS ();
+
+static const gchar *PRIVATE_FILES[] = {
+       SRCDIR "/pkcs11/ssh-store/fixtures/id_rsa_encrypted",
+       SRCDIR "/pkcs11/ssh-store/fixtures/id_rsa_plain",
+       SRCDIR "/pkcs11/ssh-store/fixtures/id_dsa_encrypted",
+       SRCDIR "/pkcs11/ssh-store/fixtures/id_dsa_plain"
+};
+
+static const gchar *PUBLIC_FILES[] = {
+       SRCDIR "/pkcs11/ssh-store/fixtures/id_rsa_test.pub",
+       SRCDIR "/pkcs11/ssh-store/fixtures/id_dsa_test.pub"
+};
+
+#define COMMENT "A public key comment"
+
+static void
+test_parse_public (void)
+{
+       gcry_sexp_t sexp;
+       gchar *comment;
+       gchar *data;
+       gsize n_data;
+       int algorithm;
+       gboolean is_private;
+       guint i;
+       GkmDataResult res;
+
+
+       for (i = 0; i < G_N_ELEMENTS (PUBLIC_FILES); ++i) {
+
+               if (!g_file_get_contents (PUBLIC_FILES[i], &data, &n_data, NULL))
+                       g_assert_not_reached ();
+
+               res = gkm_ssh_openssh_parse_public_key (data, n_data, &sexp, &comment);
+               if (res != GKM_DATA_SUCCESS) {
+                       g_warning ("couldn't parse public key: %s", PUBLIC_FILES[i]);
+                       g_assert_cmpint (res, ==, GKM_DATA_SUCCESS);
+               }
+
+               if (!gkm_sexp_parse_key (sexp, &algorithm, &is_private, NULL))
+                       g_assert_not_reached ();
+
+               g_assert_cmpstr (comment, ==, COMMENT);
+               g_assert_cmpint (algorithm, !=, 0);
+               g_assert (!is_private);
+
+               g_free (data);
+               g_free (comment);
+               gcry_sexp_release (sexp);
+       }
+}
+
+static void
+test_parse_private (void)
+{
+       gcry_sexp_t sexp;
+       gchar *data;
+       gsize n_data;
+       int algorithm;
+       gboolean is_private;
+       GBytes *bytes;
+       guint i;
+       GkmDataResult res;
+
+
+       for (i = 0; i < G_N_ELEMENTS (PRIVATE_FILES); ++i) {
+
+               if (!g_file_get_contents (PRIVATE_FILES[i], &data, &n_data, NULL))
+                       g_assert_not_reached ();
+
+               bytes = g_bytes_new_take (data, n_data);
+               res = gkm_ssh_openssh_parse_private_key (bytes, "password", 8, &sexp);
+               g_bytes_unref (bytes);
+
+               if (res != GKM_DATA_SUCCESS) {
+                       g_warning ("couldn't parse private key: %s", PRIVATE_FILES[i]);
+                       g_assert_cmpint (res, ==, GKM_DATA_SUCCESS);
+               }
+
+               if (!gkm_sexp_parse_key (sexp, &algorithm, &is_private, NULL))
+                       g_assert_not_reached ();
+
+               g_assert_cmpint (algorithm, !=, 0);
+               g_assert (is_private);
+
+               gcry_sexp_release (sexp);
+       }
+}
+
+int
+main (int argc, char **argv)
+{
+#if !GLIB_CHECK_VERSION(2,35,0)
+       g_type_init ();
+#endif
+       g_test_init (&argc, &argv, NULL);
+
+       g_test_add_func ("/ssh-store/openssh/parse_private", test_parse_private);
+       g_test_add_func ("/ssh-store/openssh/parse_public", test_parse_public);
+
+       return g_test_run ();
+}



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