Branch data Line data Source code
1 : : /*
2 : : * gnome-keyring
3 : : *
4 : : * Copyright (C) 2007 Stefan 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-service.h"
27 : :
28 : : #include "gcr-ssh-agent-interaction.h"
29 : : #include "gcr-ssh-agent-preload.h"
30 : : #include "gcr-ssh-agent-private.h"
31 : : #include "gcr-ssh-agent-process.h"
32 : : #include "gcr-ssh-agent-util.h"
33 : :
34 : : #include "egg/egg-buffer.h"
35 : : #include "egg/egg-error.h"
36 : : #include "egg/egg-secure-memory.h"
37 : :
38 : : #include <gio/gunixsocketaddress.h>
39 : : #include <glib/gstdio.h>
40 : : #include <gcr/gcr.h>
41 : :
42 : : #if WITH_SYSTEMD
43 : : #include <systemd/sd-daemon.h>
44 : : #endif
45 : :
46 : : #include <glib/gi18n-lib.h>
47 : :
48 : 40 : EGG_SECURE_DECLARE (ssh_agent);
49 : :
50 : : typedef gboolean (*GcrSshAgentOperation) (GcrSshAgentService *agent, GSocketConnection *connection, EggBuffer *req, EggBuffer *resp, GCancellable *cancellable, GError **error);
51 : : static const GcrSshAgentOperation operations[GCR_SSH_OP_MAX];
52 : :
53 : : enum {
54 : : PROP_0,
55 : : PROP_PATH,
56 : : PROP_PRELOAD,
57 : : PROP_INTERACTION
58 : : };
59 : :
60 : : struct _GcrSshAgentService
61 : : {
62 : : GObject object;
63 : : gchar *path;
64 : : GcrSshAgentPreload *preload;
65 : : GcrSshAgentProcess *process;
66 : : /* for mocking */
67 : : GTlsInteraction *interaction;
68 : : GSocketAddress *address;
69 : : GSocketListener *listener;
70 : : GHashTable *keys;
71 : : GMutex lock;
72 : : GCancellable *cancellable;
73 : : };
74 : :
75 [ + + + - : 108 : G_DEFINE_TYPE (GcrSshAgentService, gcr_ssh_agent_service, G_TYPE_OBJECT);
+ + ]
76 : :
77 : : static void
78 : 15 : gcr_ssh_agent_service_init (GcrSshAgentService *self)
79 : : {
80 : 15 : self->keys = g_hash_table_new_full (g_bytes_hash, g_bytes_equal,
81 : : (GDestroyNotify)g_bytes_unref, NULL);
82 : 15 : g_mutex_init (&self->lock);
83 : 15 : }
84 : :
85 : : static void
86 : 15 : gcr_ssh_agent_service_constructed (GObject *object)
87 : : {
88 : 15 : GcrSshAgentService *self = GCR_SSH_AGENT_SERVICE (object);
89 : : gchar *path;
90 : :
91 : 15 : path = g_strdup_printf ("%s/.ssh", self->path);
92 : 15 : self->process = gcr_ssh_agent_process_new (path);
93 : 15 : g_free (path);
94 : :
95 : 15 : self->listener = G_SOCKET_LISTENER (g_threaded_socket_service_new (-1));
96 : 15 : self->cancellable = g_cancellable_new ();
97 : :
98 : 15 : G_OBJECT_CLASS (gcr_ssh_agent_service_parent_class)->constructed (object);
99 : 15 : }
100 : :
101 : : static void
102 : 45 : gcr_ssh_agent_service_set_property (GObject *object,
103 : : guint prop_id,
104 : : const GValue *value,
105 : : GParamSpec *pspec)
106 : : {
107 : 45 : GcrSshAgentService *self = GCR_SSH_AGENT_SERVICE (object);
108 : :
109 [ + + + - ]: 45 : switch (prop_id) {
110 : 15 : case PROP_PATH:
111 : 15 : self->path = g_value_dup_string (value);
112 : 15 : break;
113 : 15 : case PROP_PRELOAD:
114 : 15 : self->preload = g_value_dup_object (value);
115 : 15 : break;
116 : 15 : case PROP_INTERACTION:
117 : 15 : self->interaction = g_value_dup_object (value);
118 : 15 : break;
119 : 0 : default:
120 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
121 : 0 : break;
122 : : }
123 : 45 : }
124 : :
125 : : static void
126 : 15 : gcr_ssh_agent_service_finalize (GObject *object)
127 : : {
128 : 15 : GcrSshAgentService *self = GCR_SSH_AGENT_SERVICE (object);
129 : :
130 : 15 : g_free (self->path);
131 : 15 : g_object_unref (self->preload);
132 [ + - ]: 15 : g_clear_object (&self->interaction);
133 : :
134 : 15 : g_object_unref (self->process);
135 : 15 : g_object_unref (self->listener);
136 [ + - ]: 15 : g_clear_object (&self->address);
137 : 15 : g_mutex_clear (&self->lock);
138 : 15 : g_hash_table_unref (self->keys);
139 : 15 : g_object_unref (self->cancellable);
140 : :
141 : 15 : G_OBJECT_CLASS (gcr_ssh_agent_service_parent_class)->finalize (object);
142 : 15 : }
143 : :
144 : : static void
145 : 1 : gcr_ssh_agent_service_class_init (GcrSshAgentServiceClass *klass)
146 : : {
147 : 1 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
148 : 1 : gobject_class->constructed = gcr_ssh_agent_service_constructed;
149 : 1 : gobject_class->set_property = gcr_ssh_agent_service_set_property;
150 : 1 : gobject_class->finalize = gcr_ssh_agent_service_finalize;
151 : 1 : g_object_class_install_property (gobject_class, PROP_PATH,
152 : : g_param_spec_string ("path", "Path", "Path",
153 : : "",
154 : : G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
155 : 1 : g_object_class_install_property (gobject_class, PROP_PRELOAD,
156 : : g_param_spec_object ("preload", "Preload", "Preload",
157 : : GCR_TYPE_SSH_AGENT_PRELOAD,
158 : : G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
159 : 1 : g_object_class_install_property (gobject_class, PROP_INTERACTION,
160 : : g_param_spec_object ("interaction", "Interaction", "Interaction",
161 : : G_TYPE_TLS_INTERACTION,
162 : : G_PARAM_WRITABLE));
163 : 1 : }
164 : :
165 : : static gboolean
166 : 28 : relay_request (GcrSshAgentService *self,
167 : : GSocketConnection *connection,
168 : : EggBuffer *req,
169 : : EggBuffer *resp,
170 : : GCancellable *cancellable,
171 : : GError **error)
172 : : {
173 : 28 : return _gcr_ssh_agent_call (connection, req, resp, cancellable, error);
174 : : }
175 : :
176 : : static gboolean
177 : 28 : handle_request (GcrSshAgentService *self,
178 : : GSocketConnection *connection,
179 : : EggBuffer *req,
180 : : EggBuffer *resp,
181 : : GCancellable *cancellable,
182 : : GError **error)
183 : : {
184 : : GcrSshAgentOperation func;
185 : : guchar op;
186 : :
187 : 28 : egg_buffer_reset (resp);
188 : 28 : egg_buffer_add_uint32 (resp, 0);
189 : :
190 : : /* Decode the operation; on failure, just pass through */
191 [ + - ]: 28 : if (egg_buffer_get_byte (req, 4, NULL, &op) &&
192 [ + + + + ]: 28 : op <= GCR_SSH_OP_MAX && operations[op] != NULL)
193 : 25 : func = operations[op];
194 : : else
195 : 3 : func = relay_request;
196 : :
197 : 28 : return func (self, connection, req, resp, cancellable, error);
198 : : }
199 : :
200 : : static void
201 : 14 : add_key (GcrSshAgentService *self,
202 : : GBytes *key)
203 : : {
204 : 14 : g_mutex_lock (&self->lock);
205 : 14 : g_hash_table_add (self->keys, g_bytes_ref (key));
206 : 14 : g_mutex_unlock (&self->lock);
207 : 14 : }
208 : :
209 : : static void
210 : 1 : remove_key (GcrSshAgentService *self,
211 : : GBytes *key)
212 : : {
213 : 1 : g_mutex_lock (&self->lock);
214 : 1 : g_hash_table_remove (self->keys, key);
215 : 1 : g_mutex_unlock (&self->lock);
216 : 1 : }
217 : :
218 : : static void
219 : 2 : clear_keys (GcrSshAgentService *self)
220 : : {
221 : 2 : g_mutex_lock (&self->lock);
222 : 2 : g_hash_table_remove_all (self->keys);
223 : 2 : g_mutex_unlock (&self->lock);
224 : 2 : }
225 : :
226 : : static void
227 : 3 : ensure_key (GcrSshAgentService *self,
228 : : GBytes *key)
229 : : {
230 : : GcrSshAskpass *askpass;
231 : 3 : GError *error = NULL;
232 : : gint status;
233 : : GcrSshAgentKeyInfo *info;
234 : : gchar *unique;
235 : : const gchar *label;
236 : : GHashTable *fields;
237 : : GTlsInteraction *interaction;
238 : : gchar *standard_error;
239 : :
240 : 3 : gchar *argv[] = {
241 : : SSH_ADD_EXECUTABLE,
242 : : NULL,
243 : : NULL
244 : : };
245 : :
246 [ + + ]: 3 : if (gcr_ssh_agent_service_lookup_key (self, key))
247 : 2 : return;
248 : :
249 : 2 : info = gcr_ssh_agent_preload_lookup_by_public_key (self->preload, key);
250 [ + + ]: 2 : if (!info)
251 : 1 : return;
252 : :
253 : 1 : argv[1] = info->filename;
254 : :
255 : 1 : fields = g_hash_table_new (g_str_hash, g_str_equal);
256 : 1 : unique = g_strdup_printf ("ssh-store:%s", info->filename);
257 : 1 : g_hash_table_insert (fields, "unique", unique);
258 : :
259 [ - + ]: 1 : label = info->comment[0] != '\0' ? info->comment : _("Unnamed");
260 : :
261 [ + - ]: 1 : if (self->interaction) {
262 : 1 : interaction = g_object_ref (self->interaction);
263 : : } else {
264 : 0 : interaction = gcr_ssh_agent_interaction_new (NULL, label, fields);
265 : : }
266 : 1 : askpass = gcr_ssh_askpass_new (interaction);
267 : 1 : g_object_unref (interaction);
268 : :
269 [ - + ]: 1 : if (!g_spawn_sync (NULL, argv, NULL,
270 : : G_SPAWN_STDOUT_TO_DEV_NULL,
271 : : gcr_ssh_askpass_child_setup, askpass,
272 : : NULL, &standard_error, &status, &error)) {
273 : 0 : g_warning ("couldn't run %s: %s", argv[0], error->message);
274 : 0 : g_error_free (error);
275 [ - + ]: 1 : } else if (!g_spawn_check_exit_status (status, &error)) {
276 : 0 : g_message ("the %s command failed: %s", argv[0], error->message);
277 : 0 : g_printerr ("%s", _gcr_ssh_agent_canon_error (standard_error));
278 : 0 : g_free (standard_error);
279 : : } else {
280 : 1 : add_key (self, key);
281 : : }
282 : :
283 : 1 : g_hash_table_unref (fields);
284 : 1 : g_free (unique);
285 : 1 : gcr_ssh_agent_key_info_free (info);
286 : 1 : g_object_unref (askpass);
287 : : }
288 : :
289 : : static gboolean
290 : 15 : on_run (GThreadedSocketService *service,
291 : : GSocketConnection *connection,
292 : : GObject *source_object,
293 : : gpointer user_data)
294 : : {
295 : 15 : GcrSshAgentService *self = g_object_ref (GCR_SSH_AGENT_SERVICE (user_data));
296 : : EggBuffer req;
297 : : EggBuffer resp;
298 : : GError *error;
299 : : GSocketConnection *agent_connection;
300 : : gboolean ret;
301 : :
302 : 15 : egg_buffer_init_full (&req, 128, egg_secure_realloc);
303 : 15 : egg_buffer_init_full (&resp, 128, (EggBufferAllocator)g_realloc);
304 : :
305 : 15 : error = NULL;
306 : 15 : agent_connection = gcr_ssh_agent_process_connect (self->process, self->cancellable, &error);
307 [ + - ]: 15 : if (!agent_connection) {
308 : 0 : g_warning ("couldn't connect to ssh-agent: %s", error->message);
309 : 0 : g_error_free (error);
310 : 0 : goto out;
311 : : }
312 : :
313 : : while (TRUE) {
314 : : /* Read in the request */
315 : 42 : error = NULL;
316 [ + + ]: 42 : if (!_gcr_ssh_agent_read_packet (connection, &req, self->cancellable, &error)) {
317 [ + + ]: 15 : if (error->code != G_IO_ERROR_CANCELLED &&
318 [ + + ]: 8 : error->code != G_IO_ERROR_CONNECTION_CLOSED)
319 : 1 : g_message ("couldn't read from client: %s", error->message);
320 : 15 : g_error_free (error);
321 : 15 : break;
322 : : }
323 : :
324 : : /* Handle the request */
325 : 27 : error = NULL;
326 [ + + ]: 28 : while (!(ret = handle_request (self, agent_connection, &req, &resp, self->cancellable, &error))) {
327 [ - + ]: 1 : if (gcr_ssh_agent_process_get_pid (self->process) != 0) {
328 [ # # ]: 0 : if (error->code != G_IO_ERROR_CANCELLED)
329 : 0 : g_message ("couldn't handle client request: %s", error->message);
330 : 0 : g_error_free (error);
331 : 0 : goto out;
332 : : }
333 : :
334 : : /* Reconnect to the ssh-agent */
335 [ + - ]: 1 : g_clear_object (&agent_connection);
336 : 1 : g_clear_error (&error);
337 : 1 : agent_connection = gcr_ssh_agent_process_connect (self->process, self->cancellable, &error);
338 [ - + ]: 1 : if (!agent_connection) {
339 [ # # ]: 0 : if (error->code != G_IO_ERROR_CANCELLED)
340 : 0 : g_message ("couldn't connect to ssh-agent: %s", error->message);
341 : 0 : g_error_free (error);
342 : 0 : goto out;
343 : : }
344 : : }
345 : :
346 : : /* Write the reply back out */
347 : 27 : error = NULL;
348 [ - + ]: 27 : if (!_gcr_ssh_agent_write_packet (connection, &resp, self->cancellable, &error)) {
349 [ # # ]: 0 : if (error->code != G_IO_ERROR_CANCELLED)
350 : 0 : g_message ("couldn't write to client: %s", error->message);
351 : 0 : g_error_free (error);
352 : 0 : break;
353 : : }
354 : : }
355 : :
356 : 15 : out:
357 : 15 : egg_buffer_uninit (&req);
358 : 15 : egg_buffer_uninit (&resp);
359 : :
360 : 15 : g_object_unref (agent_connection);
361 : 15 : g_object_unref (self);
362 : :
363 : 15 : return TRUE;
364 : : }
365 : :
366 : : static void
367 : 1 : on_closed (GcrSshAgentProcess *process,
368 : : gpointer user_data)
369 : : {
370 : 1 : GcrSshAgentService *self = GCR_SSH_AGENT_SERVICE (user_data);
371 : 1 : clear_keys (self);
372 : 1 : }
373 : :
374 : : gboolean
375 : 15 : gcr_ssh_agent_service_start (GcrSshAgentService *self)
376 : : {
377 : 15 : GError *error = NULL;
378 : :
379 : : #if WITH_SYSTEMD
380 : : int ret;
381 : :
382 : 15 : ret = sd_listen_fds (0);
383 [ - + ]: 15 : if (ret > 1) {
384 : 0 : g_warning ("too many file descriptors received");
385 : 0 : return FALSE;
386 [ - + ]: 15 : } else if (ret == 1) {
387 : : GSocket *socket;
388 : :
389 : 0 : socket = g_socket_new_from_fd (SD_LISTEN_FDS_START, &error);
390 [ # # ]: 0 : if (!socket) {
391 : 0 : g_warning ("couldn't create a socket: %s", error->message);
392 : 0 : g_error_free (error);
393 : 0 : return FALSE;
394 : : }
395 [ # # ]: 0 : if (!g_socket_listener_add_socket (self->listener,
396 : : socket,
397 : 0 : G_OBJECT (self),
398 : : &error)) {
399 : 0 : g_warning ("couldn't bind socket on %d: %s",
400 : : g_socket_get_fd (socket),
401 : : error->message);
402 : 0 : g_object_unref (socket);
403 : 0 : g_error_free (error);
404 : 0 : return FALSE;
405 : : }
406 : : } else
407 : : #endif
408 : : {
409 : : gchar *path;
410 : 15 : path = g_strdup_printf ("%s/ssh", self->path);
411 : 15 : g_unlink (path);
412 : 15 : self->address = g_unix_socket_address_new (path);
413 : 15 : g_free (path);
414 : :
415 [ - + ]: 15 : if (!g_socket_listener_add_address (self->listener,
416 : : self->address,
417 : : G_SOCKET_TYPE_STREAM,
418 : : G_SOCKET_PROTOCOL_DEFAULT,
419 : : NULL,
420 : : NULL,
421 : : &error)) {
422 : 0 : g_warning ("couldn't listen on %s: %s",
423 : : g_unix_socket_address_get_path (G_UNIX_SOCKET_ADDRESS (self->address)),
424 : : error->message);
425 : 0 : g_error_free (error);
426 : 0 : return FALSE;
427 : : }
428 : :
429 : : /* For the ssh-add command called upon SSH_AGENTC_SIGN_REQUEST */
430 : 15 : g_setenv ("SSH_AUTH_SOCK", g_unix_socket_address_get_path (G_UNIX_SOCKET_ADDRESS (self->address)), TRUE);
431 : : }
432 : :
433 : 15 : g_signal_connect (self->listener, "run", G_CALLBACK (on_run), self);
434 : 15 : g_signal_connect (self->process, "closed", G_CALLBACK (on_closed), self);
435 : :
436 : 15 : g_socket_service_start (G_SOCKET_SERVICE (self->listener));
437 : :
438 : 15 : return TRUE;
439 : : }
440 : :
441 : : void
442 : 15 : gcr_ssh_agent_service_stop (GcrSshAgentService *self)
443 : : {
444 [ + - ]: 15 : if (self->address)
445 : 15 : g_unlink (g_unix_socket_address_get_path (G_UNIX_SOCKET_ADDRESS (self->address)));
446 : :
447 : 15 : g_cancellable_cancel (self->cancellable);
448 : 15 : g_socket_service_stop (G_SOCKET_SERVICE (self->listener));
449 : 15 : }
450 : :
451 : : GcrSshAgentService *
452 : 15 : gcr_ssh_agent_service_new (const gchar *path,
453 : : GcrSshAgentPreload *preload)
454 : : {
455 [ - + ]: 15 : g_return_val_if_fail (path, NULL);
456 [ - + ]: 15 : g_return_val_if_fail (preload, NULL);
457 : :
458 : 15 : return g_object_new (GCR_TYPE_SSH_AGENT_SERVICE,
459 : : "path", path,
460 : : "preload", preload,
461 : : NULL);
462 : : }
463 : :
464 : : GcrSshAgentPreload *
465 : 9 : gcr_ssh_agent_service_get_preload (GcrSshAgentService *self)
466 : : {
467 : 9 : return self->preload;
468 : : }
469 : :
470 : : GcrSshAgentProcess *
471 : 2 : gcr_ssh_agent_service_get_process (GcrSshAgentService *self)
472 : : {
473 : 2 : return self->process;
474 : : }
475 : :
476 : : gboolean
477 : 6 : gcr_ssh_agent_service_lookup_key (GcrSshAgentService *self,
478 : : GBytes *key)
479 : : {
480 : : gboolean ret;
481 : 6 : g_mutex_lock (&self->lock);
482 : 6 : ret = g_hash_table_contains (self->keys, key);
483 : 6 : g_mutex_unlock (&self->lock);
484 : 6 : return ret;
485 : : }
486 : :
487 : : /* ---------------------------------------------------------------------------- */
488 : :
489 : : static gboolean
490 : 9 : op_add_identity (GcrSshAgentService *self,
491 : : GSocketConnection *connection,
492 : : EggBuffer *req,
493 : : EggBuffer *resp,
494 : : GCancellable *cancellable,
495 : : GError **error)
496 : : {
497 : : const guchar *blob;
498 : 9 : gsize offset = 5;
499 : : gsize length;
500 : 9 : GBytes *key = NULL;
501 : : gboolean ret;
502 : :
503 : : /* If parsing the request fails, just pass through */
504 : 9 : ret = egg_buffer_get_byte_array (req, offset, &offset, &blob, &length);
505 [ + + ]: 9 : if (ret)
506 : 8 : key = g_bytes_new (blob, length);
507 : : else
508 : 1 : g_message ("got unparseable add identity request for ssh-agent");
509 : :
510 : 9 : ret = relay_request (self, connection, req, resp, cancellable, error);
511 [ + + ]: 9 : if (key) {
512 [ + + ]: 8 : if (ret)
513 : 7 : add_key (self, key);
514 : 8 : g_bytes_unref (key);
515 : : }
516 : :
517 : 9 : return ret;
518 : : }
519 : :
520 : : static GHashTable *
521 : 9 : parse_identities_answer (EggBuffer *resp)
522 : : {
523 : : GHashTable *answer;
524 : : const guchar *blob;
525 : : gchar *comment;
526 : : gsize length;
527 : 9 : gsize offset = 4;
528 : : guint32 count;
529 : : guchar op;
530 : : guint32 i;
531 : :
532 [ + - ]: 9 : if (!egg_buffer_get_byte (resp, offset, &offset, &op) ||
533 [ + - - + ]: 18 : op != GCR_SSH_RES_IDENTITIES_ANSWER ||
534 : 9 : !egg_buffer_get_uint32 (resp, offset, &offset, &count)) {
535 : 0 : g_message ("got unexpected response back from ssh-agent when requesting identities");
536 : 0 : return NULL;
537 : : }
538 : :
539 : 9 : answer = g_hash_table_new_full (g_bytes_hash, g_bytes_equal, (GDestroyNotify)g_bytes_unref, g_free);
540 : :
541 [ + + ]: 15 : for (i = 0; i < count; i++) {
542 [ + - - + ]: 12 : if (!egg_buffer_get_byte_array (resp, offset, &offset, &blob, &length) ||
543 : 6 : !egg_buffer_get_string (resp, offset, &offset, &comment, g_realloc)) {
544 : 0 : g_message ("got unparseable response back from ssh-agent when requesting identities");
545 : 0 : g_hash_table_unref (answer);
546 : 0 : return NULL;
547 : : }
548 : 6 : g_hash_table_insert (answer, g_bytes_new (blob, length), comment);
549 : : }
550 : :
551 : 9 : return answer;
552 : : }
553 : :
554 : :
555 : : static gboolean
556 : 9 : op_request_identities (GcrSshAgentService *self,
557 : : GSocketConnection *connection,
558 : : EggBuffer *req,
559 : : EggBuffer *resp,
560 : : GCancellable *cancellable,
561 : : GError **error)
562 : : {
563 : : GHashTable *answer;
564 : : GHashTableIter iter;
565 : : gsize length;
566 : : guint32 added;
567 : : GBytes *key;
568 : : GList *keys;
569 : : GList *l;
570 : : GcrSshAgentPreload *preload;
571 : :
572 [ - + ]: 9 : if (!relay_request (self, connection, req, resp, cancellable, error))
573 : 0 : return FALSE;
574 : :
575 : : /* Parse all the keys, and if it fails, just fall through */
576 : 9 : answer = parse_identities_answer (resp);
577 [ - + ]: 9 : if (!answer)
578 : 0 : return TRUE;
579 : :
580 : 9 : g_hash_table_iter_init (&iter, answer);
581 [ + + ]: 15 : while (g_hash_table_iter_next (&iter, (gpointer *)&key, NULL))
582 : 6 : add_key (self, key);
583 : :
584 : 9 : added = 0;
585 : :
586 : : /* Add any preloaded keys not already in answer */
587 : 9 : preload = gcr_ssh_agent_service_get_preload (self);
588 : 9 : keys = gcr_ssh_agent_preload_get_keys (preload);
589 [ + - + + ]: 18 : for (l = keys; l != NULL; l = g_list_next (l)) {
590 : 9 : GcrSshAgentKeyInfo *info = l->data;
591 [ + + ]: 9 : if (!g_hash_table_contains (answer, info->public_key)) {
592 : 3 : const guchar *blob = g_bytes_get_data (info->public_key, &length);
593 : 3 : egg_buffer_add_byte_array (resp, blob, length);
594 : 3 : egg_buffer_add_string (resp, info->comment);
595 : 3 : added++;
596 : : }
597 : : }
598 : :
599 : 9 : g_list_free_full (keys, (GDestroyNotify)gcr_ssh_agent_key_info_free);
600 : :
601 : : /* Set the correct amount of keys including the ones we added */
602 : 9 : egg_buffer_set_uint32 (resp, 5, added + g_hash_table_size (answer));
603 : 9 : g_hash_table_unref (answer);
604 : :
605 : : /* Set the correct total size of the payload */
606 : 9 : egg_buffer_set_uint32 (resp, 0, resp->len - 4);
607 : :
608 : 9 : return TRUE;
609 : : }
610 : :
611 : : static gboolean
612 : 4 : op_sign_request (GcrSshAgentService *self,
613 : : GSocketConnection *connection,
614 : : EggBuffer *req,
615 : : EggBuffer *resp,
616 : : GCancellable *cancellable,
617 : : GError **error)
618 : : {
619 : : const guchar *blob;
620 : : gsize length;
621 : 4 : gsize offset = 5;
622 : : GBytes *key;
623 : :
624 : : /* If parsing the request fails, just pass through */
625 [ + + ]: 4 : if (egg_buffer_get_byte_array (req, offset, &offset, &blob, &length)) {
626 : 3 : key = g_bytes_new (blob, length);
627 : 3 : ensure_key (self, key);
628 : 3 : g_bytes_unref (key);
629 : : } else {
630 : 1 : g_message ("got unparseable sign request for ssh-agent");
631 : : }
632 : :
633 : 4 : return relay_request (self, connection, req, resp, cancellable, error);
634 : : }
635 : :
636 : : static gboolean
637 : 2 : op_remove_identity (GcrSshAgentService *self,
638 : : GSocketConnection *connection,
639 : : EggBuffer *req,
640 : : EggBuffer *resp,
641 : : GCancellable *cancellable,
642 : : GError **error)
643 : : {
644 : : const guchar *blob;
645 : : gsize length;
646 : 2 : gsize offset = 5;
647 : 2 : GBytes *key = NULL;
648 : : gboolean ret;
649 : :
650 : : /* If parsing the request fails, just pass through */
651 : 2 : ret = egg_buffer_get_byte_array (req, offset, &offset, &blob, &length);
652 [ + + ]: 2 : if (ret)
653 : 1 : key = g_bytes_new (blob, length);
654 : : else
655 : 1 : g_message ("got unparseable remove request for ssh-agent");
656 : :
657 : : /* Call out ssh-agent anyway to make sure that the key is removed */
658 : 2 : ret = relay_request (self, connection, req, resp, cancellable, error);
659 [ + + ]: 2 : if (key) {
660 [ + - ]: 1 : if (ret)
661 : 1 : remove_key (self, key);
662 : 1 : g_bytes_unref (key);
663 : : }
664 : 2 : return ret;
665 : : }
666 : :
667 : : static gboolean
668 : 1 : op_remove_all_identities (GcrSshAgentService *self,
669 : : GSocketConnection *connection,
670 : : EggBuffer *req,
671 : : EggBuffer *resp,
672 : : GCancellable *cancellable,
673 : : GError **error)
674 : : {
675 : : gboolean ret;
676 : :
677 : 1 : ret = relay_request (self, connection, req, resp, cancellable, error);
678 [ + - ]: 1 : if (ret)
679 : 1 : clear_keys (self);
680 : :
681 : 1 : return ret;
682 : : }
683 : :
684 : : static const GcrSshAgentOperation operations[GCR_SSH_OP_MAX] = {
685 : : NULL, /* 0 */
686 : : NULL, /* GCR_SSH_OP_REQUEST_RSA_IDENTITIES */
687 : : NULL, /* 2 */
688 : : NULL, /* GCR_SSH_OP_RSA_CHALLENGE */
689 : : NULL, /* 4 */
690 : : NULL, /* 5 */
691 : : NULL, /* 6 */
692 : : NULL, /* GCR_SSH_OP_ADD_RSA_IDENTITY */
693 : : NULL, /* GCR_SSH_OP_REMOVE_RSA_IDENTITY */
694 : : NULL, /* GCR_SSH_OP_REMOVE_ALL_RSA_IDENTITIES */
695 : : NULL, /* 10 */
696 : : op_request_identities, /* GCR_SSH_OP_REQUEST_IDENTITIES */
697 : : NULL, /* 12 */
698 : : op_sign_request, /* GCR_SSH_OP_SIGN_REQUEST */
699 : : NULL, /* 14 */
700 : : NULL, /* 15 */
701 : : NULL, /* 16 */
702 : : op_add_identity, /* GCR_SSH_OP_ADD_IDENTITY */
703 : : op_remove_identity, /* GCR_SSH_OP_REMOVE_IDENTITY */
704 : : op_remove_all_identities, /* GCR_SSH_OP_REMOVE_ALL_IDENTITIES */
705 : : NULL, /* GCR_SSH_OP_ADD_SMARTCARD_KEY */
706 : : NULL, /* GCR_SSH_OP_REMOVE_SMARTCARD_KEY */
707 : : NULL, /* GCR_SSH_OP_LOCK */
708 : : NULL, /* GCR_SSH_OP_UNLOCK */
709 : : NULL, /* GCR_SSH_OP_ADD_RSA_ID_CONSTRAINED */
710 : : op_add_identity, /* GCR_SSH_OP_ADD_ID_CONSTRAINED */
711 : : NULL, /* GCR_SSH_OP_ADD_SMARTCARD_KEY_CONSTRAINED */
712 : : };
|