core: be strict about connection argument in D-Bus methods
[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_STRICT
1740                                                            | NM_SETTING_PARSE_FLAGS_NORMALIZE,
1741                                                            &error);
1742                 if (!tmp)
1743                         goto error;
1744         }
1745
1746         subject = _new_auth_subject (context, &error);
1747         if (!subject)
1748                 goto error;
1749
1750         /* And that the new connection settings will be visible to the user
1751          * that's sending the update request.  You can't make a connection
1752          * invisible to yourself.
1753          */
1754         if (!nm_auth_is_subject_in_acl (tmp ? tmp : NM_CONNECTION (self),
1755                                         subject,
1756                                         &error_desc)) {
1757                 error = g_error_new_literal (NM_SETTINGS_ERROR,
1758                                              NM_SETTINGS_ERROR_PERMISSION_DENIED,
1759                                              error_desc);
1760                 g_free (error_desc);
1761                 goto error;
1762         }
1763
1764         info = g_malloc0 (sizeof (*info));
1765         info->context = context;
1766         info->agent_mgr = g_object_ref (priv->agent_mgr);
1767         info->subject = subject;
1768         info->save_to_disk = save_to_disk;
1769         info->new_settings = tmp;
1770
1771         permission = get_update_modify_permission (NM_CONNECTION (self),
1772                                                    tmp ? tmp : NM_CONNECTION (self));
1773         auth_start (self, context, subject, permission, update_auth_cb, info);
1774         return;
1775
1776 error:
1777         nm_audit_log_connection_op (NM_AUDIT_OP_CONN_UPDATE, self, FALSE, subject,
1778                                     error->message);
1779
1780         g_clear_object (&tmp);
1781         g_clear_object (&subject);
1782
1783         g_dbus_method_invocation_take_error (context, error);
1784 }
1785
1786 static void
1787 impl_settings_connection_update (NMSettingsConnection *self,
1788                                  GDBusMethodInvocation *context,
1789                                  GVariant *new_settings)
1790 {
1791         settings_connection_update_helper (self, context, new_settings, TRUE);
1792 }
1793
1794 static void
1795 impl_settings_connection_update_unsaved (NMSettingsConnection *self,
1796                                          GDBusMethodInvocation *context,
1797                                          GVariant *new_settings)
1798 {
1799         settings_connection_update_helper (self, context, new_settings, FALSE);
1800 }
1801
1802 static void
1803 impl_settings_connection_save (NMSettingsConnection *self,
1804                                GDBusMethodInvocation *context)
1805 {
1806         /* Do nothing if the connection is already synced with disk */
1807         if (nm_settings_connection_get_unsaved (self))
1808                 settings_connection_update_helper (self, context, NULL, TRUE);
1809         else
1810                 g_dbus_method_invocation_return_value (context, NULL);
1811 }
1812
1813 static void
1814 con_delete_cb (NMSettingsConnection *self,
1815                GError *error,
1816                gpointer user_data)
1817 {
1818         CallbackInfo *info = user_data;
1819
1820         if (error)
1821                 g_dbus_method_invocation_return_gerror (info->context, error);
1822         else
1823                 g_dbus_method_invocation_return_value (info->context, NULL);
1824
1825         nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self,
1826                                     !error, info->subject, error ? error->message : NULL);
1827         g_free (info);
1828 }
1829
1830 static void
1831 delete_auth_cb (NMSettingsConnection *self,
1832                 GDBusMethodInvocation *context,
1833                 NMAuthSubject *subject,
1834                 GError *error,
1835                 gpointer data)
1836 {
1837         CallbackInfo *info;
1838
1839         if (error) {
1840                 nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self, FALSE, subject,
1841                                             error->message);
1842                 g_dbus_method_invocation_return_gerror (context, error);
1843                 return;
1844         }
1845
1846         info = g_malloc0 (sizeof (*info));
1847         info->context = context;
1848         info->subject = subject;
1849
1850         nm_settings_connection_delete (self, con_delete_cb, info);
1851 }
1852
1853 static const char *
1854 get_modify_permission_basic (NMSettingsConnection *self)
1855 {
1856         NMSettingConnection *s_con;
1857
1858         /* If the caller is the only user in the connection's permissions, then
1859          * we use the 'modify.own' permission instead of 'modify.system'.  If the
1860          * request affects more than just the caller, require 'modify.system'.
1861          */
1862         s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
1863         g_assert (s_con);
1864         if (nm_setting_connection_get_num_permissions (s_con) == 1)
1865                 return NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN;
1866
1867         return NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM;
1868 }
1869
1870 static void
1871 impl_settings_connection_delete (NMSettingsConnection *self,
1872                                  GDBusMethodInvocation *context)
1873 {
1874         NMAuthSubject *subject = NULL;
1875         GError *error = NULL;
1876
1877         if (!check_writable (NM_CONNECTION (self), &error))
1878                 goto out_err;
1879
1880         subject = _new_auth_subject (context, &error);
1881         if (subject) {
1882                 auth_start (self, context, subject, get_modify_permission_basic (self), delete_auth_cb, NULL);
1883                 g_object_unref (subject);
1884         } else
1885                 goto out_err;
1886
1887         return;
1888 out_err:
1889         nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self, FALSE, subject, error->message);
1890         g_dbus_method_invocation_take_error (context, error);
1891 }
1892
1893 /**************************************************************/
1894
1895 static void
1896 dbus_get_agent_secrets_cb (NMSettingsConnection *self,
1897                            NMSettingsConnectionCallId call_id,
1898                            const char *agent_username,
1899                            const char *setting_name,
1900                            GError *error,
1901                            gpointer user_data)
1902 {
1903         GDBusMethodInvocation *context = user_data;
1904         GVariant *dict;
1905
1906         if (error)
1907                 g_dbus_method_invocation_return_gerror (context, error);
1908         else {
1909                 /* Return secrets from agent and backing storage to the D-Bus caller;
1910                  * nm_settings_connection_get_secrets() will have updated itself with
1911                  * secrets from backing storage and those returned from the agent
1912                  * by the time we get here.
1913                  */
1914                 dict = nm_connection_to_dbus (NM_CONNECTION (self), NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
1915                 if (!dict)
1916                         dict = g_variant_new_array (G_VARIANT_TYPE ("{sa{sv}}"), NULL, 0);
1917                 g_dbus_method_invocation_return_value (context, g_variant_new ("(@a{sa{sv}})", dict));
1918         }
1919 }
1920
1921 static void
1922 dbus_get_secrets_auth_cb (NMSettingsConnection *self,
1923                           GDBusMethodInvocation *context,
1924                           NMAuthSubject *subject,
1925                           GError *error,
1926                           gpointer user_data)
1927 {
1928         char *setting_name = user_data;
1929
1930         if (!error) {
1931                 nm_settings_connection_get_secrets (self,
1932                                                     NULL,
1933                                                     subject,
1934                                                     setting_name,
1935                                                     NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED
1936                                                       | NM_SECRET_AGENT_GET_SECRETS_FLAG_NO_ERRORS,
1937                                                     NULL,
1938                                                     dbus_get_agent_secrets_cb,
1939                                                     context);
1940         }
1941
1942         if (error)
1943                 g_dbus_method_invocation_return_gerror (context, error);
1944
1945         g_free (setting_name);
1946 }
1947
1948 static void
1949 impl_settings_connection_get_secrets (NMSettingsConnection *self,
1950                                       GDBusMethodInvocation *context,
1951                                       const gchar *setting_name)
1952 {
1953         NMAuthSubject *subject;
1954         GError *error = NULL;
1955
1956         subject = _new_auth_subject (context, &error);
1957         if (subject) {
1958                 auth_start (self,
1959                             context,
1960                             subject,
1961                             get_modify_permission_basic (self),
1962                             dbus_get_secrets_auth_cb,
1963                             g_strdup (setting_name));
1964                 g_object_unref (subject);
1965         } else
1966                 g_dbus_method_invocation_take_error (context, error);
1967 }
1968
1969 static void
1970 clear_secrets_cb (NMSettingsConnection *self,
1971                   GError *error,
1972                   gpointer user_data)
1973 {
1974         CallbackInfo *info = user_data;
1975
1976         if (error)
1977                 g_dbus_method_invocation_return_gerror (info->context, error);
1978         else
1979                 g_dbus_method_invocation_return_value (info->context, NULL);
1980
1981         nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self,
1982                                     !error, info->subject, error ? error->message : NULL);
1983         g_free (info);
1984 }
1985
1986 static void
1987 dbus_clear_secrets_auth_cb (NMSettingsConnection *self,
1988                             GDBusMethodInvocation *context,
1989                             NMAuthSubject *subject,
1990                             GError *error,
1991                             gpointer user_data)
1992 {
1993         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
1994         CallbackInfo *info;
1995
1996         if (error) {
1997                 g_dbus_method_invocation_return_gerror (context, error);
1998                 nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self,
1999                                             FALSE, subject, error->message);
2000         } else {
2001                 /* Clear secrets in connection and caches */
2002                 nm_connection_clear_secrets (NM_CONNECTION (self));
2003                 if (priv->system_secrets)
2004                         nm_connection_clear_secrets (priv->system_secrets);
2005                 if (priv->agent_secrets)
2006                         nm_connection_clear_secrets (priv->agent_secrets);
2007
2008                 /* Tell agents to remove secrets for this connection */
2009                 nm_agent_manager_delete_secrets (priv->agent_mgr,
2010                                                  nm_connection_get_path (NM_CONNECTION (self)),
2011                                                  NM_CONNECTION (self));
2012
2013                 info = g_malloc0 (sizeof (*info));
2014                 info->context = context;
2015                 info->subject = subject;
2016
2017                 nm_settings_connection_commit_changes (self, NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, clear_secrets_cb, info);
2018         }
2019 }
2020
2021 static void
2022 impl_settings_connection_clear_secrets (NMSettingsConnection *self,
2023                                         GDBusMethodInvocation *context)
2024 {
2025         NMAuthSubject *subject;
2026         GError *error = NULL;
2027
2028         subject = _new_auth_subject (context, &error);
2029         if (subject) {
2030                 auth_start (self,
2031                             context,
2032                             subject,
2033                             get_modify_permission_basic (self),
2034                             dbus_clear_secrets_auth_cb,
2035                             NULL);
2036                 g_object_unref (subject);
2037         } else {
2038                 nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self,
2039                                             FALSE, NULL, error->message);
2040                 g_dbus_method_invocation_take_error (context, error);
2041         }
2042 }
2043
2044 /**************************************************************/
2045
2046 void
2047 nm_settings_connection_signal_remove (NMSettingsConnection *self)
2048 {
2049         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2050
2051         if (priv->removed)
2052                 g_return_if_reached ();
2053         priv->removed = TRUE;
2054         g_signal_emit_by_name (self, NM_SETTINGS_CONNECTION_REMOVED);
2055 }
2056
2057 gboolean
2058 nm_settings_connection_get_unsaved (NMSettingsConnection *self)
2059 {
2060         return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_FLAGS_UNSAVED);
2061 }
2062
2063 /**************************************************************/
2064
2065 NMSettingsConnectionFlags
2066 nm_settings_connection_get_flags (NMSettingsConnection *self)
2067 {
2068         g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_FLAGS_NONE);
2069
2070         return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->flags;
2071 }
2072
2073 NMSettingsConnectionFlags
2074 nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionFlags flags, gboolean set)
2075 {
2076         NMSettingsConnectionFlags new_flags;
2077
2078         g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_FLAGS_NONE);
2079         g_return_val_if_fail ((flags & ~NM_SETTINGS_CONNECTION_FLAGS_ALL) == 0, NM_SETTINGS_CONNECTION_FLAGS_NONE);
2080
2081         new_flags = NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->flags;
2082         if (set)
2083                 new_flags |= flags;
2084         else
2085                 new_flags &= ~flags;
2086         return nm_settings_connection_set_flags_all (self, new_flags);
2087 }
2088
2089 NMSettingsConnectionFlags
2090 nm_settings_connection_set_flags_all (NMSettingsConnection *self, NMSettingsConnectionFlags flags)
2091 {
2092         NMSettingsConnectionPrivate *priv;
2093         NMSettingsConnectionFlags old_flags;
2094
2095         g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_FLAGS_NONE);
2096         g_return_val_if_fail ((flags & ~NM_SETTINGS_CONNECTION_FLAGS_ALL) == 0, NM_SETTINGS_CONNECTION_FLAGS_NONE);
2097         priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2098
2099         old_flags = priv->flags;
2100         if (old_flags != flags) {
2101                 priv->flags = flags;
2102                 g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTION_FLAGS);
2103                 if (NM_FLAGS_HAS (old_flags, NM_SETTINGS_CONNECTION_FLAGS_UNSAVED) != NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_FLAGS_UNSAVED))
2104                         g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTION_UNSAVED);
2105         }
2106         return old_flags;
2107 }
2108
2109 /*************************************************************/
2110
2111 /**
2112  * nm_settings_connection_get_timestamp:
2113  * @self: the #NMSettingsConnection
2114  * @out_timestamp: the connection's timestamp
2115  *
2116  * Returns the time (in seconds since the Unix epoch) when the connection
2117  * was last successfully activated.
2118  *
2119  * Returns: %TRUE if the timestamp has ever been set, otherwise %FALSE.
2120  **/
2121 gboolean
2122 nm_settings_connection_get_timestamp (NMSettingsConnection *self,
2123                                       guint64 *out_timestamp)
2124 {
2125         g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
2126
2127         if (out_timestamp)
2128                 *out_timestamp = NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->timestamp;
2129         return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->timestamp_set;
2130 }
2131
2132 /**
2133  * nm_settings_connection_update_timestamp:
2134  * @self: the #NMSettingsConnection
2135  * @timestamp: timestamp to set into the connection and to store into
2136  * the timestamps database
2137  * @flush_to_disk: if %TRUE, commit timestamp update to persistent storage
2138  *
2139  * Updates the connection and timestamps database with the provided timestamp.
2140  **/
2141 void
2142 nm_settings_connection_update_timestamp (NMSettingsConnection *self,
2143                                          guint64 timestamp,
2144                                          gboolean flush_to_disk)
2145 {
2146         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2147         const char *connection_uuid;
2148         GKeyFile *timestamps_file;
2149         char *data, *tmp;
2150         gsize len;
2151         GError *error = NULL;
2152
2153         g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
2154
2155         /* Update timestamp in private storage */
2156         priv->timestamp = timestamp;
2157         priv->timestamp_set = TRUE;
2158
2159         if (flush_to_disk == FALSE)
2160                 return;
2161
2162         /* Save timestamp to timestamps database file */
2163         timestamps_file = g_key_file_new ();
2164         if (!g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) {
2165                 if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
2166                         _LOGW ("error parsing timestamps file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message);
2167                 g_clear_error (&error);
2168         }
2169
2170         connection_uuid = nm_settings_connection_get_uuid (self);
2171         tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp);
2172         g_key_file_set_value (timestamps_file, "timestamps", connection_uuid, tmp);
2173         g_free (tmp);
2174
2175         data = g_key_file_to_data (timestamps_file, &len, &error);
2176         if (data) {
2177                 g_file_set_contents (SETTINGS_TIMESTAMPS_FILE, data, len, &error);
2178                 g_free (data);
2179         }
2180         if (error) {
2181                 _LOGW ("error saving timestamp to file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message);
2182                 g_error_free (error);
2183         }
2184         g_key_file_free (timestamps_file);
2185 }
2186
2187 /**
2188  * nm_settings_connection_read_and_fill_timestamp:
2189  * @self: the #NMSettingsConnection
2190  *
2191  * Retrieves timestamp of the connection's last usage from database file and
2192  * stores it into the connection private data.
2193  **/
2194 void
2195 nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *self)
2196 {
2197         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2198         const char *connection_uuid;
2199         guint64 timestamp = 0;
2200         GKeyFile *timestamps_file;
2201         GError *err = NULL;
2202         char *tmp_str;
2203
2204         g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
2205
2206         /* Get timestamp from database file */
2207         timestamps_file = g_key_file_new ();
2208         g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL);
2209         connection_uuid = nm_settings_connection_get_uuid (self);
2210         tmp_str = g_key_file_get_value (timestamps_file, "timestamps", connection_uuid, &err);
2211         if (tmp_str) {
2212                 timestamp = g_ascii_strtoull (tmp_str, NULL, 10);
2213                 g_free (tmp_str);
2214         }
2215
2216         /* Update connection's timestamp */
2217         if (!err) {
2218                 priv->timestamp = timestamp;
2219                 priv->timestamp_set = TRUE;
2220         } else {
2221                 _LOGD ("failed to read connection timestamp: %s", err->message);
2222                 g_clear_error (&err);
2223         }
2224         g_key_file_free (timestamps_file);
2225 }
2226
2227 /**
2228  * nm_settings_connection_get_seen_bssids:
2229  * @self: the #NMSettingsConnection
2230  *
2231  * Returns current list of seen BSSIDs for the connection.
2232  *
2233  * Returns: (transfer container) list of seen BSSIDs (in the standard hex-digits-and-colons notation).
2234  * The caller is responsible for freeing the list, but not the content.
2235  **/
2236 char **
2237 nm_settings_connection_get_seen_bssids (NMSettingsConnection *self)
2238 {
2239         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2240         GHashTableIter iter;
2241         char **bssids, *bssid;
2242         int i;
2243
2244         g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL);
2245
2246         bssids = g_new (char *, g_hash_table_size (priv->seen_bssids) + 1);
2247
2248         i = 0;
2249         g_hash_table_iter_init (&iter, priv->seen_bssids);
2250         while (g_hash_table_iter_next (&iter, NULL, (gpointer) &bssid))
2251                 bssids[i++] = bssid;
2252         bssids[i] = NULL;
2253
2254         return bssids;
2255 }
2256
2257 /**
2258  * nm_settings_connection_has_seen_bssid:
2259  * @self: the #NMSettingsConnection
2260  * @bssid: the BSSID to check the seen BSSID list for
2261  *
2262  * Returns: %TRUE if the given @bssid is in the seen BSSIDs list
2263  **/
2264 gboolean
2265 nm_settings_connection_has_seen_bssid (NMSettingsConnection *self,
2266                                        const char *bssid)
2267 {
2268         g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
2269         g_return_val_if_fail (bssid != NULL, FALSE);
2270
2271         return !!g_hash_table_lookup (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->seen_bssids, bssid);
2272 }
2273
2274 /**
2275  * nm_settings_connection_add_seen_bssid:
2276  * @self: the #NMSettingsConnection
2277  * @seen_bssid: BSSID to set into the connection and to store into
2278  * the seen-bssids database
2279  *
2280  * Updates the connection and seen-bssids database with the provided BSSID.
2281  **/
2282 void
2283 nm_settings_connection_add_seen_bssid (NMSettingsConnection *self,
2284                                        const char *seen_bssid)
2285 {
2286         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2287         const char *connection_uuid;
2288         GKeyFile *seen_bssids_file;
2289         char *data, *bssid_str;
2290         const char **list;
2291         gsize len;
2292         GError *error = NULL;
2293         GHashTableIter iter;
2294         guint n;
2295
2296         g_return_if_fail (seen_bssid != NULL);
2297
2298         if (g_hash_table_lookup (priv->seen_bssids, seen_bssid))
2299                 return;  /* Already in the list */
2300
2301         /* Add the new BSSID; let the hash take ownership of the allocated BSSID string */
2302         bssid_str = g_strdup (seen_bssid);
2303         g_hash_table_insert (priv->seen_bssids, bssid_str, bssid_str);
2304
2305         /* Build up a list of all the BSSIDs in string form */
2306         n = 0;
2307         list = g_malloc0 (g_hash_table_size (priv->seen_bssids) * sizeof (char *));
2308         g_hash_table_iter_init (&iter, priv->seen_bssids);
2309         while (g_hash_table_iter_next (&iter, NULL, (gpointer) &bssid_str))
2310                 list[n++] = bssid_str;
2311
2312         /* Save BSSID to seen-bssids file */
2313         seen_bssids_file = g_key_file_new ();
2314         g_key_file_set_list_separator (seen_bssids_file, ',');
2315         if (!g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) {
2316                 if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
2317                         _LOGW ("error parsing seen-bssids file '%s': %s",
2318                                SETTINGS_SEEN_BSSIDS_FILE, error->message);
2319                 }
2320                 g_clear_error (&error);
2321         }
2322
2323         connection_uuid = nm_settings_connection_get_uuid (self);
2324         g_key_file_set_string_list (seen_bssids_file, "seen-bssids", connection_uuid, list, n);
2325         g_free (list);
2326
2327         data = g_key_file_to_data (seen_bssids_file, &len, &error);
2328         if (data) {
2329                 g_file_set_contents (SETTINGS_SEEN_BSSIDS_FILE, data, len, &error);
2330                 g_free (data);
2331         }
2332         g_key_file_free (seen_bssids_file);
2333
2334         if (error) {
2335                 _LOGW ("error saving seen-bssids to file '%s': %s",
2336                        SETTINGS_SEEN_BSSIDS_FILE, error->message);
2337                 g_error_free (error);
2338         }
2339 }
2340
2341 /**
2342  * nm_settings_connection_read_and_fill_seen_bssids:
2343  * @self: the #NMSettingsConnection
2344  *
2345  * Retrieves seen BSSIDs of the connection from database file and stores then into the
2346  * connection private data.
2347  **/
2348 void
2349 nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *self)
2350 {
2351         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2352         const char *connection_uuid;
2353         GKeyFile *seen_bssids_file;
2354         char **tmp_strv = NULL;
2355         gsize i, len = 0;
2356         NMSettingWireless *s_wifi;
2357
2358         /* Get seen BSSIDs from database file */
2359         seen_bssids_file = g_key_file_new ();
2360         g_key_file_set_list_separator (seen_bssids_file, ',');
2361         if (g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL)) {
2362                 connection_uuid = nm_settings_connection_get_uuid (self);
2363                 tmp_strv = g_key_file_get_string_list (seen_bssids_file, "seen-bssids", connection_uuid, &len, NULL);
2364         }
2365         g_key_file_free (seen_bssids_file);
2366
2367         /* Update connection's seen-bssids */
2368         if (tmp_strv) {
2369                 g_hash_table_remove_all (priv->seen_bssids);
2370                 for (i = 0; i < len; i++)
2371                         g_hash_table_insert (priv->seen_bssids, tmp_strv[i], tmp_strv[i]);
2372                 g_free (tmp_strv);
2373         } else {
2374                 /* If this connection didn't have an entry in the seen-bssids database,
2375                  * maybe this is the first time we've read it in, so populate the
2376                  * seen-bssids list from the deprecated seen-bssids property of the
2377                  * wifi setting.
2378                  */
2379                 s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (self));
2380                 if (s_wifi) {
2381                         len = nm_setting_wireless_get_num_seen_bssids (s_wifi);
2382                         for (i = 0; i < len; i++) {
2383                                 char *bssid_dup = g_strdup (nm_setting_wireless_get_seen_bssid (s_wifi, i));
2384
2385                                 g_hash_table_insert (priv->seen_bssids, bssid_dup, bssid_dup);
2386                         }
2387                 }
2388         }
2389 }
2390
2391 #define AUTOCONNECT_RETRIES_DEFAULT 4
2392 #define AUTOCONNECT_RESET_RETRIES_TIMER 300
2393
2394 int
2395 nm_settings_connection_get_autoconnect_retries (NMSettingsConnection *self)
2396 {
2397         return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_retries;
2398 }
2399
2400 void
2401 nm_settings_connection_set_autoconnect_retries (NMSettingsConnection *self,
2402                                                 int retries)
2403 {
2404         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2405
2406         priv->autoconnect_retries = retries;
2407         if (retries)
2408                 priv->autoconnect_retry_time = 0;
2409         else
2410                 priv->autoconnect_retry_time = nm_utils_get_monotonic_timestamp_s () + AUTOCONNECT_RESET_RETRIES_TIMER;
2411 }
2412
2413 void
2414 nm_settings_connection_reset_autoconnect_retries (NMSettingsConnection *self)
2415 {
2416         nm_settings_connection_set_autoconnect_retries (self, AUTOCONNECT_RETRIES_DEFAULT);
2417 }
2418
2419 gint32
2420 nm_settings_connection_get_autoconnect_retry_time (NMSettingsConnection *self)
2421 {
2422         return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_retry_time;
2423 }
2424
2425 NMDeviceStateReason
2426 nm_settings_connection_get_autoconnect_blocked_reason (NMSettingsConnection *self)
2427 {
2428         return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_blocked_reason;
2429 }
2430
2431 void
2432 nm_settings_connection_set_autoconnect_blocked_reason (NMSettingsConnection *self,
2433                                                        NMDeviceStateReason reason)
2434 {
2435         NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_blocked_reason = reason;
2436 }
2437
2438 gboolean
2439 nm_settings_connection_can_autoconnect (NMSettingsConnection *self)
2440 {
2441         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2442         NMSettingConnection *s_con;
2443         const char *permission;
2444
2445         if (   !priv->visible
2446             || priv->autoconnect_retries == 0
2447             || priv->autoconnect_blocked_reason != NM_DEVICE_STATE_REASON_NONE)
2448                 return FALSE;
2449
2450         s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
2451         if (!nm_setting_connection_get_autoconnect (s_con))
2452                 return FALSE;
2453
2454         permission = nm_utils_get_shared_wifi_permission (NM_CONNECTION (self));
2455         if (permission) {
2456                 if (nm_settings_connection_check_permission (self, permission) == FALSE)
2457                         return FALSE;
2458         }
2459
2460         return TRUE;
2461 }
2462
2463 /**
2464  * nm_settings_connection_get_nm_generated:
2465  * @self: an #NMSettingsConnection
2466  *
2467  * Gets the "nm-generated" flag on @self.
2468  *
2469  * A connection is "nm-generated" if it was generated by
2470  * nm_device_generate_connection() and has not been modified or
2471  * saved by the user since then.
2472  */
2473 gboolean
2474 nm_settings_connection_get_nm_generated (NMSettingsConnection *self)
2475 {
2476         return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED);
2477 }
2478
2479 /**
2480  * nm_settings_connection_get_nm_generated_assumed:
2481  * @self: an #NMSettingsConnection
2482  *
2483  * Gets the "nm-generated-assumed" flag on @self.
2484  *
2485  * The connection is a generated connection especially
2486  * generated for connection assumption.
2487  */
2488 gboolean
2489 nm_settings_connection_get_nm_generated_assumed (NMSettingsConnection *self)
2490 {
2491         return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED);
2492 }
2493
2494 gboolean
2495 nm_settings_connection_get_ready (NMSettingsConnection *self)
2496 {
2497         return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->ready;
2498 }
2499
2500 void
2501 nm_settings_connection_set_ready (NMSettingsConnection *self,
2502                                   gboolean ready)
2503 {
2504         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2505
2506         ready = !!ready;
2507         if (priv->ready != ready) {
2508                 priv->ready = ready;
2509                 g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTION_READY);
2510         }
2511 }
2512
2513 /**
2514  * nm_settings_connection_set_filename:
2515  * @self: an #NMSettingsConnection
2516  * @filename: @self's filename
2517  *
2518  * Called by a backend to sets the filename that @self is read
2519  * from/written to.
2520  */
2521 void
2522 nm_settings_connection_set_filename (NMSettingsConnection *self,
2523                                      const char *filename)
2524 {
2525         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2526
2527         if (g_strcmp0 (filename, priv->filename) != 0) {
2528                 g_free (priv->filename);
2529                 priv->filename = g_strdup (filename);
2530                 g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTION_FILENAME);
2531         }
2532 }
2533
2534 /**
2535  * nm_settings_connection_get_filename:
2536  * @self: an #NMSettingsConnection
2537  *
2538  * Gets the filename that @self was read from/written to.  This may be
2539  * %NULL if @self is unsaved, or if it is associated with a backend that
2540  * does not store each connection in a separate file.
2541  *
2542  * Returns: @self's filename.
2543  */
2544 const char *
2545 nm_settings_connection_get_filename (NMSettingsConnection *self)
2546 {
2547         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2548
2549         return priv->filename;
2550 }
2551
2552 const char *
2553 nm_settings_connection_get_id (NMSettingsConnection *self)
2554 {
2555         return nm_connection_get_id (NM_CONNECTION (self));
2556 }
2557
2558 const char *
2559 nm_settings_connection_get_uuid (NMSettingsConnection *self)
2560 {
2561         return nm_connection_get_uuid (NM_CONNECTION (self));
2562 }
2563
2564 /**************************************************************/
2565
2566 static void
2567 nm_settings_connection_init (NMSettingsConnection *self)
2568 {
2569         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2570
2571         priv->visible = FALSE;
2572         priv->ready = TRUE;
2573
2574         priv->session_monitor = g_object_ref (nm_session_monitor_get ());
2575         priv->session_changed_id = nm_session_monitor_connect (priv->session_monitor, session_changed_cb, self);
2576
2577         priv->agent_mgr = g_object_ref (nm_agent_manager_get ());
2578
2579         priv->seen_bssids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
2580
2581         priv->autoconnect_retries = AUTOCONNECT_RETRIES_DEFAULT;
2582         priv->autoconnect_blocked_reason = NM_DEVICE_STATE_REASON_NONE;
2583
2584         g_signal_connect (self, NM_CONNECTION_SECRETS_CLEARED, G_CALLBACK (secrets_cleared_cb), NULL);
2585         g_signal_connect (self, NM_CONNECTION_CHANGED, G_CALLBACK (changed_cb), GUINT_TO_POINTER (TRUE));
2586 }
2587
2588 static void
2589 constructed (GObject *object)
2590 {
2591         NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
2592
2593         _LOGD ("constructed (%s)", G_OBJECT_TYPE_NAME (self));
2594
2595         G_OBJECT_CLASS (nm_settings_connection_parent_class)->constructed (object);
2596 }
2597
2598 static void
2599 dispose (GObject *object)
2600 {
2601         NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
2602         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2603
2604         _LOGD ("disposing");
2605
2606         /* Cancel in-progress secrets requests */
2607         if (priv->agent_mgr) {
2608                 while (priv->get_secret_requests) {
2609                         GetSecretsInfo *info = priv->get_secret_requests->data;
2610
2611                         _get_secrets_cancel (self, info, TRUE);
2612                         g_return_if_fail (!priv->get_secret_requests || (info != priv->get_secret_requests->data));
2613                 }
2614         }
2615
2616         nm_clear_g_source (&priv->updated_idle_id);
2617
2618         /* Disconnect handlers.
2619          * changed_cb() has to be disconnected *before* nm_connection_clear_secrets(),
2620          * because nm_connection_clear_secrets() emits NM_CONNECTION_CHANGED signal.
2621          */
2622         g_signal_handlers_disconnect_by_func (self, G_CALLBACK (secrets_cleared_cb), NULL);
2623         g_signal_handlers_disconnect_by_func (self, G_CALLBACK (changed_cb), GUINT_TO_POINTER (TRUE));
2624
2625         nm_connection_clear_secrets (NM_CONNECTION (self));
2626         g_clear_object (&priv->system_secrets);
2627         g_clear_object (&priv->agent_secrets);
2628
2629         /* Cancel PolicyKit requests */
2630         g_slist_free_full (priv->pending_auths, (GDestroyNotify) nm_auth_chain_unref);
2631         priv->pending_auths = NULL;
2632
2633         g_clear_pointer (&priv->seen_bssids, (GDestroyNotify) g_hash_table_destroy);
2634
2635         set_visible (self, FALSE);
2636
2637         if (priv->session_monitor) {
2638                 nm_session_monitor_disconnect (priv->session_monitor, priv->session_changed_id);
2639                 priv->session_changed_id = 0;
2640                 g_clear_object (&priv->session_monitor);
2641         }
2642         g_clear_object (&priv->agent_mgr);
2643
2644         g_clear_pointer (&priv->filename, g_free);
2645
2646         G_OBJECT_CLASS (nm_settings_connection_parent_class)->dispose (object);
2647 }
2648
2649 static void
2650 get_property (GObject *object, guint prop_id,
2651               GValue *value, GParamSpec *pspec)
2652 {
2653         NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
2654         NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
2655
2656         switch (prop_id) {
2657         case PROP_VISIBLE:
2658                 g_value_set_boolean (value, priv->visible);
2659                 break;
2660         case PROP_UNSAVED:
2661                 g_value_set_boolean (value, nm_settings_connection_get_unsaved (self));
2662                 break;
2663         case PROP_READY:
2664                 g_value_set_boolean (value, nm_settings_connection_get_ready (self));
2665                 break;
2666         case PROP_FLAGS:
2667                 g_value_set_uint (value, nm_settings_connection_get_flags (self));
2668                 break;
2669         case PROP_FILENAME:
2670                 g_value_set_string (value, nm_settings_connection_get_filename (self));
2671                 break;
2672         default:
2673                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2674                 break;
2675         }
2676 }
2677
2678 static void
2679 set_property (GObject *object, guint prop_id,
2680               const GValue *value, GParamSpec *pspec)
2681 {
2682         NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
2683
2684         switch (prop_id) {
2685         case PROP_READY:
2686                 nm_settings_connection_set_ready (self, g_value_get_boolean (value));
2687                 break;
2688         case PROP_FLAGS:
2689                 nm_settings_connection_set_flags_all (self, g_value_get_uint (value));
2690                 break;
2691         case PROP_FILENAME:
2692                 nm_settings_connection_set_filename (self, g_value_get_string (value));
2693                 break;
2694         default:
2695                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2696                 break;
2697         }
2698 }
2699
2700 static void
2701 nm_settings_connection_class_init (NMSettingsConnectionClass *class)
2702 {
2703         GObjectClass *object_class = G_OBJECT_CLASS (class);
2704         NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (class);
2705
2706         g_type_class_add_private (class, sizeof (NMSettingsConnectionPrivate));
2707
2708         exported_object_class->export_path = NM_DBUS_PATH_SETTINGS "/%u";
2709
2710         /* Virtual methods */
2711         object_class->constructed = constructed;
2712         object_class->dispose = dispose;
2713         object_class->get_property = get_property;
2714         object_class->set_property = set_property;
2715
2716         class->replace_and_commit = replace_and_commit;
2717         class->commit_changes = commit_changes;
2718         class->delete = do_delete;
2719         class->supports_secrets = supports_secrets;
2720
2721         /* Properties */
2722         g_object_class_install_property
2723                 (object_class, PROP_VISIBLE,
2724                  g_param_spec_boolean (NM_SETTINGS_CONNECTION_VISIBLE, "", "",
2725                                        FALSE,
2726                                        G_PARAM_READABLE |
2727                                        G_PARAM_STATIC_STRINGS));
2728
2729         g_object_class_install_property
2730                 (object_class, PROP_UNSAVED,
2731                  g_param_spec_boolean (NM_SETTINGS_CONNECTION_UNSAVED, "", "",
2732                                        FALSE,
2733                                        G_PARAM_READABLE |
2734                                        G_PARAM_STATIC_STRINGS));
2735
2736         g_object_class_install_property
2737                 (object_class, PROP_READY,
2738                  g_param_spec_boolean (NM_SETTINGS_CONNECTION_READY, "", "",
2739                                        TRUE,
2740                                        G_PARAM_READWRITE |
2741                                        G_PARAM_STATIC_STRINGS));
2742
2743         g_object_class_install_property
2744             (object_class, PROP_FLAGS,
2745              g_param_spec_uint (NM_SETTINGS_CONNECTION_FLAGS, "", "",
2746                                 NM_SETTINGS_CONNECTION_FLAGS_NONE,
2747                                 NM_SETTINGS_CONNECTION_FLAGS_ALL,
2748                                 NM_SETTINGS_CONNECTION_FLAGS_NONE,
2749                                 G_PARAM_READWRITE |
2750                                 G_PARAM_STATIC_STRINGS));
2751
2752         g_object_class_install_property
2753                 (object_class, PROP_FILENAME,
2754                  g_param_spec_string (NM_SETTINGS_CONNECTION_FILENAME, "", "",
2755                                       NULL,
2756                                       G_PARAM_READWRITE |
2757                                       G_PARAM_STATIC_STRINGS));
2758
2759         /* Signals */
2760
2761         /* Emitted when the connection is changed for any reason */
2762         signals[UPDATED] =
2763                 g_signal_new (NM_SETTINGS_CONNECTION_UPDATED,
2764                               G_TYPE_FROM_CLASS (class),
2765                               G_SIGNAL_RUN_FIRST,
2766                               0,
2767                               NULL, NULL,
2768                               g_cclosure_marshal_VOID__VOID,
2769                               G_TYPE_NONE, 0);
2770
2771         /* Emitted when connection is changed from D-Bus */
2772         signals[UPDATED_BY_USER] =
2773                 g_signal_new (NM_SETTINGS_CONNECTION_UPDATED_BY_USER,
2774                               G_TYPE_FROM_CLASS (class),
2775                               G_SIGNAL_RUN_FIRST,
2776                               0, NULL, NULL,
2777                               g_cclosure_marshal_VOID__VOID,
2778                               G_TYPE_NONE, 0);
2779
2780         signals[REMOVED] = 
2781                 g_signal_new (NM_SETTINGS_CONNECTION_REMOVED,
2782                               G_TYPE_FROM_CLASS (class),
2783                               G_SIGNAL_RUN_FIRST,
2784                               0,
2785                               NULL, NULL,
2786                               g_cclosure_marshal_VOID__VOID,
2787                               G_TYPE_NONE, 0);
2788
2789         nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (class),
2790                                                 NMDBUS_TYPE_SETTINGS_CONNECTION_SKELETON,
2791                                                 "Update", impl_settings_connection_update,
2792                                                 "UpdateUnsaved", impl_settings_connection_update_unsaved,
2793                                                 "Delete", impl_settings_connection_delete,
2794                                                 "GetSettings", impl_settings_connection_get_settings,
2795                                                 "GetSecrets", impl_settings_connection_get_secrets,
2796                                                 "ClearSecrets", impl_settings_connection_clear_secrets,
2797                                                 "Save", impl_settings_connection_save,
2798                                                 NULL);
2799 }
2800
2801 static void
2802 nm_settings_connection_connection_interface_init (NMConnectionInterface *iface)
2803 {
2804 }
2805