Branch data Line data Source code
1 : : /*
2 : : * gnome-keyring
3 : : *
4 : : * Copyright (C) 2014 Stef Walter
5 : : * Copyright (C) 2018 Red Hat, Inc.
6 : : *
7 : : * This program is free software; you can redistribute it and/or modify
8 : : * it under the terms of the GNU Lesser General Public License as
9 : : * published by the Free Software Foundation; either version 2.1 of
10 : : * the License, or (at your option) any later version.
11 : : *
12 : : * This program is distributed in the hope that it will be useful, but
13 : : * WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General Public
18 : : * License along with this program; if not, see
19 : : * <http://www.gnu.org/licenses/>.
20 : : *
21 : : * Author: Stef Walter <stef@thewalter.net>, Daiki Ueno
22 : : */
23 : :
24 : : #include "config.h"
25 : :
26 : : #include "gcr-ssh-agent-process.h"
27 : : #include "gcr-ssh-agent-private.h"
28 : : #include "gcr-ssh-agent-util.h"
29 : :
30 : : #include <gio/gunixsocketaddress.h>
31 : : #include <glib-unix.h>
32 : : #include <glib/gstdio.h>
33 : :
34 : : enum {
35 : : PROP_0,
36 : : PROP_PATH
37 : : };
38 : :
39 : : enum {
40 : : CLOSED,
41 : : LAST_SIGNAL
42 : : };
43 : :
44 : : static guint signals[LAST_SIGNAL] = { 0 };
45 : :
46 : : struct _GcrSshAgentProcess
47 : : {
48 : : GObject object;
49 : : gchar *path;
50 : : gint output;
51 : : GMutex lock;
52 : : GPid pid;
53 : : guint output_id;
54 : : guint child_id;
55 : : gboolean ready;
56 : : };
57 : :
58 [ + + + - : 97 : G_DEFINE_TYPE (GcrSshAgentProcess, gcr_ssh_agent_process, G_TYPE_OBJECT);
+ + ]
59 : :
60 : : static void
61 : 22 : gcr_ssh_agent_process_init (GcrSshAgentProcess *self)
62 : : {
63 : 22 : self->output = -1;
64 : 22 : g_mutex_init (&self->lock);
65 : 22 : }
66 : :
67 : : static void
68 : 22 : gcr_ssh_agent_process_finalize (GObject *object)
69 : : {
70 : 22 : GcrSshAgentProcess *self = GCR_SSH_AGENT_PROCESS (object);
71 : :
72 [ + + ]: 22 : if (self->output != -1)
73 : 21 : close (self->output);
74 [ + + ]: 22 : if (self->output_id)
75 : 21 : g_source_remove (self->output_id);
76 [ + + ]: 22 : if (self->child_id)
77 : 21 : g_source_remove (self->child_id);
78 [ + + ]: 22 : if (self->pid)
79 : 21 : kill (self->pid, SIGTERM);
80 : 22 : g_unlink (self->path);
81 : 22 : g_free (self->path);
82 : 22 : g_mutex_clear (&self->lock);
83 : :
84 : 22 : G_OBJECT_CLASS (gcr_ssh_agent_process_parent_class)->finalize (object);
85 : 22 : }
86 : :
87 : : static void
88 : 22 : gcr_ssh_agent_process_set_property (GObject *object,
89 : : guint prop_id,
90 : : const GValue *value,
91 : : GParamSpec *pspec)
92 : : {
93 : 22 : GcrSshAgentProcess *self = GCR_SSH_AGENT_PROCESS (object);
94 : :
95 [ + - ]: 22 : switch (prop_id) {
96 : 22 : case PROP_PATH:
97 : 22 : self->path = g_value_dup_string (value);
98 : 22 : break;
99 : 0 : default:
100 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
101 : 0 : break;
102 : : }
103 : 22 : }
104 : :
105 : : static void
106 : 2 : gcr_ssh_agent_process_class_init (GcrSshAgentProcessClass *klass)
107 : : {
108 : 2 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
109 : 2 : gobject_class->finalize = gcr_ssh_agent_process_finalize;
110 : 2 : gobject_class->set_property = gcr_ssh_agent_process_set_property;
111 : 2 : g_object_class_install_property (gobject_class, PROP_PATH,
112 : : g_param_spec_string ("path", "Path", "Path",
113 : : "",
114 : : G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
115 : 2 : signals[CLOSED] = g_signal_new_class_handler ("closed",
116 : : G_TYPE_FROM_CLASS (klass),
117 : : G_SIGNAL_RUN_LAST,
118 : : NULL, NULL, NULL, NULL,
119 : : G_TYPE_NONE, 0);
120 : 2 : }
121 : :
122 : : static void
123 : 2 : on_child_watch (GPid pid,
124 : : gint status,
125 : : gpointer user_data)
126 : : {
127 : 2 : GcrSshAgentProcess *self = GCR_SSH_AGENT_PROCESS (user_data);
128 : 2 : GError *error = NULL;
129 : :
130 [ - + ]: 2 : if (pid != self->pid)
131 : 0 : return;
132 : :
133 : 2 : g_mutex_lock (&self->lock);
134 : :
135 : 2 : self->pid = 0;
136 : 2 : self->output_id = 0;
137 : 2 : self->child_id = 0;
138 : :
139 [ + - ]: 2 : if (!g_spawn_check_exit_status (status, &error)) {
140 : 2 : g_message ("ssh-agent: %s", error->message);
141 : 2 : g_error_free (error);
142 : : }
143 : :
144 : 2 : g_spawn_close_pid (pid);
145 : :
146 : 2 : g_mutex_unlock (&self->lock);
147 : :
148 : 2 : g_signal_emit (self, signals[CLOSED], 0);
149 : : }
150 : :
151 : : static gboolean
152 : 25 : on_output_watch (gint fd,
153 : : GIOCondition condition,
154 : : gpointer user_data)
155 : : {
156 : 25 : GcrSshAgentProcess *self = GCR_SSH_AGENT_PROCESS (user_data);
157 : : guint8 buf[1024];
158 : : gssize len;
159 : :
160 [ + + ]: 25 : if (condition & G_IO_IN) {
161 : 23 : self->ready = TRUE;
162 : :
163 : 23 : len = read (fd, buf, sizeof (buf));
164 [ - + ]: 23 : if (len < 0) {
165 [ # # # # ]: 0 : if (errno != EAGAIN && errno != EINTR)
166 : 0 : g_message ("couldn't read from ssh-agent stdout: %m");
167 : 0 : condition |= G_IO_ERR;
168 : : }
169 : : }
170 : :
171 [ + + - + ]: 25 : if (condition & G_IO_HUP || condition & G_IO_ERR)
172 : 2 : return FALSE;
173 : :
174 : 23 : return TRUE;
175 : : }
176 : :
177 : : static gboolean
178 : 23 : agent_start_inlock (GcrSshAgentProcess *self,
179 : : GError **error)
180 : : {
181 : 23 : const gchar *argv[] = { SSH_AGENT_EXECUTABLE, "-D", "-a", self->path, NULL };
182 : : GPid pid;
183 : :
184 [ - + ]: 23 : if (!g_spawn_async_with_pipes ("/", (gchar **)argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_CLOEXEC_PIPES,
185 : : NULL, NULL, &pid, NULL, &self->output, NULL, error))
186 : 0 : return FALSE;
187 : :
188 : 23 : self->ready = FALSE;
189 : 23 : self->output_id = g_unix_fd_add (self->output,
190 : : G_IO_IN | G_IO_HUP | G_IO_ERR,
191 : : on_output_watch, self);
192 : :
193 : 23 : self->pid = pid;
194 : 23 : self->child_id = g_child_watch_add (self->pid, on_child_watch, self);
195 : :
196 : 23 : return TRUE;
197 : : }
198 : :
199 : : static gboolean
200 : 0 : on_timeout (gpointer user_data)
201 : : {
202 : 0 : gboolean *timedout = user_data;
203 : 0 : *timedout = TRUE;
204 : 0 : return TRUE;
205 : : }
206 : :
207 : : GSocketConnection *
208 : 24 : gcr_ssh_agent_process_connect (GcrSshAgentProcess *self,
209 : : GCancellable *cancellable,
210 : : GError **error)
211 : : {
212 : 24 : gboolean started = FALSE;
213 : 24 : gboolean timedout = FALSE;
214 : : guint source;
215 : : GSocketClient *client;
216 : : GSocketAddress *address;
217 : : GSocketConnection *connection;
218 : :
219 : 24 : g_mutex_lock (&self->lock);
220 : :
221 [ + + ]: 24 : if (self->pid == 0) {
222 [ - + ]: 23 : if (!agent_start_inlock (self, error)) {
223 : 0 : g_mutex_unlock (&self->lock);
224 : 0 : return NULL;
225 : : }
226 : 23 : started = TRUE;
227 : : }
228 : :
229 [ + + + - ]: 24 : if (started && !self->ready) {
230 : 23 : source = g_timeout_add_seconds (5, on_timeout, &timedout);
231 [ + + + - ]: 54682 : while (!self->ready && !timedout)
232 : 54659 : g_main_context_iteration (NULL, FALSE);
233 : 23 : g_source_remove (source);
234 : : }
235 : :
236 [ - + ]: 24 : if (!self->ready) {
237 : 0 : g_mutex_unlock (&self->lock);
238 : 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
239 : : "ssh-agent process is not ready");
240 : 0 : return NULL;
241 : : }
242 : :
243 : 24 : address = g_unix_socket_address_new (self->path);
244 : 24 : client = g_socket_client_new ();
245 : :
246 : 24 : connection = g_socket_client_connect (client,
247 : 24 : G_SOCKET_CONNECTABLE (address),
248 : : cancellable,
249 : : error);
250 : 24 : g_object_unref (address);
251 : 24 : g_object_unref (client);
252 : :
253 : 24 : g_mutex_unlock (&self->lock);
254 : :
255 : 24 : return connection;
256 : : }
257 : :
258 : : GcrSshAgentProcess *
259 : 22 : gcr_ssh_agent_process_new (const gchar *path)
260 : : {
261 [ - + ]: 22 : g_return_val_if_fail (path, NULL);
262 : :
263 : 22 : return g_object_new (GCR_TYPE_SSH_AGENT_PROCESS, "path", path, NULL);
264 : : }
265 : :
266 : : GPid
267 : 6 : gcr_ssh_agent_process_get_pid (GcrSshAgentProcess *self)
268 : : {
269 : 6 : return self->pid;
270 : : }
|