bce3dff104a3ac9b89664ae6170f49d85f0799a6
[NetworkManager.git] / src / settings / nm-settings-connection.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* NetworkManager system settings service
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  * Copyright 2008 Novell, Inc.
19  * Copyright 2008 - 2014 Red Hat, Inc.
20  */
21
22 #include "nm-default.h"
23
24 #include <string.h>
25
26 #include "nm-dbus-interface.h"
27 #include "nm-settings-connection.h"
28 #include "nm-session-monitor.h"
29 #include "nm-auth-utils.h"
30 #include "nm-auth-subject.h"
31 #include "nm-agent-manager.h"
32 #include "NetworkManagerUtils.h"
33 #include "nm-core-internal.h"
34 #include "nm-audit-manager.h"
35
36 #include "nmdbus-settings-connection.h"
37
38 #define SETTINGS_TIMESTAMPS_FILE  NMSTATEDIR "/timestamps"
39 #define SETTINGS_SEEN_BSSIDS_FILE NMSTATEDIR "/seen-bssids"
40
41 #define _NMLOG_DOMAIN        LOGD_SETTINGS
42 #define _NMLOG_PREFIX_NAME   "settings-connection"
43 #define _NMLOG(level, ...) \
44     G_STMT_START { \
45         const NMLogLevel __level = (level); \
46         \
47         if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \
48             char __prefix[128]; \
49             const char *__p_prefix = _NMLOG_PREFIX_NAME; \
50             \
51             if (self) { \
52                 const char *__uuid = nm_settings_connection_get_uuid (self); \
53                 \
54                 g_snprintf (__prefix, sizeof (__prefix), "%s[%p%s%s]", _NMLOG_PREFIX_NAME, self, __uuid ? "," : "", __uuid ? __uuid : ""); \
55                 __p_prefix = __prefix; \
56             } \
57             _nm_log (__level, _NMLOG_DOMAIN, 0, \
58                      "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
59                      __p_prefix _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
60         } \
61     } G_STMT_END
62
63 static void nm_settings_connection_connection_interface_init (NMConnectionInterface *iface);
64
65 G_DEFINE_TYPE_WITH_CODE (NMSettingsConnection, nm_settings_connection, NM_TYPE_EXPORTED_OBJECT,
66                          G_IMPLEMENT_INTERFACE (NM_TYPE_CONNECTION, nm_settings_connection_connection_interface_init)
67                          )
68
69 #define NM_SETTINGS_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
70                                                NM_TYPE_SETTINGS_CONNECTION, \
71                                                NMSettingsConnectionPrivate))
72
73 enum {
74         PROP_0 = 0,
75         PROP_VISIBLE,
76         PROP_UNSAVED,
77         PROP_READY,
78         PROP_FLAGS,
79         PROP_FILENAME,
80 };
81
82 enum {
83         UPDATED,
84         REMOVED,
85         UPDATED_BY_USER,
86         LAST_SIGNAL
87 };
88 static guint signals[LAST_SIGNAL] = { 0 };
89
90 typedef struct {
91         gboolean removed;
92
93         NMAgentManager *agent_mgr;
94         NMSessionMonitor *session_monitor;
95         guint session_changed_id;
96
97         NMSettingsConnectionFlags flags;
98         gboolean ready;
99
100         guint updated_idle_id;
101
102         GSList *pending_auths; /* List of pending authentication requests */
103         gboolean visible; /* Is this connection is visible by some session? */
104
105         GSList *get_secret_requests;  /* in-progress secrets requests */
106
107         /* Caches secrets from on-disk connections; were they not cached any
108          * call to nm_connection_clear_secrets() wipes them out and we'd have
109          * to re-read them from disk which defeats the purpose of having the
110          * connection in-memory at all.
111          */
112         NMConnection *system_secrets;
113
114         /* Caches secrets from agents during the activation process; if new system
115          * secrets are returned from an agent, they get written out to disk,
116          * triggering a re-read of the connection, which reads only system
117          * secrets, and would wipe out any agent-owned or not-saved secrets the
118          * agent also returned.
119          */
120         NMConnection *agent_secrets;
121
122         guint64 timestamp;   /* Up-to-date timestamp of connection use */
123         gboolean timestamp_set;
124         GHashTable *seen_bssids; /* Up-to-date BSSIDs that's been seen for the connection */
125
126         int autoconnect_retries;
127         gint32 autoconnect_retry_time;
128         NMDeviceStateReason autoconnect_blocked_reason;
129
130         char *filename;
131
132 } NMSettingsConnectionPrivate;
133
134 /*******************************************************************/
135
136 gboolean
137 nm_settings_connection_has_unmodified_applied_connection (NMSettingsConnection *self,
138                                                           NMConnection *applied_connection,
139                                                           NMSettingCompareFlags compare_flags)
140 {
141         g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
142         g_return_val_if_fail (NM_IS_CONNECTION (applied_connection), FALSE);
143
144         /* for convenience, we *always* ignore certain settings. */
145         compare_flags |= NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS | NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP;
146
147         return nm_connection_compare (NM_CONNECTION (self), applied_connection, compare_flags);
148 }
149
150 /**************************************************************/
151
152 /* Return TRUE to keep, FALSE to drop */
153 typedef gboolean (*ForEachSecretFunc) (NMSettingSecretFlags flags,
154                                        gpointer user_data);
155
156 /* Returns always a non-NULL, non-floating variant that must
157  * be unrefed by the caller. */
158 static GVariant *
159 for_each_secret (NMConnection *self,
160                  GVariant *secrets,
161                  gboolean remove_non_secrets,
162                  ForEachSecretFunc callback,
163                  gpointer callback_data)
164 {
165         GVariantBuilder secrets_builder, setting_builder;
166         GVariantIter secrets_iter, *setting_iter;
167         const char *setting_name;
168
169         /* This function, given a dict of dicts representing new secrets of
170          * an NMConnection, walks through each toplevel dict (which represents a
171          * NMSetting), and for each setting, walks through that setting dict's
172          * properties.  For each property that's a secret, it will check that
173          * secret's flags in the backing NMConnection object, and call a supplied
174          * callback.
175          *
176          * The one complexity is that the VPN setting's 'secrets' property is
177          * *also* a dict (since the key/value pairs are arbitrary and known
178          * only to the VPN plugin itself).  That means we have three levels of
179          * dicts that we potentially have to traverse here.  When we hit the
180          * VPN setting's 'secrets' property, we special-case that and iterate over
181          * each item in that 'secrets' dict, calling the supplied callback
182          * each time.
183          */
184
185         g_return_val_if_fail (callback, NULL);
186
187         g_variant_iter_init (&secrets_iter, secrets);
188         g_variant_builder_init (&secrets_builder, NM_VARIANT_TYPE_CONNECTION);
189         while (g_variant_iter_next (&secrets_iter, "{&sa{sv}}", &setting_name, &setting_iter)) {
190                 NMSetting *setting;
191                 const char *secret_name;
192                 GVariant *val;
193
194                 setting = nm_connection_get_setting_by_name (self, setting_name);
195                 if (setting == NULL) {
196                         g_variant_iter_free (setting_iter);
197                         continue;
198                 }
199
200                 g_variant_builder_init (&setting_builder, NM_VARIANT_TYPE_SETTING);
201                 while (g_variant_iter_next (setting_iter, "{&sv}", &secret_name, &val)) {
202                         NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
203
204                         /* VPN secrets need slightly different treatment here since the
205                          * "secrets" property is actually a hash table of secrets.
206                          */
207                         if (NM_IS_SETTING_VPN (setting) && !g_strcmp0 (secret_name, NM_SETTING_VPN_SECRETS)) {
208                                 GVariantBuilder vpn_secrets_builder;
209                                 GVariantIter vpn_secrets_iter;
210                                 const char *vpn_secret_name, *secret;
211
212                                 /* Iterate through each secret from the VPN dict in the overall secrets dict */
213                                 g_variant_builder_init (&vpn_secrets_builder, G_VARIANT_TYPE ("a{ss}"));
214                                 g_variant_iter_init (&vpn_secrets_iter, val);
215                                 while (g_variant_iter_next (&vpn_secrets_iter, "{&s&s}", &vpn_secret_name, &secret)) {
216                                         if (!nm_setting_get_secret_flags (setting, vpn_secret_name, &secret_flags, NULL)) {
217                                                 if (!remove_non_secrets)
218                                                         g_variant_builder_add (&vpn_secrets_builder, "{ss}", vpn_secret_name, secret);
219                                                 continue;
220                                         }
221
222                                         if (callback (secret_flags, callback_data))
223                                                 g_variant_builder_add (&vpn_secrets_builder, "{ss}", vpn_secret_name, secret);
224                                 }
225
226                                 g_variant_builder_add (&setting_builder, "{sv}",
227                                                        secret_name, g_variant_builder_end (&vpn_secrets_builder));
228                         } else {
229                                 if (!nm_setting_get_secret_flags (setting, secret_name, &secret_flags, NULL)) {
230                                         if (!remove_non_secrets)
231                                                 g_variant_builder_add (&setting_builder, "{sv}", secret_name, val);
232                                         continue;
233                                 }
234                                 if (callback (secret_flags, callback_data))
235                                         g_variant_builder_add (&setting_builder, "{sv}", secret_name, val);
236                         }
237                         g_variant_unref (val);
238                 }
239
240                 g_variant_iter_free (setting_iter);
241                 g_variant_builder_add (&secrets_builder, "{sa{sv}}", setting_name, &setting_builder);
242         }
243
244         return g_variant_ref_sink (g_variant_builder_end (&secrets_builder));
245 }
246
247 typedef gboolean (*FindSecretFunc) (NMSettingSecretFlags flags,
248                                     gpointer user_data);
249
250 typedef struct {
251         FindSecretFunc find_func;
252         gpointer find_func_data;
253         gboolean found;
254 } FindSecretData;
255
256 static gboolean
257 find_secret_for_each_func (NMSettingSecretFlags flags,
258                            gpointer user_data)
259 {
260         FindSecretData *data = user_data;
261
262         if (!data->found)
263                 data->found = data->find_func (flags, data->find_func_data);
264         return FALSE;
265 }
266
267 static gboolean
268 find_secret (NMConnection *self,
269              GVariant *secrets,
270              FindSecretFunc callback,
271              gpointer callback_data)
272 {
273         FindSecretData data;
274         GVariant *dummy;
275
276         data.find_func = callback;
277         data.find_func_data = callback_data;
278         data.found = FALSE;
279
280         dummy = for_each_secret (self, secrets, FALSE, find_secret_for_each_func, &data);
281         g_variant_unref (dummy);
282         return data.found;
283 }
284
285 /**************************************************************/
286
287 static void
288 set_visible (NMSettingsConnection *self, gboolean new_visible)
289 {
290         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
291
292         if (new_visible == priv->visible)
293                 return;
294         priv->visible = new_visible;
295         g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTION_VISIBLE);
296 }
297
298 gboolean
299 nm_settings_connection_is_visible (NMSettingsConnection *self)
300 {
301         g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
302
303         return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->visible;
304 }
305
306 void
307 nm_settings_connection_recheck_visibility (NMSettingsConnection *self)
308 {
309         NMSettingsConnectionPrivate *priv;
310         NMSettingConnection *s_con;
311         guint32 num, i;
312
313         g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
314
315         priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
316
317         s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
318         g_assert (s_con);
319
320         /* Check every user in the ACL for a session */
321         num = nm_setting_connection_get_num_permissions (s_con);
322         if (num == 0) {
323                 /* Visible to all */
324                 set_visible (self, TRUE);
325                 return;
326         }
327
328         for (i = 0; i < num; i++) {
329                 const char *user;
330                 uid_t uid;
331
332                 if (!nm_setting_connection_get_permission (s_con, i, NULL, &user, NULL))
333                         continue;
334                 if (!nm_session_monitor_user_to_uid (user, &uid))
335                         continue;
336                 if (!nm_session_monitor_session_exists (priv->session_monitor, uid, FALSE))
337                         continue;
338
339                 set_visible (self, TRUE);
340                 return;
341         }
342
343         set_visible (self, FALSE);
344 }
345
346 static void
347 session_changed_cb (NMSessionMonitor *self, gpointer user_data)
348 {
349         nm_settings_connection_recheck_visibility (NM_SETTINGS_CONNECTION (user_data));
350 }
351
352 /**************************************************************/
353
354 /* Return TRUE if any active user in the connection's ACL has the given
355  * permission without having to authorize for it via PolicyKit.  Connections
356  * visible to everyone automatically pass the check.
357  */
358 gboolean
359 nm_settings_connection_check_permission (NMSettingsConnection *self,
360                                          const char *permission)
361 {
362         NMSettingsConnectionPrivate *priv;
363         NMSettingConnection *s_con;
364         guint32 num, i;
365         const char *puser;
366
367         g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
368
369         priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
370
371         if (priv->visible == FALSE)
372                 return FALSE;
373
374         s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
375         g_assert (s_con);
376
377         /* Check every user in the ACL for a session */
378         num = nm_setting_connection_get_num_permissions (s_con);
379         if (num == 0) {
380                 /* Visible to all so it's OK to auto-activate */
381                 return TRUE;
382         }
383
384         for (i = 0; i < num; i++) {
385                 /* For each user get their secret agent and check if that agent has the
386                  * required permission.
387                  *
388                  * FIXME: what if the user isn't running an agent?  PolKit needs a bus
389                  * name or a PID but if the user isn't running an agent they won't have
390                  * either.
391                  */
392                 if (nm_setting_connection_get_permission (s_con, i, NULL, &puser, NULL)) {
393                         NMSecretAgent *agent = nm_agent_manager_get_agent_by_user (priv->agent_mgr, puser);
394
395                         if (agent && nm_secret_agent_has_permission (agent, permission))
396                                 return TRUE;
397                 }
398         }
399
400         return FALSE;
401 }
402
403 /**************************************************************/
404
405 static gboolean
406 secrets_filter_cb (NMSetting *setting,
407                    const char *secret,
408                    NMSettingSecretFlags flags,
409                    gpointer user_data)
410 {
411         NMSettingSecretFlags filter_flags = GPOINTER_TO_UINT (user_data);
412
413         /* Returns TRUE to remove the secret */
414
415         /* Can't use bitops with SECRET_FLAG_NONE so handle that specifically */
416         if (   (flags == NM_SETTING_SECRET_FLAG_NONE)
417             && (filter_flags == NM_SETTING_SECRET_FLAG_NONE))
418                 return FALSE;
419
420         /* Otherwise if the secret has at least one of the desired flags keep it */
421         return (flags & filter_flags) ? FALSE : TRUE;
422 }
423
424 static void
425 update_system_secrets_cache (NMSettingsConnection *self)
426 {
427         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
428
429         if (priv->system_secrets)
430                 g_object_unref (priv->system_secrets);
431         priv->system_secrets = nm_simple_connection_new_clone (NM_CONNECTION (self));
432
433         /* Clear out non-system-owned and not-saved secrets */
434         nm_connection_clear_secrets_with_flags (priv->system_secrets,
435                                                 secrets_filter_cb,
436                                                 GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_NONE));
437 }
438
439 static void
440 update_agent_secrets_cache (NMSettingsConnection *self, NMConnection *new)
441 {
442         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
443         NMSettingSecretFlags filter_flags = NM_SETTING_SECRET_FLAG_NOT_SAVED | NM_SETTING_SECRET_FLAG_AGENT_OWNED;
444
445         if (priv->agent_secrets)
446                 g_object_unref (priv->agent_secrets);
447         priv->agent_secrets = nm_simple_connection_new_clone (new ? new : NM_CONNECTION (self));
448
449         /* Clear out non-system-owned secrets */
450         nm_connection_clear_secrets_with_flags (priv->agent_secrets,
451                                                 secrets_filter_cb,
452                                                 GUINT_TO_POINTER (filter_flags));
453 }
454
455 static void
456 secrets_cleared_cb (NMSettingsConnection *self)
457 {
458         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
459
460         /* Clear agent secrets when connection's secrets are cleared since agent
461          * secrets are transient.
462          */
463         if (priv->agent_secrets)
464                 g_object_unref (priv->agent_secrets);
465         priv->agent_secrets = NULL;
466 }
467
468 static gboolean
469 emit_updated (NMSettingsConnection *self)
470 {
471         NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->updated_idle_id = 0;
472         g_signal_emit (self, signals[UPDATED], 0);
473         return FALSE;
474 }
475
476 static void
477 set_unsaved (NMSettingsConnection *self, gboolean now_unsaved)
478 {
479         NMSettingsConnectionFlags flags = nm_settings_connection_get_flags (self);
480
481         if (NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_FLAGS_UNSAVED) != !!now_unsaved) {
482                 if (now_unsaved)
483                         flags |= NM_SETTINGS_CONNECTION_FLAGS_UNSAVED;
484                 else {
485                         flags &= ~(NM_SETTINGS_CONNECTION_FLAGS_UNSAVED |
486                                    NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED |
487                                    NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED);
488                 }
489                 nm_settings_connection_set_flags_all (self, flags);
490         }
491 }
492
493 static void
494 changed_cb (NMSettingsConnection *self, gpointer user_data)
495 {
496         gboolean update_unsaved = !!user_data;
497
498         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
499
500         if (update_unsaved)
501                 set_unsaved (self, TRUE);
502         if (priv->updated_idle_id == 0)
503                 priv->updated_idle_id = g_idle_add ((GSourceFunc) emit_updated, self);
504 }
505
506 /* Update the settings of this connection to match that of 'new_connection',
507  * taking care to make a private copy of secrets.
508  */
509 gboolean
510 nm_settings_connection_replace_settings (NMSettingsConnection *self,
511                                          NMConnection *new_connection,
512                                          gboolean update_unsaved,
513                                          const char *log_diff_name,
514                                          GError **error)
515 {
516         NMSettingsConnectionPrivate *priv;
517         gboolean success = FALSE;
518
519         g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
520         g_return_val_if_fail (NM_IS_CONNECTION (new_connection), FALSE);
521
522         priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
523
524         if (!nm_connection_normalize (new_connection, NULL, NULL, error))
525                 return FALSE;
526
527         if (   nm_connection_get_path (NM_CONNECTION (self))
528             && g_strcmp0 (nm_settings_connection_get_uuid (self), nm_connection_get_uuid (new_connection)) != 0) {
529                 /* Updating the UUID is not allowed once the path is exported. */
530                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
531                              "connection %s cannot change the UUID from %s to %s", nm_settings_connection_get_id (self),
532                              nm_settings_connection_get_uuid (self), nm_connection_get_uuid (new_connection));
533                 return FALSE;
534         }
535
536         /* Do nothing if there's nothing to update */
537         if (nm_connection_compare (NM_CONNECTION (self),
538                                    new_connection,
539                                    NM_SETTING_COMPARE_FLAG_EXACT)) {
540                 return TRUE;
541         }
542
543         /* Disconnect the changed signal to ensure we don't set Unsaved when
544          * it's not required.
545          */
546         g_signal_handlers_block_by_func (self, G_CALLBACK (changed_cb), GUINT_TO_POINTER (TRUE));
547
548         if (log_diff_name)
549                 nm_utils_log_connection_diff (new_connection, NM_CONNECTION (self), LOGL_DEBUG, LOGD_CORE, log_diff_name, "++ ");
550
551         nm_connection_replace_settings_from_connection (NM_CONNECTION (self), new_connection);
552
553         _LOGD ("replace settings from connection %p (%s)", new_connection, nm_connection_get_id (NM_CONNECTION (self)));
554
555         nm_settings_connection_set_flags (self,
556                                           NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED | NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED,
557                                           FALSE);
558
559         /* Cache the just-updated system secrets in case something calls
560          * nm_connection_clear_secrets() and clears them.
561          */
562         update_system_secrets_cache (self);
563         success = TRUE;
564
565         /* Add agent and always-ask secrets back; they won't necessarily be
566          * in the replacement connection data if it was eg reread from disk.
567          */
568         if (priv->agent_secrets) {
569                 GVariant *dict;
570
571                 dict = nm_connection_to_dbus (priv->agent_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
572                 if (dict) {
573                         (void) nm_connection_update_secrets (NM_CONNECTION (self), NULL, dict, NULL);
574                         g_variant_unref (dict);
575                 }
576         }
577
578         nm_settings_connection_recheck_visibility (self);
579
580         /* Manually emit changed signal since we disconnected the handler, but
581          * only update Unsaved if the caller wanted us to.
582          */
583         changed_cb (self, GUINT_TO_POINTER (update_unsaved));
584
585         g_signal_emit (self, signals[UPDATED_BY_USER], 0);
586
587         g_signal_handlers_unblock_by_func (self, G_CALLBACK (changed_cb), GUINT_TO_POINTER (TRUE));
588
589         return success;
590 }
591
592 static void
593 ignore_cb (NMSettingsConnection *self,
594            GError *error,
595            gpointer user_data)
596 {
597 }
598
599 /* Replaces the settings in this connection with those in 'new_connection'. If
600  * any changes are made, commits them to permanent storage and to any other
601  * subsystems watching this connection. Before returning, 'callback' is run
602  * with the given 'user_data' along with any errors encountered.
603  */
604 static void
605 replace_and_commit (NMSettingsConnection *self,
606                     NMConnection *new_connection,
607                     NMSettingsConnectionCommitFunc callback,
608                     gpointer user_data)
609 {
610         GError *error = NULL;
611         NMSettingsConnectionCommitReason commit_reason = NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION;
612
613         if (g_strcmp0 (nm_connection_get_id (NM_CONNECTION (self)),
614                        nm_connection_get_id (new_connection)) != 0)
615                 commit_reason |= NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED;
616
617         if (nm_settings_connection_replace_settings (self, new_connection, TRUE, "replace-and-commit-disk", &error))
618                 nm_settings_connection_commit_changes (self, commit_reason, callback, user_data);
619         else {
620                 g_assert (error);
621                 if (callback)
622                         callback (self, error, user_data);
623                 g_clear_error (&error);
624         }
625 }
626
627 void
628 nm_settings_connection_replace_and_commit (NMSettingsConnection *self,
629                                            NMConnection *new_connection,
630                                            NMSettingsConnectionCommitFunc callback,
631                                            gpointer user_data)
632 {
633         g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
634         g_return_if_fail (NM_IS_CONNECTION (new_connection));
635
636         NM_SETTINGS_CONNECTION_GET_CLASS (self)->replace_and_commit (self, new_connection, callback, user_data);
637 }
638
639 static void
640 commit_changes (NMSettingsConnection *self,
641                 NMSettingsConnectionCommitReason commit_reason,
642                 NMSettingsConnectionCommitFunc callback,
643                 gpointer user_data)
644 {
645         /* Subclasses only call this function if the save was successful, so at
646          * this point the connection is synced to disk and no longer unsaved.
647          */
648         set_unsaved (self, FALSE);
649
650         g_object_ref (self);
651         callback (self, NULL, user_data);
652         g_object_unref (self);
653 }
654
655 void
656 nm_settings_connection_commit_changes (NMSettingsConnection *self,
657                                        NMSettingsConnectionCommitReason commit_reason,
658                                        NMSettingsConnectionCommitFunc callback,
659                                        gpointer user_data)
660 {
661         g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
662
663         if (NM_SETTINGS_CONNECTION_GET_CLASS (self)->commit_changes) {
664                 NM_SETTINGS_CONNECTION_GET_CLASS (self)->commit_changes (self,
665                                                                          commit_reason,
666                                                                          callback ? callback : ignore_cb,
667                                                                          user_data);
668         } else {
669                 GError *error = g_error_new (NM_SETTINGS_ERROR,
670                                              NM_SETTINGS_ERROR_FAILED,
671                                              "%s: %s:%d commit_changes() unimplemented", __func__, __FILE__, __LINE__);
672                 if (callback)
673                         callback (self, error, user_data);
674                 g_error_free (error);
675         }
676 }
677
678 void
679 nm_settings_connection_delete (NMSettingsConnection *self,
680                                NMSettingsConnectionDeleteFunc callback,
681                                gpointer user_data)
682 {
683         g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
684
685         if (NM_SETTINGS_CONNECTION_GET_CLASS (self)->delete) {
686                 NM_SETTINGS_CONNECTION_GET_CLASS (self)->delete (self,
687                                                                  callback ? callback : ignore_cb,
688                                                                  user_data);
689         } else {
690                 GError *error = g_error_new (NM_SETTINGS_ERROR,
691                                              NM_SETTINGS_ERROR_FAILED,
692                                              "%s: %s:%d delete() unimplemented", __func__, __FILE__, __LINE__);
693                 if (callback)
694                         callback (self, error, user_data);
695                 g_error_free (error);
696         }
697 }
698
699 static void
700 remove_entry_from_db (NMSettingsConnection *self, const char* db_name)
701 {
702         GKeyFile *key_file;
703         const char *db_file;
704
705         if (strcmp (db_name, "timestamps") == 0)
706                 db_file = SETTINGS_TIMESTAMPS_FILE;
707         else if (strcmp (db_name, "seen-bssids") == 0)
708                 db_file = SETTINGS_SEEN_BSSIDS_FILE;
709         else
710                 return;
711
712         key_file = g_key_file_new ();
713         if (g_key_file_load_from_file (key_file, db_file, G_KEY_FILE_KEEP_COMMENTS, NULL)) {
714                 const char *connection_uuid;
715                 char *data;
716                 gsize len;
717                 GError *error = NULL;
718
719                 connection_uuid = nm_settings_connection_get_uuid (self);
720
721                 g_key_file_remove_key (key_file, db_name, connection_uuid, NULL);
722                 data = g_key_file_to_data (key_file, &len, &error);
723                 if (data) {
724                         g_file_set_contents (db_file, data, len, &error);
725                         g_free (data);
726                 }
727                 if (error) {
728                         _LOGW ("error writing %s file '%s': %s", db_name, db_file, error->message);
729                         g_error_free (error);
730                 }
731         }
732         g_key_file_free (key_file);
733 }
734
735 static void
736 do_delete (NMSettingsConnection *self,
737            NMSettingsConnectionDeleteFunc callback,
738            gpointer user_data)
739 {
740         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
741         NMConnection *for_agents;
742
743         g_object_ref (self);
744         set_visible (self, FALSE);
745
746         /* Tell agents to remove secrets for this connection */
747         for_agents = nm_simple_connection_new_clone (NM_CONNECTION (self));
748         nm_connection_clear_secrets (for_agents);
749         nm_agent_manager_delete_secrets (priv->agent_mgr,
750                                          nm_connection_get_path (NM_CONNECTION (self)),
751                                          for_agents);
752         g_object_unref (for_agents);
753
754         /* Remove timestamp from timestamps database file */
755         remove_entry_from_db (self, "timestamps");
756
757         /* Remove connection from seen-bssids database file */
758         remove_entry_from_db (self, "seen-bssids");
759
760         nm_settings_connection_signal_remove (self);
761
762         callback (self, NULL, user_data);
763
764         g_object_unref (self);
765 }
766
767 /**************************************************************/
768
769
770 typedef enum {
771         GET_SECRETS_INFO_TYPE_REQ,
772         GET_SECRETS_INFO_TYPE_IDLE,
773 } GetSecretsInfoType;
774
775 struct _NMSettingsConnectionCallId {
776         NMSettingsConnection *self;
777         gboolean had_applied_connection;
778         NMConnection *applied_connection;
779         NMSettingsConnectionSecretsFunc callback;
780         gpointer callback_data;
781
782         GetSecretsInfoType type;
783         union {
784                 struct {
785                         NMAgentManagerCallId id;
786                 } req;
787                 struct {
788                         guint32 id;
789                         GError *error;
790                 } idle;
791         } t;
792 };
793
794 typedef struct _NMSettingsConnectionCallId GetSecretsInfo;
795
796 static GetSecretsInfo *
797 _get_secrets_info_new (NMSettingsConnection *self,
798                        NMConnection *applied_connection,
799                        NMSettingsConnectionSecretsFunc callback,
800                        gpointer callback_data)
801 {
802         GetSecretsInfo *info;
803
804         info = g_slice_new0 (GetSecretsInfo);
805
806         info->self = self;
807         if (applied_connection) {
808                 info->had_applied_connection = TRUE;
809                 info->applied_connection = applied_connection;
810                 g_object_add_weak_pointer (G_OBJECT (applied_connection), (gpointer *) &info->applied_connection);
811         }
812         info->callback = callback;
813         info->callback_data = callback_data;
814
815         return info;
816 }
817
818 static void
819 _get_secrets_info_callback (GetSecretsInfo *info,
820                             const char *agent_username,
821                             const char *setting_name,
822                             GError *error)
823 {
824         if (info->callback) {
825                 info->callback (info->self,
826                                 info,
827                                 agent_username,
828                                 setting_name,
829                                 error,
830                                 info->callback_data);
831         }
832 }
833
834 static void
835 _get_secrets_info_free (GetSecretsInfo *info)
836 {
837         g_return_if_fail (info && info->self);
838
839         if (info->applied_connection)
840                 g_object_remove_weak_pointer (G_OBJECT (info->applied_connection), (gpointer *) &info->applied_connection);
841
842         if (info->type == GET_SECRETS_INFO_TYPE_IDLE)
843                 g_clear_error (&info->t.idle.error);
844
845         memset (info, 0, sizeof (*info));
846         g_slice_free (GetSecretsInfo, info);
847 }
848
849 static gboolean
850 supports_secrets (NMSettingsConnection *self, const char *setting_name)
851 {
852         /* All secrets supported */
853         return TRUE;
854 }
855
856 typedef struct {
857         NMSettingSecretFlags required;
858         NMSettingSecretFlags forbidden;
859 } ForEachSecretFlags;
860
861 static gboolean
862 validate_secret_flags (NMSettingSecretFlags flags,
863                        gpointer user_data)
864 {
865         ForEachSecretFlags *cmp_flags = user_data;
866
867         if (!NM_FLAGS_ALL (flags, cmp_flags->required))
868                 return FALSE;
869         if (NM_FLAGS_ANY (flags, cmp_flags->forbidden))
870                 return FALSE;
871         return TRUE;
872 }
873
874 static gboolean
875 secret_is_system_owned (NMSettingSecretFlags flags,
876                         gpointer user_data)
877 {
878         return !NM_FLAGS_HAS (flags, NM_SETTING_SECRET_FLAG_AGENT_OWNED);
879 }
880
881 static void
882 new_secrets_commit_cb (NMSettingsConnection *self,
883                        GError *error,
884                        gpointer user_data)
885 {
886         if (error)
887                 _LOGW ("Error saving new secrets to backing storage: %s", error->message);
888 }
889
890 static void
891 get_cmp_flags (NMSettingsConnection *self, /* only needed for logging */
892                GetSecretsInfo *info, /* only needed for logging */
893                NMConnection *connection,
894                const char *agent_dbus_owner,
895                gboolean agent_has_modify,
896                const char *setting_name, /* only needed for logging */
897                NMSecretAgentGetSecretsFlags flags,
898                GVariant *secrets,
899                gboolean *agent_had_system,
900                ForEachSecretFlags *cmp_flags)
901 {
902         gboolean is_self = (((NMConnection *) self) == connection);
903
904         g_return_if_fail (secrets);
905
906         cmp_flags->required = NM_SETTING_SECRET_FLAG_NONE;
907         cmp_flags->forbidden = NM_SETTING_SECRET_FLAG_NONE;
908
909         *agent_had_system = FALSE;
910
911         if (agent_dbus_owner) {
912                 if (is_self) {
913                         _LOGD ("(%s:%p) secrets returned from agent %s",
914                                setting_name,
915                                info,
916                                agent_dbus_owner);
917                 }
918
919                 /* If the agent returned any system-owned secrets (initial connect and no
920                  * secrets given when the connection was created, or something like that)
921                  * make sure the agent's UID has the 'modify' permission before we use or
922                  * save those system-owned secrets.  If not, discard them and use the
923                  * existing secrets, or fail the connection.
924                  */
925                 *agent_had_system = find_secret (connection, secrets, secret_is_system_owned, NULL);
926                 if (*agent_had_system) {
927                         if (flags == NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE) {
928                                 /* No user interaction was allowed when requesting secrets; the
929                                  * agent is being bad.  Remove system-owned secrets.
930                                  */
931                                 if (is_self) {
932                                         _LOGD ("(%s:%p) interaction forbidden but agent %s returned system secrets",
933                                                setting_name,
934                                                info,
935                                                agent_dbus_owner);
936                                 }
937
938                                 cmp_flags->required |= NM_SETTING_SECRET_FLAG_AGENT_OWNED;
939                         } else if (agent_has_modify == FALSE) {
940                                 /* Agent didn't successfully authenticate; clear system-owned secrets
941                                  * from the secrets the agent returned.
942                                  */
943                                 if (is_self) {
944                                         _LOGD ("(%s:%p) agent failed to authenticate but provided system secrets",
945                                                setting_name,
946                                                info);
947                                 }
948
949                                 cmp_flags->required |= NM_SETTING_SECRET_FLAG_AGENT_OWNED;
950                         }
951                 }
952         } else {
953                 if (is_self) {
954                         _LOGD ("(%s:%p) existing secrets returned",
955                                setting_name,
956                                info);
957                 }
958         }
959
960         /* If no user interaction was allowed, make sure that no "unsaved" secrets
961          * came back.  Unsaved secrets by definition require user interaction.
962          */
963         if (flags == NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE) {
964                 cmp_flags->forbidden |= (  NM_SETTING_SECRET_FLAG_NOT_SAVED
965                                          | NM_SETTING_SECRET_FLAG_NOT_REQUIRED);
966         }
967 }
968
969 static void
970 get_secrets_done_cb (NMAgentManager *manager,
971                      NMAgentManagerCallId call_id_a,
972                      const char *agent_dbus_owner,
973                      const char *agent_username,
974                      gboolean agent_has_modify,
975                      const char *setting_name,
976                      NMSecretAgentGetSecretsFlags flags,
977                      GVariant *secrets,
978                      GError *error,
979                      gpointer user_data)
980 {
981         GetSecretsInfo *info = user_data;
982         NMSettingsConnection *self;
983         NMSettingsConnectionPrivate *priv;
984         NMConnection *applied_connection;
985         gs_free_error GError *local = NULL;
986         GVariant *dict;
987         gboolean agent_had_system = FALSE;
988         ForEachSecretFlags cmp_flags = { NM_SETTING_SECRET_FLAG_NONE, NM_SETTING_SECRET_FLAG_NONE };
989
990         if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
991                 return;
992
993         self = info->self;
994         g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
995
996         priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
997
998         g_return_if_fail (g_slist_find (priv->get_secret_requests, info));
999
1000         priv->get_secret_requests = g_slist_remove (priv->get_secret_requests, info);
1001
1002         if (error) {
1003                 _LOGD ("(%s:%p) secrets request error: %s",
1004                        setting_name, info, error->message);
1005
1006                 _get_secrets_info_callback (info, NULL, setting_name, error);
1007                 goto out;
1008         }
1009
1010         if (   info->had_applied_connection
1011             && !info->applied_connection) {
1012                 g_set_error_literal (&local, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_SETTING_NOT_FOUND,
1013                                      "Applied connection deleted since requesting secrets");
1014                 _get_secrets_info_callback (info, NULL, setting_name, local);
1015                 goto out;
1016         }
1017
1018         if (   info->had_applied_connection
1019             && !nm_settings_connection_has_unmodified_applied_connection (self, info->applied_connection, NM_SETTING_COMPARE_FLAG_NONE)) {
1020                 g_set_error_literal (&local, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
1021                                      "The connection was modified since activation");
1022                 _get_secrets_info_callback (info, NULL, setting_name, local);
1023                 goto out;
1024         }
1025
1026         if (!nm_connection_get_setting_by_name (NM_CONNECTION (self), setting_name)) {
1027                 g_set_error (&local, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_SETTING_NOT_FOUND,
1028                              "Connection didn't have requested setting '%s'.",
1029                              setting_name);
1030                 _get_secrets_info_callback (info, NULL, setting_name, local);
1031                 goto out;
1032         }
1033
1034         get_cmp_flags (self,
1035                        info,
1036                        NM_CONNECTION (self),
1037                        agent_dbus_owner,
1038                        agent_has_modify,
1039                        setting_name,
1040                        flags,
1041                        secrets,
1042                        &agent_had_system,
1043                        &cmp_flags);
1044
1045         _LOGD ("(%s:%p) secrets request completed",
1046                setting_name,
1047                info);
1048
1049         dict = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
1050
1051         /* Update the connection with our existing secrets from backing storage */
1052         nm_connection_clear_secrets (NM_CONNECTION (self));
1053         if (!dict || nm_connection_update_secrets (NM_CONNECTION (self), setting_name, dict, &local)) {
1054                 GVariant *filtered_secrets;
1055
1056                 /* Update the connection with the agent's secrets; by this point if any
1057                  * system-owned secrets exist in 'secrets' the agent that provided them
1058                  * will have been authenticated, so those secrets can replace the existing
1059                  * system secrets.
1060                  */
1061                 filtered_secrets = for_each_secret (NM_CONNECTION (self), secrets, TRUE, validate_secret_flags, &cmp_flags);
1062                 if (nm_connection_update_secrets (NM_CONNECTION (self), setting_name, filtered_secrets, &local)) {
1063                         /* Now that all secrets are updated, copy and cache new secrets,
1064                          * then save them to backing storage.
1065                          */
1066                         update_system_secrets_cache (self);
1067                         update_agent_secrets_cache (self, NULL);
1068
1069                         /* Only save secrets to backing storage if the agent returned any
1070                          * new system secrets.  If it didn't, then the secrets are agent-
1071                          * owned and there's no point to writing out the connection when
1072                          * nothing has changed, since agent-owned secrets don't get saved here.
1073                          */
1074                         if (agent_had_system) {
1075                                 _LOGD ("(%s:%p) saving new secrets to backing storage",
1076                                        setting_name,
1077                                        info);
1078
1079                                 nm_settings_connection_commit_changes (self, NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, new_secrets_commit_cb, NULL);
1080                         } else {
1081                                 _LOGD ("(%s:%p) new agent secrets processed",
1082                                        setting_name,
1083                                        info);
1084                         }
1085
1086                 } else {
1087                         _LOGD ("(%s:%p) failed to update with agent secrets: %s",
1088                                setting_name,
1089                                info,
1090                                local->message);
1091                 }
1092                 g_variant_unref (filtered_secrets);
1093         } else {
1094                 _LOGD ("(%s:%p) failed to update with existing secrets: %s",
1095                        setting_name,
1096                        info,
1097                        local->message);
1098         }
1099
1100         applied_connection = info->applied_connection;
1101         if (applied_connection) {
1102                 get_cmp_flags (self,
1103                                info,
1104                                applied_connection,
1105                                agent_dbus_owner,
1106                                agent_has_modify,
1107                                setting_name,
1108                                flags,
1109                                secrets,
1110                                &agent_had_system,
1111                                &cmp_flags);
1112
1113                 nm_connection_clear_secrets (applied_connection);
1114
1115                 if (!dict || nm_connection_update_secrets (applied_connection, setting_name, dict, NULL)) {
1116                         GVariant *filtered_secrets;
1117
1118                         filtered_secrets = for_each_secret (applied_connection, secrets, TRUE, validate_secret_flags, &cmp_flags);
1119                         nm_connection_update_secrets (applied_connection, setting_name, filtered_secrets, NULL);
1120                         g_variant_unref (filtered_secrets);
1121                 }
1122         }
1123
1124         _get_secrets_info_callback (info, agent_username, setting_name, local);
1125         g_clear_error (&local);
1126         if (dict)
1127                 g_variant_unref (dict);
1128
1129 out:
1130         _get_secrets_info_free (info);
1131 }
1132
1133 static gboolean
1134 get_secrets_idle_cb (GetSecretsInfo *info)
1135 {
1136         NMSettingsConnectionPrivate *priv;
1137
1138         g_return_val_if_fail (info && NM_IS_SETTINGS_CONNECTION (info->self), G_SOURCE_REMOVE);
1139
1140         priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (info->self);
1141
1142         g_return_val_if_fail (g_slist_find (priv->get_secret_requests, info), G_SOURCE_REMOVE);
1143
1144         priv->get_secret_requests = g_slist_remove (priv->get_secret_requests, info);
1145
1146         _get_secrets_info_callback (info, NULL, NULL, info->t.idle.error);
1147
1148         _get_secrets_info_free (info);
1149         return G_SOURCE_REMOVE;
1150 }
1151
1152 /**
1153  * nm_settings_connection_get_secrets:
1154  * @self: the #NMSettingsConnection
1155  * @applied_connection: (allow-none): if provided, only request secrets
1156  *   if @self equals to @applied_connection. Also, update the secrets
1157  *   in the @applied_connection.
1158  * @subject: the #NMAuthSubject originating the request
1159  * @setting_name: the setting to return secrets for
1160  * @flags: flags to modify the secrets request
1161  * @hints: key names in @setting_name for which secrets may be required, or some
1162  *   other information about the request
1163  * @callback: the function to call with returned secrets
1164  * @callback_data: user data to pass to @callback
1165  *
1166  * Retrieves secrets from persistent storage and queries any secret agents for
1167  * additional secrets.
1168  *
1169  * With the returned call-id, the call can be cancelled. It is an error
1170  * to cancel a call more then once or a call that already completed.
1171  * The callback will always be invoked exactly once, also for cancellation
1172  * and disposing of @self. In those latter cases, the callback will be invoked
1173  * synchronously during cancellation/disposing.
1174  *
1175  * Returns: a call ID which may be used to cancel the ongoing secrets request.
1176  **/
1177 NMSettingsConnectionCallId
1178 nm_settings_connection_get_secrets (NMSettingsConnection *self,
1179                                     NMConnection *applied_connection,
1180                                     NMAuthSubject *subject,
1181                                     const char *setting_name,
1182                                     NMSecretAgentGetSecretsFlags flags,
1183                                     const char **hints,
1184                                     NMSettingsConnectionSecretsFunc callback,
1185                                     gpointer callback_data)
1186 {
1187         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
1188         GVariant *existing_secrets;
1189         NMAgentManagerCallId call_id_a;
1190         gs_free char *joined_hints = NULL;
1191         GetSecretsInfo *info;
1192         GError *local = NULL;
1193
1194         g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL);
1195         g_return_val_if_fail (   !applied_connection
1196                               || (   NM_IS_CONNECTION (applied_connection)
1197                                   && (((NMConnection *) self) != applied_connection)), NULL);
1198
1199         info = _get_secrets_info_new (self,
1200                                       applied_connection,
1201                                       callback,
1202                                       callback_data);
1203
1204         priv->get_secret_requests = g_slist_append (priv->get_secret_requests, info);
1205
1206         /* Use priv->secrets to work around the fact that nm_connection_clear_secrets()
1207          * will clear secrets on this object's settings.
1208          */
1209         if (!priv->system_secrets) {
1210                 g_set_error_literal (&local, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
1211                                      "secrets cache invalid");
1212                 goto schedule_dummy;
1213         }
1214
1215         /* Make sure the request actually requests something we can return */
1216         if (!nm_connection_get_setting_by_name (NM_CONNECTION (self), setting_name)) {
1217                 g_set_error (&local, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_SETTING_NOT_FOUND,
1218                              "Connection didn't have requested setting '%s'.",
1219                              setting_name);
1220                 goto schedule_dummy;
1221         }
1222
1223         if (   applied_connection
1224             && !nm_settings_connection_has_unmodified_applied_connection (self, applied_connection, NM_SETTING_COMPARE_FLAG_NONE)) {
1225                 g_set_error_literal (&local, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
1226                                      "The connection was modified since activation");
1227                 goto schedule_dummy;
1228         }
1229
1230         existing_secrets = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
1231         if (existing_secrets)
1232                 g_variant_ref_sink (existing_secrets);
1233         call_id_a = nm_agent_manager_get_secrets (priv->agent_mgr,
1234                                                   nm_connection_get_path (NM_CONNECTION (self)),
1235                                                   NM_CONNECTION (self),
1236                                                   subject,
1237                                                   existing_secrets,
1238                                                   setting_name,
1239                                                   flags,
1240                                                   hints,
1241                                                   get_secrets_done_cb,
1242                                                   info);
1243         g_assert (call_id_a);
1244         if (existing_secrets)
1245                 g_variant_unref (existing_secrets);
1246
1247         _LOGD ("(%s:%p) secrets requested flags 0x%X hints '%s'",
1248                setting_name,
1249                call_id_a,
1250                flags,
1251                (hints && hints[0]) ? (joined_hints = g_strjoinv (",", (char **) hints)) : "(none)");
1252
1253         if (call_id_a) {
1254                 info->type = GET_SECRETS_INFO_TYPE_REQ;
1255                 info->t.req.id = call_id_a;
1256         } else {
1257 schedule_dummy:
1258                 info->type = GET_SECRETS_INFO_TYPE_IDLE;
1259                 g_propagate_error (&info->t.idle.error, local);
1260                 info->t.idle.id = g_idle_add ((GSourceFunc) get_secrets_idle_cb, info);
1261         }
1262         return info;
1263 }
1264
1265 static void
1266 _get_secrets_cancel (NMSettingsConnection *self,
1267                      GetSecretsInfo *info,
1268                      gboolean is_disposing)
1269 {
1270         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
1271         gs_free_error GError *error = NULL;
1272
1273         if (!g_slist_find (priv->get_secret_requests, info))
1274                 g_return_if_reached ();
1275
1276         priv->get_secret_requests = g_slist_remove (priv->get_secret_requests, info);
1277
1278         if (info->type == GET_SECRETS_INFO_TYPE_REQ)
1279                 nm_agent_manager_cancel_secrets (priv->agent_mgr, info->t.req.id);
1280         else
1281                 g_source_remove (info->t.idle.id);
1282
1283         nm_utils_error_set_cancelled (&error, is_disposing, "NMSettingsConnection");
1284
1285         _get_secrets_info_callback (info, NULL, NULL, error);
1286
1287         _get_secrets_info_free (info);
1288 }
1289
1290 void
1291 nm_settings_connection_cancel_secrets (NMSettingsConnection *self,
1292                                        NMSettingsConnectionCallId call_id)
1293 {
1294         _LOGD ("(%p) secrets canceled", call_id);
1295
1296         _get_secrets_cancel (self, call_id, FALSE);
1297 }
1298
1299 /**** User authorization **************************************/
1300
1301 typedef void (*AuthCallback) (NMSettingsConnection *self,
1302                               GDBusMethodInvocation *context,
1303                               NMAuthSubject *subject,
1304                               GError *error,
1305                               gpointer data);
1306
1307 static void
1308 pk_auth_cb (NMAuthChain *chain,
1309             GError *chain_error,
1310             GDBusMethodInvocation *context,
1311             gpointer user_data)
1312 {
1313         NMSettingsConnection *self = NM_SETTINGS_CONNECTION (user_data);
1314         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
1315         GError *error = NULL;
1316         NMAuthCallResult result;
1317         const char *perm;
1318         AuthCallback callback;
1319         gpointer callback_data;
1320         NMAuthSubject *subject;
1321
1322         priv->pending_auths = g_slist_remove (priv->pending_auths, chain);
1323
1324         perm = nm_auth_chain_get_data (chain, "perm");
1325         g_assert (perm);
1326         result = nm_auth_chain_get_result (chain, perm);
1327
1328         /* If our NMSettingsConnection is already gone, do nothing */
1329         if (chain_error) {
1330                 error = g_error_new (NM_SETTINGS_ERROR,
1331                                      NM_SETTINGS_ERROR_FAILED,
1332                                      "Error checking authorization: %s",
1333                                      chain_error->message ? chain_error->message : "(unknown)");
1334         } else if (result != NM_AUTH_CALL_RESULT_YES) {
1335                 error = g_error_new_literal (NM_SETTINGS_ERROR,
1336                                              NM_SETTINGS_ERROR_PERMISSION_DENIED,
1337                                              "Insufficient privileges.");
1338         }
1339
1340         callback = nm_auth_chain_get_data (chain, "callback");
1341         callback_data = nm_auth_chain_get_data (chain, "callback-data");
1342         subject = nm_auth_chain_get_data (chain, "subject");
1343         callback (self, context, subject, error, callback_data);
1344
1345         g_clear_error (&error);
1346         nm_auth_chain_unref (chain);
1347 }
1348
1349 /**
1350  * _new_auth_subject:
1351  * @context: the D-Bus method invocation context
1352  * @error: on failure, a #GError
1353  *
1354  * Creates an NMAuthSubject for the caller.
1355  *
1356  * Returns: the #NMAuthSubject on success, or %NULL on failure and sets @error
1357  */
1358 static NMAuthSubject *
1359 _new_auth_subject (GDBusMethodInvocation *context, GError **error)
1360 {
1361         NMAuthSubject *subject;
1362
1363         subject = nm_auth_subject_new_unix_process_from_context (context);
1364         if (!subject) {
1365                 g_set_error_literal (error,
1366                                      NM_SETTINGS_ERROR,
1367                                      NM_SETTINGS_ERROR_PERMISSION_DENIED,
1368                                      "Unable to determine UID of request.");
1369         }
1370
1371         return subject;
1372 }
1373
1374 static void
1375 auth_start (NMSettingsConnection *self,
1376             GDBusMethodInvocation *context,
1377             NMAuthSubject *subject,
1378             const char *check_permission,
1379             AuthCallback callback,
1380             gpointer callback_data)
1381 {
1382         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
1383         NMAuthChain *chain;
1384         GError *error = NULL;
1385         char *error_desc = NULL;
1386
1387         g_return_if_fail (context != NULL);
1388         g_return_if_fail (NM_IS_AUTH_SUBJECT (subject));
1389
1390         /* Ensure the caller can view this connection */
1391         if (!nm_auth_is_subject_in_acl (NM_CONNECTION (self),
1392                                         subject,
1393                                         &error_desc)) {
1394                 error = g_error_new_literal (NM_SETTINGS_ERROR,
1395                                              NM_SETTINGS_ERROR_PERMISSION_DENIED,
1396                                              error_desc);
1397                 g_free (error_desc);
1398
1399                 callback (self, context, subject, error, callback_data);
1400                 g_clear_error (&error);
1401                 return;
1402         }
1403
1404         if (!check_permission) {
1405                 /* Don't need polkit auth, automatic success */
1406                 callback (self, context, subject, NULL, callback_data);
1407                 return;
1408         }
1409
1410         chain = nm_auth_chain_new_subject (subject, context, pk_auth_cb, self);
1411         if (!chain) {
1412                 g_set_error_literal (&error,
1413                                      NM_SETTINGS_ERROR,
1414                                      NM_SETTINGS_ERROR_PERMISSION_DENIED,
1415                                      "Unable to authenticate the request.");
1416                 callback (self, context, subject, error, callback_data);
1417                 g_clear_error (&error);
1418                 return;
1419         }
1420
1421         priv->pending_auths = g_slist_append (priv->pending_auths, chain);
1422         nm_auth_chain_set_data (chain, "perm", (gpointer) check_permission, NULL);
1423         nm_auth_chain_set_data (chain, "callback", callback, NULL);
1424         nm_auth_chain_set_data (chain, "callback-data", callback_data, NULL);
1425         nm_auth_chain_set_data (chain, "subject", g_object_ref (subject), g_object_unref);
1426         nm_auth_chain_add_call (chain, check_permission, TRUE);
1427 }
1428
1429 /**** DBus method handlers ************************************/
1430
1431 static gboolean
1432 check_writable (NMConnection *self, GError **error)
1433 {
1434         NMSettingConnection *s_con;
1435
1436         g_return_val_if_fail (NM_IS_CONNECTION (self), FALSE);
1437
1438         s_con = nm_connection_get_setting_connection (self);
1439         if (!s_con) {
1440                 g_set_error_literal (error,
1441                                      NM_SETTINGS_ERROR,
1442                                      NM_SETTINGS_ERROR_INVALID_CONNECTION,
1443                                      "Connection did not have required 'connection' setting");
1444                 return FALSE;
1445         }
1446
1447         /* If the connection is read-only, that has to be changed at the source of
1448          * the problem (ex a system settings plugin that can't write connections out)
1449          * instead of over D-Bus.
1450          */
1451         if (nm_setting_connection_get_read_only (s_con)) {
1452                 g_set_error_literal (error,
1453                                      NM_SETTINGS_ERROR,
1454                                      NM_SETTINGS_ERROR_READ_ONLY_CONNECTION,
1455                                      "Connection is read-only");
1456                 return FALSE;
1457         }
1458
1459         return TRUE;
1460 }
1461
1462 static void
1463 get_settings_auth_cb (NMSettingsConnection *self, 
1464                       GDBusMethodInvocation *context,
1465                       NMAuthSubject *subject,
1466                       GError *error,
1467                       gpointer data)
1468 {
1469         if (error)
1470                 g_dbus_method_invocation_return_gerror (context, error);
1471         else {
1472                 GVariant *settings;
1473                 NMConnection *dupl_con;
1474                 NMSettingConnection *s_con;
1475                 NMSettingWireless *s_wifi;
1476                 guint64 timestamp = 0;
1477                 char **bssids;
1478
1479                 dupl_con = nm_simple_connection_new_clone (NM_CONNECTION (self));
1480                 g_assert (dupl_con);
1481
1482                 /* Timestamp is not updated in connection's 'timestamp' property,
1483                  * because it would force updating the connection and in turn
1484                  * writing to /etc periodically, which we want to avoid. Rather real
1485                  * timestamps are kept track of in a private variable. So, substitute
1486                  * timestamp property with the real one here before returning the settings.
1487                  */
1488                 nm_settings_connection_get_timestamp (self, &timestamp);
1489                 if (timestamp) {
1490                         s_con = nm_connection_get_setting_connection (NM_CONNECTION (dupl_con));
1491                         g_assert (s_con);
1492                         g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, timestamp, NULL);
1493                 }
1494                 /* Seen BSSIDs are not updated in 802-11-wireless 'seen-bssids' property
1495                  * from the same reason as timestamp. Thus we put it here to GetSettings()
1496                  * return settings too.
1497                  */
1498                 bssids = nm_settings_connection_get_seen_bssids (self);
1499                 s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (dupl_con));
1500                 if (bssids && bssids[0] && s_wifi)
1501                         g_object_set (s_wifi, NM_SETTING_WIRELESS_SEEN_BSSIDS, bssids, NULL);
1502                 g_free (bssids);
1503
1504                 /* Secrets should *never* be returned by the GetSettings method, they
1505                  * get returned by the GetSecrets method which can be better
1506                  * protected against leakage of secrets to unprivileged callers.
1507                  */
1508                 settings = nm_connection_to_dbus (NM_CONNECTION (dupl_con), NM_CONNECTION_SERIALIZE_NO_SECRETS);
1509                 g_assert (settings);
1510                 g_dbus_method_invocation_return_value (context,
1511                                                        g_variant_new ("(@a{sa{sv}})", settings));
1512                 g_object_unref (dupl_con);
1513         }
1514 }
1515
1516 static void
1517 impl_settings_connection_get_settings (NMSettingsConnection *self,
1518                                        GDBusMethodInvocation *context)
1519 {
1520         NMAuthSubject *subject;
1521         GError *error = NULL;
1522
1523         subject = _new_auth_subject (context, &error);
1524         if (subject) {
1525                 auth_start (self, context, subject, NULL, get_settings_auth_cb, NULL);
1526                 g_object_unref (subject);
1527         } else
1528                 g_dbus_method_invocation_take_error (context, error);
1529 }
1530
1531 typedef struct {
1532         GDBusMethodInvocation *context;
1533         NMAgentManager *agent_mgr;
1534         NMAuthSubject *subject;
1535         NMConnection *new_settings;
1536         gboolean save_to_disk;
1537 } UpdateInfo;
1538
1539 typedef struct {
1540         GDBusMethodInvocation *context;
1541         NMAuthSubject *subject;
1542 } CallbackInfo;
1543
1544 static void
1545 has_some_secrets_cb (NMSetting *setting,
1546                      const char *key,
1547                      const GValue *value,
1548                      GParamFlags flags,
1549                      gpointer user_data)
1550 {
1551         GParamSpec *pspec;
1552
1553         if (NM_IS_SETTING_VPN (setting)) {
1554                 if (nm_setting_vpn_get_num_secrets (NM_SETTING_VPN(setting)))
1555                         *((gboolean *) user_data) = TRUE;
1556                 return;
1557         }
1558
1559         pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), key);
1560         if (pspec) {
1561                 if (   (flags & NM_SETTING_PARAM_SECRET)
1562                     && !g_param_value_defaults (pspec, (GValue *)value))
1563                         *((gboolean *) user_data) = TRUE;
1564         }
1565 }
1566
1567 static gboolean
1568 any_secrets_present (NMConnection *self)
1569 {
1570         gboolean has_secrets = FALSE;
1571
1572         nm_connection_for_each_setting_value (self, has_some_secrets_cb, &has_secrets);
1573         return has_secrets;
1574 }
1575
1576 static void
1577 cached_secrets_to_connection (NMSettingsConnection *self, NMConnection *connection)
1578 {
1579         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
1580         GVariant *secrets_dict;
1581
1582         if (priv->agent_secrets) {
1583                 secrets_dict = nm_connection_to_dbus (priv->agent_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
1584                 if (secrets_dict) {
1585                         (void) nm_connection_update_secrets (connection, NULL, secrets_dict, NULL);
1586                         g_variant_unref (secrets_dict);
1587                 }
1588         }
1589         if (priv->system_secrets) {
1590                 secrets_dict = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
1591                 if (secrets_dict) {
1592                         (void) nm_connection_update_secrets (connection, NULL, secrets_dict, NULL);
1593                         g_variant_unref (secrets_dict);
1594                 }
1595         }
1596 }
1597
1598 static void
1599 update_complete (NMSettingsConnection *self,
1600                  UpdateInfo *info,
1601                  GError *error)
1602 {
1603         if (error)
1604                 g_dbus_method_invocation_return_gerror (info->context, error);
1605         else
1606                 g_dbus_method_invocation_return_value (info->context, NULL);
1607
1608         nm_audit_log_connection_op (NM_AUDIT_OP_CONN_UPDATE, self, !error,
1609                                     info->subject, error ? error->message : NULL);
1610
1611         g_clear_object (&info->subject);
1612         g_clear_object (&info->agent_mgr);
1613         g_clear_object (&info->new_settings);
1614         memset (info, 0, sizeof (*info));
1615         g_free (info);
1616 }
1617
1618 static void
1619 con_update_cb (NMSettingsConnection *self,
1620                GError *error,
1621                gpointer user_data)
1622 {
1623         UpdateInfo *info = user_data;
1624         NMConnection *for_agent;
1625
1626         if (!error) {
1627                 /* Dupe the connection so we can clear out non-agent-owned secrets,
1628                  * as agent-owned secrets are the only ones we send back be saved.
1629                  * Only send secrets to agents of the same UID that called update too.
1630                  */
1631                 for_agent = nm_simple_connection_new_clone (NM_CONNECTION (self));
1632                 nm_connection_clear_secrets_with_flags (for_agent,
1633                                                         secrets_filter_cb,
1634                                                         GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED));
1635                 nm_agent_manager_save_secrets (info->agent_mgr,
1636                                                nm_connection_get_path (NM_CONNECTION (self)),
1637                                                for_agent,
1638                                                info->subject);
1639                 g_object_unref (for_agent);
1640         }
1641
1642         update_complete (self, info, error);
1643 }
1644
1645 static void
1646 update_auth_cb (NMSettingsConnection *self,
1647                 GDBusMethodInvocation *context,
1648                 NMAuthSubject *subject,
1649                 GError *error,
1650                 gpointer data)
1651 {
1652         UpdateInfo *info = data;
1653         GError *local = NULL;
1654
1655         if (error) {
1656                 update_complete (self, info, error);
1657                 return;
1658         }
1659
1660         if (!any_secrets_present (info->new_settings)) {
1661                 /* If the new connection has no secrets, we do not want to remove all
1662                  * secrets, rather we keep all the existing ones. Do that by merging
1663                  * them in to the new connection.
1664                  */
1665                 cached_secrets_to_connection (self, info->new_settings);
1666         } else {
1667                 /* Cache the new secrets from the agent, as stuff like inotify-triggered
1668                  * changes to connection's backing config files will blow them away if
1669                  * they're in the main connection.
1670                  */
1671                 update_agent_secrets_cache (self, info->new_settings);
1672         }
1673
1674         if (info->save_to_disk) {
1675                 nm_settings_connection_replace_and_commit (self,
1676                                                            info->new_settings,
1677                                                            con_update_cb,
1678                                                            info);
1679         } else {
1680                 if (!nm_settings_connection_replace_settings (self, info->new_settings, TRUE, "replace-and-commit-memory", &local))
1681                         g_assert (local);
1682                 con_update_cb (self, local, info);
1683                 g_clear_error (&local);
1684         }
1685 }
1686
1687 static const char *
1688 get_update_modify_permission (NMConnection *old, NMConnection *new)
1689 {
1690         NMSettingConnection *s_con;
1691         guint32 orig_num = 0, new_num = 0;
1692
1693         s_con = nm_connection_get_setting_connection (old);
1694         g_assert (s_con);
1695         orig_num = nm_setting_connection_get_num_permissions (s_con);
1696
1697         s_con = nm_connection_get_setting_connection (new);
1698         g_assert (s_con);
1699         new_num = nm_setting_connection_get_num_permissions (s_con);
1700
1701         /* If the caller is the only user in either connection's permissions, then
1702          * we use the 'modify.own' permission instead of 'modify.system'.
1703          */
1704         if (orig_num == 1 && new_num == 1)
1705                 return NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN;
1706
1707         /* If the update request affects more than just the caller (ie if the old
1708          * settings were system-wide, or the new ones are), require 'modify.system'.
1709          */
1710         return NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM;
1711 }
1712
1713 static void
1714 settings_connection_update_helper (NMSettingsConnection *self,
1715                                    GDBusMethodInvocation *context,
1716                                    GVariant *new_settings,
1717                                    gboolean save_to_disk)
1718 {
1719         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
1720         NMAuthSubject *subject = NULL;
1721         NMConnection *tmp = NULL;
1722         GError *error = NULL;
1723         UpdateInfo *info;
1724         const char *permission;
1725         char *error_desc = NULL;
1726
1727         g_assert (new_settings != NULL || save_to_disk == TRUE);
1728
1729         /* If the connection is read-only, that has to be changed at the source of
1730          * the problem (ex a system settings plugin that can't write connections out)
1731          * instead of over D-Bus.
1732          */
1733         if (!check_writable (NM_CONNECTION (self), &error))
1734                 goto error;
1735
1736         /* Check if the settings are valid first */
1737         if (new_settings) {
1738                 tmp = _nm_simple_connection_new_from_dbus (new_settings,
1739                                                            NM_SETTING_PARSE_FLAGS_NORMALIZE,
1740                                                            &error);
1741                 if (!tmp)
1742                         goto error;
1743         }
1744
1745         subject = _new_auth_subject (context, &error);
1746         if (!subject)
1747                 goto error;
1748
1749         /* And that the new connection settings will be visible to the user
1750          * that's sending the update request.  You can't make a connection
1751          * invisible to yourself.
1752          */
1753         if (!nm_auth_is_subject_in_acl (tmp ? tmp : NM_CONNECTION (self),
1754                                         subject,
1755                                         &error_desc)) {
1756                 error = g_error_new_literal (NM_SETTINGS_ERROR,
1757                                              NM_SETTINGS_ERROR_PERMISSION_DENIED,
1758                                              error_desc);
1759                 g_free (error_desc);
1760                 goto error;
1761         }
1762
1763         info = g_malloc0 (sizeof (*info));
1764         info->context = context;
1765         info->agent_mgr = g_object_ref (priv->agent_mgr);
1766         info->subject = subject;
1767         info->save_to_disk = save_to_disk;
1768         info->new_settings = tmp;
1769
1770         permission = get_update_modify_permission (NM_CONNECTION (self),
1771                                                    tmp ? tmp : NM_CONNECTION (self));
1772         auth_start (self, context, subject, permission, update_auth_cb, info);
1773         return;
1774
1775 error:
1776         nm_audit_log_connection_op (NM_AUDIT_OP_CONN_UPDATE, self, FALSE, subject,
1777                                     error->message);
1778
1779         g_clear_object (&tmp);
1780         g_clear_object (&subject);
1781
1782         g_dbus_method_invocation_take_error (context, error);
1783 }
1784
1785 static void
1786 impl_settings_connection_update (NMSettingsConnection *self,
1787                                  GDBusMethodInvocation *context,
1788                                  GVariant *new_settings)
1789 {
1790         settings_connection_update_helper (self, context, new_settings, TRUE);
1791 }
1792
1793 static void
1794 impl_settings_connection_update_unsaved (NMSettingsConnection *self,
1795                                          GDBusMethodInvocation *context,
1796                                          GVariant *new_settings)
1797 {
1798         settings_connection_update_helper (self, context, new_settings, FALSE);
1799 }
1800
1801 static void
1802 impl_settings_connection_save (NMSettingsConnection *self,
1803                                GDBusMethodInvocation *context)
1804 {
1805         /* Do nothing if the connection is already synced with disk */
1806         if (nm_settings_connection_get_unsaved (self))
1807                 settings_connection_update_helper (self, context, NULL, TRUE);
1808         else
1809                 g_dbus_method_invocation_return_value (context, NULL);
1810 }
1811
1812 static void
1813 con_delete_cb (NMSettingsConnection *self,
1814                GError *error,
1815                gpointer user_data)
1816 {
1817         CallbackInfo *info = user_data;
1818
1819         if (error)
1820                 g_dbus_method_invocation_return_gerror (info->context, error);
1821         else
1822                 g_dbus_method_invocation_return_value (info->context, NULL);
1823
1824         nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self,
1825                                     !error, info->subject, error ? error->message : NULL);
1826         g_free (info);
1827 }
1828
1829 static void
1830 delete_auth_cb (NMSettingsConnection *self,
1831                 GDBusMethodInvocation *context,
1832                 NMAuthSubject *subject,
1833                 GError *error,
1834                 gpointer data)
1835 {
1836         CallbackInfo *info;
1837
1838         if (error) {
1839                 nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self, FALSE, subject,
1840                                             error->message);
1841                 g_dbus_method_invocation_return_gerror (context, error);
1842                 return;
1843         }
1844
1845         info = g_malloc0 (sizeof (*info));
1846         info->context = context;
1847         info->subject = subject;
1848
1849         nm_settings_connection_delete (self, con_delete_cb, info);
1850 }
1851
1852 static const char *
1853 get_modify_permission_basic (NMSettingsConnection *self)
1854 {
1855         NMSettingConnection *s_con;
1856
1857         /* If the caller is the only user in the connection's permissions, then
1858          * we use the 'modify.own' permission instead of 'modify.system'.  If the
1859          * request affects more than just the caller, require 'modify.system'.
1860          */
1861         s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
1862         g_assert (s_con);
1863         if (nm_setting_connection_get_num_permissions (s_con) == 1)
1864                 return NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN;
1865
1866         return NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM;
1867 }
1868
1869 static void
1870 impl_settings_connection_delete (NMSettingsConnection *self,
1871                                  GDBusMethodInvocation *context)
1872 {
1873         NMAuthSubject *subject = NULL;
1874         GError *error = NULL;
1875
1876         if (!check_writable (NM_CONNECTION (self), &error))
1877                 goto out_err;
1878
1879         subject = _new_auth_subject (context, &error);
1880         if (subject) {
1881                 auth_start (self, context, subject, get_modify_permission_basic (self), delete_auth_cb, NULL);
1882                 g_object_unref (subject);
1883         } else
1884                 goto out_err;
1885
1886         return;
1887 out_err:
1888         nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self, FALSE, subject, error->message);
1889         g_dbus_method_invocation_take_error (context, error);
1890 }
1891
1892 /**************************************************************/
1893
1894 static void
1895 dbus_get_agent_secrets_cb (NMSettingsConnection *self,
1896                            NMSettingsConnectionCallId call_id,
1897                            const char *agent_username,
1898                            const char *setting_name,
1899                            GError *error,
1900                            gpointer user_data)
1901 {
1902         GDBusMethodInvocation *context = user_data;
1903         GVariant *dict;
1904
1905         if (error)
1906                 g_dbus_method_invocation_return_gerror (context, error);
1907         else {
1908                 /* Return secrets from agent and backing storage to the D-Bus caller;
1909                  * nm_settings_connection_get_secrets() will have updated itself with
1910                  * secrets from backing storage and those returned from the agent
1911                  * by the time we get here.
1912                  */
1913                 dict = nm_connection_to_dbus (NM_CONNECTION (self), NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
1914                 if (!dict)
1915                         dict = g_variant_new_array (G_VARIANT_TYPE ("{sa{sv}}"), NULL, 0);
1916                 g_dbus_method_invocation_return_value (context, g_variant_new ("(@a{sa{sv}})", dict));
1917         }
1918 }
1919
1920 static void
1921 dbus_get_secrets_auth_cb (NMSettingsConnection *self,
1922                           GDBusMethodInvocation *context,
1923                           NMAuthSubject *subject,
1924                           GError *error,
1925                           gpointer user_data)
1926 {
1927         char *setting_name = user_data;
1928
1929         if (!error) {
1930                 nm_settings_connection_get_secrets (self,
1931                                                     NULL,
1932                                                     subject,
1933                                                     setting_name,
1934                                                     NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED
1935                                                       | NM_SECRET_AGENT_GET_SECRETS_FLAG_NO_ERRORS,
1936                                                     NULL,
1937                                                     dbus_get_agent_secrets_cb,
1938                                                     context);
1939         }
1940
1941         if (error)
1942                 g_dbus_method_invocation_return_gerror (context, error);
1943
1944         g_free (setting_name);
1945 }
1946
1947 static void
1948 impl_settings_connection_get_secrets (NMSettingsConnection *self,
1949                                       GDBusMethodInvocation *context,
1950                                       const gchar *setting_name)
1951 {
1952         NMAuthSubject *subject;
1953         GError *error = NULL;
1954
1955         subject = _new_auth_subject (context, &error);
1956         if (subject) {
1957                 auth_start (self,
1958                             context,
1959                             subject,
1960                             get_modify_permission_basic (self),
1961                             dbus_get_secrets_auth_cb,
1962                             g_strdup (setting_name));
1963                 g_object_unref (subject);
1964         } else
1965                 g_dbus_method_invocation_take_error (context, error);
1966 }
1967
1968 static void
1969 clear_secrets_cb (NMSettingsConnection *self,
1970                   GError *error,
1971                   gpointer user_data)
1972 {
1973         CallbackInfo *info = user_data;
1974
1975         if (error)
1976                 g_dbus_method_invocation_return_gerror (info->context, error);
1977         else
1978                 g_dbus_method_invocation_return_value (info->context, NULL);
1979
1980         nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self,
1981                                     !error, info->subject, error ? error->message : NULL);
1982         g_free (info);
1983 }
1984
1985 static void
1986 dbus_clear_secrets_auth_cb (NMSettingsConnection *self,
1987                             GDBusMethodInvocation *context,
1988                             NMAuthSubject *subject,
1989                             GError *error,
1990                             gpointer user_data)
1991 {
1992         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
1993         CallbackInfo *info;
1994
1995         if (error) {
1996                 g_dbus_method_invocation_return_gerror (context, error);
1997                 nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self,
1998                                             FALSE, subject, error->message);
1999         } else {
2000                 /* Clear secrets in connection and caches */
2001                 nm_connection_clear_secrets (NM_CONNECTION (self));
2002                 if (priv->system_secrets)
2003                         nm_connection_clear_secrets (priv->system_secrets);
2004                 if (priv->agent_secrets)
2005                         nm_connection_clear_secrets (priv->agent_secrets);
2006
2007                 /* Tell agents to remove secrets for this connection */
2008                 nm_agent_manager_delete_secrets (priv->agent_mgr,
2009                                                  nm_connection_get_path (NM_CONNECTION (self)),
2010                                                  NM_CONNECTION (self));
2011
2012                 info = g_malloc0 (sizeof (*info));
2013                 info->context = context;
2014                 info->subject = subject;
2015
2016                 nm_settings_connection_commit_changes (self, NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, clear_secrets_cb, info);
2017         }
2018 }
2019
2020 static void
2021 impl_settings_connection_clear_secrets (NMSettingsConnection *self,
2022                                         GDBusMethodInvocation *context)
2023 {
2024         NMAuthSubject *subject;
2025         GError *error = NULL;
2026
2027         subject = _new_auth_subject (context, &error);
2028         if (subject) {
2029                 auth_start (self,
2030                             context,
2031                             subject,
2032                             get_modify_permission_basic (self),
2033                             dbus_clear_secrets_auth_cb,
2034                             NULL);
2035                 g_object_unref (subject);
2036         } else {
2037                 nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self,
2038                                             FALSE, NULL, error->message);
2039                 g_dbus_method_invocation_take_error (context, error);
2040         }
2041 }
2042
2043 /**************************************************************/
2044
2045 void
2046 nm_settings_connection_signal_remove (NMSettingsConnection *self)
2047 {
2048         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2049
2050         if (priv->removed)
2051                 g_return_if_reached ();
2052         priv->removed = TRUE;
2053         g_signal_emit_by_name (self, NM_SETTINGS_CONNECTION_REMOVED);
2054 }
2055
2056 gboolean
2057 nm_settings_connection_get_unsaved (NMSettingsConnection *self)
2058 {
2059         return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_FLAGS_UNSAVED);
2060 }
2061
2062 /**************************************************************/
2063
2064 NMSettingsConnectionFlags
2065 nm_settings_connection_get_flags (NMSettingsConnection *self)
2066 {
2067         g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_FLAGS_NONE);
2068
2069         return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->flags;
2070 }
2071
2072 NMSettingsConnectionFlags
2073 nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionFlags flags, gboolean set)
2074 {
2075         NMSettingsConnectionFlags new_flags;
2076
2077         g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_FLAGS_NONE);
2078         g_return_val_if_fail ((flags & ~NM_SETTINGS_CONNECTION_FLAGS_ALL) == 0, NM_SETTINGS_CONNECTION_FLAGS_NONE);
2079
2080         new_flags = NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->flags;
2081         if (set)
2082                 new_flags |= flags;
2083         else
2084                 new_flags &= ~flags;
2085         return nm_settings_connection_set_flags_all (self, new_flags);
2086 }
2087
2088 NMSettingsConnectionFlags
2089 nm_settings_connection_set_flags_all (NMSettingsConnection *self, NMSettingsConnectionFlags flags)
2090 {
2091         NMSettingsConnectionPrivate *priv;
2092         NMSettingsConnectionFlags old_flags;
2093
2094         g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_FLAGS_NONE);
2095         g_return_val_if_fail ((flags & ~NM_SETTINGS_CONNECTION_FLAGS_ALL) == 0, NM_SETTINGS_CONNECTION_FLAGS_NONE);
2096         priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2097
2098         old_flags = priv->flags;
2099         if (old_flags != flags) {
2100                 priv->flags = flags;
2101                 g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTION_FLAGS);
2102                 if (NM_FLAGS_HAS (old_flags, NM_SETTINGS_CONNECTION_FLAGS_UNSAVED) != NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_FLAGS_UNSAVED))
2103                         g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTION_UNSAVED);
2104         }
2105         return old_flags;
2106 }
2107
2108 /*************************************************************/
2109
2110 /**
2111  * nm_settings_connection_get_timestamp:
2112  * @self: the #NMSettingsConnection
2113  * @out_timestamp: the connection's timestamp
2114  *
2115  * Returns the time (in seconds since the Unix epoch) when the connection
2116  * was last successfully activated.
2117  *
2118  * Returns: %TRUE if the timestamp has ever been set, otherwise %FALSE.
2119  **/
2120 gboolean
2121 nm_settings_connection_get_timestamp (NMSettingsConnection *self,
2122                                       guint64 *out_timestamp)
2123 {
2124         g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
2125
2126         if (out_timestamp)
2127                 *out_timestamp = NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->timestamp;
2128         return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->timestamp_set;
2129 }
2130
2131 /**
2132  * nm_settings_connection_update_timestamp:
2133  * @self: the #NMSettingsConnection
2134  * @timestamp: timestamp to set into the connection and to store into
2135  * the timestamps database
2136  * @flush_to_disk: if %TRUE, commit timestamp update to persistent storage
2137  *
2138  * Updates the connection and timestamps database with the provided timestamp.
2139  **/
2140 void
2141 nm_settings_connection_update_timestamp (NMSettingsConnection *self,
2142                                          guint64 timestamp,
2143                                          gboolean flush_to_disk)
2144 {
2145         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2146         const char *connection_uuid;
2147         GKeyFile *timestamps_file;
2148         char *data, *tmp;
2149         gsize len;
2150         GError *error = NULL;
2151
2152         g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
2153
2154         /* Update timestamp in private storage */
2155         priv->timestamp = timestamp;
2156         priv->timestamp_set = TRUE;
2157
2158         if (flush_to_disk == FALSE)
2159                 return;
2160
2161         /* Save timestamp to timestamps database file */
2162         timestamps_file = g_key_file_new ();
2163         if (!g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) {
2164                 if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
2165                         _LOGW ("error parsing timestamps file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message);
2166                 g_clear_error (&error);
2167         }
2168
2169         connection_uuid = nm_settings_connection_get_uuid (self);
2170         tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp);
2171         g_key_file_set_value (timestamps_file, "timestamps", connection_uuid, tmp);
2172         g_free (tmp);
2173
2174         data = g_key_file_to_data (timestamps_file, &len, &error);
2175         if (data) {
2176                 g_file_set_contents (SETTINGS_TIMESTAMPS_FILE, data, len, &error);
2177                 g_free (data);
2178         }
2179         if (error) {
2180                 _LOGW ("error saving timestamp to file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message);
2181                 g_error_free (error);
2182         }
2183         g_key_file_free (timestamps_file);
2184 }
2185
2186 /**
2187  * nm_settings_connection_read_and_fill_timestamp:
2188  * @self: the #NMSettingsConnection
2189  *
2190  * Retrieves timestamp of the connection's last usage from database file and
2191  * stores it into the connection private data.
2192  **/
2193 void
2194 nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *self)
2195 {
2196         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2197         const char *connection_uuid;
2198         guint64 timestamp = 0;
2199         GKeyFile *timestamps_file;
2200         GError *err = NULL;
2201         char *tmp_str;
2202
2203         g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
2204
2205         /* Get timestamp from database file */
2206         timestamps_file = g_key_file_new ();
2207         g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL);
2208         connection_uuid = nm_settings_connection_get_uuid (self);
2209         tmp_str = g_key_file_get_value (timestamps_file, "timestamps", connection_uuid, &err);
2210         if (tmp_str) {
2211                 timestamp = g_ascii_strtoull (tmp_str, NULL, 10);
2212                 g_free (tmp_str);
2213         }
2214
2215         /* Update connection's timestamp */
2216         if (!err) {
2217                 priv->timestamp = timestamp;
2218                 priv->timestamp_set = TRUE;
2219         } else {
2220                 _LOGD ("failed to read connection timestamp: %s", err->message);
2221                 g_clear_error (&err);
2222         }
2223         g_key_file_free (timestamps_file);
2224 }
2225
2226 /**
2227  * nm_settings_connection_get_seen_bssids:
2228  * @self: the #NMSettingsConnection
2229  *
2230  * Returns current list of seen BSSIDs for the connection.
2231  *
2232  * Returns: (transfer container) list of seen BSSIDs (in the standard hex-digits-and-colons notation).
2233  * The caller is responsible for freeing the list, but not the content.
2234  **/
2235 char **
2236 nm_settings_connection_get_seen_bssids (NMSettingsConnection *self)
2237 {
2238         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2239         GHashTableIter iter;
2240         char **bssids, *bssid;
2241         int i;
2242
2243         g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL);
2244
2245         bssids = g_new (char *, g_hash_table_size (priv->seen_bssids) + 1);
2246
2247         i = 0;
2248         g_hash_table_iter_init (&iter, priv->seen_bssids);
2249         while (g_hash_table_iter_next (&iter, NULL, (gpointer) &bssid))
2250                 bssids[i++] = bssid;
2251         bssids[i] = NULL;
2252
2253         return bssids;
2254 }
2255
2256 /**
2257  * nm_settings_connection_has_seen_bssid:
2258  * @self: the #NMSettingsConnection
2259  * @bssid: the BSSID to check the seen BSSID list for
2260  *
2261  * Returns: %TRUE if the given @bssid is in the seen BSSIDs list
2262  **/
2263 gboolean
2264 nm_settings_connection_has_seen_bssid (NMSettingsConnection *self,
2265                                        const char *bssid)
2266 {
2267         g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
2268         g_return_val_if_fail (bssid != NULL, FALSE);
2269
2270         return !!g_hash_table_lookup (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->seen_bssids, bssid);
2271 }
2272
2273 /**
2274  * nm_settings_connection_add_seen_bssid:
2275  * @self: the #NMSettingsConnection
2276  * @seen_bssid: BSSID to set into the connection and to store into
2277  * the seen-bssids database
2278  *
2279  * Updates the connection and seen-bssids database with the provided BSSID.
2280  **/
2281 void
2282 nm_settings_connection_add_seen_bssid (NMSettingsConnection *self,
2283                                        const char *seen_bssid)
2284 {
2285         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2286         const char *connection_uuid;
2287         GKeyFile *seen_bssids_file;
2288         char *data, *bssid_str;
2289         const char **list;
2290         gsize len;
2291         GError *error = NULL;
2292         GHashTableIter iter;
2293         guint n;
2294
2295         g_return_if_fail (seen_bssid != NULL);
2296
2297         if (g_hash_table_lookup (priv->seen_bssids, seen_bssid))
2298                 return;  /* Already in the list */
2299
2300         /* Add the new BSSID; let the hash take ownership of the allocated BSSID string */
2301         bssid_str = g_strdup (seen_bssid);
2302         g_hash_table_insert (priv->seen_bssids, bssid_str, bssid_str);
2303
2304         /* Build up a list of all the BSSIDs in string form */
2305         n = 0;
2306         list = g_malloc0 (g_hash_table_size (priv->seen_bssids) * sizeof (char *));
2307         g_hash_table_iter_init (&iter, priv->seen_bssids);
2308         while (g_hash_table_iter_next (&iter, NULL, (gpointer) &bssid_str))
2309                 list[n++] = bssid_str;
2310
2311         /* Save BSSID to seen-bssids file */
2312         seen_bssids_file = g_key_file_new ();
2313         g_key_file_set_list_separator (seen_bssids_file, ',');
2314         if (!g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) {
2315                 if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
2316                         _LOGW ("error parsing seen-bssids file '%s': %s",
2317                                SETTINGS_SEEN_BSSIDS_FILE, error->message);
2318                 }
2319                 g_clear_error (&error);
2320         }
2321
2322         connection_uuid = nm_settings_connection_get_uuid (self);
2323         g_key_file_set_string_list (seen_bssids_file, "seen-bssids", connection_uuid, list, n);
2324         g_free (list);
2325
2326         data = g_key_file_to_data (seen_bssids_file, &len, &error);
2327         if (data) {
2328                 g_file_set_contents (SETTINGS_SEEN_BSSIDS_FILE, data, len, &error);
2329                 g_free (data);
2330         }
2331         g_key_file_free (seen_bssids_file);
2332
2333         if (error) {
2334                 _LOGW ("error saving seen-bssids to file '%s': %s",
2335                        SETTINGS_SEEN_BSSIDS_FILE, error->message);
2336                 g_error_free (error);
2337         }
2338 }
2339
2340 /**
2341  * nm_settings_connection_read_and_fill_seen_bssids:
2342  * @self: the #NMSettingsConnection
2343  *
2344  * Retrieves seen BSSIDs of the connection from database file and stores then into the
2345  * connection private data.
2346  **/
2347 void
2348 nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *self)
2349 {
2350         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2351         const char *connection_uuid;
2352         GKeyFile *seen_bssids_file;
2353         char **tmp_strv = NULL;
2354         gsize i, len = 0;
2355         NMSettingWireless *s_wifi;
2356
2357         /* Get seen BSSIDs from database file */
2358         seen_bssids_file = g_key_file_new ();
2359         g_key_file_set_list_separator (seen_bssids_file, ',');
2360         if (g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL)) {
2361                 connection_uuid = nm_settings_connection_get_uuid (self);
2362                 tmp_strv = g_key_file_get_string_list (seen_bssids_file, "seen-bssids", connection_uuid, &len, NULL);
2363         }
2364         g_key_file_free (seen_bssids_file);
2365
2366         /* Update connection's seen-bssids */
2367         if (tmp_strv) {
2368                 g_hash_table_remove_all (priv->seen_bssids);
2369                 for (i = 0; i < len; i++)
2370                         g_hash_table_insert (priv->seen_bssids, tmp_strv[i], tmp_strv[i]);
2371                 g_free (tmp_strv);
2372         } else {
2373                 /* If this connection didn't have an entry in the seen-bssids database,
2374                  * maybe this is the first time we've read it in, so populate the
2375                  * seen-bssids list from the deprecated seen-bssids property of the
2376                  * wifi setting.
2377                  */
2378                 s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (self));
2379                 if (s_wifi) {
2380                         len = nm_setting_wireless_get_num_seen_bssids (s_wifi);
2381                         for (i = 0; i < len; i++) {
2382                                 char *bssid_dup = g_strdup (nm_setting_wireless_get_seen_bssid (s_wifi, i));
2383
2384                                 g_hash_table_insert (priv->seen_bssids, bssid_dup, bssid_dup);
2385                         }
2386                 }
2387         }
2388 }
2389
2390 #define AUTOCONNECT_RETRIES_DEFAULT 4
2391 #define AUTOCONNECT_RESET_RETRIES_TIMER 300
2392
2393 int
2394 nm_settings_connection_get_autoconnect_retries (NMSettingsConnection *self)
2395 {
2396         return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_retries;
2397 }
2398
2399 void
2400 nm_settings_connection_set_autoconnect_retries (NMSettingsConnection *self,
2401                                                 int retries)
2402 {
2403         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2404
2405         priv->autoconnect_retries = retries;
2406         if (retries)
2407                 priv->autoconnect_retry_time = 0;
2408         else
2409                 priv->autoconnect_retry_time = nm_utils_get_monotonic_timestamp_s () + AUTOCONNECT_RESET_RETRIES_TIMER;
2410 }
2411
2412 void
2413 nm_settings_connection_reset_autoconnect_retries (NMSettingsConnection *self)
2414 {
2415         nm_settings_connection_set_autoconnect_retries (self, AUTOCONNECT_RETRIES_DEFAULT);
2416 }
2417
2418 gint32
2419 nm_settings_connection_get_autoconnect_retry_time (NMSettingsConnection *self)
2420 {
2421         return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_retry_time;
2422 }
2423
2424 NMDeviceStateReason
2425 nm_settings_connection_get_autoconnect_blocked_reason (NMSettingsConnection *self)
2426 {
2427         return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_blocked_reason;
2428 }
2429
2430 void
2431 nm_settings_connection_set_autoconnect_blocked_reason (NMSettingsConnection *self,
2432                                                        NMDeviceStateReason reason)
2433 {
2434         NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_blocked_reason = reason;
2435 }
2436
2437 gboolean
2438 nm_settings_connection_can_autoconnect (NMSettingsConnection *self)
2439 {
2440         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2441         NMSettingConnection *s_con;
2442         const char *permission;
2443
2444         if (   !priv->visible
2445             || priv->autoconnect_retries == 0
2446             || priv->autoconnect_blocked_reason != NM_DEVICE_STATE_REASON_NONE)
2447                 return FALSE;
2448
2449         s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
2450         if (!nm_setting_connection_get_autoconnect (s_con))
2451                 return FALSE;
2452
2453         permission = nm_utils_get_shared_wifi_permission (NM_CONNECTION (self));
2454         if (permission) {
2455                 if (nm_settings_connection_check_permission (self, permission) == FALSE)
2456                         return FALSE;
2457         }
2458
2459         return TRUE;
2460 }
2461
2462 /**
2463  * nm_settings_connection_get_nm_generated:
2464  * @self: an #NMSettingsConnection
2465  *
2466  * Gets the "nm-generated" flag on @self.
2467  *
2468  * A connection is "nm-generated" if it was generated by
2469  * nm_device_generate_connection() and has not been modified or
2470  * saved by the user since then.
2471  */
2472 gboolean
2473 nm_settings_connection_get_nm_generated (NMSettingsConnection *self)
2474 {
2475         return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED);
2476 }
2477
2478 /**
2479  * nm_settings_connection_get_nm_generated_assumed:
2480  * @self: an #NMSettingsConnection
2481  *
2482  * Gets the "nm-generated-assumed" flag on @self.
2483  *
2484  * The connection is a generated connection especially
2485  * generated for connection assumption.
2486  */
2487 gboolean
2488 nm_settings_connection_get_nm_generated_assumed (NMSettingsConnection *self)
2489 {
2490         return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED);
2491 }
2492
2493 gboolean
2494 nm_settings_connection_get_ready (NMSettingsConnection *self)
2495 {
2496         return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->ready;
2497 }
2498
2499 void
2500 nm_settings_connection_set_ready (NMSettingsConnection *self,
2501                                   gboolean ready)
2502 {
2503         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2504
2505         ready = !!ready;
2506         if (priv->ready != ready) {
2507                 priv->ready = ready;
2508                 g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTION_READY);
2509         }
2510 }
2511
2512 /**
2513  * nm_settings_connection_set_filename:
2514  * @self: an #NMSettingsConnection
2515  * @filename: @self's filename
2516  *
2517  * Called by a backend to sets the filename that @self is read
2518  * from/written to.
2519  */
2520 void
2521 nm_settings_connection_set_filename (NMSettingsConnection *self,
2522                                      const char *filename)
2523 {
2524         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2525
2526         if (g_strcmp0 (filename, priv->filename) != 0) {
2527                 g_free (priv->filename);
2528                 priv->filename = g_strdup (filename);
2529                 g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTION_FILENAME);
2530         }
2531 }
2532
2533 /**
2534  * nm_settings_connection_get_filename:
2535  * @self: an #NMSettingsConnection
2536  *
2537  * Gets the filename that @self was read from/written to.  This may be
2538  * %NULL if @self is unsaved, or if it is associated with a backend that
2539  * does not store each connection in a separate file.
2540  *
2541  * Returns: @self's filename.
2542  */
2543 const char *
2544 nm_settings_connection_get_filename (NMSettingsConnection *self)
2545 {
2546         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2547
2548         return priv->filename;
2549 }
2550
2551 const char *
2552 nm_settings_connection_get_id (NMSettingsConnection *self)
2553 {
2554         return nm_connection_get_id (NM_CONNECTION (self));
2555 }
2556
2557 const char *
2558 nm_settings_connection_get_uuid (NMSettingsConnection *self)
2559 {
2560         return nm_connection_get_uuid (NM_CONNECTION (self));
2561 }
2562
2563 /**************************************************************/
2564
2565 static void
2566 nm_settings_connection_init (NMSettingsConnection *self)
2567 {
2568         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2569
2570         priv->visible = FALSE;
2571         priv->ready = TRUE;
2572
2573         priv->session_monitor = g_object_ref (nm_session_monitor_get ());
2574         priv->session_changed_id = nm_session_monitor_connect (priv->session_monitor, session_changed_cb, self);
2575
2576         priv->agent_mgr = g_object_ref (nm_agent_manager_get ());
2577
2578         priv->seen_bssids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
2579
2580         priv->autoconnect_retries = AUTOCONNECT_RETRIES_DEFAULT;
2581         priv->autoconnect_blocked_reason = NM_DEVICE_STATE_REASON_NONE;
2582
2583         g_signal_connect (self, NM_CONNECTION_SECRETS_CLEARED, G_CALLBACK (secrets_cleared_cb), NULL);
2584         g_signal_connect (self, NM_CONNECTION_CHANGED, G_CALLBACK (changed_cb), GUINT_TO_POINTER (TRUE));
2585 }
2586
2587 static void
2588 constructed (GObject *object)
2589 {
2590         NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
2591
2592         _LOGD ("constructed (%s)", G_OBJECT_TYPE_NAME (self));
2593
2594         G_OBJECT_CLASS (nm_settings_connection_parent_class)->constructed (object);
2595 }
2596
2597 static void
2598 dispose (GObject *object)
2599 {
2600         NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
2601         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2602
2603         _LOGD ("disposing");
2604
2605         /* Cancel in-progress secrets requests */
2606         if (priv->agent_mgr) {
2607                 while (priv->get_secret_requests) {
2608                         GetSecretsInfo *info = priv->get_secret_requests->data;
2609
2610                         _get_secrets_cancel (self, info, TRUE);
2611                         g_return_if_fail (!priv->get_secret_requests || (info != priv->get_secret_requests->data));
2612                 }
2613         }
2614
2615         nm_clear_g_source (&priv->updated_idle_id);
2616
2617         /* Disconnect handlers.
2618          * changed_cb() has to be disconnected *before* nm_connection_clear_secrets(),
2619          * because nm_connection_clear_secrets() emits NM_CONNECTION_CHANGED signal.
2620          */
2621         g_signal_handlers_disconnect_by_func (self, G_CALLBACK (secrets_cleared_cb), NULL);
2622         g_signal_handlers_disconnect_by_func (self, G_CALLBACK (changed_cb), GUINT_TO_POINTER (TRUE));
2623
2624         nm_connection_clear_secrets (NM_CONNECTION (self));
2625         g_clear_object (&priv->system_secrets);
2626         g_clear_object (&priv->agent_secrets);
2627
2628         /* Cancel PolicyKit requests */
2629         g_slist_free_full (priv->pending_auths, (GDestroyNotify) nm_auth_chain_unref);
2630         priv->pending_auths = NULL;
2631
2632         g_clear_pointer (&priv->seen_bssids, (GDestroyNotify) g_hash_table_destroy);
2633
2634         set_visible (self, FALSE);
2635
2636         if (priv->session_monitor) {
2637                 nm_session_monitor_disconnect (priv->session_monitor, priv->session_changed_id);
2638                 priv->session_changed_id = 0;
2639                 g_clear_object (&priv->session_monitor);
2640         }
2641         g_clear_object (&priv->agent_mgr);
2642
2643         g_clear_pointer (&priv->filename, g_free);
2644
2645         G_OBJECT_CLASS (nm_settings_connection_parent_class)->dispose (object);
2646 }
2647
2648 static void
2649 get_property (GObject *object, guint prop_id,
2650               GValue *value, GParamSpec *pspec)
2651 {
2652         NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
2653         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2654
2655         switch (prop_id) {
2656         case PROP_VISIBLE:
2657                 g_value_set_boolean (value, priv->visible);
2658                 break;
2659         case PROP_UNSAVED:
2660                 g_value_set_boolean (value, nm_settings_connection_get_unsaved (self));
2661                 break;
2662         case PROP_READY:
2663                 g_value_set_boolean (value, nm_settings_connection_get_ready (self));
2664                 break;
2665         case PROP_FLAGS:
2666                 g_value_set_uint (value, nm_settings_connection_get_flags (self));
2667                 break;
2668         case PROP_FILENAME:
2669                 g_value_set_string (value, nm_settings_connection_get_filename (self));
2670                 break;
2671         default:
2672                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2673                 break;
2674         }
2675 }
2676
2677 static void
2678 set_property (GObject *object, guint prop_id,
2679               const GValue *value, GParamSpec *pspec)
2680 {
2681         NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
2682
2683         switch (prop_id) {
2684         case PROP_READY:
2685                 nm_settings_connection_set_ready (self, g_value_get_boolean (value));
2686                 break;
2687         case PROP_FLAGS:
2688                 nm_settings_connection_set_flags_all (self, g_value_get_uint (value));
2689                 break;
2690         case PROP_FILENAME:
2691                 nm_settings_connection_set_filename (self, g_value_get_string (value));
2692                 break;
2693         default:
2694                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2695                 break;
2696         }
2697 }
2698
2699 static void
2700 nm_settings_connection_class_init (NMSettingsConnectionClass *class)
2701 {
2702         GObjectClass *object_class = G_OBJECT_CLASS (class);
2703         NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (class);
2704
2705         g_type_class_add_private (class, sizeof (NMSettingsConnectionPrivate));
2706
2707         exported_object_class->export_path = NM_DBUS_PATH_SETTINGS "/%u";
2708
2709         /* Virtual methods */
2710         object_class->constructed = constructed;
2711         object_class->dispose = dispose;
2712         object_class->get_property = get_property;
2713         object_class->set_property = set_property;
2714
2715         class->replace_and_commit = replace_and_commit;
2716         class->commit_changes = commit_changes;
2717         class->delete = do_delete;
2718         class->supports_secrets = supports_secrets;
2719
2720         /* Properties */
2721         g_object_class_install_property
2722                 (object_class, PROP_VISIBLE,
2723                  g_param_spec_boolean (NM_SETTINGS_CONNECTION_VISIBLE, "", "",
2724                                        FALSE,
2725                                        G_PARAM_READABLE |
2726                                        G_PARAM_STATIC_STRINGS));
2727
2728         g_object_class_install_property
2729                 (object_class, PROP_UNSAVED,
2730                  g_param_spec_boolean (NM_SETTINGS_CONNECTION_UNSAVED, "", "",
2731                                        FALSE,
2732                                        G_PARAM_READABLE |
2733                                        G_PARAM_STATIC_STRINGS));
2734
2735         g_object_class_install_property
2736                 (object_class, PROP_READY,
2737                  g_param_spec_boolean (NM_SETTINGS_CONNECTION_READY, "", "",
2738                                        TRUE,
2739                                        G_PARAM_READWRITE |
2740                                        G_PARAM_STATIC_STRINGS));
2741
2742         g_object_class_install_property
2743             (object_class, PROP_FLAGS,
2744              g_param_spec_uint (NM_SETTINGS_CONNECTION_FLAGS, "", "",
2745                                 NM_SETTINGS_CONNECTION_FLAGS_NONE,
2746                                 NM_SETTINGS_CONNECTION_FLAGS_ALL,
2747                                 NM_SETTINGS_CONNECTION_FLAGS_NONE,
2748                                 G_PARAM_READWRITE |
2749                                 G_PARAM_STATIC_STRINGS));
2750
2751         g_object_class_install_property
2752                 (object_class, PROP_FILENAME,
2753                  g_param_spec_string (NM_SETTINGS_CONNECTION_FILENAME, "", "",
2754                                       NULL,
2755                                       G_PARAM_READWRITE |
2756                                       G_PARAM_STATIC_STRINGS));
2757
2758         /* Signals */
2759
2760         /* Emitted when the connection is changed for any reason */
2761         signals[UPDATED] =
2762                 g_signal_new (NM_SETTINGS_CONNECTION_UPDATED,
2763                               G_TYPE_FROM_CLASS (class),
2764                               G_SIGNAL_RUN_FIRST,
2765                               0,
2766                               NULL, NULL,
2767                               g_cclosure_marshal_VOID__VOID,
2768                               G_TYPE_NONE, 0);
2769
2770         /* Emitted when connection is changed from D-Bus */
2771         signals[UPDATED_BY_USER] =
2772                 g_signal_new (NM_SETTINGS_CONNECTION_UPDATED_BY_USER,
2773                               G_TYPE_FROM_CLASS (class),
2774                               G_SIGNAL_RUN_FIRST,
2775                               0, NULL, NULL,
2776                               g_cclosure_marshal_VOID__VOID,
2777                               G_TYPE_NONE, 0);
2778
2779         signals[REMOVED] = 
2780                 g_signal_new (NM_SETTINGS_CONNECTION_REMOVED,
2781                               G_TYPE_FROM_CLASS (class),
2782                               G_SIGNAL_RUN_FIRST,
2783                               0,
2784                               NULL, NULL,
2785                               g_cclosure_marshal_VOID__VOID,
2786                               G_TYPE_NONE, 0);
2787
2788         nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (class),
2789                                                 NMDBUS_TYPE_SETTINGS_CONNECTION_SKELETON,
2790                                                 "Update", impl_settings_connection_update,
2791                                                 "UpdateUnsaved", impl_settings_connection_update_unsaved,
2792                                                 "Delete", impl_settings_connection_delete,
2793                                                 "GetSettings", impl_settings_connection_get_settings,
2794                                                 "GetSecrets", impl_settings_connection_get_secrets,
2795                                                 "ClearSecrets", impl_settings_connection_clear_secrets,
2796                                                 "Save", impl_settings_connection_save,
2797                                                 NULL);
2798 }
2799
2800 static void
2801 nm_settings_connection_connection_interface_init (NMConnectionInterface *iface)
2802 {
2803 }
2804