[gnome-keyring/wip/dueno/ssh-agent] ssh: rewrite subprocess handling
- From: Daiki Ueno <dueno src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-keyring/wip/dueno/ssh-agent] ssh: rewrite subprocess handling
- Date: Tue, 12 Dec 2017 17:21:16 +0000 (UTC)
commit d43b81ac39cbd9d8f02e62d42f38a55b271674b9
Author: Daiki Ueno <dueno src gnome org>
Date: Tue Dec 12 16:39:13 2017 +0100
ssh: rewrite subprocess handling
daemon/ssh-agent/Makefile.am | 4 +-
daemon/ssh-agent/gkd-ssh-agent-client.c | 214 -----------------------
daemon/ssh-agent/gkd-ssh-agent-client.h | 33 ----
daemon/ssh-agent/gkd-ssh-agent-ops.c | 58 +++++--
daemon/ssh-agent/gkd-ssh-agent-private.h | 6 +-
daemon/ssh-agent/gkd-ssh-agent-process.c | 280 ++++++++++++++++++++++++++++++
daemon/ssh-agent/gkd-ssh-agent-process.h | 47 +++++
daemon/ssh-agent/gkd-ssh-agent.c | 17 +-
8 files changed, 382 insertions(+), 277 deletions(-)
---
diff --git a/daemon/ssh-agent/Makefile.am b/daemon/ssh-agent/Makefile.am
index 9f5f1db..6b1e719 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-ops.c b/daemon/ssh-agent/gkd-ssh-agent-ops.c
index 0669d0e..168a27a 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"
@@ -49,9 +48,16 @@ op_add_identity (GkdSshAgentCall *call)
{
GList *keys;
gsize offset;
- gconstpointer blob;
+ const guchar *blob;
gsize length;
GList *l;
+ GBytes *key;
+ gboolean ret;
+
+ offset = 5;
+ if (!egg_buffer_get_byte_array (call->req, offset, &offset, &blob, &length))
+ return FALSE;
+ key = g_bytes_new (blob, length);
/*
* Here we want to remove the preload key from our list, so that we
@@ -65,11 +71,8 @@ op_add_identity (GkdSshAgentCall *call)
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) {
+ if (g_bytes_equal (l->data, key)) {
gkd_ssh_agent_preload_clear (l->data);
break;
}
@@ -77,7 +80,12 @@ op_add_identity (GkdSshAgentCall *call)
g_list_free_full (keys, (GDestroyNotify)g_bytes_unref);
- return gkd_ssh_agent_relay (call);
+ ret = gkd_ssh_agent_process_call (call->process, call->req, call->resp);
+ if (ret)
+ gkd_ssh_agent_process_add_key (call->process, key);
+
+ g_bytes_unref (key);
+ return ret;
}
static GHashTable *
@@ -119,14 +127,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 +144,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 +176,7 @@ op_request_identities (GkdSshAgentCall *call)
}
static void
-preload_key_if_necessary (gint ssh_agent,
- GBytes *key)
+preload_key_if_necessary (GkdSshAgentCall *call, GBytes *key)
{
GTlsInteraction *interaction;
GcrSshAskpass *askpass;
@@ -181,6 +194,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);
@@ -213,13 +229,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);
+ preload_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 +244,36 @@ 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 != NULL) {
+ if (ret)
+ gkd_ssh_agent_process_remove_key (call->process, key);
+ g_bytes_unref (key);
+ }
+ return ret;
}
static gboolean
op_remove_all_identities (GkdSshAgentCall *call)
{
+ gboolean ret;
gkd_ssh_agent_preload_clear_all ();
- return gkd_ssh_agent_relay (call);
+ ret = gkd_ssh_agent_process_call (call->process, call->req, call->resp);
+ if (ret)
+ gkd_ssh_agent_process_clear_keys (call->process);
+ return ret;
}
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 159475e..0ca97ac 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 0000000..2e88119
--- /dev/null
+++ b/daemon/ssh-agent/gkd-ssh-agent-process.c
@@ -0,0 +1,280 @@
+/*
+ * 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 <sys/socket.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <unistd.h>
+
+static GkdSshAgentProcess *instance;
+
+struct _GkdSshAgentProcess
+{
+ GObject object;
+ gchar *path;
+ gint socket_fd;
+ gint output_fd;
+ 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_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_class_init (GkdSshAgentProcessClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = gkd_ssh_agent_process_finalize;
+}
+
+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;
+ const gchar *directory;
+ gboolean timedout = FALSE;
+ guint source;
+ gint sock;
+
+ g_mutex_lock (&self->lock);
+
+ if (!self->path) {
+ directory = gkd_util_get_master_directory ();
+ self->path = g_build_filename (directory, "ssh-agent-real", NULL);
+ }
+
+ 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)
+{
+ if (instance == NULL)
+ instance = g_object_new (GKD_TYPE_SSH_AGENT_PROCESS, NULL);
+ 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 0000000..cb24fda
--- /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 8fd3eb6..bae3449 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
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]