summary refs log tree commit diff
path: root/src/settings/nm-settings-connection.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/settings/nm-settings-connection.c')
-rw-r--r--src/settings/nm-settings-connection.c204
1 files changed, 138 insertions, 66 deletions
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index 22aef716..60de6b06 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -34,7 +34,6 @@
 #include "nm-dbus-manager.h"
 #include "nm-settings-error.h"
 #include "nm-dbus-glib-types.h"
-#include "nm-polkit-helpers.h"
 #include "nm-logging.h"
 #include "nm-manager-auth.h"
 #include "nm-marshal.h"
@@ -83,7 +82,6 @@ typedef struct {
 	NMDBusManager *dbus_mgr;
 	NMAgentManager *agent_mgr;
 
-	PolkitAuthority *authority;
 	GSList *pending_auths; /* List of pending authentication requests */
 	NMConnection *secrets;
 	gboolean visible; /* Is this connection is visible by some session? */
@@ -98,6 +96,83 @@ typedef struct {
 
 /**************************************************************/
 
+/* Return TRUE to continue, FALSE to stop */
+typedef gboolean (*ForEachSecretFunc) (GHashTableIter *iter,
+                                       NMSettingSecretFlags flags,
+                                       gpointer user_data);
+
+static void
+for_each_secret (NMConnection *connection,
+                 GHashTable *secrets,
+                 ForEachSecretFunc callback,
+                 gpointer callback_data)
+{
+	GHashTableIter iter;
+	const char *setting_name;
+	GHashTable *setting_hash;
+
+	/* This function, given a hash of hashes representing new secrets of
+	 * an NMConnection, walks through each toplevel hash (which represents a
+	 * NMSetting), and for each setting, walks through that setting hash's
+	 * properties.  For each property that's a secret, it will check that
+	 * secret's flags in the backing NMConnection object, and call a supplied
+	 * callback.
+	 *
+	 * The one complexity is that the VPN setting's 'secrets' property is
+	 * *also* a hash table (since the key/value pairs are arbitrary and known
+	 * only to the VPN plugin itself).  That means we have three levels of
+	 * GHashTables that we potentially have to traverse here.  When we hit the
+	 * VPN setting's 'secrets' property, we special-case that and iterate over
+	 * each item in that 'secrets' hash table, calling the supplied callback
+	 * each time.
+	 */
+
+	/* Walk through the list of setting hashes */
+	g_hash_table_iter_init (&iter, secrets);
+	while (g_hash_table_iter_next (&iter, (gpointer) &setting_name, (gpointer) &setting_hash)) {
+		NMSetting *setting;
+		GHashTableIter secret_iter;
+		const char *secret_name;
+		GValue *val;
+
+		/* Get the actual NMSetting from the connection so we can get secret flags
+		 * from the connection data, since flags aren't secrets.  What we're
+		 * iterating here is just the secrets, not a whole connection.
+		 */
+		setting = nm_connection_get_setting_by_name (connection, setting_name);
+		if (setting == NULL)
+			continue;
+
+		/* Walk through the list of keys in each setting hash */
+		g_hash_table_iter_init (&secret_iter, setting_hash);
+		while (g_hash_table_iter_next (&secret_iter, (gpointer) &secret_name, (gpointer) &val)) {
+			NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+
+			/* VPN secrets need slightly different treatment here since the
+			 * "secrets" property is actually a hash table of secrets.
+			 */
+			if (NM_IS_SETTING_VPN (setting) && (g_strcmp0 (secret_name, NM_SETTING_VPN_SECRETS) == 0)) {
+				GHashTableIter vpn_secrets_iter;
+
+				/* Iterate through each secret from the VPN hash in the overall secrets hash */
+				g_hash_table_iter_init (&vpn_secrets_iter, g_value_get_boxed (val));
+				while (g_hash_table_iter_next (&vpn_secrets_iter, (gpointer) &secret_name, NULL)) {
+					secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+					nm_setting_get_secret_flags (setting, secret_name, &secret_flags, NULL);
+					if (callback (&vpn_secrets_iter, secret_flags, callback_data) == FALSE)
+						return;
+				}
+			} else {
+				nm_setting_get_secret_flags (setting, secret_name, &secret_flags, NULL);
+				if (callback (&secret_iter, secret_flags, callback_data) == FALSE)
+					return;
+			}
+		}
+	}
+}
+
+/**************************************************************/
+
 static void
 set_visible (NMSettingsConnection *self, gboolean new_visible)
 {
@@ -112,7 +187,8 @@ set_visible (NMSettingsConnection *self, gboolean new_visible)
 gboolean
 nm_settings_connection_is_visible (NMSettingsConnection *self)
 {
-	g_return_val_if_fail (NM_SETTINGS_CONNECTION (self), FALSE);
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
 
 	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->visible;
 }
@@ -124,7 +200,8 @@ nm_settings_connection_recheck_visibility (NMSettingsConnection *self)
 	NMSettingConnection *s_con;
 	guint32 num, i;
 
-	g_return_if_fail (NM_SETTINGS_CONNECTION (self));
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
 
 	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
@@ -178,10 +255,10 @@ only_system_secrets_cb (NMSetting *setting,
 
 			g_hash_table_iter_init (&iter, (GHashTable *) g_value_get_boxed (value));
 			while (g_hash_table_iter_next (&iter, (gpointer *) &secret_name, NULL)) {
-				if (nm_setting_get_secret_flags (setting, secret_name, &secret_flags, NULL)) {
-					if (secret_flags != NM_SETTING_SECRET_FLAG_NONE)
-						nm_setting_vpn_remove_secret (NM_SETTING_VPN (setting), secret_name);
-				}
+				secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+				nm_setting_get_secret_flags (setting, secret_name, &secret_flags, NULL);
+				if (secret_flags != NM_SETTING_SECRET_FLAG_NONE)
+					nm_setting_vpn_remove_secret (NM_SETTING_VPN (setting), secret_name);
 			}
 		} else {
 			nm_setting_get_secret_flags (setting, key, &secret_flags, NULL);
@@ -204,15 +281,26 @@ update_secrets_cache (NMSettingsConnection *self)
 	nm_connection_for_each_setting_value (priv->secrets, only_system_secrets_cb, NULL);
 }
 
+static gboolean
+clear_system_secrets (GHashTableIter *iter,
+                      NMSettingSecretFlags flags,
+                      gpointer user_data)
+{
+	if (flags == NM_SETTING_SECRET_FLAG_NONE)
+		g_hash_table_iter_remove (iter);
+	return TRUE;
+}
+
 /* Update the settings of this connection to match that of 'new', taking care to
- * make a private copy of secrets. */
+ * make a private copy of secrets.
+ */
 gboolean
 nm_settings_connection_replace_settings (NMSettingsConnection *self,
                                          NMConnection *new,
                                          GError **error)
 {
 	NMSettingsConnectionPrivate *priv;
-	GHashTable *new_settings;
+	GHashTable *new_settings, *transient_secrets;
 	gboolean success = FALSE;
 
 	g_return_val_if_fail (self != NULL, FALSE);
@@ -222,18 +310,48 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self,
 
 	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
+	/* Replacing the settings might replace transient secrets, such as when
+	 * a user agent returns secrets, which might trigger the connection to be
+	 * written out, which triggers an inotify event to re-read and update the
+	 * connection, which, if we're not careful, could wipe out the transient
+	 * secrets the user agent just sent us.  Basically, only
+	 * nm_connection_clear_secrets() should wipe out transient secrets but
+	 * re-reading a connection from on-disk and updating our in-memory copy
+	 * should not.  Thus we preserve non-system-owned secrets here.
+	 */
+	transient_secrets = nm_connection_to_hash (NM_CONNECTION (self), NM_SETTING_HASH_FLAG_ONLY_SECRETS);
+	if (transient_secrets)
+		for_each_secret (NM_CONNECTION (self), transient_secrets, clear_system_secrets, NULL);
+
 	new_settings = nm_connection_to_hash (new, NM_SETTING_HASH_FLAG_ALL);
 	g_assert (new_settings);
 	if (nm_connection_replace_settings (NM_CONNECTION (self), new_settings, error)) {
+		GHashTableIter iter;
+		NMSetting *setting;
+		const char *setting_name;
+		GHashTable *setting_hash;
+
 		/* Copy the connection to keep its secrets around even if NM
 		 * calls nm_connection_clear_secrets().
 		 */
 		update_secrets_cache (self);
 
+		/* And add the transient secrets back */
+		if (transient_secrets) {
+			g_hash_table_iter_init (&iter, transient_secrets);
+			while (g_hash_table_iter_next (&iter, (gpointer) &setting_name, (gpointer) &setting_hash)) {
+				setting = nm_connection_get_setting_by_name (NM_CONNECTION (self), setting_name);
+				if (setting)
+					nm_setting_update_secrets (setting, setting_hash, NULL);
+			}
+		}
+
 		nm_settings_connection_recheck_visibility (self);
 		success = TRUE;
 	}
 	g_hash_table_destroy (new_settings);
+	if (transient_secrets)
+		g_hash_table_destroy (transient_secrets);
 	return success;
 }
 
@@ -398,11 +516,6 @@ supports_secrets (NMSettingsConnection *connection, const char *setting_name)
 	return TRUE;
 }
 
-/* Return TRUE to continue, FALSE to stop */
-typedef gboolean (*ForEachSecretFunc) (GHashTableIter *iter,
-                                       NMSettingSecretFlags flags,
-                                       gpointer user_data);
-
 static gboolean
 clear_nonagent_secrets (GHashTableIter *iter,
                         NMSettingSecretFlags flags,
@@ -430,7 +543,7 @@ has_system_owned_secrets (GHashTableIter *iter,
 {
 	gboolean *has_system_owned = user_data;
 
-	if (!(flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED)) {
+	if (flags == NM_SETTING_SECRET_FLAG_NONE) {
 		*has_system_owned = TRUE;
 		return FALSE;
 	}
@@ -438,40 +551,6 @@ has_system_owned_secrets (GHashTableIter *iter,
 }
 
 static void
-for_each_secret (NMConnection *connection,
-                 GHashTable *secrets,
-                 ForEachSecretFunc callback,
-                 gpointer callback_data)
-{
-	GHashTableIter iter;
-	const char *setting_name;
-	GHashTable *setting_hash;
-
-	/* Walk through the list of setting hashes */
-	g_hash_table_iter_init (&iter, secrets);
-	while (g_hash_table_iter_next (&iter,
-	                               (gpointer *) &setting_name,
-	                               (gpointer *) &setting_hash)) {
-		GHashTableIter setting_iter;
-		const char *secret_name;
-
-		/* Walk through the list of keys in each setting hash */
-		g_hash_table_iter_init (&setting_iter, setting_hash);
-		while (g_hash_table_iter_next (&setting_iter, (gpointer *) &secret_name, NULL)) {
-			NMSetting *setting;
-			NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
-
-			/* Get the actual NMSetting from the connection so we can get secret flags */
-			setting = nm_connection_get_setting_by_name (connection, setting_name);
-			if (setting && nm_setting_get_secret_flags (setting, secret_name, &flags, NULL)) {
-				if (callback (&setting_iter, flags, callback_data) == FALSE)
-					return;
-			}
-		}
-	}
-}
-
-static void
 new_secrets_commit_cb (NMSettingsConnection *connection,
                        GError *error,
                        gpointer user_data)
@@ -850,7 +929,7 @@ auth_start (NMSettingsConnection *self,
 	}
 
 	if (check_permission) {
-		chain = nm_auth_chain_new (priv->authority, context, NULL, pk_auth_cb, self);
+		chain = nm_auth_chain_new (context, NULL, pk_auth_cb, self);
 		g_assert (chain);
 		nm_auth_chain_set_data (chain, "perm", (gpointer) check_permission, NULL);
 		nm_auth_chain_set_data (chain, "callback", callback, NULL);
@@ -980,10 +1059,10 @@ only_agent_secrets_cb (NMSetting *setting,
 			/* VPNs are special; need to handle each secret separately */
 			g_hash_table_iter_init (&iter, (GHashTable *) g_value_get_boxed (value));
 			while (g_hash_table_iter_next (&iter, (gpointer *) &secret_name, NULL)) {
-				if (nm_setting_get_secret_flags (setting, secret_name, &secret_flags, NULL)) {
-					if (secret_flags != NM_SETTING_SECRET_FLAG_AGENT_OWNED)
-						nm_setting_vpn_remove_secret (NM_SETTING_VPN (setting), secret_name);
-				}
+				secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+				nm_setting_get_secret_flags (setting, secret_name, &secret_flags, NULL);
+				if (secret_flags != NM_SETTING_SECRET_FLAG_AGENT_OWNED)
+					nm_setting_vpn_remove_secret (NM_SETTING_VPN (setting), secret_name);
 			}
 		} else {
 			nm_setting_get_secret_flags (setting, key, &secret_flags, NULL);
@@ -1271,7 +1350,8 @@ nm_settings_connection_signal_remove (NMSettingsConnection *self)
 guint64
 nm_settings_connection_get_timestamp (NMSettingsConnection *connection)
 {
-	g_return_val_if_fail (NM_SETTINGS_CONNECTION (connection), 0);
+	g_return_val_if_fail (connection != NULL, 0);
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), 0);
 
 	return NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->timestamp;
 }
@@ -1368,18 +1448,9 @@ nm_settings_connection_init (NMSettingsConnection *self)
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 	static guint32 dbus_counter = 0;
 	char *dbus_path;
-	GError *error = NULL;
 
 	priv->dbus_mgr = nm_dbus_manager_get ();
 
-	priv->authority = polkit_authority_get_sync (NULL, &error);
-	if (!priv->authority) {
-		nm_log_warn (LOGD_SETTINGS, "failed to create PolicyKit authority: (%d) %s",
-		             error ? error->code : -1,
-		             error && error->message ? error->message : "(unknown)");
-		g_clear_error (&error);
-	}
-
 	dbus_path = g_strdup_printf ("%s/%u", NM_DBUS_PATH_SETTINGS, dbus_counter++);
 	nm_connection_set_path (NM_CONNECTION (self), dbus_path);
 	g_free (dbus_path);
@@ -1421,10 +1492,11 @@ dispose (GObject *object)
 
 	set_visible (self, FALSE);
 
+	if (priv->session_changed_id)
+		g_signal_handler_disconnect (priv->session_monitor, priv->session_changed_id);
 	g_object_unref (priv->session_monitor);
 	g_object_unref (priv->agent_mgr);
 	g_object_unref (priv->dbus_mgr);
-	g_object_unref (priv->authority);
 
 out:
 	G_OBJECT_CLASS (nm_settings_connection_parent_class)->dispose (object);