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.c644
1 files changed, 409 insertions, 235 deletions
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index d20dbc98..529f4d8f 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -15,32 +15,29 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * (C) Copyright 2008 Novell, Inc.
- * (C) Copyright 2008 - 2013 Red Hat, Inc.
+ * Copyright 2008 Novell, Inc.
+ * Copyright 2008 - 2014 Red Hat, Inc.
  */
 
 #include "config.h"
 
 #include <string.h>
-#include <netinet/ether.h>
 
-#include <NetworkManager.h>
+#include <nm-dbus-interface.h>
 #include <dbus/dbus-glib-lowlevel.h>
-#include <nm-setting-connection.h>
-#include <nm-setting-vpn.h>
-#include <nm-setting-wireless.h>
-#include <nm-utils.h>
 
 #include "nm-settings-connection.h"
 #include "nm-session-monitor.h"
 #include "nm-dbus-manager.h"
-#include "nm-settings-error.h"
 #include "nm-dbus-glib-types.h"
 #include "nm-logging.h"
-#include "nm-manager-auth.h"
+#include "nm-auth-utils.h"
+#include "nm-auth-subject.h"
 #include "nm-agent-manager.h"
 #include "NetworkManagerUtils.h"
 #include "nm-properties-changed-signal.h"
+#include "nm-core-internal.h"
+#include "nm-glib-compat.h"
 
 #define SETTINGS_TIMESTAMPS_FILE  NMSTATEDIR "/timestamps"
 #define SETTINGS_SEEN_BSSIDS_FILE NMSTATEDIR "/seen-bssids"
@@ -66,9 +63,16 @@ static void impl_settings_connection_get_secrets (NMSettingsConnection *connecti
                                                   const gchar *setting_name,
                                                   DBusGMethodInvocation *context);
 
+static void impl_settings_connection_clear_secrets (NMSettingsConnection *connection,
+                                                    DBusGMethodInvocation *context);
+
 #include "nm-settings-connection-glue.h"
 
-G_DEFINE_TYPE (NMSettingsConnection, nm_settings_connection, NM_TYPE_CONNECTION)
+static void nm_settings_connection_connection_interface_init (NMConnectionInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingsConnection, nm_settings_connection, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (NM_TYPE_CONNECTION, nm_settings_connection_connection_interface_init)
+                         )
 
 #define NM_SETTINGS_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
                                                NM_TYPE_SETTINGS_CONNECTION, \
@@ -78,6 +82,7 @@ enum {
 	PROP_0 = 0,
 	PROP_VISIBLE,
 	PROP_UNSAVED,
+	PROP_FLAGS,
 };
 
 enum {
@@ -89,21 +94,11 @@ enum {
 static guint signals[LAST_SIGNAL] = { 0 };
 
 typedef struct {
-	gboolean disposed;
-
 	NMAgentManager *agent_mgr;
 	NMSessionMonitor *session_monitor;
 	guint session_changed_id;
 
-	/* TRUE if the connection has not yet been saved to disk,
-	 * or if it contains changes that have not been saved to disk.
-	 */
-	gboolean unsaved;
-
-	/* TRUE if the connection was generated by NetworkManager and has
-	 * not been saved or modified by the user.
-	 */
-	gboolean nm_generated;
+	NMSettingsConnectionFlags flags;
 
 	guint updated_idle_id;
 
@@ -146,6 +141,7 @@ typedef gboolean (*ForEachSecretFunc) (GHashTableIter *iter,
 static void
 for_each_secret (NMConnection *connection,
                  GHashTable *secrets,
+                 gboolean remove_non_secrets,
                  ForEachSecretFunc callback,
                  gpointer callback_data)
 {
@@ -169,6 +165,8 @@ for_each_secret (NMConnection *connection,
 	 * each time.
 	 */
 
+	g_return_if_fail (callback);
+
 	/* 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)) {
@@ -177,6 +175,9 @@ for_each_secret (NMConnection *connection,
 		const char *secret_name;
 		GValue *val;
 
+		if (g_hash_table_size (setting_hash) == 0)
+			continue;
+
 		/* 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.
@@ -205,7 +206,11 @@ for_each_secret (NMConnection *connection,
 						return;
 				}
 			} else {
-				nm_setting_get_secret_flags (setting, secret_name, &secret_flags, NULL);
+				if (!nm_setting_get_secret_flags (setting, secret_name, &secret_flags, NULL)) {
+					if (remove_non_secrets)
+						g_hash_table_iter_remove (&secret_iter);
+					continue;
+				}
 				if (callback (&secret_iter, secret_flags, callback_data) == FALSE)
 					return;
 			}
@@ -355,7 +360,7 @@ update_system_secrets_cache (NMSettingsConnection *self)
 
 	if (priv->system_secrets)
 		g_object_unref (priv->system_secrets);
-	priv->system_secrets = nm_connection_duplicate (NM_CONNECTION (self));
+	priv->system_secrets = nm_simple_connection_new_clone (NM_CONNECTION (self));
 
 	/* Clear out non-system-owned and not-saved secrets */
 	nm_connection_clear_secrets_with_flags (priv->system_secrets,
@@ -371,7 +376,7 @@ update_agent_secrets_cache (NMSettingsConnection *self, NMConnection *new)
 
 	if (priv->agent_secrets)
 		g_object_unref (priv->agent_secrets);
-	priv->agent_secrets = nm_connection_duplicate (new ? new : NM_CONNECTION (self));
+	priv->agent_secrets = nm_simple_connection_new_clone (new ? new : NM_CONNECTION (self));
 
 	/* Clear out non-system-owned secrets */
 	nm_connection_clear_secrets_with_flags (priv->agent_secrets,
@@ -403,14 +408,17 @@ emit_updated (NMSettingsConnection *self)
 static void
 set_unsaved (NMSettingsConnection *self, gboolean now_unsaved)
 {
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-
-	if (priv->unsaved != now_unsaved) {
-		priv->unsaved = now_unsaved;
-		if (!priv->unsaved)
-			priv->nm_generated = FALSE;
+	NMSettingsConnectionFlags flags = nm_settings_connection_get_flags (self);
 
-		g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTION_UNSAVED);
+	if (NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_FLAGS_UNSAVED) != !!now_unsaved) {
+		if (now_unsaved)
+			flags |= NM_SETTINGS_CONNECTION_FLAGS_UNSAVED;
+		else {
+			flags &= ~(NM_SETTINGS_CONNECTION_FLAGS_UNSAVED |
+			           NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED |
+			           NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED);
+		}
+		nm_settings_connection_set_flags_all (self, flags);
 	}
 }
 
@@ -437,7 +445,6 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self,
                                          GError **error)
 {
 	NMSettingsConnectionPrivate *priv;
-	GHashTable *hash = NULL;
 	gboolean success = FALSE;
 
 	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
@@ -445,8 +452,7 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self,
 
 	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
-	nm_utils_normalize_connection (new_connection, TRUE);
-	if (!nm_connection_verify (new_connection, error))
+	if (!nm_connection_normalize (new_connection, NULL, NULL, error))
 		return FALSE;
 
 	/* Do nothing if there's nothing to update */
@@ -461,37 +467,40 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self,
 	 */
 	g_signal_handlers_block_by_func (self, G_CALLBACK (changed_cb), GUINT_TO_POINTER (TRUE));
 
-	if (nm_connection_replace_settings_from_connection (NM_CONNECTION (self),
-	                                                    new_connection,
-	                                                    error)) {
-		priv->nm_generated = FALSE;
+	nm_utils_log_connection_diff (new_connection, NM_CONNECTION (self), LOGL_DEBUG, LOGD_CORE, "update connection", "++ ");
 
-		/* Cache the just-updated system secrets in case something calls
-		 * nm_connection_clear_secrets() and clears them.
-		 */
-		update_system_secrets_cache (self);
-		success = TRUE;
+	nm_connection_replace_settings_from_connection (NM_CONNECTION (self), new_connection);
+	nm_settings_connection_set_flags (self,
+	                                  NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED | NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED,
+	                                  FALSE);
 
-		/* Add agent and always-ask secrets back; they won't necessarily be
-		 * in the replacement connection data if it was eg reread from disk.
-		 */
-		if (priv->agent_secrets) {
-			hash = nm_connection_to_hash (priv->agent_secrets, NM_SETTING_HASH_FLAG_ONLY_SECRETS);
-			if (hash) {
-				(void) nm_connection_update_secrets (NM_CONNECTION (self), NULL, hash, NULL);
-				g_hash_table_destroy (hash);
-			}
+	/* Cache the just-updated system secrets in case something calls
+	 * nm_connection_clear_secrets() and clears them.
+	 */
+	update_system_secrets_cache (self);
+	success = TRUE;
+
+	/* Add agent and always-ask secrets back; they won't necessarily be
+	 * in the replacement connection data if it was eg reread from disk.
+	 */
+	if (priv->agent_secrets) {
+		GVariant *dict;
+
+		dict = nm_connection_to_dbus (priv->agent_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
+		if (dict) {
+			(void) nm_connection_update_secrets (NM_CONNECTION (self), NULL, dict, NULL);
+			g_variant_unref (dict);
 		}
+	}
 
-		nm_settings_connection_recheck_visibility (self);
+	nm_settings_connection_recheck_visibility (self);
 
-		/* Manually emit changed signal since we disconnected the handler, but
-		 * only update Unsaved if the caller wanted us to.
-		 */
-		changed_cb (self, GUINT_TO_POINTER (update_unsaved));
+	/* Manually emit changed signal since we disconnected the handler, but
+	 * only update Unsaved if the caller wanted us to.
+	 */
+	changed_cb (self, GUINT_TO_POINTER (update_unsaved));
 
-		g_signal_emit (self, signals[UPDATED_BY_USER], 0);
-	}
+	g_signal_emit (self, signals[UPDATED_BY_USER], 0);
 
 	g_signal_handlers_unblock_by_func (self, G_CALLBACK (changed_cb), GUINT_TO_POINTER (TRUE));
 
@@ -558,7 +567,7 @@ nm_settings_connection_commit_changes (NMSettingsConnection *connection,
 		                                                               user_data);
 	} else {
 		GError *error = g_error_new (NM_SETTINGS_ERROR,
-		                             NM_SETTINGS_ERROR_INTERNAL_ERROR,
+		                             NM_SETTINGS_ERROR_FAILED,
 		                             "%s: %s:%d commit_changes() unimplemented", __func__, __FILE__, __LINE__);
 		if (callback)
 			callback (connection, error, user_data);
@@ -579,7 +588,7 @@ nm_settings_connection_delete (NMSettingsConnection *connection,
 		                                                       user_data);
 	} else {
 		GError *error = g_error_new (NM_SETTINGS_ERROR,
-		                             NM_SETTINGS_ERROR_INTERNAL_ERROR,
+		                             NM_SETTINGS_ERROR_FAILED,
 		                             "%s: %s:%d delete() unimplemented", __func__, __FILE__, __LINE__);
 		if (callback)
 			callback (connection, error, user_data);
@@ -635,7 +644,7 @@ do_delete (NMSettingsConnection *connection,
 	set_visible (connection, FALSE);
 
 	/* Tell agents to remove secrets for this connection */
-	for_agents = nm_connection_duplicate (NM_CONNECTION (connection));
+	for_agents = nm_simple_connection_new_clone (NM_CONNECTION (connection));
 	nm_connection_clear_secrets (for_agents);
 	nm_agent_manager_delete_secrets (priv->agent_mgr, for_agents);
 	g_object_unref (for_agents);
@@ -714,7 +723,7 @@ agent_secrets_done_cb (NMAgentManager *manager,
                        const char *agent_username,
                        gboolean agent_has_modify,
                        const char *setting_name,
-                       NMSettingsGetSecretsFlags flags,
+                       NMSecretAgentGetSecretsFlags flags,
                        GHashTable *secrets,
                        GError *error,
                        gpointer user_data,
@@ -726,7 +735,7 @@ agent_secrets_done_cb (NMAgentManager *manager,
 	NMSettingsConnectionSecretsFunc callback = other_data2;
 	gpointer callback_data = other_data3;
 	GError *local = NULL;
-	GHashTable *hash;
+	GVariant *dict;
 	gboolean agent_had_system = FALSE;
 
 	if (error) {
@@ -742,7 +751,7 @@ agent_secrets_done_cb (NMAgentManager *manager,
 	}
 
 	if (!nm_connection_get_setting_by_name (NM_CONNECTION (self), setting_name)) {
-		local = g_error_new (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_SETTING,
+		local = g_error_new (NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_SETTING_NOT_FOUND,
 		                     "%s.%d - Connection didn't have requested setting '%s'.",
 		                     __FILE__, __LINE__, setting_name);
 		callback (self, call_id, NULL, setting_name, local, callback_data);
@@ -764,9 +773,9 @@ agent_secrets_done_cb (NMAgentManager *manager,
 		 * save those system-owned secrets.  If not, discard them and use the
 		 * existing secrets, or fail the connection.
 		 */
-		for_each_secret (NM_CONNECTION (self), secrets, has_system_owned_secrets, &agent_had_system);
+		for_each_secret (NM_CONNECTION (self), secrets, TRUE, has_system_owned_secrets, &agent_had_system);
 		if (agent_had_system) {
-			if (flags == NM_SETTINGS_GET_SECRETS_FLAG_NONE) {
+			if (flags == NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE) {
 				/* No user interaction was allowed when requesting secrets; the
 				 * agent is being bad.  Remove system-owned secrets.
 				 */
@@ -776,7 +785,7 @@ agent_secrets_done_cb (NMAgentManager *manager,
 				            call_id,
 				            agent_dbus_owner);
 
-				for_each_secret (NM_CONNECTION (self), secrets, clear_nonagent_secrets, NULL);
+				for_each_secret (NM_CONNECTION (self), secrets, FALSE, clear_nonagent_secrets, NULL);
 			} else if (agent_has_modify == FALSE) {
 				/* Agent didn't successfully authenticate; clear system-owned secrets
 				 * from the secrets the agent returned.
@@ -786,7 +795,7 @@ agent_secrets_done_cb (NMAgentManager *manager,
 				            setting_name,
 				            call_id);
 
-				for_each_secret (NM_CONNECTION (self), secrets, clear_nonagent_secrets, NULL);
+				for_each_secret (NM_CONNECTION (self), secrets, FALSE, clear_nonagent_secrets, NULL);
 			}
 		}
 	} else {
@@ -804,19 +813,22 @@ agent_secrets_done_cb (NMAgentManager *manager,
 	/* If no user interaction was allowed, make sure that no "unsaved" secrets
 	 * came back.  Unsaved secrets by definition require user interaction.
 	 */
-	if (flags == NM_SETTINGS_GET_SECRETS_FLAG_NONE)
-		for_each_secret (NM_CONNECTION (self), secrets, clear_unsaved_secrets, NULL);
+	if (flags == NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE)
+		for_each_secret (NM_CONNECTION (self), secrets, TRUE, clear_unsaved_secrets, NULL);
 
 	/* Update the connection with our existing secrets from backing storage */
 	nm_connection_clear_secrets (NM_CONNECTION (self));
-	hash = nm_connection_to_hash (priv->system_secrets, NM_SETTING_HASH_FLAG_ONLY_SECRETS);
-	if (!hash || nm_connection_update_secrets (NM_CONNECTION (self), setting_name, hash, &local)) {
+	dict = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
+	if (!dict || nm_connection_update_secrets (NM_CONNECTION (self), setting_name, dict, &local)) {
+		GVariant *secrets_dict;
+
 		/* Update the connection with the agent's secrets; by this point if any
 		 * system-owned secrets exist in 'secrets' the agent that provided them
 		 * will have been authenticated, so those secrets can replace the existing
 		 * system secrets.
 		 */
-		if (nm_connection_update_secrets (NM_CONNECTION (self), setting_name, secrets, &local)) {
+		secrets_dict = nm_utils_connection_hash_to_dict (secrets);
+		if (nm_connection_update_secrets (NM_CONNECTION (self), setting_name, secrets_dict, &local)) {
 			/* Now that all secrets are updated, copy and cache new secrets, 
 			 * then save them to backing storage.
 			 */
@@ -849,6 +861,7 @@ agent_secrets_done_cb (NMAgentManager *manager,
 			            local ? local->code : -1,
 			            (local && local->message) ? local->message : "(unknown)");
 		}
+		g_variant_unref (secrets_dict);
 	} else {
 		nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) failed to update with existing secrets: (%d) %s",
 		            nm_connection_get_uuid (NM_CONNECTION (self)),
@@ -860,8 +873,8 @@ agent_secrets_done_cb (NMAgentManager *manager,
 
 	callback (self, call_id, agent_username, setting_name, local, callback_data);
 	g_clear_error (&local);
-	if (hash)
-		g_hash_table_destroy (hash);
+	if (dict)
+		g_variant_unref (dict);
 }
 
 /**
@@ -884,14 +897,15 @@ guint32
 nm_settings_connection_get_secrets (NMSettingsConnection *self,
                                     NMAuthSubject *subject,
                                     const char *setting_name,
-                                    NMSettingsGetSecretsFlags flags,
+                                    NMSecretAgentGetSecretsFlags flags,
                                     const char **hints,
                                     NMSettingsConnectionSecretsFunc callback,
                                     gpointer callback_data,
                                     GError **error)
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-	GHashTable *existing_secrets;
+	GVariant *existing_secrets;
+	GHashTable *existing_secrets_hash;
 	guint32 call_id = 0;
 	char *joined_hints = NULL;
 
@@ -899,7 +913,7 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
 	 * will clear secrets on this object's settings.
 	 */
 	if (!priv->system_secrets) {
-		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		             "%s.%d - Internal error; secrets cache invalid.",
 		             __FILE__, __LINE__);
 		return 0;
@@ -907,17 +921,18 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
 
 	/* Make sure the request actually requests something we can return */
 	if (!nm_connection_get_setting_by_name (NM_CONNECTION (self), setting_name)) {
-		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_SETTING,
+		g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_SETTING_NOT_FOUND,
 		             "%s.%d - Connection didn't have requested setting '%s'.",
 		             __FILE__, __LINE__, setting_name);
 		return 0;
 	}
 
-	existing_secrets = nm_connection_to_hash (priv->system_secrets, NM_SETTING_HASH_FLAG_ONLY_SECRETS);
+	existing_secrets = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
+	existing_secrets_hash = nm_utils_connection_dict_to_hash (existing_secrets);
 	call_id = nm_agent_manager_get_secrets (priv->agent_mgr,
 	                                        NM_CONNECTION (self),
 	                                        subject,
-	                                        existing_secrets,
+	                                        existing_secrets_hash,
 	                                        setting_name,
 	                                        flags,
 	                                        hints,
@@ -925,8 +940,10 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
 	                                        self,
 	                                        callback,
 	                                        callback_data);
+	if (existing_secrets_hash)
+		g_hash_table_unref (existing_secrets_hash);
 	if (existing_secrets)
-		g_hash_table_unref (existing_secrets);
+		g_variant_unref (existing_secrets);
 
 	if (nm_logging_enabled (LOGL_DEBUG, LOGD_SETTINGS)) {
 		if (hints)
@@ -989,7 +1006,7 @@ pk_auth_cb (NMAuthChain *chain,
 	/* If our NMSettingsConnection is already gone, do nothing */
 	if (chain_error) {
 		error = g_error_new (NM_SETTINGS_ERROR,
-		                     NM_SETTINGS_ERROR_GENERAL,
+		                     NM_SETTINGS_ERROR_FAILED,
 		                     "Error checking authorization: %s",
 		                     chain_error->message ? chain_error->message : "(unknown)");
 	} else if (result != NM_AUTH_CALL_RESULT_YES) {
@@ -1021,7 +1038,7 @@ _new_auth_subject (DBusGMethodInvocation *context, GError **error)
 {
 	NMAuthSubject *subject;
 
-	subject = nm_auth_subject_new_from_context (context);
+	subject = nm_auth_subject_new_unix_process_from_context (context);
 	if (!subject) {
 		g_set_error_literal (error,
 		                     NM_SETTINGS_ERROR,
@@ -1049,10 +1066,10 @@ auth_start (NMSettingsConnection *self,
 	g_return_if_fail (NM_IS_AUTH_SUBJECT (subject));
 
 	/* Ensure the caller can view this connection */
-	if (!nm_auth_uid_in_acl (NM_CONNECTION (self),
-	                         priv->session_monitor,
-	                         nm_auth_subject_get_uid (subject),
-	                         &error_desc)) {
+	if (!nm_auth_is_subject_in_acl (NM_CONNECTION (self),
+	                                priv->session_monitor,
+	                                subject,
+	                                &error_desc)) {
 		error = g_error_new_literal (NM_SETTINGS_ERROR,
 		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
 		                             error_desc);
@@ -1131,14 +1148,15 @@ get_settings_auth_cb (NMSettingsConnection *self,
 	if (error)
 		dbus_g_method_return_error (context, error);
 	else {
-		GHashTable *settings;
+		GVariant *settings;
+		GHashTable *settings_hash;
 		NMConnection *dupl_con;
 		NMSettingConnection *s_con;
 		NMSettingWireless *s_wifi;
 		guint64 timestamp = 0;
-		GSList *bssid_list;
+		char **bssids;
 
-		dupl_con = nm_connection_duplicate (NM_CONNECTION (self));
+		dupl_con = nm_simple_connection_new_clone (NM_CONNECTION (self));
 		g_assert (dupl_con);
 
 		/* Timestamp is not updated in connection's 'timestamp' property,
@@ -1157,33 +1175,22 @@ get_settings_auth_cb (NMSettingsConnection *self,
 		 * from the same reason as timestamp. Thus we put it here to GetSettings()
 		 * return settings too.
 		 */
-		bssid_list = nm_settings_connection_get_seen_bssids (self);
+		bssids = nm_settings_connection_get_seen_bssids (self);
 		s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (dupl_con));
-		if (bssid_list && s_wifi) {
-			g_object_set (s_wifi, NM_SETTING_WIRELESS_SEEN_BSSIDS, bssid_list, NULL);
-			g_slist_free_full (bssid_list, g_free);
-		}
-
-		/* 802-11-wireless.security property is deprecated. But we set it here so that
-		 * we don't disturb old clients that might expect it being properly set for
-		 * secured Wi-Fi connections.
-		 */
-		if (nm_connection_get_setting_wireless_security (NM_CONNECTION (dupl_con))) {
-			s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (dupl_con));
-			g_assert (s_wifi);
-			g_object_set (s_wifi,
-			              NM_SETTING_WIRELESS_SEC, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
-			              NULL);
-		}
+		if (bssids && bssids[0] && s_wifi)
+			g_object_set (s_wifi, NM_SETTING_WIRELESS_SEEN_BSSIDS, bssids, NULL);
+		g_free (bssids);
 
 		/* Secrets should *never* be returned by the GetSettings method, they
 		 * get returned by the GetSecrets method which can be better
 		 * protected against leakage of secrets to unprivileged callers.
 		 */
-		settings = nm_connection_to_hash (NM_CONNECTION (dupl_con), NM_SETTING_HASH_FLAG_NO_SECRETS);
+		settings = nm_connection_to_dbus (NM_CONNECTION (dupl_con), NM_CONNECTION_SERIALIZE_NO_SECRETS);
 		g_assert (settings);
-		dbus_g_method_return (context, settings);
-		g_hash_table_destroy (settings);
+		settings_hash = nm_utils_connection_dict_to_hash (settings);
+		dbus_g_method_return (context, settings_hash);
+		g_hash_table_destroy (settings_hash);
+		g_variant_unref (settings);
 		g_object_unref (dupl_con);
 	}
 }
@@ -1214,6 +1221,60 @@ typedef struct {
 } UpdateInfo;
 
 static void
+has_some_secrets_cb (NMSetting *setting,
+                     const char *key,
+                     const GValue *value,
+                     GParamFlags flags,
+                     gpointer user_data)
+{
+	GParamSpec *pspec;
+
+	if (NM_IS_SETTING_VPN (setting)) {
+		if (nm_setting_vpn_get_num_secrets (NM_SETTING_VPN(setting)))
+			*((gboolean *) user_data) = TRUE;
+		return;
+	}
+
+	pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), key);
+	if (pspec) {
+		if (   (flags & NM_SETTING_PARAM_SECRET)
+		    && !g_param_value_defaults (pspec, (GValue *)value))
+			*((gboolean *) user_data) = TRUE;
+	}
+}
+
+static gboolean
+any_secrets_present (NMConnection *connection)
+{
+	gboolean has_secrets = FALSE;
+
+	nm_connection_for_each_setting_value (connection, has_some_secrets_cb, &has_secrets);
+	return has_secrets;
+}
+
+static void
+cached_secrets_to_connection (NMSettingsConnection *self, NMConnection *connection)
+{
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+	GVariant *secrets_dict;
+
+	if (priv->agent_secrets) {
+		secrets_dict = nm_connection_to_dbus (priv->agent_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
+		if (secrets_dict) {
+			(void) nm_connection_update_secrets (connection, NULL, secrets_dict, NULL);
+			g_variant_unref (secrets_dict);
+		}
+	}
+	if (priv->system_secrets) {
+		secrets_dict = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
+		if (secrets_dict) {
+			(void) nm_connection_update_secrets (connection, NULL, secrets_dict, NULL);
+			g_variant_unref (secrets_dict);
+		}
+	}
+}
+
+static void
 update_complete (NMSettingsConnection *self,
                  UpdateInfo *info,
                  GError *error)
@@ -1243,7 +1304,7 @@ con_update_cb (NMSettingsConnection *self,
 		 * as agent-owned secrets are the only ones we send back be saved.
 		 * Only send secrets to agents of the same UID that called update too.
 		 */
-		for_agent = nm_connection_duplicate (NM_CONNECTION (self));
+		for_agent = nm_simple_connection_new_clone (NM_CONNECTION (self));
 		nm_connection_clear_secrets_with_flags (for_agent,
 		                                        secrets_filter_cb,
 		                                        GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED));
@@ -1269,11 +1330,19 @@ update_auth_cb (NMSettingsConnection *self,
 		return;
 	}
 
-	/* Cache the new secrets from the agent, as stuff like inotify-triggered
-	 * changes to connection's backing config files will blow them away if
-	 * they're in the main connection.
-	 */
-	update_agent_secrets_cache (self, info->new_settings);
+	if (!any_secrets_present (info->new_settings)) {
+		/* If the new connection has no secrets, we do not want to remove all
+		 * secrets, rather we keep all the existing ones. Do that by merging
+		 * them in to the new connection.
+		 */
+		cached_secrets_to_connection (self, info->new_settings);
+	} else {
+		/* Cache the new secrets from the agent, as stuff like inotify-triggered
+		 * changes to connection's backing config files will blow them away if
+		 * they're in the main connection.
+		 */
+		update_agent_secrets_cache (self, info->new_settings);
+	}
 
 	if (info->save_to_disk) {
 		nm_settings_connection_replace_and_commit (self,
@@ -1342,7 +1411,10 @@ impl_settings_connection_update_helper (NMSettingsConnection *self,
 
 	/* Check if the settings are valid first */
 	if (new_settings) {
-		tmp = nm_connection_new_from_hash (new_settings, &error);
+		GVariant *new_settings_dict = nm_utils_connection_hash_to_dict (new_settings);
+
+		tmp = nm_simple_connection_new_from_dbus (new_settings_dict, &error);
+		g_variant_unref (new_settings_dict);
 		if (!tmp) {
 			g_assert (error);
 			goto error;
@@ -1357,10 +1429,10 @@ impl_settings_connection_update_helper (NMSettingsConnection *self,
 	 * that's sending the update request.  You can't make a connection
 	 * invisible to yourself.
 	 */
-	if (!nm_auth_uid_in_acl (tmp ? tmp : NM_CONNECTION (self),
-	                         priv->session_monitor,
-	                         nm_auth_subject_get_uid (subject),
-	                         &error_desc)) {
+	if (!nm_auth_is_subject_in_acl (tmp ? tmp : NM_CONNECTION (self),
+	                                priv->session_monitor,
+	                                subject,
+	                                &error_desc)) {
 		error = g_error_new_literal (NM_SETTINGS_ERROR,
 		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
 		                             error_desc);
@@ -1411,7 +1483,7 @@ impl_settings_connection_save (NMSettingsConnection *self,
                                DBusGMethodInvocation *context)
 {
 	/* Do nothing if the connection is already synced with disk */
-	if (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->unsaved == TRUE)
+	if (nm_settings_connection_get_unsaved (self))
 		impl_settings_connection_update_helper (self, NULL, context, TRUE);
 	else
 		dbus_g_method_return (context);
@@ -1497,6 +1569,7 @@ dbus_get_agent_secrets_cb (NMSettingsConnection *self,
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 	DBusGMethodInvocation *context = user_data;
+	GVariant *dict;
 	GHashTable *hash;
 
 	priv->reqs = g_slist_remove (priv->reqs, GUINT_TO_POINTER (call_id));
@@ -1509,20 +1582,24 @@ dbus_get_agent_secrets_cb (NMSettingsConnection *self,
 		 * secrets from backing storage and those returned from the agent
 		 * by the time we get here.
 		 */
-		hash = nm_connection_to_hash (NM_CONNECTION (self), NM_SETTING_HASH_FLAG_ONLY_SECRETS);
-		if (!hash)
+		dict = nm_connection_to_dbus (NM_CONNECTION (self), NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
+		if (dict)
+			hash = nm_utils_connection_dict_to_hash (dict);
+		else
 			hash = g_hash_table_new (NULL, NULL);
 		dbus_g_method_return (context, hash);
 		g_hash_table_destroy (hash);
+		if (dict)
+			g_variant_unref (dict);
 	}
 }
 
 static void
-dbus_secrets_auth_cb (NMSettingsConnection *self, 
-                      DBusGMethodInvocation *context,
-                      NMAuthSubject *subject,
-                      GError *error,
-                      gpointer user_data)
+dbus_get_secrets_auth_cb (NMSettingsConnection *self, 
+                          DBusGMethodInvocation *context,
+                          NMAuthSubject *subject,
+                          GError *error,
+                          gpointer user_data)
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 	char *setting_name = user_data;
@@ -1533,7 +1610,8 @@ dbus_secrets_auth_cb (NMSettingsConnection *self,
 		call_id = nm_settings_connection_get_secrets (self,
 			                                          subject,
 			                                          setting_name,
-			                                          NM_SETTINGS_GET_SECRETS_FLAG_USER_REQUESTED,
+			                                            NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED
+			                                          | NM_SECRET_AGENT_GET_SECRETS_FLAG_NO_ERRORS,
 			                                          NULL,
 			                                          dbus_get_agent_secrets_cb,
 			                                          context,
@@ -1566,7 +1644,7 @@ impl_settings_connection_get_secrets (NMSettingsConnection *self,
 		            context,
 		            subject,
 		            get_modify_permission_basic (self),
-		            dbus_secrets_auth_cb,
+		            dbus_get_secrets_auth_cb,
 		            g_strdup (setting_name));
 		g_object_unref (subject);
 	} else {
@@ -1575,6 +1653,67 @@ impl_settings_connection_get_secrets (NMSettingsConnection *self,
 	}
 }
 
+static void
+clear_secrets_cb (NMSettingsConnection *self,
+                  GError *error,
+                  gpointer user_data)
+{
+	DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
+
+	if (error)
+		dbus_g_method_return_error (context, error);
+	else
+		dbus_g_method_return (context);
+}
+
+static void
+dbus_clear_secrets_auth_cb (NMSettingsConnection *self, 
+                            DBusGMethodInvocation *context,
+                            NMAuthSubject *subject,
+                            GError *error,
+                            gpointer user_data)
+{
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+
+	if (error)
+		dbus_g_method_return_error (context, error);
+	else {
+		/* Clear secrets in connection and caches */
+		nm_connection_clear_secrets (NM_CONNECTION (self));
+		if (priv->system_secrets)
+			nm_connection_clear_secrets (priv->system_secrets);
+		if (priv->agent_secrets)
+			nm_connection_clear_secrets (priv->agent_secrets);
+
+		/* Tell agents to remove secrets for this connection */
+		nm_agent_manager_delete_secrets (priv->agent_mgr, NM_CONNECTION (self));
+
+		nm_settings_connection_commit_changes (self, clear_secrets_cb, context);
+	}
+}
+
+static void
+impl_settings_connection_clear_secrets (NMSettingsConnection *self,
+                                        DBusGMethodInvocation *context)
+{
+	NMAuthSubject *subject;
+	GError *error = NULL;
+
+	subject = _new_auth_subject (context, &error);
+	if (subject) {
+		auth_start (self,
+		            context,
+		            subject,
+		            get_modify_permission_basic (self),
+		            dbus_clear_secrets_auth_cb,
+		            NULL);
+		g_object_unref (subject);
+	} else {
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+	}
+}
+
 /**************************************************************/
 
 void
@@ -1592,11 +1731,57 @@ nm_settings_connection_signal_remove (NMSettingsConnection *self)
 gboolean
 nm_settings_connection_get_unsaved (NMSettingsConnection *self)
 {
-	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->unsaved;
+	return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_FLAGS_UNSAVED);
 }
 
 /**************************************************************/
 
+NMSettingsConnectionFlags
+nm_settings_connection_get_flags (NMSettingsConnection *self)
+{
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_FLAGS_NONE);
+
+	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->flags;
+}
+
+NMSettingsConnectionFlags
+nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionFlags flags, gboolean set)
+{
+	NMSettingsConnectionFlags new_flags;
+
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_FLAGS_NONE);
+	g_return_val_if_fail ((flags & ~NM_SETTINGS_CONNECTION_FLAGS_ALL) == 0, NM_SETTINGS_CONNECTION_FLAGS_NONE);
+
+	new_flags = NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->flags;
+	if (set)
+		new_flags |= flags;
+	else
+		new_flags &= ~flags;
+	return nm_settings_connection_set_flags_all (self, new_flags);
+}
+
+NMSettingsConnectionFlags
+nm_settings_connection_set_flags_all (NMSettingsConnection *self, NMSettingsConnectionFlags flags)
+{
+	NMSettingsConnectionPrivate *priv;
+	NMSettingsConnectionFlags old_flags;
+
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_FLAGS_NONE);
+	g_return_val_if_fail ((flags & ~NM_SETTINGS_CONNECTION_FLAGS_ALL) == 0, NM_SETTINGS_CONNECTION_FLAGS_NONE);
+	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+
+	old_flags = priv->flags;
+	if (old_flags != flags) {
+		priv->flags = flags;
+		g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTION_FLAGS);
+		if (NM_FLAGS_HAS (old_flags, NM_SETTINGS_CONNECTION_FLAGS_UNSAVED) != NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_FLAGS_UNSAVED))
+			g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTION_UNSAVED);
+	}
+	return old_flags;
+}
+
+/*************************************************************/
+
 /**
  * nm_settings_connection_get_timestamp:
  * @connection: the #NMSettingsConnection
@@ -1714,59 +1899,34 @@ nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *connection
 	g_key_file_free (timestamps_file);
 }
 
-static guint
-mac_hash (gconstpointer v)
-{
-	const guint8 *p = v;
-	guint32 i, h = 5381;
-
-	for (i = 0; i < ETH_ALEN; i++)
-		h = (h << 5) + h + p[i];
-	return h;
-}
-
-static gboolean
-mac_equal (gconstpointer a, gconstpointer b)
-{
-	return memcmp (a, b, ETH_ALEN) == 0;
-}
-
-static guint8 *
-mac_dup (const struct ether_addr *old)
-{
-	guint8 *new;
-
-	g_return_val_if_fail (old != NULL, NULL);
-
-	new = g_malloc0 (ETH_ALEN);
-	memcpy (new, old, ETH_ALEN);
-	return new;
-}
-
 /**
  * nm_settings_connection_get_seen_bssids:
  * @connection: the #NMSettingsConnection
  *
  * Returns current list of seen BSSIDs for the connection.
  *
- * Returns: (transfer full) list of seen BSSIDs (in the standard hex-digits-and-colons notation).
- * The caller is responsible for freeing the list.
+ * Returns: (transfer container) list of seen BSSIDs (in the standard hex-digits-and-colons notation).
+ * The caller is responsible for freeing the list, but not the content.
  **/
-GSList *
+char **
 nm_settings_connection_get_seen_bssids (NMSettingsConnection *connection)
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
 	GHashTableIter iter;
-	char *bssid_str;
-	GSList *bssid_list = NULL;
+	char **bssids, *bssid;
+	int i;
 
 	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), NULL);
 
+	bssids = g_new (char *, g_hash_table_size (priv->seen_bssids) + 1);
+
+	i = 0;
 	g_hash_table_iter_init (&iter, priv->seen_bssids);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &bssid_str))
-		bssid_list = g_slist_prepend (bssid_list, g_strdup (bssid_str));
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &bssid))
+		bssids[i++] = bssid;
+	bssids[i] = NULL;
 
-	return bssid_list;
+	return bssids;
 }
 
 /**
@@ -1778,7 +1938,7 @@ nm_settings_connection_get_seen_bssids (NMSettingsConnection *connection)
  **/
 gboolean
 nm_settings_connection_has_seen_bssid (NMSettingsConnection *connection,
-                                       const struct ether_addr *bssid)
+                                       const char *bssid)
 {
 	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), FALSE);
 	g_return_val_if_fail (bssid != NULL, FALSE);
@@ -1796,7 +1956,7 @@ nm_settings_connection_has_seen_bssid (NMSettingsConnection *connection,
  **/
 void
 nm_settings_connection_add_seen_bssid (NMSettingsConnection *connection,
-                                       const struct ether_addr *seen_bssid)
+                                       const char *seen_bssid)
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
 	const char *connection_uuid;
@@ -1814,8 +1974,8 @@ nm_settings_connection_add_seen_bssid (NMSettingsConnection *connection,
 		return;  /* Already in the list */
 
 	/* Add the new BSSID; let the hash take ownership of the allocated BSSID string */
-	bssid_str = nm_utils_hwaddr_ntoa (seen_bssid, ARPHRD_ETHER);
-	g_hash_table_insert (priv->seen_bssids, mac_dup (seen_bssid), bssid_str);
+	bssid_str = g_strdup (seen_bssid);
+	g_hash_table_insert (priv->seen_bssids, bssid_str, bssid_str);
 
 	/* Build up a list of all the BSSIDs in string form */
 	n = 0;
@@ -1853,19 +2013,6 @@ nm_settings_connection_add_seen_bssid (NMSettingsConnection *connection,
 	}
 }
 
-static void
-add_seen_bssid_string (NMSettingsConnection *self, const char *bssid)
-{
-	struct ether_addr mac;
-
-	g_return_if_fail (bssid != NULL);
-	if (ether_aton_r (bssid, &mac)) {
-		g_hash_table_insert (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->seen_bssids,
-		                     mac_dup (&mac),
-		                     g_strdup (bssid));
-	}
-}
-
 /**
  * nm_settings_connection_read_and_fill_seen_bssids:
  * @connection: the #NMSettingsConnection
@@ -1896,8 +2043,8 @@ nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *connecti
 	if (tmp_strv) {
 		g_hash_table_remove_all (priv->seen_bssids);
 		for (i = 0; i < len; i++)
-			add_seen_bssid_string (connection, tmp_strv[i]);
-		g_strfreev (tmp_strv);
+			g_hash_table_insert (priv->seen_bssids, tmp_strv[i], tmp_strv[i]);
+		g_free (tmp_strv);
 	} else {
 		/* If this connection didn't have an entry in the seen-bssids database,
 		 * maybe this is the first time we've read it in, so populate the
@@ -1907,8 +2054,11 @@ nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *connecti
 		s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (connection));
 		if (s_wifi) {
 			len = nm_setting_wireless_get_num_seen_bssids (s_wifi);
-			for (i = 0; i < len; i++)
-				add_seen_bssid_string (connection, nm_setting_wireless_get_seen_bssid (s_wifi, i));
+			for (i = 0; i < len; i++) {
+				char *bssid_dup = g_strdup (nm_setting_wireless_get_seen_bssid (s_wifi, i));
+
+				g_hash_table_insert (priv->seen_bssids, bssid_dup, bssid_dup);
+			}
 		}
 	}
 }
@@ -1992,30 +2142,28 @@ nm_settings_connection_can_autoconnect (NMSettingsConnection *connection)
  * Gets the "nm-generated" flag on @connection.
  *
  * A connection is "nm-generated" if it was generated by
- * nm_device_generate_connection() and then assumed by #NMManager, and
- * it has not been modified or saved by the user since then. In other
- * words, an "nm-generated" connection reflects state that is entirely
- * external to NetworkManager.
+ * nm_device_generate_connection() and has not been modified or
+ * saved by the user since then.
  */
 gboolean
 nm_settings_connection_get_nm_generated (NMSettingsConnection *connection)
 {
-	return NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->nm_generated;
+	return NM_FLAGS_HAS (nm_settings_connection_get_flags (connection), NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED);
 }
 
 /**
- * nm_settings_connection_set_nm_generated:
+ * nm_settings_connection_get_nm_generated_assumed:
  * @connection: an #NMSettingsConnection
  *
- * Sets the "nm-generated" flag on @connection; see
- * nm_settings_connection_get_nm_generated().
+ * Gets the "nm-generated-assumed" flag on @connection.
+ *
+ * The connection is a generated connection especially
+ * generated for connection assumption.
  */
-void
-nm_settings_connection_set_nm_generated (NMSettingsConnection *connection)
+gboolean
+nm_settings_connection_get_nm_generated_assumed (NMSettingsConnection *connection)
 {
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
-
-	priv->nm_generated = TRUE;
+	return NM_FLAGS_HAS (nm_settings_connection_get_flags (connection), NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED);
 }
 
 /**************************************************************/
@@ -2035,7 +2183,7 @@ nm_settings_connection_init (NMSettingsConnection *self)
 
 	priv->agent_mgr = nm_agent_manager_get ();
 
-	priv->seen_bssids = g_hash_table_new_full (mac_hash, mac_equal, g_free, g_free);
+	priv->seen_bssids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
 	priv->autoconnect_retries = AUTOCONNECT_RETRIES_DEFAULT;
 	priv->autoconnect_blocked_reason = NM_DEVICE_STATE_REASON_NONE;
@@ -2051,40 +2199,42 @@ dispose (GObject *object)
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 	GSList *iter;
 
-	if (priv->disposed)
-		goto out;
-	priv->disposed = TRUE;
-
 	if (priv->updated_idle_id) {
 		g_source_remove (priv->updated_idle_id);
 		priv->updated_idle_id = 0;
 	}
 
-	if (priv->system_secrets)
-		g_object_unref (priv->system_secrets);
-	if (priv->agent_secrets)
-		g_object_unref (priv->agent_secrets);
+	/* Disconnect handlers.
+	 * changed_cb() has to be disconnected *before* nm_connection_clear_secrets(),
+	 * because nm_connection_clear_secrets() emits NM_CONNECTION_CHANGED signal.
+	 */
+	g_signal_handlers_disconnect_by_func (self, G_CALLBACK (secrets_cleared_cb), NULL);
+	g_signal_handlers_disconnect_by_func (self, G_CALLBACK (changed_cb), GUINT_TO_POINTER (TRUE));
+
+	nm_connection_clear_secrets (NM_CONNECTION (self));
+	g_clear_object (&priv->system_secrets);
+	g_clear_object (&priv->agent_secrets);
 
 	/* Cancel PolicyKit requests */
-	for (iter = priv->pending_auths; iter; iter = g_slist_next (iter))
-		nm_auth_chain_unref ((NMAuthChain *) iter->data);
-	g_slist_free (priv->pending_auths);
+	g_slist_free_full (priv->pending_auths, (GDestroyNotify) nm_auth_chain_unref);
 	priv->pending_auths = NULL;
 
 	/* Cancel in-progress secrets requests */
 	for (iter = priv->reqs; iter; iter = g_slist_next (iter))
 		nm_agent_manager_cancel_secrets (priv->agent_mgr, GPOINTER_TO_UINT (iter->data));
 	g_slist_free (priv->reqs);
+	priv->reqs = NULL;
 
-	g_hash_table_destroy (priv->seen_bssids);
+	g_clear_pointer (&priv->seen_bssids, (GDestroyNotify) g_hash_table_destroy);
 
 	set_visible (self, FALSE);
 
-	if (priv->session_changed_id)
+	if (priv->session_changed_id) {
 		g_signal_handler_disconnect (priv->session_monitor, priv->session_changed_id);
-	g_object_unref (priv->agent_mgr);
+		priv->session_changed_id = 0;
+	}
+	g_clear_object (&priv->agent_mgr);
 
-out:
 	G_OBJECT_CLASS (nm_settings_connection_parent_class)->dispose (object);
 }
 
@@ -2092,14 +2242,18 @@ static void
 get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
 {
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (object);
+	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
 	switch (prop_id) {
 	case PROP_VISIBLE:
 		g_value_set_boolean (value, priv->visible);
 		break;
 	case PROP_UNSAVED:
-		g_value_set_boolean (value, priv->unsaved);
+		g_value_set_boolean (value, nm_settings_connection_get_unsaved (self));
+		break;
+	case PROP_FLAGS:
+		g_value_set_uint (value, nm_settings_connection_get_flags (self));
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -2111,7 +2265,16 @@ static void
 set_property (GObject *object, guint prop_id,
               const GValue *value, GParamSpec *pspec)
 {
-	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
+
+	switch (prop_id) {
+	case PROP_FLAGS:
+		nm_settings_connection_set_flags_all (self, g_value_get_uint (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
 }
 
 static void
@@ -2133,21 +2296,26 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class)
 	/* Properties */
 	g_object_class_install_property
 		(object_class, PROP_VISIBLE,
-		 g_param_spec_boolean (NM_SETTINGS_CONNECTION_VISIBLE,
-		                       "Visible",
-		                       "Visible",
+		 g_param_spec_boolean (NM_SETTINGS_CONNECTION_VISIBLE, "", "",
 		                       FALSE,
-		                       G_PARAM_READABLE));
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_UNSAVED,
-		 g_param_spec_boolean (NM_SETTINGS_CONNECTION_UNSAVED,
-		                       "Unsaved",
-		                       "TRUE when the connection has not yet been saved "
-		                       "to permanent storage (eg disk) or when it "
-		                       "has been changed but not yet saved.",
+		 g_param_spec_boolean (NM_SETTINGS_CONNECTION_UNSAVED, "", "",
 		                       FALSE,
-		                       G_PARAM_READABLE));
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+	    (object_class, PROP_FLAGS,
+	     g_param_spec_uint (NM_SETTINGS_CONNECTION_FLAGS, "", "",
+	                        NM_SETTINGS_CONNECTION_FLAGS_NONE,
+	                        NM_SETTINGS_CONNECTION_FLAGS_ALL,
+	                        NM_SETTINGS_CONNECTION_FLAGS_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS));
 
 	/* Signals */
 
@@ -2183,3 +2351,9 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class)
 	                                        G_TYPE_FROM_CLASS (class),
 	                                        &dbus_glib_nm_settings_connection_object_info);
 }
+
+static void
+nm_settings_connection_connection_interface_init (NMConnectionInterface *iface)
+{
+}
+