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 * (C) Copyright 2008 Novell, Inc.
19 * (C) Copyright 2008 - 2013 Red Hat, Inc.
20 */
21
22 #include "config.h"
23
24 #include <string.h>
25 #include <netinet/ether.h>
26
27 #include <NetworkManager.h>
28 #include <dbus/dbus-glib-lowlevel.h>
29 #include <nm-setting-connection.h>
30 #include <nm-setting-vpn.h>
31 #include <nm-setting-wireless.h>
32 #include <nm-utils.h>
33
34 #include "nm-settings-connection.h"
35 #include "nm-session-monitor.h"
36 #include "nm-dbus-manager.h"
37 #include "nm-settings-error.h"
38 #include "nm-dbus-glib-types.h"
39 #include "nm-logging.h"
40 #include "nm-manager-auth.h"
41 #include "nm-agent-manager.h"
42 #include "NetworkManagerUtils.h"
43 #include "nm-properties-changed-signal.h"
44
45 #define SETTINGS_TIMESTAMPS_FILE NMSTATEDIR "/timestamps"
46 #define SETTINGS_SEEN_BSSIDS_FILE NMSTATEDIR "/seen-bssids"
47
48 static void impl_settings_connection_get_settings (NMSettingsConnection *connection,
49 DBusGMethodInvocation *context);
50
51 static void impl_settings_connection_update (NMSettingsConnection *connection,
52 GHashTable *new_settings,
53 DBusGMethodInvocation *context);
54
55 static void impl_settings_connection_update_unsaved (NMSettingsConnection *connection,
56 GHashTable *new_settings,
57 DBusGMethodInvocation *context);
58
59 static void impl_settings_connection_save (NMSettingsConnection *connection,
60 DBusGMethodInvocation *context);
61
62 static void impl_settings_connection_delete (NMSettingsConnection *connection,
63 DBusGMethodInvocation *context);
64
65 static void impl_settings_connection_get_secrets (NMSettingsConnection *connection,
66 const gchar *setting_name,
67 DBusGMethodInvocation *context);
68
69 #include "nm-settings-connection-glue.h"
70
71 G_DEFINE_TYPE (NMSettingsConnection, nm_settings_connection, NM_TYPE_CONNECTION)
72
73 #define NM_SETTINGS_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
74 NM_TYPE_SETTINGS_CONNECTION, \
75 NMSettingsConnectionPrivate))
76
77 enum {
78 PROP_0 = 0,
79 PROP_VISIBLE,
80 PROP_UNSAVED,
81 };
82
83 enum {
84 UPDATED,
85 REMOVED,
86 LAST_SIGNAL
87 };
88 static guint signals[LAST_SIGNAL] = { 0 };
89
90 typedef struct {
91 gboolean disposed;
92
93 NMAgentManager *agent_mgr;
94 NMSessionMonitor *session_monitor;
95 guint session_changed_id;
96
97 /* TRUE if the connection has not yet been saved to disk,
98 * or it it contains changes that have not been saved to disk.
99 */
100 gboolean unsaved;
101
102 guint updated_idle_id;
103
104 GSList *pending_auths; /* List of pending authentication requests */
105 gboolean visible; /* Is this connection is visible by some session? */
106 GSList *reqs; /* in-progress secrets requests */
107
108 /* Caches secrets from on-disk connections; were they not cached any
109 * call to nm_connection_clear_secrets() wipes them out and we'd have
110 * to re-read them from disk which defeats the purpose of having the
111 * connection in-memory at all.
112 */
113 NMConnection *system_secrets;
114
115 /* Caches secrets from agents during the activation process; if new system
116 * secrets are returned from an agent, they get written out to disk,
117 * triggering a re-read of the connection, which reads only system
118 * secrets, and would wipe out any agent-owned or not-saved secrets the
119 * agent also returned.
120 */
121 NMConnection *agent_secrets;
122
123 guint64 timestamp; /* Up-to-date timestamp of connection use */
124 gboolean timestamp_set;
125 GHashTable *seen_bssids; /* Up-to-date BSSIDs that's been seen for the connection */
126 } NMSettingsConnectionPrivate;
127
128 /**************************************************************/
129
130 /* Return TRUE to continue, FALSE to stop */
131 typedef gboolean (*ForEachSecretFunc) (GHashTableIter *iter,
132 NMSettingSecretFlags flags,
133 gpointer user_data);
134
135 static void
136 for_each_secret (NMConnection *connection,
137 GHashTable *secrets,
138 ForEachSecretFunc callback,
139 gpointer callback_data)
140 {
141 GHashTableIter iter;
142 const char *setting_name;
143 GHashTable *setting_hash;
144
145 /* This function, given a hash of hashes representing new secrets of
146 * an NMConnection, walks through each toplevel hash (which represents a
147 * NMSetting), and for each setting, walks through that setting hash's
148 * properties. For each property that's a secret, it will check that
149 * secret's flags in the backing NMConnection object, and call a supplied
150 * callback.
151 *
152 * The one complexity is that the VPN setting's 'secrets' property is
153 * *also* a hash table (since the key/value pairs are arbitrary and known
154 * only to the VPN plugin itself). That means we have three levels of
155 * GHashTables that we potentially have to traverse here. When we hit the
156 * VPN setting's 'secrets' property, we special-case that and iterate over
157 * each item in that 'secrets' hash table, calling the supplied callback
158 * each time.
159 */
160
161 /* Walk through the list of setting hashes */
162 g_hash_table_iter_init (&iter, secrets);
163 while (g_hash_table_iter_next (&iter, (gpointer) &setting_name, (gpointer) &setting_hash)) {
164 NMSetting *setting;
165 GHashTableIter secret_iter;
166 const char *secret_name;
167 GValue *val;
168
169 /* Get the actual NMSetting from the connection so we can get secret flags
170 * from the connection data, since flags aren't secrets. What we're
171 * iterating here is just the secrets, not a whole connection.
172 */
173 setting = nm_connection_get_setting_by_name (connection, setting_name);
174 if (setting == NULL)
175 continue;
176
177 /* Walk through the list of keys in each setting hash */
178 g_hash_table_iter_init (&secret_iter, setting_hash);
179 while (g_hash_table_iter_next (&secret_iter, (gpointer) &secret_name, (gpointer) &val)) {
180 NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
181
182 /* VPN secrets need slightly different treatment here since the
183 * "secrets" property is actually a hash table of secrets.
184 */
185 if (NM_IS_SETTING_VPN (setting) && (g_strcmp0 (secret_name, NM_SETTING_VPN_SECRETS) == 0)) {
186 GHashTableIter vpn_secrets_iter;
187
188 /* Iterate through each secret from the VPN hash in the overall secrets hash */
189 g_hash_table_iter_init (&vpn_secrets_iter, g_value_get_boxed (val));
190 while (g_hash_table_iter_next (&vpn_secrets_iter, (gpointer) &secret_name, NULL)) {
191 secret_flags = NM_SETTING_SECRET_FLAG_NONE;
192 nm_setting_get_secret_flags (setting, secret_name, &secret_flags, NULL);
193 if (callback (&vpn_secrets_iter, secret_flags, callback_data) == FALSE)
194 return;
195 }
196 } else {
197 nm_setting_get_secret_flags (setting, secret_name, &secret_flags, NULL);
198 if (callback (&secret_iter, secret_flags, callback_data) == FALSE)
199 return;
200 }
201 }
202 }
203 }
204
205 /**************************************************************/
206
207 static void
208 set_visible (NMSettingsConnection *self, gboolean new_visible)
209 {
210 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
211
212 if (new_visible == priv->visible)
213 return;
214 priv->visible = new_visible;
215 g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTION_VISIBLE);
216 }
217
218 gboolean
219 nm_settings_connection_is_visible (NMSettingsConnection *self)
220 {
221 g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
222
223 return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->visible;
224 }
225
226 void
227 nm_settings_connection_recheck_visibility (NMSettingsConnection *self)
228 {
229 NMSettingsConnectionPrivate *priv;
230 NMSettingConnection *s_con;
231 guint32 num, i;
232
233 g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
234
235 priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
236
237 s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
238 g_assert (s_con);
239
240 /* Check every user in the ACL for a session */
241 num = nm_setting_connection_get_num_permissions (s_con);
242 if (num == 0) {
243 /* Visible to all */
244 set_visible (self, TRUE);
245 return;
246 }
247
248 for (i = 0; i < num; i++) {
249 const char *puser;
250
251 if (nm_setting_connection_get_permission (s_con, i, NULL, &puser, NULL)) {
252 if (nm_session_monitor_user_has_session (priv->session_monitor, puser, NULL, NULL)) {
253 set_visible (self, TRUE);
254 return;
255 }
256 }
257 }
258
259 set_visible (self, FALSE);
260 }
261
262 static void
263 session_changed_cb (NMSessionMonitor *self, gpointer user_data)
264 {
265 nm_settings_connection_recheck_visibility (NM_SETTINGS_CONNECTION (user_data));
266 }
267
268 /**************************************************************/
269
270 /* Return TRUE if any active user in the connection's ACL has the given
271 * permission without having to authorize for it via PolicyKit. Connections
272 * visible to everyone automatically pass the check.
273 */
274 gboolean
275 nm_settings_connection_check_permission (NMSettingsConnection *self,
276 const char *permission)
277 {
278 NMSettingsConnectionPrivate *priv;
279 NMSettingConnection *s_con;
280 guint32 num, i;
281 const char *puser;
282
283 g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
284
285 priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
286
287 if (priv->visible == FALSE)
288 return FALSE;
289
290 s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
291 g_assert (s_con);
292
293 /* Check every user in the ACL for a session */
294 num = nm_setting_connection_get_num_permissions (s_con);
295 if (num == 0) {
296 /* Visible to all so it's OK to auto-activate */
297 return TRUE;
298 }
299
300 for (i = 0; i < num; i++) {
301 /* For each user get their secret agent and check if that agent has the
302 * required permission.
303 *
304 * FIXME: what if the user isn't running an agent? PolKit needs a bus
305 * name or a PID but if the user isn't running an agent they won't have
306 * either.
307 */
308 if (nm_setting_connection_get_permission (s_con, i, NULL, &puser, NULL)) {
309 NMSecretAgent *agent = nm_agent_manager_get_agent_by_user (priv->agent_mgr, puser);
310
311 if (agent && nm_secret_agent_has_permission (agent, permission))
312 return TRUE;
313 }
314 }
315
316 return FALSE;
317 }
318
319 /**************************************************************/
320
321 static gboolean
322 secrets_filter_cb (NMSetting *setting,
323 const char *secret,
324 NMSettingSecretFlags flags,
325 gpointer user_data)
326 {
327 NMSettingSecretFlags filter_flags = GPOINTER_TO_UINT (user_data);
328
329 /* Returns TRUE to remove the secret */
330
331 /* Can't use bitops with SECRET_FLAG_NONE so handle that specifically */
332 if ( (flags == NM_SETTING_SECRET_FLAG_NONE)
333 && (filter_flags == NM_SETTING_SECRET_FLAG_NONE))
334 return FALSE;
335
336 /* Otherwise if the secret has at least one of the desired flags keep it */
337 return (flags & filter_flags) ? FALSE : TRUE;
338 }
339
340 static void
341 update_system_secrets_cache (NMSettingsConnection *self)
342 {
343 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
344
345 if (priv->system_secrets)
346 g_object_unref (priv->system_secrets);
347 priv->system_secrets = nm_connection_duplicate (NM_CONNECTION (self));
348
349 /* Clear out non-system-owned and not-saved secrets */
350 nm_connection_clear_secrets_with_flags (priv->system_secrets,
351 secrets_filter_cb,
352 GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_NONE));
353 }
354
355 static void
356 update_agent_secrets_cache (NMSettingsConnection *self, NMConnection *new)
357 {
358 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
359 NMSettingSecretFlags filter_flags = NM_SETTING_SECRET_FLAG_NOT_SAVED | NM_SETTING_SECRET_FLAG_AGENT_OWNED;
360
361 if (priv->agent_secrets)
362 g_object_unref (priv->agent_secrets);
363 priv->agent_secrets = nm_connection_duplicate (new ? new : NM_CONNECTION (self));
364
365 /* Clear out non-system-owned secrets */
366 nm_connection_clear_secrets_with_flags (priv->agent_secrets,
367 secrets_filter_cb,
368 GUINT_TO_POINTER (filter_flags));
369 }
370
371 static void
372 secrets_cleared_cb (NMSettingsConnection *self)
373 {
374 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
375
376 /* Clear agent secrets when connection's secrets are cleared since agent
377 * secrets are transient.
378 */
379 if (priv->agent_secrets)
380 g_object_unref (priv->agent_secrets);
381 priv->agent_secrets = NULL;
382 }
383
384 static gboolean
385 emit_updated (NMSettingsConnection *self)
386 {
387 NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->updated_idle_id = 0;
388 g_signal_emit (self, signals[UPDATED], 0);
389 return FALSE;
390 }
391
392 static void
393 set_unsaved (NMSettingsConnection *self, gboolean now_unsaved)
394 {
395 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
396
397 if (priv->unsaved != now_unsaved) {
398 priv->unsaved = now_unsaved;
399 g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTION_UNSAVED);
400 }
401 }
402
403 static void
404 changed_cb (NMSettingsConnection *self, gpointer user_data)
405 {
406 gboolean update_unsaved = !!user_data;
407
408 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
409
410 if (update_unsaved)
411 set_unsaved (self, TRUE);
412 if (priv->updated_idle_id == 0)
413 priv->updated_idle_id = g_idle_add ((GSourceFunc) emit_updated, self);
414 }
415
416 /* Update the settings of this connection to match that of 'new_connection',
417 * taking care to make a private copy of secrets.
418 */
419 gboolean
420 nm_settings_connection_replace_settings (NMSettingsConnection *self,
421 NMConnection *new_connection,
422 gboolean update_unsaved,
423 GError **error)
424 {
425 NMSettingsConnectionPrivate *priv;
426 GHashTable *hash = NULL;
427 gboolean success = FALSE;
428
429 g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
430 g_return_val_if_fail (NM_IS_CONNECTION (new_connection), FALSE);
431
432 priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
433
434 nm_utils_normalize_connection (new_connection, TRUE);
435 if (!nm_connection_verify (new_connection, error))
436 return FALSE;
437
438 /* Do nothing if there's nothing to update */
439 if (nm_connection_compare (NM_CONNECTION (self),
440 new_connection,
441 NM_SETTING_COMPARE_FLAG_EXACT)) {
442 return TRUE;
443 }
444
445 /* Disconnect the changed signal to ensure we don't set Unsaved when
446 * it's not required.
447 */
448 g_signal_handlers_block_by_func (self, G_CALLBACK (changed_cb), GUINT_TO_POINTER (TRUE));
449
450 if (nm_connection_replace_settings_from_connection (NM_CONNECTION (self),
451 new_connection,
452 error)) {
453 /* Cache the just-updated system secrets in case something calls
454 * nm_connection_clear_secrets() and clears them.
455 */
456 update_system_secrets_cache (self);
457 success = TRUE;
458
459 /* Add agent and always-ask secrets back; they won't necessarily be
460 * in the replacement connection data if it was eg reread from disk.
461 */
462 if (priv->agent_secrets) {
463 hash = nm_connection_to_hash (priv->agent_secrets, NM_SETTING_HASH_FLAG_ONLY_SECRETS);
464 if (hash) {
465 success = nm_connection_update_secrets (NM_CONNECTION (self), NULL, hash, error);
466 g_hash_table_destroy (hash);
467 }
468 }
469
470 nm_settings_connection_recheck_visibility (self);
471
472 /* Manually emit changed signal since we disconnected the handler, but
473 * only update Unsaved if the caller wanted us to.
474 */
475 changed_cb (self, GUINT_TO_POINTER (update_unsaved));
476 }
477
478 g_signal_handlers_unblock_by_func (self, G_CALLBACK (changed_cb), GUINT_TO_POINTER (TRUE));
479
480 return success;
481 }
482
483 static void
484 ignore_cb (NMSettingsConnection *connection,
485 GError *error,
486 gpointer user_data)
487 {
488 }
489
490 /* Replaces the settings in this connection with those in 'new_connection'. If
491 * any changes are made, commits them to permanent storage and to any other
492 * subsystems watching this connection. Before returning, 'callback' is run
493 * with the given 'user_data' along with any errors encountered.
494 */
495 void
496 nm_settings_connection_replace_and_commit (NMSettingsConnection *self,
497 NMConnection *new_connection,
498 NMSettingsConnectionCommitFunc callback,
499 gpointer user_data)
500 {
501 GError *error = NULL;
502
503 g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
504 g_return_if_fail (NM_IS_CONNECTION (new_connection));
505
506 if (nm_settings_connection_replace_settings (self, new_connection, TRUE, &error)) {
507 nm_settings_connection_commit_changes (self, callback, user_data);
508 } else {
509 if (callback)
510 callback (self, error, user_data);
511 g_clear_error (&error);
512 }
513 }
514
515 static void
516 commit_changes (NMSettingsConnection *self,
517 NMSettingsConnectionCommitFunc callback,
518 gpointer user_data)
519 {
520 /* Subclasses only call this function if the save was successful, so at
521 * this point the connection is synced to disk and no longer unsaved.
522 */
523 set_unsaved (self, FALSE);
524
525 g_object_ref (self);
526 callback (self, NULL, user_data);
527 g_object_unref (self);
528 }
529
530 void
531 nm_settings_connection_commit_changes (NMSettingsConnection *connection,
532 NMSettingsConnectionCommitFunc callback,
533 gpointer user_data)
534 {
535 g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
536
537 if (NM_SETTINGS_CONNECTION_GET_CLASS (connection)->commit_changes) {
538 NM_SETTINGS_CONNECTION_GET_CLASS (connection)->commit_changes (connection,
539 callback ? callback : ignore_cb,
540 user_data);
541 } else {
542 GError *error = g_error_new (NM_SETTINGS_ERROR,
543 NM_SETTINGS_ERROR_INTERNAL_ERROR,
544 "%s: %s:%d commit_changes() unimplemented", __func__, __FILE__, __LINE__);
545 if (callback)
546 callback (connection, error, user_data);
547 g_error_free (error);
548 }
549 }
550
551 void
552 nm_settings_connection_delete (NMSettingsConnection *connection,
553 NMSettingsConnectionDeleteFunc callback,
554 gpointer user_data)
555 {
556 g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
557
558 if (NM_SETTINGS_CONNECTION_GET_CLASS (connection)->delete) {
559 NM_SETTINGS_CONNECTION_GET_CLASS (connection)->delete (connection,
560 callback ? callback : ignore_cb,
561 user_data);
562 } else {
563 GError *error = g_error_new (NM_SETTINGS_ERROR,
564 NM_SETTINGS_ERROR_INTERNAL_ERROR,
565 "%s: %s:%d delete() unimplemented", __func__, __FILE__, __LINE__);
566 if (callback)
567 callback (connection, error, user_data);
568 g_error_free (error);
569 }
570 }
571
572 static void
573 remove_entry_from_db (NMSettingsConnection *connection, const char* db_name)
574 {
575 GKeyFile *key_file;
576 const char *db_file;
577
578 if (strcmp (db_name, "timestamps") == 0)
579 db_file = SETTINGS_TIMESTAMPS_FILE;
580 else if (strcmp (db_name, "seen-bssids") == 0)
581 db_file = SETTINGS_SEEN_BSSIDS_FILE;
582 else
583 return;
584
585 key_file = g_key_file_new ();
586 if (g_key_file_load_from_file (key_file, db_file, G_KEY_FILE_KEEP_COMMENTS, NULL)) {
587 const char *connection_uuid;
588 char *data;
589 gsize len;
590 GError *error = NULL;
591
592 connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection));
593
594 g_key_file_remove_key (key_file, db_name, connection_uuid, NULL);
595 data = g_key_file_to_data (key_file, &len, &error);
596 if (data) {
597 g_file_set_contents (db_file, data, len, &error);
598 g_free (data);
599 }
600 if (error) {
601 nm_log_warn (LOGD_SETTINGS, "error writing %s file '%s': %s", db_name, db_file, error->message);
602 g_error_free (error);
603 }
604 }
605 g_key_file_free (key_file);
606 }
607
608 static void
609 do_delete (NMSettingsConnection *connection,
610 NMSettingsConnectionDeleteFunc callback,
611 gpointer user_data)
612 {
613 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
614 NMConnection *for_agents;
615
616 g_object_ref (connection);
617 set_visible (connection, FALSE);
618
619 /* Tell agents to remove secrets for this connection */
620 for_agents = nm_connection_duplicate (NM_CONNECTION (connection));
621 nm_connection_clear_secrets (for_agents);
622 nm_agent_manager_delete_secrets (priv->agent_mgr, for_agents, FALSE, 0);
623 g_object_unref (for_agents);
624
625 /* Remove timestamp from timestamps database file */
626 remove_entry_from_db (connection, "timestamps");
627
628 /* Remove connection from seen-bssids database file */
629 remove_entry_from_db (connection, "seen-bssids");
630
631 nm_settings_connection_signal_remove (connection);
632
633 callback (connection, NULL, user_data);
634
635 g_object_unref (connection);
636 }
637
638 /**************************************************************/
639
640 static gboolean
641 supports_secrets (NMSettingsConnection *connection, const char *setting_name)
642 {
643 /* All secrets supported */
644 return TRUE;
645 }
646
647 static gboolean
648 clear_nonagent_secrets (GHashTableIter *iter,
649 NMSettingSecretFlags flags,
650 gpointer user_data)
651 {
652 if (flags != NM_SETTING_SECRET_FLAG_AGENT_OWNED)
653 g_hash_table_iter_remove (iter);
654 return TRUE;
655 }
656
657 static gboolean
658 clear_unsaved_secrets (GHashTableIter *iter,
659 NMSettingSecretFlags flags,
660 gpointer user_data)
661 {
662 if (flags & (NM_SETTING_SECRET_FLAG_NOT_SAVED | NM_SETTING_SECRET_FLAG_NOT_REQUIRED))
663 g_hash_table_iter_remove (iter);
664 return TRUE;
665 }
666
667 static gboolean
668 has_system_owned_secrets (GHashTableIter *iter,
669 NMSettingSecretFlags flags,
670 gpointer user_data)
671 {
672 gboolean *has_system_owned = user_data;
673
674 if (flags == NM_SETTING_SECRET_FLAG_NONE) {
675 *has_system_owned = TRUE;
676 return FALSE;
677 }
678 return TRUE;
679 }
680
681 static void
682 new_secrets_commit_cb (NMSettingsConnection *connection,
683 GError *error,
684 gpointer user_data)
685 {
686 if (error) {
687 nm_log_warn (LOGD_SETTINGS, "Error saving new secrets to backing storage: (%d) %s",
688 error->code, error->message ? error->message : "(unknown)");
689 }
690 }
691
692 static void
693 agent_secrets_done_cb (NMAgentManager *manager,
694 guint32 call_id,
695 const char *agent_dbus_owner,
696 const char *agent_username,
697 gboolean agent_has_modify,
698 const char *setting_name,
699 NMSettingsGetSecretsFlags flags,
700 GHashTable *secrets,
701 GError *error,
702 gpointer user_data,
703 gpointer other_data2,
704 gpointer other_data3)
705 {
706 NMSettingsConnection *self = NM_SETTINGS_CONNECTION (user_data);
707 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
708 NMSettingsConnectionSecretsFunc callback = other_data2;
709 gpointer callback_data = other_data3;
710 GError *local = NULL;
711 GHashTable *hash;
712 gboolean agent_had_system = FALSE;
713
714 if (error) {
715 nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) secrets request error: (%d) %s",
716 nm_connection_get_uuid (NM_CONNECTION (self)),
717 setting_name,
718 call_id,
719 error->code,
720 error->message ? error->message : "(unknown)");
721
722 callback (self, call_id, NULL, setting_name, error, callback_data);
723 return;
724 }
725
726 if (!nm_connection_get_setting_by_name (NM_CONNECTION (self), setting_name)) {
727 local = g_error_new (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_SETTING,
728 "%s.%d - Connection didn't have requested setting '%s'.",
729 __FILE__, __LINE__, setting_name);
730 callback (self, call_id, NULL, setting_name, local, callback_data);
731 g_clear_error (&local);
732 return;
733 }
734
735 g_assert (secrets);
736 if (agent_dbus_owner) {
737 nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) secrets returned from agent %s",
738 nm_connection_get_uuid (NM_CONNECTION (self)),
739 setting_name,
740 call_id,
741 agent_dbus_owner);
742
743 /* If the agent returned any system-owned secrets (initial connect and no
744 * secrets given when the connection was created, or something like that)
745 * make sure the agent's UID has the 'modify' permission before we use or
746 * save those system-owned secrets. If not, discard them and use the
747 * existing secrets, or fail the connection.
748 */
749 for_each_secret (NM_CONNECTION (self), secrets, has_system_owned_secrets, &agent_had_system);
750 if (agent_had_system) {
751 if (flags == NM_SETTINGS_GET_SECRETS_FLAG_NONE) {
752 /* No user interaction was allowed when requesting secrets; the
753 * agent is being bad. Remove system-owned secrets.
754 */
755 nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) interaction forbidden but agent %s returned system secrets",
756 nm_connection_get_uuid (NM_CONNECTION (self)),
757 setting_name,
758 call_id,
759 agent_dbus_owner);
760
761 for_each_secret (NM_CONNECTION (self), secrets, clear_nonagent_secrets, NULL);
762 } else if (agent_has_modify == FALSE) {
763 /* Agent didn't successfully authenticate; clear system-owned secrets
764 * from the secrets the agent returned.
765 */
766 nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) agent failed to authenticate but provided system secrets",
767 nm_connection_get_uuid (NM_CONNECTION (self)),
768 setting_name,
769 call_id);
770
771 for_each_secret (NM_CONNECTION (self), secrets, clear_nonagent_secrets, NULL);
772 }
773 }
774 } else {
775 nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) existing secrets returned",
776 nm_connection_get_uuid (NM_CONNECTION (self)),
777 setting_name,
778 call_id);
779 }
780
781 nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) secrets request completed",
782 nm_connection_get_uuid (NM_CONNECTION (self)),
783 setting_name,
784 call_id);
785
786 /* If no user interaction was allowed, make sure that no "unsaved" secrets
787 * came back. Unsaved secrets by definition require user interaction.
788 */
789 if (flags == NM_SETTINGS_GET_SECRETS_FLAG_NONE)
790 for_each_secret (NM_CONNECTION (self), secrets, clear_unsaved_secrets, NULL);
791
792 /* Update the connection with our existing secrets from backing storage */
793 nm_connection_clear_secrets (NM_CONNECTION (self));
794 hash = nm_connection_to_hash (priv->system_secrets, NM_SETTING_HASH_FLAG_ONLY_SECRETS);
795 if (!hash || nm_connection_update_secrets (NM_CONNECTION (self), setting_name, hash, &local)) {
796 /* Update the connection with the agent's secrets; by this point if any
797 * system-owned secrets exist in 'secrets' the agent that provided them
798 * will have been authenticated, so those secrets can replace the existing
799 * system secrets.
800 */
801 if (nm_connection_update_secrets (NM_CONNECTION (self), setting_name, secrets, &local)) {
802 /* Now that all secrets are updated, copy and cache new secrets,
803 * then save them to backing storage.
804 */
805 update_system_secrets_cache (self);
806 update_agent_secrets_cache (self, NULL);
807
808 /* Only save secrets to backing storage if the agent returned any
809 * new system secrets. If it didn't, then the secrets are agent-
810 * owned and there's no point to writing out the connection when
811 * nothing has changed, since agent-owned secrets don't get saved here.
812 */
813 if (agent_had_system) {
814 nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) saving new secrets to backing storage",
815 nm_connection_get_uuid (NM_CONNECTION (self)),
816 setting_name,
817 call_id);
818
819 nm_settings_connection_commit_changes (self, new_secrets_commit_cb, NULL);
820 } else {
821 nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) new agent secrets processed",
822 nm_connection_get_uuid (NM_CONNECTION (self)),
823 setting_name,
824 call_id);
825 }
826 } else {
827 nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) failed to update with agent secrets: (%d) %s",
828 nm_connection_get_uuid (NM_CONNECTION (self)),
829 setting_name,
830 call_id,
831 local ? local->code : -1,
832 (local && local->message) ? local->message : "(unknown)");
833 }
834 } else {
835 nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) failed to update with existing secrets: (%d) %s",
836 nm_connection_get_uuid (NM_CONNECTION (self)),
837 setting_name,
838 call_id,
839 local ? local->code : -1,
840 (local && local->message) ? local->message : "(unknown)");
841 }
842
843 callback (self, call_id, agent_username, setting_name, local, callback_data);
844 g_clear_error (&local);
845 if (hash)
846 g_hash_table_destroy (hash);
847 }
848
849 /**
850 * nm_settings_connection_get_secrets:
851 * @connection: the #NMSettingsConnection
852 * @filter_by_uid: if TRUE, only request secrets from agents registered by the
853 * same UID as @uid.
854 * @uid: when @filter_by_uid is TRUE, only request secrets from agents belonging
855 * to this UID
856 * @setting_name: the setting to return secrets for
857 * @flags: flags to modify the secrets request
858 * @hints: key names in @setting_name for which secrets may be required, or some
859 * other information about the request
860 * @callback: the function to call with returned secrets
861 * @callback_data: user data to pass to @callback
862 *
863 * Retrieves secrets from persistent storage and queries any secret agents for
864 * additional secrets.
865 *
866 * Returns: a call ID which may be used to cancel the ongoing secrets request
867 **/
868 guint32
869 nm_settings_connection_get_secrets (NMSettingsConnection *self,
870 gboolean filter_by_uid,
871 gulong uid,
872 const char *setting_name,
873 NMSettingsGetSecretsFlags flags,
874 const char **hints,
875 NMSettingsConnectionSecretsFunc callback,
876 gpointer callback_data,
877 GError **error)
878 {
879 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
880 GHashTable *existing_secrets;
881 guint32 call_id = 0;
882 char *joined_hints = NULL;
883
884 /* Use priv->secrets to work around the fact that nm_connection_clear_secrets()
885 * will clear secrets on this object's settings.
886 */
887 if (!priv->system_secrets) {
888 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
889 "%s.%d - Internal error; secrets cache invalid.",
890 __FILE__, __LINE__);
891 return 0;
892 }
893
894 /* Make sure the request actually requests something we can return */
895 if (!nm_connection_get_setting_by_name (NM_CONNECTION (self), setting_name)) {
896 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_SETTING,
897 "%s.%d - Connection didn't have requested setting '%s'.",
898 __FILE__, __LINE__, setting_name);
899 return 0;
900 }
901
902 existing_secrets = nm_connection_to_hash (priv->system_secrets, NM_SETTING_HASH_FLAG_ONLY_SECRETS);
903 call_id = nm_agent_manager_get_secrets (priv->agent_mgr,
904 NM_CONNECTION (self),
905 filter_by_uid,
906 uid,
907 existing_secrets,
908 setting_name,
909 flags,
910 hints,
911 agent_secrets_done_cb,
912 self,
913 callback,
914 callback_data);
915 if (existing_secrets)
916 g_hash_table_unref (existing_secrets);
917
918 if (nm_logging_level_enabled (LOGL_DEBUG)) {
919 if (hints)
920 joined_hints = g_strjoinv (",", (char **) hints);
921 nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) secrets requested flags 0x%X hints '%s'",
922 nm_connection_get_uuid (NM_CONNECTION (self)),
923 setting_name,
924 call_id,
925 flags,
926 joined_hints ? joined_hints : "(none)");
927 g_free (joined_hints);
928 }
929
930 return call_id;
931 }
932
933 void
934 nm_settings_connection_cancel_secrets (NMSettingsConnection *self,
935 guint32 call_id)
936 {
937 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
938
939 nm_log_dbg (LOGD_SETTINGS, "(%s:%u) secrets canceled",
940 nm_connection_get_uuid (NM_CONNECTION (self)),
941 call_id);
942
943 priv->reqs = g_slist_remove (priv->reqs, GUINT_TO_POINTER (call_id));
944 nm_agent_manager_cancel_secrets (priv->agent_mgr, call_id);
945 }
946
947 /**** User authorization **************************************/
948
949 typedef void (*AuthCallback) (NMSettingsConnection *connection,
950 DBusGMethodInvocation *context,
951 gulong sender_uid,
952 GError *error,
953 gpointer data);
954
955 static void
956 pk_auth_cb (NMAuthChain *chain,
957 GError *chain_error,
958 DBusGMethodInvocation *context,
959 gpointer user_data)
960 {
961 NMSettingsConnection *self = NM_SETTINGS_CONNECTION (user_data);
962 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
963 GError *error = NULL;
964 NMAuthCallResult result;
965 const char *perm;
966 AuthCallback callback;
967 gpointer callback_data;
968 gulong sender_uid;
969
970 priv->pending_auths = g_slist_remove (priv->pending_auths, chain);
971
972 perm = nm_auth_chain_get_data (chain, "perm");
973 g_assert (perm);
974 result = nm_auth_chain_get_result (chain, perm);
975
976 /* If our NMSettingsConnection is already gone, do nothing */
977 if (chain_error) {
978 error = g_error_new (NM_SETTINGS_ERROR,
979 NM_SETTINGS_ERROR_GENERAL,
980 "Error checking authorization: %s",
981 chain_error->message ? chain_error->message : "(unknown)");
982 } else if (result != NM_AUTH_CALL_RESULT_YES) {
983 error = g_error_new_literal (NM_SETTINGS_ERROR,
984 NM_SETTINGS_ERROR_PERMISSION_DENIED,
985 "Insufficient privileges.");
986 }
987
988 callback = nm_auth_chain_get_data (chain, "callback");
989 callback_data = nm_auth_chain_get_data (chain, "callback-data");
990 sender_uid = nm_auth_chain_get_data_ulong (chain, "sender-uid");
991 callback (self, context, sender_uid, error, callback_data);
992
993 g_clear_error (&error);
994 nm_auth_chain_unref (chain);
995 }
996
997 static gboolean
998 check_user_in_acl (NMConnection *connection,
999 DBusGMethodInvocation *context,
1000 NMSessionMonitor *session_monitor,
1001 gulong *out_sender_uid,
1002 GError **error)
1003 {
1004 gulong sender_uid = G_MAXULONG;
1005 char *error_desc = NULL;
1006
1007 g_return_val_if_fail (connection != NULL, FALSE);
1008 g_return_val_if_fail (context != NULL, FALSE);
1009 g_return_val_if_fail (session_monitor != NULL, FALSE);
1010
1011 /* Get the caller's UID */
1012 if (!nm_dbus_manager_get_caller_info (nm_dbus_manager_get (), context, NULL, &sender_uid)) {
1013 g_set_error_literal (error,
1014 NM_SETTINGS_ERROR,
1015 NM_SETTINGS_ERROR_PERMISSION_DENIED,
1016 "Unable to determine UID of request.");
1017 return FALSE;
1018 }
1019
1020 /* Make sure the UID can view this connection */
1021 if (!nm_auth_uid_in_acl (connection, session_monitor, sender_uid, &error_desc)) {
1022 g_set_error_literal (error,
1023 NM_SETTINGS_ERROR,
1024 NM_SETTINGS_ERROR_PERMISSION_DENIED,
1025 error_desc);
1026 g_free (error_desc);
1027 return FALSE;
1028 }
1029
1030 if (out_sender_uid)
1031 *out_sender_uid = sender_uid;
1032 return TRUE;
1033 }
1034
1035 static void
1036 auth_start (NMSettingsConnection *self,
1037 DBusGMethodInvocation *context,
1038 const char *check_permission,
1039 AuthCallback callback,
1040 gpointer callback_data)
1041 {
1042 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
1043 NMAuthChain *chain;
1044 gulong sender_uid = G_MAXULONG;
1045 GError *error = NULL;
1046 const char *error_desc = NULL;
1047
1048 if (!check_user_in_acl (NM_CONNECTION (self),
1049 context,
1050 priv->session_monitor,
1051 &sender_uid,
1052 &error)) {
1053 callback (self, context, G_MAXULONG, error, callback_data);
1054 g_clear_error (&error);
1055 return;
1056 }
1057
1058 if (check_permission) {
1059 chain = nm_auth_chain_new (context, pk_auth_cb, self, &error_desc);
1060 if (chain) {
1061 priv->pending_auths = g_slist_append (priv->pending_auths, chain);
1062
1063 nm_auth_chain_set_data (chain, "perm", (gpointer) check_permission, NULL);
1064 nm_auth_chain_set_data (chain, "callback", callback, NULL);
1065 nm_auth_chain_set_data (chain, "callback-data", callback_data, NULL);
1066 nm_auth_chain_set_data_ulong (chain, "sender-uid", sender_uid);
1067
1068 nm_auth_chain_add_call (chain, check_permission, TRUE);
1069 } else {
1070 g_set_error_literal (&error,
1071 NM_SETTINGS_ERROR,
1072 NM_SETTINGS_ERROR_PERMISSION_DENIED,
1073 error_desc);
1074 callback (self, context, G_MAXULONG, error, callback_data);
1075 g_clear_error (&error);
1076 }
1077 } else {
1078 /* Don't need polkit auth, automatic success */
1079 callback (self, context, sender_uid, NULL, callback_data);
1080 }
1081 }
1082
1083 /**** DBus method handlers ************************************/
1084
1085 static gboolean
1086 check_writable (NMConnection *connection, GError **error)
1087 {
1088 NMSettingConnection *s_con;
1089
1090 g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
1091
1092 s_con = nm_connection_get_setting_connection (connection);
1093 if (!s_con) {
1094 g_set_error_literal (error,
1095 NM_SETTINGS_ERROR,
1096 NM_SETTINGS_ERROR_INVALID_CONNECTION,
1097 "Connection did not have required 'connection' setting");
1098 return FALSE;
1099 }
1100
1101 /* If the connection is read-only, that has to be changed at the source of
1102 * the problem (ex a system settings plugin that can't write connections out)
1103 * instead of over D-Bus.
1104 */
1105 if (nm_setting_connection_get_read_only (s_con)) {
1106 g_set_error_literal (error,
1107 NM_SETTINGS_ERROR,
1108 NM_SETTINGS_ERROR_READ_ONLY_CONNECTION,
1109 "Connection is read-only");
1110 return FALSE;
1111 }
1112
1113 return TRUE;
1114 }
1115
1116 static void
1117 get_settings_auth_cb (NMSettingsConnection *self,
1118 DBusGMethodInvocation *context,
1119 gulong sender_uid,
1120 GError *error,
1121 gpointer data)
1122 {
1123 if (error)
1124 dbus_g_method_return_error (context, error);
1125 else {
1126 GHashTable *settings;
1127 NMConnection *dupl_con;
1128 NMSettingConnection *s_con;
1129 NMSettingWireless *s_wifi;
1130 guint64 timestamp = 0;
1131 GSList *bssid_list;
1132
1133 dupl_con = nm_connection_duplicate (NM_CONNECTION (self));
1134 g_assert (dupl_con);
1135
1136 /* Timestamp is not updated in connection's 'timestamp' property,
1137 * because it would force updating the connection and in turn
1138 * writing to /etc periodically, which we want to avoid. Rather real
1139 * timestamps are kept track of in a private variable. So, substitute
1140 * timestamp property with the real one here before returning the settings.
1141 */
1142 nm_settings_connection_get_timestamp (self, ×tamp);
1143 if (timestamp) {
1144 s_con = nm_connection_get_setting_connection (NM_CONNECTION (dupl_con));
1145 g_assert (s_con);
1146 g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, timestamp, NULL);
1147 }
1148 /* Seen BSSIDs are not updated in 802-11-wireless 'seen-bssids' property
1149 * from the same reason as timestamp. Thus we put it here to GetSettings()
1150 * return settings too.
1151 */
1152 bssid_list = nm_settings_connection_get_seen_bssids (self);
1153 s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (dupl_con));
1154 if (bssid_list && s_wifi) {
1155 g_object_set (s_wifi, NM_SETTING_WIRELESS_SEEN_BSSIDS, bssid_list, NULL);
1156 g_slist_free_full (bssid_list, g_free);
1157 }
1158
1159 /* 802-11-wireless.security property is deprecated. But we set it here so that
1160 * we don't disturb old clients that might expect it being properly set for
1161 * secured Wi-Fi connections.
1162 */
1163 if (nm_connection_get_setting_wireless_security (NM_CONNECTION (dupl_con))) {
1164 s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (dupl_con));
1165 g_assert (s_wifi);
1166 g_object_set (s_wifi,
1167 NM_SETTING_WIRELESS_SEC, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
1168 NULL);
1169 }
1170
1171 /* Secrets should *never* be returned by the GetSettings method, they
1172 * get returned by the GetSecrets method which can be better
1173 * protected against leakage of secrets to unprivileged callers.
1174 */
1175 settings = nm_connection_to_hash (NM_CONNECTION (dupl_con), NM_SETTING_HASH_FLAG_NO_SECRETS);
1176 g_assert (settings);
1177 dbus_g_method_return (context, settings);
1178 g_hash_table_destroy (settings);
1179 g_object_unref (dupl_con);
1180 }
1181 }
1182
1183 static void
1184 impl_settings_connection_get_settings (NMSettingsConnection *self,
1185 DBusGMethodInvocation *context)
1186 {
1187 auth_start (self, context, NULL, get_settings_auth_cb, NULL);
1188 }
1189
1190 typedef struct {
1191 DBusGMethodInvocation *context;
1192 NMAgentManager *agent_mgr;
1193 gulong sender_uid;
1194 NMConnection *new_settings;
1195 gboolean save_to_disk;
1196 } UpdateInfo;
1197
1198 static void
1199 update_complete (NMSettingsConnection *self,
1200 UpdateInfo *info,
1201 GError *error)
1202 {
1203 if (error)
1204 dbus_g_method_return_error (info->context, error);
1205 else
1206 dbus_g_method_return (info->context);
1207
1208 g_clear_object (&info->agent_mgr);
1209 g_clear_object (&info->new_settings);
1210 memset (info, 0, sizeof (*info));
1211 g_free (info);
1212 }
1213
1214 static void
1215 con_update_cb (NMSettingsConnection *self,
1216 GError *error,
1217 gpointer user_data)
1218 {
1219 UpdateInfo *info = user_data;
1220 NMConnection *for_agent;
1221
1222 if (!error) {
1223 /* Dupe the connection so we can clear out non-agent-owned secrets,
1224 * as agent-owned secrets are the only ones we send back be saved.
1225 * Only send secrets to agents of the same UID that called update too.
1226 */
1227 for_agent = nm_connection_duplicate (NM_CONNECTION (self));
1228 nm_connection_clear_secrets_with_flags (for_agent,
1229 secrets_filter_cb,
1230 GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED));
1231 nm_agent_manager_save_secrets (info->agent_mgr, for_agent, TRUE, info->sender_uid);
1232 g_object_unref (for_agent);
1233 }
1234
1235 update_complete (self, info, error);
1236 }
1237
1238 static void
1239 update_auth_cb (NMSettingsConnection *self,
1240 DBusGMethodInvocation *context,
1241 gulong sender_uid,
1242 GError *error,
1243 gpointer data)
1244 {
1245 UpdateInfo *info = data;
1246 GError *local = NULL;
1247
1248 if (error) {
1249 update_complete (self, info, error);
1250 return;
1251 }
1252
1253 info->sender_uid = sender_uid;
1254
1255 /* Cache the new secrets from the agent, as stuff like inotify-triggered
1256 * changes to connection's backing config files will blow them away if
1257 * they're in the main connection.
1258 */
1259 update_agent_secrets_cache (self, info->new_settings);
1260
1261 if (info->save_to_disk) {
1262 nm_settings_connection_replace_and_commit (self,
1263 info->new_settings,
1264 con_update_cb,
1265 info);
1266 } else {
1267 /* Do nothing if there's nothing to update */
1268 if (!nm_connection_compare (NM_CONNECTION (self), info->new_settings, NM_SETTING_COMPARE_FLAG_EXACT)) {
1269 if (!nm_settings_connection_replace_settings (self, info->new_settings, TRUE, &local))
1270 g_assert (local);
1271 }
1272 con_update_cb (self, local, info);
1273 g_clear_error (&local);
1274 }
1275 }
1276
1277 static const char *
1278 get_update_modify_permission (NMConnection *old, NMConnection *new)
1279 {
1280 NMSettingConnection *s_con;
1281 guint32 orig_num = 0, new_num = 0;
1282
1283 s_con = nm_connection_get_setting_connection (old);
1284 g_assert (s_con);
1285 orig_num = nm_setting_connection_get_num_permissions (s_con);
1286
1287 s_con = nm_connection_get_setting_connection (new);
1288 g_assert (s_con);
1289 new_num = nm_setting_connection_get_num_permissions (s_con);
1290
1291 /* If the caller is the only user in either connection's permissions, then
1292 * we use the 'modify.own' permission instead of 'modify.system'.
1293 */
1294 if (orig_num == 1 && new_num == 1)
1295 return NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN;
1296
1297 /* If the update request affects more than just the caller (ie if the old
1298 * settings were system-wide, or the new ones are), require 'modify.system'.
1299 */
1300 return NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM;
1301 }
1302
1303 static void
1304 impl_settings_connection_update_helper (NMSettingsConnection *self,
1305 GHashTable *new_settings,
1306 DBusGMethodInvocation *context,
1307 gboolean save_to_disk)
1308 {
1309 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
1310 NMConnection *tmp = NULL;
1311 GError *error = NULL;
1312 UpdateInfo *info;
1313 const char *permission;
1314
1315 g_assert (new_settings != NULL || save_to_disk == TRUE);
1316
1317 /* If the connection is read-only, that has to be changed at the source of
1318 * the problem (ex a system settings plugin that can't write connections out)
1319 * instead of over D-Bus.
1320 */
1321 if (!check_writable (NM_CONNECTION (self), &error)) {
1322 dbus_g_method_return_error (context, error);
1323 g_error_free (error);
1324 return;
1325 }
1326
1327 /* Check if the settings are valid first */
1328 if (new_settings) {
1329 tmp = nm_connection_new_from_hash (new_settings, &error);
1330 if (!tmp) {
1331 g_assert (error);
1332 dbus_g_method_return_error (context, error);
1333 g_error_free (error);
1334 return;
1335 }
1336 }
1337
1338 /* And that the new connection settings will be visible to the user
1339 * that's sending the update request. You can't make a connection
1340 * invisible to yourself.
1341 */
1342 if (!check_user_in_acl (tmp ? tmp : NM_CONNECTION (self),
1343 context,
1344 priv->session_monitor,
1345 NULL,
1346 &error)) {
1347 dbus_g_method_return_error (context, error);
1348 g_clear_error (&error);
1349 g_object_unref (tmp);
1350 return;
1351 }
1352
1353 info = g_malloc0 (sizeof (*info));
1354 info->context = context;
1355 info->agent_mgr = g_object_ref (priv->agent_mgr);
1356 info->sender_uid = G_MAXULONG;
1357 info->save_to_disk = save_to_disk;
1358 info->new_settings = tmp;
1359
1360 permission = get_update_modify_permission (NM_CONNECTION (self),
1361 tmp ? tmp : NM_CONNECTION (self));
1362 auth_start (self, context, permission, update_auth_cb, info);
1363 }
1364
1365 static void
1366 impl_settings_connection_update (NMSettingsConnection *self,
1367 GHashTable *new_settings,
1368 DBusGMethodInvocation *context)
1369 {
1370 g_assert (new_settings);
1371 impl_settings_connection_update_helper (self, new_settings, context, TRUE);
1372 }
1373
1374 static void
1375 impl_settings_connection_update_unsaved (NMSettingsConnection *self,
1376 GHashTable *new_settings,
1377 DBusGMethodInvocation *context)
1378 {
1379 g_assert (new_settings);
1380 impl_settings_connection_update_helper (self, new_settings, context, FALSE);
1381 }
1382
1383 static void
1384 impl_settings_connection_save (NMSettingsConnection *self,
1385 DBusGMethodInvocation *context)
1386 {
1387 /* Do nothing if the connection is already synced with disk */
1388 if (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->unsaved == TRUE)
1389 impl_settings_connection_update_helper (self, NULL, context, TRUE);
1390 else
1391 dbus_g_method_return (context);
1392 }
1393
1394 static void
1395 con_delete_cb (NMSettingsConnection *connection,
1396 GError *error,
1397 gpointer user_data)
1398 {
1399 DBusGMethodInvocation *context = user_data;
1400
1401 if (error)
1402 dbus_g_method_return_error (context, error);
1403 else
1404 dbus_g_method_return (context);
1405 }
1406
1407 static void
1408 delete_auth_cb (NMSettingsConnection *self,
1409 DBusGMethodInvocation *context,
1410 gulong sender_uid,
1411 GError *error,
1412 gpointer data)
1413 {
1414 if (error) {
1415 dbus_g_method_return_error (context, error);
1416 return;
1417 }
1418
1419 nm_settings_connection_delete (self, con_delete_cb, context);
1420 }
1421
1422 static const char *
1423 get_modify_permission_basic (NMSettingsConnection *connection)
1424 {
1425 NMSettingConnection *s_con;
1426
1427 /* If the caller is the only user in the connection's permissions, then
1428 * we use the 'modify.own' permission instead of 'modify.system'. If the
1429 * request affects more than just the caller, require 'modify.system'.
1430 */
1431 s_con = nm_connection_get_setting_connection (NM_CONNECTION (connection));
1432 g_assert (s_con);
1433 if (nm_setting_connection_get_num_permissions (s_con) == 1)
1434 return NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN;
1435
1436 return NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM;
1437 }
1438
1439 static void
1440 impl_settings_connection_delete (NMSettingsConnection *self,
1441 DBusGMethodInvocation *context)
1442 {
1443 GError *error = NULL;
1444
1445 if (!check_writable (NM_CONNECTION (self), &error)) {
1446 dbus_g_method_return_error (context, error);
1447 g_error_free (error);
1448 return;
1449 }
1450
1451 auth_start (self, context, get_modify_permission_basic (self), delete_auth_cb, NULL);
1452 }
1453
1454 /**************************************************************/
1455
1456 static void
1457 dbus_get_agent_secrets_cb (NMSettingsConnection *self,
1458 guint32 call_id,
1459 const char *agent_username,
1460 const char *setting_name,
1461 GError *error,
1462 gpointer user_data)
1463 {
1464 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
1465 DBusGMethodInvocation *context = user_data;
1466 GHashTable *hash;
1467
1468 priv->reqs = g_slist_remove (priv->reqs, GUINT_TO_POINTER (call_id));
1469
1470 if (error)
1471 dbus_g_method_return_error (context, error);
1472 else {
1473 /* Return secrets from agent and backing storage to the D-Bus caller;
1474 * nm_settings_connection_get_secrets() will have updated itself with
1475 * secrets from backing storage and those returned from the agent
1476 * by the time we get here.
1477 */
1478 hash = nm_connection_to_hash (NM_CONNECTION (self), NM_SETTING_HASH_FLAG_ONLY_SECRETS);
1479 if (!hash)
1480 hash = g_hash_table_new (NULL, NULL);
1481 dbus_g_method_return (context, hash);
1482 g_hash_table_destroy (hash);
1483 }
1484 }
1485
1486 static void
1487 dbus_secrets_auth_cb (NMSettingsConnection *self,
1488 DBusGMethodInvocation *context,
1489 gulong sender_uid,
1490 GError *error,
1491 gpointer user_data)
1492 {
1493 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
1494 char *setting_name = user_data;
1495 guint32 call_id = 0;
1496 GError *local = NULL;
1497
1498 if (!error) {
1499 call_id = nm_settings_connection_get_secrets (self,
1500 TRUE,
1501 sender_uid,
1502 setting_name,
1503 NM_SETTINGS_GET_SECRETS_FLAG_USER_REQUESTED,
1504 NULL,
1505 dbus_get_agent_secrets_cb,
1506 context,
1507 &local);
1508 if (call_id > 0) {
1509 /* track the request and wait for the callback */
1510 priv->reqs = g_slist_append (priv->reqs, GUINT_TO_POINTER (call_id));
1511 }
1512 }
1513
1514 if (error || local) {
1515 dbus_g_method_return_error (context, error ? error : local);
1516 g_clear_error (&local);
1517 }
1518
1519 g_free (setting_name);
1520 }
1521
1522 static void
1523 impl_settings_connection_get_secrets (NMSettingsConnection *self,
1524 const gchar *setting_name,
1525 DBusGMethodInvocation *context)
1526 {
1527 auth_start (self,
1528 context,
1529 get_modify_permission_basic (self),
1530 dbus_secrets_auth_cb,
1531 g_strdup (setting_name));
1532 }
1533
1534 /**************************************************************/
1535
1536 void
1537 nm_settings_connection_signal_remove (NMSettingsConnection *self)
1538 {
1539 /* Emit removed first */
1540 g_signal_emit_by_name (self, NM_SETTINGS_CONNECTION_REMOVED);
1541
1542 /* And unregistered last to ensure the removed signal goes out before
1543 * we take the connection off the bus.
1544 */
1545 nm_dbus_manager_unregister_object (nm_dbus_manager_get (), G_OBJECT (self));
1546 }
1547
1548 gboolean
1549 nm_settings_connection_get_unsaved (NMSettingsConnection *self)
1550 {
1551 return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->unsaved;
1552 }
1553
1554 /**************************************************************/
1555
1556 /**
1557 * nm_settings_connection_get_timestamp:
1558 * @connection: the #NMSettingsConnection
1559 * @out_timestamp: the connection's timestamp
1560 *
1561 * Returns the time (in seconds since the Unix epoch) when the connection
1562 * was last successfully activated.
1563 *
1564 * Returns: %TRUE if the timestamp has ever been set, otherwise %FALSE.
1565 **/
1566 gboolean
1567 nm_settings_connection_get_timestamp (NMSettingsConnection *connection,
1568 guint64 *out_timestamp)
1569 {
1570 g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), FALSE);
1571
1572 if (out_timestamp)
1573 *out_timestamp = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->timestamp;
1574 return NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->timestamp_set;
1575 }
1576
1577 /**
1578 * nm_settings_connection_update_timestamp:
1579 * @connection: the #NMSettingsConnection
1580 * @timestamp: timestamp to set into the connection and to store into
1581 * the timestamps database
1582 * @flush_to_disk: if %TRUE, commit timestamp update to persistent storage
1583 *
1584 * Updates the connection and timestamps database with the provided timestamp.
1585 **/
1586 void
1587 nm_settings_connection_update_timestamp (NMSettingsConnection *connection,
1588 guint64 timestamp,
1589 gboolean flush_to_disk)
1590 {
1591 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
1592 const char *connection_uuid;
1593 GKeyFile *timestamps_file;
1594 char *data, *tmp;
1595 gsize len;
1596 GError *error = NULL;
1597
1598 g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
1599
1600 /* Update timestamp in private storage */
1601 priv->timestamp = timestamp;
1602 priv->timestamp_set = TRUE;
1603
1604 if (flush_to_disk == FALSE)
1605 return;
1606
1607 /* Save timestamp to timestamps database file */
1608 timestamps_file = g_key_file_new ();
1609 if (!g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) {
1610 if (!(error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT))
1611 nm_log_warn (LOGD_SETTINGS, "error parsing timestamps file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message);
1612 g_clear_error (&error);
1613 }
1614
1615 connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection));
1616 tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp);
1617 g_key_file_set_value (timestamps_file, "timestamps", connection_uuid, tmp);
1618 g_free (tmp);
1619
1620 data = g_key_file_to_data (timestamps_file, &len, &error);
1621 if (data) {
1622 g_file_set_contents (SETTINGS_TIMESTAMPS_FILE, data, len, &error);
1623 g_free (data);
1624 }
1625 if (error) {
1626 nm_log_warn (LOGD_SETTINGS, "error saving timestamp to file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message);
1627 g_error_free (error);
1628 }
1629 g_key_file_free (timestamps_file);
1630 }
1631
1632 /**
1633 * nm_settings_connection_read_and_fill_timestamp:
1634 * @connection: the #NMSettingsConnection
1635 *
1636 * Retrieves timestamp of the connection's last usage from database file and
1637 * stores it into the connection private data.
1638 **/
1639 void
1640 nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *connection)
1641 {
1642 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
1643 const char *connection_uuid;
1644 guint64 timestamp = 0;
1645 GKeyFile *timestamps_file;
1646 GError *err = NULL;
1647 char *tmp_str;
1648
1649 g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
1650
1651 /* Get timestamp from database file */
1652 timestamps_file = g_key_file_new ();
1653 g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL);
1654 connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection));
1655 tmp_str = g_key_file_get_value (timestamps_file, "timestamps", connection_uuid, &err);
1656 if (tmp_str) {
1657 timestamp = g_ascii_strtoull (tmp_str, NULL, 10);
1658 g_free (tmp_str);
1659 }
1660
1661 /* Update connection's timestamp */
1662 if (!err) {
1663 priv->timestamp = timestamp;
1664 priv->timestamp_set = TRUE;
1665 } else {
1666 nm_log_dbg (LOGD_SETTINGS, "failed to read connection timestamp for '%s': (%d) %s",
1667 connection_uuid, err->code, err->message);
1668 g_clear_error (&err);
1669 }
1670 g_key_file_free (timestamps_file);
1671 }
1672
1673 static guint
1674 mac_hash (gconstpointer v)
1675 {
1676 const guint8 *p = v;
1677 guint32 i, h = 5381;
1678
1679 for (i = 0; i < ETH_ALEN; i++)
1680 h = (h << 5) + h + p[i];
1681 return h;
1682 }
1683
1684 static gboolean
1685 mac_equal (gconstpointer a, gconstpointer b)
1686 {
1687 return memcmp (a, b, ETH_ALEN) == 0;
1688 }
1689
1690 static guint8 *
1691 mac_dup (const struct ether_addr *old)
1692 {
1693 guint8 *new;
1694
1695 g_return_val_if_fail (old != NULL, NULL);
1696
1697 new = g_malloc0 (ETH_ALEN);
1698 memcpy (new, old, ETH_ALEN);
1699 return new;
1700 }
1701
1702 /**
1703 * nm_settings_connection_get_seen_bssids:
1704 * @connection: the #NMSettingsConnection
1705 *
1706 * Returns current list of seen BSSIDs for the connection.
1707 *
1708 * Returns: (transfer full) list of seen BSSIDs (in the standard hex-digits-and-colons notation).
1709 * The caller is responsible for freeing the list.
1710 **/
1711 GSList *
1712 nm_settings_connection_get_seen_bssids (NMSettingsConnection *connection)
1713 {
1714 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
1715 GHashTableIter iter;
1716 char *bssid_str;
1717 GSList *bssid_list = NULL;
1718
1719 g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), NULL);
1720
1721 g_hash_table_iter_init (&iter, priv->seen_bssids);
1722 while (g_hash_table_iter_next (&iter, NULL, (gpointer) &bssid_str))
1723 bssid_list = g_slist_prepend (bssid_list, g_strdup (bssid_str));
1724
1725 return bssid_list;
1726 }
1727
1728 /**
1729 * nm_settings_connection_has_seen_bssid:
1730 * @connection: the #NMSettingsConnection
1731 * @bssid: the BSSID to check the seen BSSID list for
1732 *
1733 * Returns: TRUE if the given @bssid is in the seen BSSIDs list
1734 **/
1735 gboolean
1736 nm_settings_connection_has_seen_bssid (NMSettingsConnection *connection,
1737 const struct ether_addr *bssid)
1738 {
1739 g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), FALSE);
1740 g_return_val_if_fail (bssid != NULL, FALSE);
1741
1742 return !!g_hash_table_lookup (NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->seen_bssids, bssid);
1743 }
1744
1745 /**
1746 * nm_settings_connection_add_seen_bssid:
1747 * @connection: the #NMSettingsConnection
1748 * @seen_bssid: BSSID to set into the connection and to store into
1749 * the seen-bssids database
1750 *
1751 * Updates the connection and seen-bssids database with the provided BSSID.
1752 **/
1753 void
1754 nm_settings_connection_add_seen_bssid (NMSettingsConnection *connection,
1755 const struct ether_addr *seen_bssid)
1756 {
1757 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
1758 const char *connection_uuid;
1759 GKeyFile *seen_bssids_file;
1760 char *data, *bssid_str;
1761 const char **list;
1762 gsize len;
1763 GError *error = NULL;
1764 GHashTableIter iter;
1765 guint n;
1766
1767 g_return_if_fail (seen_bssid != NULL);
1768
1769 if (g_hash_table_lookup (priv->seen_bssids, seen_bssid))
1770 return; /* Already in the list */
1771
1772 /* Add the new BSSID; let the hash take ownership of the allocated BSSID string */
1773 bssid_str = nm_utils_hwaddr_ntoa (seen_bssid, ARPHRD_ETHER);
1774 g_return_if_fail (bssid_str != NULL);
1775 g_hash_table_insert (priv->seen_bssids, mac_dup (seen_bssid), bssid_str);
1776
1777 /* Build up a list of all the BSSIDs in string form */
1778 n = 0;
1779 list = g_malloc0 (g_hash_table_size (priv->seen_bssids) * sizeof (char *));
1780 g_hash_table_iter_init (&iter, priv->seen_bssids);
1781 while (g_hash_table_iter_next (&iter, NULL, (gpointer) &bssid_str))
1782 list[n++] = bssid_str;
1783
1784 /* Save BSSID to seen-bssids file */
1785 seen_bssids_file = g_key_file_new ();
1786 g_key_file_set_list_separator (seen_bssids_file, ',');
1787 if (!g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) {
1788 if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
1789 nm_log_warn (LOGD_SETTINGS, "error parsing seen-bssids file '%s': %s",
1790 SETTINGS_SEEN_BSSIDS_FILE, error->message);
1791 }
1792 g_clear_error (&error);
1793 }
1794
1795 connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection));
1796 g_key_file_set_string_list (seen_bssids_file, "seen-bssids", connection_uuid, list, n);
1797 g_free (list);
1798
1799 data = g_key_file_to_data (seen_bssids_file, &len, &error);
1800 if (data) {
1801 g_file_set_contents (SETTINGS_SEEN_BSSIDS_FILE, data, len, &error);
1802 g_free (data);
1803 }
1804 g_key_file_free (seen_bssids_file);
1805
1806 if (error) {
1807 nm_log_warn (LOGD_SETTINGS, "error saving seen-bssids to file '%s': %s",
1808 SETTINGS_SEEN_BSSIDS_FILE, error->message);
1809 g_error_free (error);
1810 }
1811 }
1812
1813 static void
1814 add_seen_bssid_string (NMSettingsConnection *self, const char *bssid)
1815 {
1816 struct ether_addr mac;
1817
1818 g_return_if_fail (bssid != NULL);
1819 if (ether_aton_r (bssid, &mac)) {
1820 g_hash_table_insert (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->seen_bssids,
1821 mac_dup (&mac),
1822 g_strdup (bssid));
1823 }
1824 }
1825
1826 /**
1827 * nm_settings_connection_read_and_fill_seen_bssids:
1828 * @connection: the #NMSettingsConnection
1829 *
1830 * Retrieves seen BSSIDs of the connection from database file and stores then into the
1831 * connection private data.
1832 **/
1833 void
1834 nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *connection)
1835 {
1836 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
1837 const char *connection_uuid;
1838 GKeyFile *seen_bssids_file;
1839 char **tmp_strv = NULL;
1840 gsize i, len = 0;
1841 NMSettingWireless *s_wifi;
1842
1843 /* Get seen BSSIDs from database file */
1844 seen_bssids_file = g_key_file_new ();
1845 g_key_file_set_list_separator (seen_bssids_file, ',');
1846 if (g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL)) {
1847 connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection));
1848 tmp_strv = g_key_file_get_string_list (seen_bssids_file, "seen-bssids", connection_uuid, &len, NULL);
1849 }
1850 g_key_file_free (seen_bssids_file);
1851
1852 /* Update connection's seen-bssids */
1853 if (tmp_strv) {
1854 g_hash_table_remove_all (priv->seen_bssids);
1855 for (i = 0; i < len; i++)
1856 add_seen_bssid_string (connection, tmp_strv[i]);
1857 g_strfreev (tmp_strv);
1858 } else {
1859 /* If this connection didn't have an entry in the seen-bssids database,
1860 * maybe this is the first time we've read it in, so populate the
1861 * seen-bssids list from the deprecated seen-bssids property of the
1862 * wifi setting.
1863 */
1864 s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (connection));
1865 if (s_wifi) {
1866 len = nm_setting_wireless_get_num_seen_bssids (s_wifi);
1867 for (i = 0; i < len; i++)
1868 add_seen_bssid_string (connection, nm_setting_wireless_get_seen_bssid (s_wifi, i));
1869 }
1870 }
1871 }
1872
1873 /**************************************************************/
1874
1875 static void
1876 nm_settings_connection_init (NMSettingsConnection *self)
1877 {
1878 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
1879
1880 priv->visible = FALSE;
1881
1882 priv->session_monitor = nm_session_monitor_get ();
1883 priv->session_changed_id = g_signal_connect (priv->session_monitor,
1884 NM_SESSION_MONITOR_CHANGED,
1885 G_CALLBACK (session_changed_cb),
1886 self);
1887
1888 priv->agent_mgr = nm_agent_manager_get ();
1889
1890 priv->seen_bssids = g_hash_table_new_full (mac_hash, mac_equal, g_free, g_free);
1891
1892 g_signal_connect (self, NM_CONNECTION_SECRETS_CLEARED, G_CALLBACK (secrets_cleared_cb), NULL);
1893 g_signal_connect (self, NM_CONNECTION_CHANGED, G_CALLBACK (changed_cb), GUINT_TO_POINTER (TRUE));
1894 }
1895
1896 static void
1897 dispose (GObject *object)
1898 {
1899 NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
1900 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
1901 GSList *iter;
1902
1903 if (priv->disposed)
1904 goto out;
1905 priv->disposed = TRUE;
1906
1907 if (priv->updated_idle_id) {
1908 g_source_remove (priv->updated_idle_id);
1909 priv->updated_idle_id = 0;
1910 }
1911
1912 if (priv->system_secrets)
1913 g_object_unref (priv->system_secrets);
1914 if (priv->agent_secrets)
1915 g_object_unref (priv->agent_secrets);
1916
1917 /* Cancel PolicyKit requests */
1918 for (iter = priv->pending_auths; iter; iter = g_slist_next (iter))
1919 nm_auth_chain_unref ((NMAuthChain *) iter->data);
1920 g_slist_free (priv->pending_auths);
1921 priv->pending_auths = NULL;
1922
1923 /* Cancel in-progress secrets requests */
1924 for (iter = priv->reqs; iter; iter = g_slist_next (iter))
1925 nm_agent_manager_cancel_secrets (priv->agent_mgr, GPOINTER_TO_UINT (iter->data));
1926 g_slist_free (priv->reqs);
1927
1928 g_hash_table_destroy (priv->seen_bssids);
1929
1930 set_visible (self, FALSE);
1931
1932 if (priv->session_changed_id)
1933 g_signal_handler_disconnect (priv->session_monitor, priv->session_changed_id);
1934 g_object_unref (priv->session_monitor);
1935 g_object_unref (priv->agent_mgr);
1936
1937 out:
1938 G_OBJECT_CLASS (nm_settings_connection_parent_class)->dispose (object);
1939 }
1940
1941 static void
1942 get_property (GObject *object, guint prop_id,
1943 GValue *value, GParamSpec *pspec)
1944 {
1945 NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (object);
1946
1947 switch (prop_id) {
1948 case PROP_VISIBLE:
1949 g_value_set_boolean (value, priv->visible);
1950 break;
1951 case PROP_UNSAVED:
1952 g_value_set_boolean (value, priv->unsaved);
1953 break;
1954 default:
1955 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1956 break;
1957 }
1958 }
1959
1960 static void
1961 set_property (GObject *object, guint prop_id,
1962 const GValue *value, GParamSpec *pspec)
1963 {
1964 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1965 }
1966
1967 static void
1968 nm_settings_connection_class_init (NMSettingsConnectionClass *class)
1969 {
1970 GObjectClass *object_class = G_OBJECT_CLASS (class);
1971
1972 g_type_class_add_private (class, sizeof (NMSettingsConnectionPrivate));
1973
1974 /* Virtual methods */
1975 object_class->dispose = dispose;
1976 object_class->get_property = get_property;
1977 object_class->set_property = set_property;
1978
1979 class->commit_changes = commit_changes;
1980 class->delete = do_delete;
1981 class->supports_secrets = supports_secrets;
1982
1983 /* Properties */
1984 g_object_class_install_property
1985 (object_class, PROP_VISIBLE,
1986 g_param_spec_boolean (NM_SETTINGS_CONNECTION_VISIBLE,
1987 "Visible",
1988 "Visible",
1989 FALSE,
1990 G_PARAM_READABLE));
1991
1992 g_object_class_install_property
1993 (object_class, PROP_UNSAVED,
1994 g_param_spec_boolean (NM_SETTINGS_CONNECTION_UNSAVED,
1995 "Unsaved",
1996 "TRUE when the connection has not yet been saved "
1997 "to permanent storage (eg disk) or when it "
1998 "has been changed but not yet saved.",
1999 FALSE,
2000 G_PARAM_READABLE));
2001
2002 /* Signals */
2003 signals[UPDATED] =
2004 g_signal_new (NM_SETTINGS_CONNECTION_UPDATED,
2005 G_TYPE_FROM_CLASS (class),
2006 G_SIGNAL_RUN_FIRST,
2007 0,
2008 NULL, NULL,
2009 g_cclosure_marshal_VOID__VOID,
2010 G_TYPE_NONE, 0);
2011
2012 signals[REMOVED] =
2013 g_signal_new (NM_SETTINGS_CONNECTION_REMOVED,
2014 G_TYPE_FROM_CLASS (class),
2015 G_SIGNAL_RUN_FIRST,
2016 0,
2017 NULL, NULL,
2018 g_cclosure_marshal_VOID__VOID,
2019 G_TYPE_NONE, 0);
2020
2021 nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
2022 G_TYPE_FROM_CLASS (class),
2023 &dbus_glib_nm_settings_connection_object_info);
2024 }
2025