No issues found
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /*
3 * Copyright 2011 Red Hat, Inc.
4 * 2011 Giovanni Campagna <scampa.giovanni@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 */
21
22 #include "config.h"
23 #include <string.h>
24 #include <gnome-keyring.h>
25 #include <dbus/dbus-glib.h>
26
27 #include "shell-network-agent.h"
28
29 enum {
30 SIGNAL_NEW_REQUEST,
31 SIGNAL_CANCEL_REQUEST,
32 SIGNAL_LAST
33 };
34
35 static gint signals[SIGNAL_LAST];
36
37 typedef struct {
38 gpointer keyring_op;
39 ShellNetworkAgent *self;
40
41 gchar *request_id;
42 NMConnection *connection;
43 gchar *setting_name;
44 gchar **hints;
45 NMSecretAgentGetSecretsFlags flags;
46 NMSecretAgentGetSecretsFunc callback;
47 gpointer callback_data;
48
49 /* <gchar *setting_key, gchar *secret> */
50 GHashTable *entries;
51 GHashTable *vpn_entries;
52 gboolean is_vpn;
53 } ShellAgentRequest;
54
55 struct _ShellNetworkAgentPrivate {
56 /* <gchar *request_id, ShellAgentRequest *request> */
57 GHashTable *requests;
58 };
59
60 G_DEFINE_TYPE (ShellNetworkAgent, shell_network_agent, NM_TYPE_SECRET_AGENT)
61
62 static void
63 shell_agent_request_free (gpointer data)
64 {
65 ShellAgentRequest *request = data;
66
67 if (request->keyring_op)
68 gnome_keyring_cancel_request (request->keyring_op);
69
70 g_object_unref (request->self);
71 g_object_unref (request->connection);
72 g_free (request->setting_name);
73 g_strfreev (request->hints);
74 g_hash_table_destroy (request->entries);
75
76 g_slice_free (ShellAgentRequest, request);
77 }
78
79 static void
80 shell_agent_request_cancel (ShellAgentRequest *request)
81 {
82 GError *error;
83 ShellNetworkAgent *self;
84
85 self = request->self;
86
87 error = g_error_new (NM_SECRET_AGENT_ERROR,
88 NM_SECRET_AGENT_ERROR_AGENT_CANCELED,
89 "Canceled by NetworkManager");
90 request->callback (NM_SECRET_AGENT (self), request->connection,
91 NULL, error, request->callback_data);
92
93 g_signal_emit (self, signals[SIGNAL_CANCEL_REQUEST], 0, request->request_id);
94
95 g_hash_table_remove (self->priv->requests, request->request_id);
96 g_error_free (error);
97 }
98
99 static void
100 shell_network_agent_init (ShellNetworkAgent *agent)
101 {
102 ShellNetworkAgentPrivate *priv;
103
104 priv = agent->priv = G_TYPE_INSTANCE_GET_PRIVATE (agent, SHELL_TYPE_NETWORK_AGENT, ShellNetworkAgentPrivate);
105
106 priv->requests = g_hash_table_new_full (g_str_hash, g_str_equal,
107 g_free, shell_agent_request_free);
108 }
109
110 static void
111 shell_network_agent_finalize (GObject *object)
112 {
113 ShellNetworkAgentPrivate *priv = SHELL_NETWORK_AGENT (object)->priv;
114 GError *error;
115 GHashTableIter iter;
116 gpointer key;
117 gpointer value;
118
119 error = g_error_new (NM_SECRET_AGENT_ERROR,
120 NM_SECRET_AGENT_ERROR_AGENT_CANCELED,
121 "The secret agent is going away");
122
123 g_hash_table_iter_init (&iter, priv->requests);
124 while (g_hash_table_iter_next (&iter, &key, &value))
125 {
126 ShellAgentRequest *request = value;
127
128 request->callback (NM_SECRET_AGENT (object),
129 request->connection,
130 NULL, error,
131 request->callback_data);
132 }
133
134 g_hash_table_destroy (priv->requests);
135 g_error_free (error);
136
137 G_OBJECT_CLASS (shell_network_agent_parent_class)->finalize (object);
138 }
139
140 static void
141 request_secrets_from_ui (ShellAgentRequest *request)
142 {
143 g_signal_emit (request->self, signals[SIGNAL_NEW_REQUEST], 0,
144 request->request_id,
145 request->connection,
146 request->setting_name,
147 request->hints,
148 (int)request->flags);
149 }
150
151 static void
152 check_always_ask_cb (NMSetting *setting,
153 const gchar *key,
154 const GValue *value,
155 GParamFlags flags,
156 gpointer user_data)
157 {
158 gboolean *always_ask = user_data;
159 NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
160
161 if (flags & NM_SETTING_PARAM_SECRET)
162 {
163 if (nm_setting_get_secret_flags (setting, key, &secret_flags, NULL))
164 {
165 if (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)
166 *always_ask = TRUE;
167 }
168 }
169 }
170
171 static gboolean
172 has_always_ask (NMSetting *setting)
173 {
174 gboolean always_ask = FALSE;
175
176 nm_setting_enumerate_values (setting, check_always_ask_cb, &always_ask);
177 return always_ask;
178 }
179
180 static gboolean
181 is_connection_always_ask (NMConnection *connection)
182 {
183 NMSettingConnection *s_con;
184 const gchar *ctype;
185 NMSetting *setting;
186
187 /* For the given connection type, check if the secrets for that connection
188 * are always-ask or not.
189 */
190 s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
191 g_assert (s_con);
192 ctype = nm_setting_connection_get_connection_type (s_con);
193
194 setting = nm_connection_get_setting_by_name (connection, ctype);
195 g_return_val_if_fail (setting != NULL, FALSE);
196
197 if (has_always_ask (setting))
198 return TRUE;
199
200 /* Try type-specific settings too; be a bit paranoid and only consider
201 * secrets from settings relevant to the connection type.
202 */
203 if (NM_IS_SETTING_WIRELESS (setting))
204 {
205 setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY);
206 if (setting && has_always_ask (setting))
207 return TRUE;
208 setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
209 if (setting && has_always_ask (setting))
210 return TRUE;
211 }
212 else if (NM_IS_SETTING_WIRED (setting))
213 {
214 setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE);
215 if (setting && has_always_ask (setting))
216 return TRUE;
217 setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
218 if (setting && has_always_ask (setting))
219 return TRUE;
220 }
221
222 return FALSE;
223 }
224
225 static void
226 gvalue_destroy_notify (gpointer data)
227 {
228 GValue *value = data;
229 g_value_unset (value);
230 g_slice_free (GValue, value);
231 }
232
233 static gboolean
234 strv_has (gchar **haystack,
235 gchar *needle)
236 {
237 gchar *iter;
238 for (iter = *haystack; iter; iter++)
239 {
240 if (g_strcmp0 (iter, needle) == 0)
241 return TRUE;
242 }
243
244 return FALSE;
245 }
246
247 static void
248 get_secrets_keyring_cb (GnomeKeyringResult result,
249 GList *list,
250 gpointer user_data)
251 {
252 ShellAgentRequest *closure;
253 ShellNetworkAgent *self;
254 ShellNetworkAgentPrivate *priv;
255 GError *error = NULL;
256 gint n_found = 0;
257 GList *iter;
258 GHashTable *outer;
259
260 if (result == GNOME_KEYRING_RESULT_CANCELLED)
261 return;
262
263 closure = user_data;
264 self = closure->self;
265 priv = self->priv;
266
267 closure->keyring_op = NULL;
268
269 if (result == GNOME_KEYRING_RESULT_DENIED)
270 {
271 g_set_error (&error,
272 NM_SECRET_AGENT_ERROR,
273 NM_SECRET_AGENT_ERROR_USER_CANCELED,
274 "Access to the secret storage was denied by the user");
275
276 closure->callback (NM_SECRET_AGENT (closure->self), closure->connection, NULL, error, closure->callback_data);
277
278 goto out;
279 }
280
281 if (result != GNOME_KEYRING_RESULT_OK &&
282 result != GNOME_KEYRING_RESULT_NO_MATCH)
283 {
284 g_set_error (&error,
285 NM_SECRET_AGENT_ERROR,
286 NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
287 "Internal error while retrieving secrets from the keyring (result %d)", result);
288
289 closure->callback (NM_SECRET_AGENT (closure->self), closure->connection, NULL, error, closure->callback_data);
290
291 goto out;
292 }
293
294 for (iter = list; iter; iter = g_list_next (iter))
295 {
296 GnomeKeyringFound *item = iter->data;
297 int i;
298
299 for (i = 0; i < item->attributes->len; i++)
300 {
301 GnomeKeyringAttribute *attr = &gnome_keyring_attribute_list_index (item->attributes, i);
302
303 if (g_strcmp0 (attr->name, SHELL_KEYRING_SK_TAG) == 0
304 && (attr->type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING))
305 {
306 gchar *secret_name = g_strdup (attr->value.string);
307
308 if (!closure->is_vpn)
309 {
310 GValue *secret_value = g_slice_new0 (GValue);
311 g_value_init (secret_value, G_TYPE_STRING);
312 g_value_set_string (secret_value, item->secret);
313
314 g_hash_table_insert (closure->entries, secret_name, secret_value);
315 }
316 else
317 g_hash_table_insert (closure->vpn_entries, secret_name, g_strdup (item->secret));
318
319 if (closure->hints)
320 n_found += strv_has (closure->hints, secret_name);
321 else
322 n_found += 1;
323
324 break;
325 }
326 }
327 }
328
329 if (n_found == 0 &&
330 (closure->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION))
331 {
332 nm_connection_update_secrets (closure->connection, closure->setting_name, closure->entries, NULL);
333
334 request_secrets_from_ui (closure);
335 return;
336 }
337
338 outer = g_hash_table_new (g_str_hash, g_str_equal);
339 g_hash_table_insert (outer, closure->setting_name, closure->entries);
340
341 closure->callback (NM_SECRET_AGENT (closure->self), closure->connection, outer, NULL, closure->callback_data);
342
343 g_hash_table_destroy (outer);
344
345 out:
346 g_hash_table_remove (priv->requests, closure->request_id);
347 g_clear_error (&error);
348 }
349
350 static void
351 shell_network_agent_get_secrets (NMSecretAgent *agent,
352 NMConnection *connection,
353 const gchar *connection_path,
354 const gchar *setting_name,
355 const gchar **hints,
356 NMSecretAgentGetSecretsFlags flags,
357 NMSecretAgentGetSecretsFunc callback,
358 gpointer callback_data)
359 {
360 ShellNetworkAgent *self = SHELL_NETWORK_AGENT (agent);
361 ShellAgentRequest *request;
362 NMSettingConnection *setting_connection;
363 const char *connection_type;
364 char *request_id;
365
366 request_id = g_strdup_printf ("%s/%s", connection_path, setting_name);
367 if ((request = g_hash_table_lookup (self->priv->requests, request_id)) != NULL)
368 {
369 /* We already have a request pending for this (connection, setting)
370 * Cancel it before starting the new one.
371 * This will also free the request structure and associated resources.
372 */
373 shell_agent_request_cancel (request);
374 }
375
376 setting_connection = nm_connection_get_setting_connection (connection);
377 connection_type = nm_setting_connection_get_connection_type (setting_connection);
378
379 request = g_slice_new (ShellAgentRequest);
380 request->self = g_object_ref (self);
381 request->connection = g_object_ref (connection);
382 request->setting_name = g_strdup (setting_name);
383 request->hints = g_strdupv ((gchar **)hints);
384 request->flags = flags;
385 request->callback = callback;
386 request->callback_data = callback_data;
387 request->is_vpn = !strcmp(connection_type, NM_SETTING_VPN_SETTING_NAME);
388 request->keyring_op = NULL;
389 request->entries = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, gvalue_destroy_notify);
390
391 if (request->is_vpn)
392 {
393 GValue *secret_value;
394
395 request->vpn_entries = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
396
397 secret_value = g_slice_new0 (GValue);
398 g_value_init (secret_value, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING));
399 g_value_take_boxed (secret_value, request->vpn_entries);
400 g_hash_table_insert (request->entries, g_strdup(NM_SETTING_VPN_SECRETS), secret_value);
401 }
402 else
403 request->vpn_entries = NULL;
404
405 request->request_id = request_id;
406 g_hash_table_replace (self->priv->requests, request->request_id, request);
407
408 if ((flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW) ||
409 ((flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION)
410 && is_connection_always_ask (request->connection)))
411 {
412 request_secrets_from_ui (request);
413 return;
414 }
415
416 request->keyring_op = gnome_keyring_find_itemsv (GNOME_KEYRING_ITEM_GENERIC_SECRET,
417 get_secrets_keyring_cb,
418 request,
419 NULL, /* GDestroyNotify */
420 SHELL_KEYRING_UUID_TAG,
421 GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
422 nm_connection_get_uuid (connection),
423 SHELL_KEYRING_SN_TAG,
424 GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
425 setting_name,
426 NULL);
427 }
428
429 void
430 shell_network_agent_set_password (ShellNetworkAgent *self,
431 gchar *request_id,
432 gchar *setting_key,
433 gchar *setting_value)
434 {
435 ShellNetworkAgentPrivate *priv;
436 ShellAgentRequest *request;
437 GValue *value;
438
439 g_return_if_fail (SHELL_IS_NETWORK_AGENT (self));
440
441 priv = self->priv;
442 request = g_hash_table_lookup (priv->requests, request_id);
443 g_return_if_fail (request != NULL);
444
445 if (!request->is_vpn)
446 {
447 value = g_slice_new0 (GValue);
448 g_value_init (value, G_TYPE_STRING);
449 g_value_set_string (value, setting_value);
450
451 g_hash_table_replace (request->entries, g_strdup (setting_key), value);
452 }
453 else
454 {
455 g_hash_table_replace (request->vpn_entries, g_strdup (setting_key), g_strdup (setting_value));
456 }
457 }
458
459 void
460 shell_network_agent_respond (ShellNetworkAgent *self,
461 gchar *request_id,
462 ShellNetworkAgentResponse response)
463 {
464 ShellNetworkAgentPrivate *priv;
465 ShellAgentRequest *request;
466 NMConnection *dup;
467 GHashTable *outer;
468
469 g_return_if_fail (SHELL_IS_NETWORK_AGENT (self));
470
471 priv = self->priv;
472 request = g_hash_table_lookup (priv->requests, request_id);
473 g_return_if_fail (request != NULL);
474
475 if (response == SHELL_NETWORK_AGENT_USER_CANCELED)
476 {
477 GError *error = g_error_new (NM_SECRET_AGENT_ERROR,
478 NM_SECRET_AGENT_ERROR_USER_CANCELED,
479 "Network dialog was canceled by the user");
480
481 request->callback (NM_SECRET_AGENT (self), request->connection, NULL, error, request->callback_data);
482 g_error_free (error);
483 g_hash_table_remove (priv->requests, request_id);
484 return;
485 }
486
487 if (response == SHELL_NETWORK_AGENT_INTERNAL_ERROR)
488 {
489 GError *error = g_error_new (NM_SECRET_AGENT_ERROR,
490 NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
491 "An internal error occurred while processing the request.");
492
493 request->callback (NM_SECRET_AGENT (self), request->connection, NULL, error, request->callback_data);
494 g_error_free (error);
495 g_hash_table_remove (priv->requests, request_id);
496 return;
497 }
498
499 /* response == SHELL_NETWORK_AGENT_CONFIRMED */
500
501 /* Save updated secrets */
502 dup = nm_connection_duplicate (request->connection);
503
504 nm_connection_update_secrets (dup, request->setting_name, request->entries, NULL);
505 nm_secret_agent_save_secrets (NM_SECRET_AGENT (self), dup, NULL, NULL);
506
507 outer = g_hash_table_new (g_str_hash, g_str_equal);
508 g_hash_table_insert (outer, request->setting_name, request->entries);
509
510 request->callback (NM_SECRET_AGENT (self), request->connection, outer, NULL, request->callback_data);
511
512 g_hash_table_destroy (outer);
513 g_object_unref (dup);
514 g_hash_table_remove (priv->requests, request_id);
515 }
516
517 static void
518 shell_network_agent_cancel_get_secrets (NMSecretAgent *agent,
519 const gchar *connection_path,
520 const gchar *setting_name)
521 {
522 ShellNetworkAgent *self = SHELL_NETWORK_AGENT (agent);
523 ShellNetworkAgentPrivate *priv = self->priv;
524 gchar *request_id;
525 ShellAgentRequest *request;
526
527 request_id = g_strdup_printf ("%s/%s", connection_path, setting_name);
528 request = g_hash_table_lookup (priv->requests, request_id);
529 g_free (request_id);
530
531 if (!request)
532 {
533 /* We've already sent the result, but the caller cancelled the
534 * operation before receiving that result.
535 */
536 return;
537 }
538
539 shell_agent_request_cancel (request);
540 }
541
542 /************************* saving of secrets ****************************************/
543
544 static GnomeKeyringAttributeList *
545 create_keyring_add_attr_list (NMConnection *connection,
546 const gchar *connection_uuid,
547 const gchar *connection_id,
548 const gchar *setting_name,
549 const gchar *setting_key,
550 gchar **out_display_name)
551 {
552 GnomeKeyringAttributeList *attrs = NULL;
553 NMSettingConnection *s_con;
554
555 if (connection)
556 {
557 s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
558 g_return_val_if_fail (s_con != NULL, NULL);
559 connection_uuid = nm_setting_connection_get_uuid (s_con);
560 connection_id = nm_setting_connection_get_id (s_con);
561 }
562
563 g_return_val_if_fail (connection_uuid != NULL, NULL);
564 g_return_val_if_fail (connection_id != NULL, NULL);
565 g_return_val_if_fail (setting_name != NULL, NULL);
566 g_return_val_if_fail (setting_key != NULL, NULL);
567
568 if (out_display_name)
569 {
570 *out_display_name = g_strdup_printf ("Network secret for %s/%s/%s",
571 connection_id,
572 setting_name,
573 setting_key);
574 }
575
576 attrs = gnome_keyring_attribute_list_new ();
577 gnome_keyring_attribute_list_append_string (attrs,
578 SHELL_KEYRING_UUID_TAG,
579 connection_uuid);
580 gnome_keyring_attribute_list_append_string (attrs,
581 SHELL_KEYRING_SN_TAG,
582 setting_name);
583 gnome_keyring_attribute_list_append_string (attrs,
584 SHELL_KEYRING_SK_TAG,
585 setting_key);
586 return attrs;
587 }
588
589 typedef struct
590 {
591 /* Sort of ref count, indicates the number of secrets we still need to save */
592 gint n_secrets;
593
594 NMSecretAgent *self;
595 NMConnection *connection;
596 gpointer callback;
597 gpointer callback_data;
598 } KeyringRequest;
599
600 static void
601 keyring_request_free (KeyringRequest *r)
602 {
603 g_object_unref (r->self);
604 g_object_unref (r->connection);
605
606 g_slice_free (KeyringRequest, r);
607 }
608
609 static void
610 save_secret_cb (GnomeKeyringResult result,
611 guint val,
612 gpointer user_data)
613 {
614 KeyringRequest *call = user_data;
615 NMSecretAgentSaveSecretsFunc callback = call->callback;
616
617 call->n_secrets--;
618
619 if (call->n_secrets == 0)
620 {
621 if (callback)
622 callback (call->self, call->connection, NULL, call->callback_data);
623 keyring_request_free (call);
624 }
625 }
626
627 static void
628 save_one_secret (KeyringRequest *r,
629 NMSetting *setting,
630 const gchar *key,
631 const gchar *secret,
632 const gchar *display_name)
633 {
634 GnomeKeyringAttributeList *attrs;
635 gchar *alt_display_name = NULL;
636 const gchar *setting_name;
637 NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
638
639 /* Only save agent-owned secrets (not system-owned or always-ask) */
640 nm_setting_get_secret_flags (setting, key, &secret_flags, NULL);
641 if (secret_flags != NM_SETTING_SECRET_FLAG_AGENT_OWNED)
642 return;
643
644 setting_name = nm_setting_get_name (setting);
645 g_assert (setting_name);
646
647 attrs = create_keyring_add_attr_list (r->connection, NULL, NULL,
648 setting_name,
649 key,
650 display_name ? NULL : &alt_display_name);
651 g_assert (attrs);
652 r->n_secrets++;
653 gnome_keyring_item_create (NULL,
654 GNOME_KEYRING_ITEM_GENERIC_SECRET,
655 display_name ? display_name : alt_display_name,
656 attrs,
657 secret,
658 TRUE,
659 save_secret_cb,
660 r,
661 NULL);
662
663 gnome_keyring_attribute_list_free (attrs);
664 g_free (alt_display_name);
665 }
666
667 static void
668 vpn_secret_iter_cb (const gchar *key,
669 const gchar *secret,
670 gpointer user_data)
671 {
672 KeyringRequest *r = user_data;
673 NMSetting *setting;
674 const gchar *service_name, *id;
675 gchar *display_name;
676
677 if (secret && strlen (secret))
678 {
679 setting = nm_connection_get_setting (r->connection, NM_TYPE_SETTING_VPN);
680 g_assert (setting);
681 service_name = nm_setting_vpn_get_service_type (NM_SETTING_VPN (setting));
682 g_assert (service_name);
683 id = nm_connection_get_id (r->connection);
684 g_assert (id);
685
686 display_name = g_strdup_printf ("VPN %s secret for %s/%s/" NM_SETTING_VPN_SETTING_NAME,
687 key,
688 id,
689 service_name);
690 save_one_secret (r, setting, key, secret, display_name);
691 g_free (display_name);
692 }
693 }
694
695 static void
696 write_one_secret_to_keyring (NMSetting *setting,
697 const gchar *key,
698 const GValue *value,
699 GParamFlags flags,
700 gpointer user_data)
701 {
702 KeyringRequest *r = user_data;
703 const gchar *secret;
704
705 /* Non-secrets obviously don't get saved in the keyring */
706 if (!(flags & NM_SETTING_PARAM_SECRET))
707 return;
708
709 if (NM_IS_SETTING_VPN (setting) && (g_strcmp0 (key, NM_SETTING_VPN_SECRETS) == 0))
710 {
711 /* Process VPN secrets specially since it's a hash of secrets, not just one */
712 nm_setting_vpn_foreach_secret (NM_SETTING_VPN (setting),
713 vpn_secret_iter_cb,
714 r);
715 }
716 else
717 {
718 secret = g_value_get_string (value);
719 if (secret && strlen (secret))
720 save_one_secret (r, setting, key, secret, NULL);
721 }
722 }
723
724 static void
725 save_delete_cb (NMSecretAgent *agent,
726 NMConnection *connection,
727 GError *error,
728 gpointer user_data)
729 {
730 KeyringRequest *r = user_data;
731
732 /* Ignore errors; now save all new secrets */
733 nm_connection_for_each_setting_value (connection, write_one_secret_to_keyring, r);
734
735 /* If no secrets actually got saved there may be nothing to do so
736 * try to complete the request here. If there were secrets to save the
737 * request will get completed when those keyring calls return (at the next
738 * mainloop iteration).
739 */
740 if (r->n_secrets == 0)
741 {
742 if (r->callback)
743 ((NMSecretAgentSaveSecretsFunc)r->callback) (agent, connection, NULL, r->callback_data);
744 keyring_request_free (r);
745 }
746 }
747
748 static void
749 shell_network_agent_save_secrets (NMSecretAgent *agent,
750 NMConnection *connection,
751 const gchar *connection_path,
752 NMSecretAgentSaveSecretsFunc callback,
753 gpointer callback_data)
754 {
755 KeyringRequest *r;
756
757 r = g_slice_new (KeyringRequest);
758 r->n_secrets = 0;
759 r->self = g_object_ref (agent);
760 r->connection = g_object_ref (connection);
761 r->callback = callback;
762 r->callback_data = callback_data;
763
764 /* First delete any existing items in the keyring */
765 nm_secret_agent_delete_secrets (agent, connection, save_delete_cb, r);
766 }
767
768 static void
769 keyring_delete_cb (GnomeKeyringResult result, gpointer user_data)
770 {
771 /* Ignored */
772 }
773
774 static void
775 delete_find_items_cb (GnomeKeyringResult result, GList *list, gpointer user_data)
776 {
777 KeyringRequest *r = user_data;
778 GList *iter;
779 GError *error = NULL;
780 NMSecretAgentDeleteSecretsFunc callback = r->callback;
781
782 if ((result == GNOME_KEYRING_RESULT_OK) || (result == GNOME_KEYRING_RESULT_NO_MATCH))
783 {
784 for (iter = list; iter != NULL; iter = g_list_next (iter))
785 {
786 GnomeKeyringFound *found = (GnomeKeyringFound *) iter->data;
787
788 gnome_keyring_item_delete (found->keyring, found->item_id, keyring_delete_cb, NULL, NULL);
789 }
790 }
791 else
792 {
793 error = g_error_new (NM_SECRET_AGENT_ERROR,
794 NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
795 "The request could not be completed. Keyring result: %d",
796 result);
797 }
798
799 callback (r->self, r->connection, error, r->callback_data);
800 g_clear_error (&error);
801 }
802
803 static void
804 shell_network_agent_delete_secrets (NMSecretAgent *agent,
805 NMConnection *connection,
806 const gchar *connection_path,
807 NMSecretAgentDeleteSecretsFunc callback,
808 gpointer callback_data)
809 {
810 KeyringRequest *r;
811 NMSettingConnection *s_con;
812 const gchar *uuid;
813
814 r = g_slice_new (KeyringRequest);
815 r->n_secrets = 0; /* ignored by delete secrets calls */
816 r->self = g_object_ref (agent);
817 r->connection = g_object_ref (connection);
818 r->callback = callback;
819 r->callback_data = callback_data;
820
821 s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
822 g_assert (s_con);
823 uuid = nm_setting_connection_get_uuid (s_con);
824 g_assert (uuid);
825
826 gnome_keyring_find_itemsv (GNOME_KEYRING_ITEM_GENERIC_SECRET,
827 delete_find_items_cb,
828 r,
829 (GDestroyNotify)keyring_request_free,
830 SHELL_KEYRING_UUID_TAG,
831 GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
832 uuid,
833 NULL);
834 }
835
836 void
837 shell_network_agent_class_init (ShellNetworkAgentClass *klass)
838 {
839 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
840 NMSecretAgentClass *agent_class = NM_SECRET_AGENT_CLASS (klass);
841
842 gobject_class->finalize = shell_network_agent_finalize;
843
844 agent_class->get_secrets = shell_network_agent_get_secrets;
845 agent_class->cancel_get_secrets = shell_network_agent_cancel_get_secrets;
846 agent_class->save_secrets = shell_network_agent_save_secrets;
847 agent_class->delete_secrets = shell_network_agent_delete_secrets;
848
849 signals[SIGNAL_NEW_REQUEST] = g_signal_new ("new-request",
850 G_TYPE_FROM_CLASS (klass),
851 0, /* flags */
852 0, /* class offset */
853 NULL, /* accumulator */
854 NULL, /* accu_data */
855 NULL, /* marshaller */
856 G_TYPE_NONE, /* return */
857 5, /* n_params */
858 G_TYPE_STRING,
859 NM_TYPE_CONNECTION,
860 G_TYPE_STRING,
861 G_TYPE_STRV,
862 G_TYPE_INT);
863
864 signals[SIGNAL_CANCEL_REQUEST] = g_signal_new ("cancel-request",
865 G_TYPE_FROM_CLASS (klass),
866 0, /* flags */
867 0, /* class offset */
868 NULL, /* accumulator */
869 NULL, /* accu_data */
870 NULL, /* marshaller */
871 G_TYPE_NONE,
872 1, /* n_params */
873 G_TYPE_STRING);
874
875 g_type_class_add_private (klass, sizeof (ShellNetworkAgentPrivate));
876 }