diff options
author | Daiki Ueno <dueno@src.gnome.org> | 2017-12-12 16:39:13 +0100 |
---|---|---|
committer | Daiki Ueno <dueno@src.gnome.org> | 2018-02-12 15:11:38 +0100 |
commit | 907605e1d4586e74740a192ffe56e1996c9d8608 (patch) | |
tree | c3a1729b34f89cc0bc945fc976888a3c0b177aa7 | |
parent | a56f794bebb0f1b0c57017ab8b7ba69e6475ae85 (diff) | |
download | gnome-keyring-907605e1d4586e74740a192ffe56e1996c9d8608.tar.gz |
ssh-agent: rewrite subprocess handling
-rw-r--r-- | daemon/ssh-agent/Makefile.am | 4 | ||||
-rw-r--r-- | daemon/ssh-agent/gkd-ssh-agent-client.c | 214 | ||||
-rw-r--r-- | daemon/ssh-agent/gkd-ssh-agent-client.h | 33 | ||||
-rw-r--r-- | daemon/ssh-agent/gkd-ssh-agent-ops.c | 88 | ||||
-rw-r--r-- | daemon/ssh-agent/gkd-ssh-agent-private.h | 6 | ||||
-rw-r--r-- | daemon/ssh-agent/gkd-ssh-agent-process.c | 311 | ||||
-rw-r--r-- | daemon/ssh-agent/gkd-ssh-agent-process.h | 47 | ||||
-rw-r--r-- | daemon/ssh-agent/gkd-ssh-agent.c | 17 |
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 |