summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaiki Ueno <dueno@src.gnome.org>2017-12-12 16:39:13 +0100
committerDaiki Ueno <dueno@src.gnome.org>2018-02-12 15:11:38 +0100
commit907605e1d4586e74740a192ffe56e1996c9d8608 (patch)
treec3a1729b34f89cc0bc945fc976888a3c0b177aa7
parenta56f794bebb0f1b0c57017ab8b7ba69e6475ae85 (diff)
downloadgnome-keyring-907605e1d4586e74740a192ffe56e1996c9d8608.tar.gz
ssh-agent: rewrite subprocess handling
-rw-r--r--daemon/ssh-agent/Makefile.am4
-rw-r--r--daemon/ssh-agent/gkd-ssh-agent-client.c214
-rw-r--r--daemon/ssh-agent/gkd-ssh-agent-client.h33
-rw-r--r--daemon/ssh-agent/gkd-ssh-agent-ops.c88
-rw-r--r--daemon/ssh-agent/gkd-ssh-agent-private.h6
-rw-r--r--daemon/ssh-agent/gkd-ssh-agent-process.c311
-rw-r--r--daemon/ssh-agent/gkd-ssh-agent-process.h47
-rw-r--r--daemon/ssh-agent/gkd-ssh-agent.c17
8 files changed, 423 insertions, 297 deletions
diff --git a/daemon/ssh-agent/Makefile.am b/daemon/ssh-agent/Makefile.am
index ca41d82f..73a2edf9 100644
--- a/daemon/ssh-agent/Makefile.am
+++ b/daemon/ssh-agent/Makefile.am
@@ -8,8 +8,8 @@ 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-client.c \
+ daemon/ssh-agent/gkd-ssh-agent-process.h \
+ daemon/ssh-agent/gkd-ssh-agent-process.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 \
diff --git a/daemon/ssh-agent/gkd-ssh-agent-client.c b/daemon/ssh-agent/gkd-ssh-agent-client.c
deleted file mode 100644
index 2cf44c43..00000000
--- a/daemon/ssh-agent/gkd-ssh-agent-client.c
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * 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-client.h"
-#include "gkd-ssh-agent-private.h"
-
-#include "daemon/gkd-util.h"
-
-#include <glib-unix.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 gint ssh_agent_out = -1;
-static guint ssh_agent_watch;
-static gboolean ssh_agent_ready;
-static GMutex ssh_agent_mutex;
-
-static void
-on_child_watch (GPid pid,
- gint status,
- gpointer user_data)
-{
- GError *error = NULL;
-
- 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);
- }
-
- g_mutex_unlock (&ssh_agent_mutex);
-}
-
-static gboolean
-agent_watch_output (gint fd,
- GIOCondition condition,
- gpointer user_data)
-{
- guint8 buf[1024];
- gssize len;
-
- if (condition & G_IO_IN) {
-g_message ("setting ready");
- ssh_agent_ready = TRUE;
-
- len = read (fd, buf, sizeof (buf));
- if (len < 0) {
- if (errno != EAGAIN && errno != EINTR)
- g_message ("couldn't read from ssh-agent stdout: %m");
- condition |= G_IO_ERR;
- } else if (len > 0) {
- gkd_ssh_agent_write_all (1, buf, len, "stdout");
- }
- }
-
- if (condition & G_IO_HUP || condition & G_IO_ERR) {
- ssh_agent_watch = 0;
- return FALSE;
- }
-
- return TRUE;
-}
-
-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_with_pipes ("/", (gchar **)argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
- NULL, NULL, &pid, NULL, &ssh_agent_out, NULL, &error)) {
- g_warning ("couldn't run %s: %s", SSH_AGENT, error->message);
- g_error_free (error);
- return FALSE;
- }
-
- ssh_agent_ready = FALSE;
- ssh_agent_watch = g_unix_fd_add (ssh_agent_out,
- G_IO_IN | G_IO_HUP | G_IO_ERR,
- agent_watch_output, NULL);
-
- ssh_agent_pid = pid;
- g_child_watch_add (ssh_agent_pid, on_child_watch, NULL);
- return TRUE;
-}
-
-static gboolean
-agent_check (GPid pid)
-{
- return pid && (kill (pid, 0) == 0);
-}
-
-static void
-agent_terminate (gint pid)
-{
- kill (pid, SIGTERM);
-}
-
-static gboolean
-agent_ready_timeout (gpointer user_data)
-{
- gboolean *timedout = user_data;
-g_message ("timed out");
- *timedout = TRUE;
- return TRUE;
-}
-
-gint
-gkd_ssh_agent_client_connect (void)
-{
- gboolean started = FALSE;
- struct sockaddr_un addr;
- const gchar *directory;
- gboolean timedout = FALSE;
- guint source;
- gint sock;
-
- g_mutex_lock (&ssh_agent_mutex);
-
- if (!ssh_agent_path) {
- directory = gkd_util_get_master_directory ();
- ssh_agent_path = g_build_filename (directory, "ssh-agent-real", NULL);
- }
-
- ssh_agent_ready = agent_check (ssh_agent_pid);
- if (!ssh_agent_ready)
- started = agent_start_inlock (ssh_agent_path);
-
- addr.sun_family = AF_UNIX;
- g_strlcpy (addr.sun_path, ssh_agent_path, sizeof (addr.sun_path));
-
- g_mutex_unlock (&ssh_agent_mutex);
-
- if (started && !ssh_agent_ready) {
- source = g_timeout_add_seconds (5, agent_ready_timeout, &timedout);
- while (started && !ssh_agent_ready && !timedout) {
-g_message ("waiting for agent: %u", (guint)timedout);
- g_main_context_iteration (NULL, FALSE);
- }
- g_source_remove (source);
-g_message ("waited for agent");
- }
-
- if (!ssh_agent_ready)
- return -1;
-
- sock = socket (AF_UNIX, SOCK_STREAM, 0);
- g_return_val_if_fail (sock >= 0, -1);
-
- 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;
- }
-
- return sock;
-}
-
-void
-gkd_ssh_agent_client_cleanup (void)
-{
- g_mutex_lock (&ssh_agent_mutex);
-
- if (ssh_agent_pid)
- agent_terminate (ssh_agent_pid);
- ssh_agent_pid = 0;
-
- if (ssh_agent_watch)
- g_source_remove (ssh_agent_watch);
- ssh_agent_watch = 0;
- ssh_agent_ready = FALSE;
-
- if (ssh_agent_out != -1)
- close (ssh_agent_out);
- ssh_agent_out = -1;
-
- g_free (ssh_agent_path);
- ssh_agent_path = NULL;
-
- 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
deleted file mode 100644
index c9990738..00000000
--- a/daemon/ssh-agent/gkd-ssh-agent-client.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.h>
-
-gint gkd_ssh_agent_client_connect (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 0669d0ef..eb944762 100644
--- a/daemon/ssh-agent/gkd-ssh-agent-ops.c
+++ b/daemon/ssh-agent/gkd-ssh-agent-ops.c
@@ -23,7 +23,6 @@
#include "config.h"
-#include "gkd-ssh-agent-client.h"
#include "gkd-ssh-agent-preload.h"
#include "gkd-ssh-agent-private.h"
#include "gkd-ssh-interaction.h"
@@ -47,37 +46,31 @@ EGG_SECURE_DECLARE (ssh_agent_ops);
static gboolean
op_add_identity (GkdSshAgentCall *call)
{
- GList *keys;
- gsize offset;
- gconstpointer blob;
+ const guchar *blob;
+ gsize offset = 5;
gsize length;
- GList *l;
+ GBytes *key = NULL;
+ gboolean ret;
- /*
- * 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.
- */
+ /* If parsing the request fails, just pass through */
+ if (egg_buffer_get_byte_array (call->req, offset, &offset, &blob, &length)) {
+ key = g_bytes_new (blob, length);
+ } else {
+ g_warning ("got unparseable add identity request for ssh-agent");
+ }
- keys = gkd_ssh_agent_preload_keys ();
+ ret = gkd_ssh_agent_process_call (call->process, call->req, call->resp);
- 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_preload_clear (l->data);
- break;
+ if (key) {
+ /* Move the key from preloaded list to loaded list */
+ if (ret) {
+ gkd_ssh_agent_preload_clear (key);
+ gkd_ssh_agent_process_add_key (call->process, key);
}
+ g_bytes_unref (key);
}
- g_list_free_full (keys, (GDestroyNotify)g_bytes_unref);
-
- return gkd_ssh_agent_relay (call);
+ return ret;
}
static GHashTable *
@@ -119,14 +112,16 @@ static gboolean
op_request_identities (GkdSshAgentCall *call)
{
GHashTable *answer;
+ GHashTableIter iter;
const guchar *blob;
gchar *comment;
gsize length;
guint32 added;
+ GBytes *key;
GList *keys;
GList *l;
- if (!gkd_ssh_agent_relay (call))
+ if (!gkd_ssh_agent_process_call (call->process, call->req, call->resp))
return FALSE;
/* Parse all the keys, and if it fails, just fall through */
@@ -134,6 +129,10 @@ op_request_identities (GkdSshAgentCall *call)
if (!answer)
return TRUE;
+ g_hash_table_iter_init (&iter, answer);
+ while (g_hash_table_iter_next (&iter, (gpointer *)&key, NULL))
+ gkd_ssh_agent_process_add_key (call->process, key);
+
added = 0;
/* Add any preloaded keys not already in answer */
@@ -162,8 +161,8 @@ op_request_identities (GkdSshAgentCall *call)
}
static void
-preload_key_if_necessary (gint ssh_agent,
- GBytes *key)
+load_key_if_necessary (GkdSshAgentCall *call,
+ GBytes *key)
{
GTlsInteraction *interaction;
GcrSshAskpass *askpass;
@@ -181,6 +180,9 @@ preload_key_if_necessary (gint ssh_agent,
if (!filename)
return;
+ if (gkd_ssh_agent_process_lookup_key (call->process, key))
+ return;
+
interaction = gkd_ssh_interaction_new (key);
askpass = gcr_ssh_askpass_new (interaction);
g_object_unref (interaction);
@@ -194,7 +196,9 @@ preload_key_if_necessary (gint ssh_agent,
g_message ("the %s command failed: %s", argv[0], error->message);
} else {
+ /* Move the key from preloaded list to loaded list */
gkd_ssh_agent_preload_clear (key);
+ gkd_ssh_agent_process_add_key (call->process, key);
}
g_clear_error (&error);
@@ -213,13 +217,13 @@ op_sign_request (GkdSshAgentCall *call)
/* If parsing the request fails, just pass through */
if (egg_buffer_get_byte_array (call->req, offset, &offset, &blob, &length)) {
key = g_bytes_new (blob, length);
- preload_key_if_necessary (call->ssh_agent, key);
+ load_key_if_necessary (call, key);
g_bytes_unref (key);
} else {
g_warning ("got unparseable sign request for ssh-agent");
}
- return gkd_ssh_agent_relay (call);
+ return gkd_ssh_agent_process_call (call->process, call->req, call->resp);
}
static gboolean
@@ -228,26 +232,38 @@ op_remove_identity (GkdSshAgentCall *call)
const guchar *blob;
gsize length;
gsize offset = 5;
- GBytes *key;
+ GBytes *key = NULL;
+ gboolean ret;
/* 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_preload_clear (key);
- g_bytes_unref (key);
} else {
g_warning ("got unparseable remove request for ssh-agent");
}
/* If the key doesn't exist what happens here? */
- return gkd_ssh_agent_relay (call);
+ ret = gkd_ssh_agent_process_call (call->process, call->req, call->resp);
+ if (key) {
+ if (ret) {
+ gkd_ssh_agent_preload_clear (key);
+ gkd_ssh_agent_process_remove_key (call->process, key);
+ }
+ g_bytes_unref (key);
+ }
+ return ret;
}
static gboolean
op_remove_all_identities (GkdSshAgentCall *call)
{
- gkd_ssh_agent_preload_clear_all ();
- return gkd_ssh_agent_relay (call);
+ if (gkd_ssh_agent_process_call (call->process, call->req, call->resp)) {
+ gkd_ssh_agent_preload_clear_all ();
+ gkd_ssh_agent_process_clear_keys (call->process);
+ return TRUE;
+ }
+
+ return FALSE;
}
const GkdSshAgentOperation gkd_ssh_agent_operations[GKD_SSH_OP_MAX] = {
diff --git a/daemon/ssh-agent/gkd-ssh-agent-private.h b/daemon/ssh-agent/gkd-ssh-agent-private.h
index 159475e4..0ca97ace 100644
--- a/daemon/ssh-agent/gkd-ssh-agent-private.h
+++ b/daemon/ssh-agent/gkd-ssh-agent-private.h
@@ -23,7 +23,7 @@
#ifndef GKDSSHPRIVATE_H_
#define GKDSSHPRIVATE_H_
-#include "gkd-ssh-agent-client.h"
+#include "gkd-ssh-agent-process.h"
#include "egg/egg-buffer.h"
@@ -33,7 +33,7 @@ typedef struct _GkdSshAgentCall {
int sock;
EggBuffer *req;
EggBuffer *resp;
- gint ssh_agent;
+ GkdSshAgentProcess *process;
} GkdSshAgentCall;
/* -----------------------------------------------------------------------------
@@ -97,8 +97,6 @@ gboolean gkd_ssh_agent_read_packet (gint fd,
gboolean gkd_ssh_agent_write_packet (gint fd,
EggBuffer *buffer);
-gboolean gkd_ssh_agent_relay (GkdSshAgentCall *call);
-
gboolean gkd_ssh_agent_write_all (int fd,
const guchar *buf,
int len,
diff --git a/daemon/ssh-agent/gkd-ssh-agent-process.c b/daemon/ssh-agent/gkd-ssh-agent-process.c
new file mode 100644
index 00000000..268b87fb
--- /dev/null
+++ b/daemon/ssh-agent/gkd-ssh-agent-process.c
@@ -0,0 +1,311 @@
+/*
+ * 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-process.h"
+#include "gkd-ssh-agent-private.h"
+
+#include "daemon/gkd-util.h"
+
+#include <glib-unix.h>
+#include <glib/gstdio.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <unistd.h>
+
+enum {
+ PROP_0,
+ PROP_PATH
+};
+
+struct _GkdSshAgentProcess
+{
+ GObject object;
+ gchar *path;
+ gint socket_fd; /* socket opened by the ssh-agent process */
+ gint output_fd; /* stdout of the ssh-agent process */
+ GHashTable *keys; /* keys actually known to the ssh-agent process */
+ GMutex lock;
+ GPid pid;
+ guint output_id;
+ guint child_id;
+ gboolean ready;
+};
+
+G_DEFINE_TYPE (GkdSshAgentProcess, gkd_ssh_agent_process, G_TYPE_OBJECT);
+
+static void
+gkd_ssh_agent_process_init (GkdSshAgentProcess *self)
+{
+ self->socket_fd = -1;
+ self->output_fd = -1;
+ self->keys = g_hash_table_new_full (g_bytes_hash, g_bytes_equal,
+ (GDestroyNotify)g_bytes_unref, NULL);
+ g_mutex_init (&self->lock);
+}
+
+static void
+gkd_ssh_agent_process_finalize (GObject *object)
+{
+ GkdSshAgentProcess *self = GKD_SSH_AGENT_PROCESS (object);
+
+ if (self->socket_fd != -1)
+ close (self->socket_fd);
+ if (self->output_fd != -1)
+ close (self->output_fd);
+ if (self->output_id)
+ g_source_remove (self->output_id);
+ if (self->child_id)
+ g_source_remove (self->child_id);
+ if (self->pid)
+ kill (self->pid, SIGTERM);
+ if (self->keys)
+ g_hash_table_unref (self->keys);
+ g_unlink (self->path);
+ g_free (self->path);
+ g_mutex_clear (&self->lock);
+
+ G_OBJECT_CLASS (gkd_ssh_agent_process_parent_class)->finalize (object);
+}
+
+static void
+gkd_ssh_agent_process_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GkdSshAgentProcess *self = GKD_SSH_AGENT_PROCESS (object);
+
+ switch (prop_id) {
+ case PROP_PATH:
+ self->path = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gkd_ssh_agent_process_class_init (GkdSshAgentProcessClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = gkd_ssh_agent_process_finalize;
+ gobject_class->set_property = gkd_ssh_agent_process_set_property;
+ g_object_class_install_property (gobject_class, PROP_PATH,
+ g_param_spec_string ("path", "Path", "Path",
+ "",
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
+}
+
+static void
+on_child_watch (GPid pid,
+ gint status,
+ gpointer user_data)
+{
+ GkdSshAgentProcess *self = GKD_SSH_AGENT_PROCESS (user_data);
+ GError *error = NULL;
+
+ if (pid != self->pid)
+ return;
+
+ g_mutex_lock (&self->lock);
+
+ self->pid = 0;
+
+ if (!g_spawn_check_exit_status (status, &error)) {
+ g_message ("ssh-agent: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_mutex_unlock (&self->lock);
+}
+
+static gboolean
+on_output_watch (gint fd,
+ GIOCondition condition,
+ gpointer user_data)
+{
+ GkdSshAgentProcess *self = GKD_SSH_AGENT_PROCESS (user_data);
+ guint8 buf[1024];
+ gssize len;
+
+ if (condition & G_IO_IN) {
+ self->ready = TRUE;
+
+ len = read (fd, buf, sizeof (buf));
+ if (len < 0) {
+ if (errno != EAGAIN && errno != EINTR)
+ g_message ("couldn't read from ssh-agent stdout: %m");
+ condition |= G_IO_ERR;
+ } else if (len > 0) {
+ gkd_ssh_agent_write_all (1, buf, len, "stdout");
+ }
+ }
+
+ if (condition & G_IO_HUP || condition & G_IO_ERR)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+agent_start_inlock (GkdSshAgentProcess *self)
+{
+ const gchar *argv[] = { SSH_AGENT, "-D", "-a", self->path, NULL };
+ GError *error = NULL;
+ GPid pid;
+
+ if (!g_spawn_async_with_pipes ("/", (gchar **)argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
+ NULL, NULL, &pid, NULL, &self->output_fd, NULL, &error)) {
+ g_warning ("couldn't run %s: %s", SSH_AGENT, error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ self->ready = FALSE;
+ self->output_id = g_unix_fd_add (self->output_fd,
+ G_IO_IN | G_IO_HUP | G_IO_ERR,
+ on_output_watch, self);
+
+ self->pid = pid;
+ self->child_id = g_child_watch_add (self->pid, on_child_watch, self);
+
+ return TRUE;
+}
+
+static gboolean
+on_timeout (gpointer user_data)
+{
+ gboolean *timedout = user_data;
+ *timedout = TRUE;
+ return TRUE;
+}
+
+gboolean
+gkd_ssh_agent_process_connect (GkdSshAgentProcess *self)
+{
+ gboolean started = FALSE;
+ struct sockaddr_un addr;
+ gboolean timedout = FALSE;
+ guint source;
+ gint sock;
+
+ g_mutex_lock (&self->lock);
+
+ if (self->pid == 0 || kill (self->pid, 0) != 0)
+ started = agent_start_inlock (self);
+
+ addr.sun_family = AF_UNIX;
+ g_strlcpy (addr.sun_path, self->path, sizeof (addr.sun_path));
+
+ if (started && !self->ready) {
+ source = g_timeout_add_seconds (5, on_timeout, &timedout);
+ while (!self->ready && !timedout)
+ g_main_context_iteration (NULL, FALSE);
+ g_source_remove (source);
+ }
+
+ if (!self->ready)
+ return FALSE;
+
+ sock = socket (AF_UNIX, SOCK_STREAM, 0);
+ g_return_val_if_fail (sock >= 0, -1);
+
+ 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;
+ }
+
+ self->socket_fd = sock;
+
+ g_mutex_unlock (&self->lock);
+
+ return sock != -1;
+}
+
+gboolean
+gkd_ssh_agent_process_call (GkdSshAgentProcess *self,
+ EggBuffer *req,
+ EggBuffer *resp)
+{
+ return gkd_ssh_agent_write_packet (self->socket_fd, req) &&
+ gkd_ssh_agent_read_packet (self->socket_fd, resp);
+}
+
+gboolean
+gkd_ssh_agent_process_lookup_key (GkdSshAgentProcess *self,
+ GBytes *key)
+{
+ gboolean ret;
+ g_mutex_lock (&self->lock);
+ ret = g_hash_table_contains (self->keys, key);
+ g_mutex_unlock (&self->lock);
+ return ret;
+}
+
+void
+gkd_ssh_agent_process_add_key (GkdSshAgentProcess *self,
+ GBytes *key)
+{
+ g_mutex_lock (&self->lock);
+ g_hash_table_add (self->keys, g_bytes_ref (key));
+ g_mutex_unlock (&self->lock);
+}
+
+void
+gkd_ssh_agent_process_remove_key (GkdSshAgentProcess *self,
+ GBytes *key)
+{
+ g_mutex_lock (&self->lock);
+ g_hash_table_remove (self->keys, key);
+ g_mutex_lock (&self->lock);
+}
+
+void
+gkd_ssh_agent_process_clear_keys (GkdSshAgentProcess *self)
+{
+ g_mutex_lock (&self->lock);
+ g_hash_table_remove_all (self->keys);
+ g_mutex_unlock (&self->lock);
+}
+
+GkdSshAgentProcess *
+gkd_ssh_agent_process_get_default (void)
+{
+ static volatile gsize initialized = 0;
+ static GkdSshAgentProcess *instance = NULL;
+
+ if (g_once_init_enter (&initialized)) {
+ const gchar *directory = gkd_util_get_master_directory ();
+ gchar *path = g_build_filename (directory, "ssh-agent-real", NULL);
+ instance = g_object_new (GKD_TYPE_SSH_AGENT_PROCESS, "path", path, NULL);
+ g_free (path);
+ g_once_init_leave (&initialized, 1);
+ }
+
+ return instance;
+}
diff --git a/daemon/ssh-agent/gkd-ssh-agent-process.h b/daemon/ssh-agent/gkd-ssh-agent-process.h
new file mode 100644
index 00000000..cb24fdae
--- /dev/null
+++ b/daemon/ssh-agent/gkd-ssh-agent-process.h
@@ -0,0 +1,47 @@
+/*
+ * 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_PROCESS_H__
+#define __GKD_SSH_AGENT_PROCESS_H__
+
+#include <glib-object.h>
+
+#include "egg/egg-buffer.h"
+
+#define GKD_TYPE_SSH_AGENT_PROCESS gkd_ssh_agent_process_get_type ()
+G_DECLARE_FINAL_TYPE(GkdSshAgentProcess, gkd_ssh_agent_process, GKD, SSH_AGENT_PROCESS, GObject)
+
+GkdSshAgentProcess *gkd_ssh_agent_process_get_default (void);
+gboolean gkd_ssh_agent_process_connect (GkdSshAgentProcess *self);
+gboolean gkd_ssh_agent_process_call (GkdSshAgentProcess *self,
+ EggBuffer *req,
+ EggBuffer *resp);
+gboolean gkd_ssh_agent_process_lookup_key (GkdSshAgentProcess *self,
+ GBytes *key);
+void gkd_ssh_agent_process_add_key (GkdSshAgentProcess *self,
+ GBytes *key);
+void gkd_ssh_agent_process_remove_key (GkdSshAgentProcess *self,
+ GBytes *key);
+void gkd_ssh_agent_process_clear_keys (GkdSshAgentProcess *self);
+
+#endif /* __GKD_SSH_AGENT_PROCESS_H__ */
diff --git a/daemon/ssh-agent/gkd-ssh-agent.c b/daemon/ssh-agent/gkd-ssh-agent.c
index 8fd3eb6b..bae34493 100644
--- a/daemon/ssh-agent/gkd-ssh-agent.c
+++ b/daemon/ssh-agent/gkd-ssh-agent.c
@@ -138,11 +138,10 @@ gkd_ssh_agent_write_packet (gint fd,
return gkd_ssh_agent_write_all (fd, buffer->buf, buffer->len, "client");
}
-gboolean
-gkd_ssh_agent_relay (GkdSshAgentCall *call)
+static gboolean
+agent_relay (GkdSshAgentCall *call)
{
- return gkd_ssh_agent_write_packet (call->ssh_agent, call->req) &&
- gkd_ssh_agent_read_packet (call->ssh_agent, call->resp);
+ return gkd_ssh_agent_process_call (call->process, call->req, call->resp);
}
static gpointer
@@ -164,8 +163,10 @@ run_client_thread (gpointer data)
call.req = &req;
call.resp = &resp;
- call.ssh_agent = gkd_ssh_agent_client_connect ();
- if (call.ssh_agent < 0)
+ call.process = gkd_ssh_agent_process_get_default ();
+ if (!call.process)
+ goto out;
+ if (!gkd_ssh_agent_process_connect (call.process))
goto out;
for (;;) {
@@ -184,7 +185,7 @@ run_client_thread (gpointer data)
if (op >= GKD_SSH_OP_MAX || gkd_ssh_agent_operations[op])
func = gkd_ssh_agent_operations[op];
else
- func = gkd_ssh_agent_relay;
+ func = agent_relay;
if (!func (&call))
break;
@@ -287,7 +288,7 @@ gkd_ssh_agent_shutdown (void)
g_list_free (socket_clients);
socket_clients = NULL;
- gkd_ssh_agent_client_cleanup ();
+ g_object_unref (gkd_ssh_agent_process_get_default ());
}
int