gnome-shell-3.6.3.1/src/shell-network-agent.c

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 }