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