about summary refs log tree commit diff
path: root/src/settings
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2011-10-28 23:04:16 +0200
committerMichael Biebl <biebl@debian.org>2011-10-28 23:04:16 +0200
commit485d149fe80915d94ed49ea6c2c0552cf7a3e79a (patch)
tree6a48492b46b8c1e3df1c58626c28f05a978c61f7 /src/settings
parent263bf4c0c89bb88dc995acd9a6a2de9095fbd461 (diff)
Imported Upstream version 0.9.1.95 upstream/0.9.1.95
Diffstat (limited to 'src/settings')
-rw-r--r--src/settings/Makefile.in8
-rw-r--r--src/settings/nm-settings-connection.c289
-rw-r--r--src/settings/nm-settings.c62
-rw-r--r--src/settings/nm-settings.h2
-rw-r--r--src/settings/plugins/Makefile.in8
-rw-r--r--src/settings/plugins/ifcfg-rh/Makefile.in8
-rw-r--r--src/settings/plugins/ifcfg-rh/plugin.c6
-rw-r--r--src/settings/plugins/ifcfg-rh/reader.c4
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.c20
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/Makefile.in8
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in8
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c202
-rw-r--r--src/settings/plugins/ifcfg-rh/writer.c3
-rw-r--r--src/settings/plugins/ifcfg-suse/Makefile.in8
-rw-r--r--src/settings/plugins/ifnet/Makefile.in8
-rw-r--r--src/settings/plugins/ifnet/connection_parser.c18
-rw-r--r--src/settings/plugins/ifnet/net_utils.c2
-rw-r--r--src/settings/plugins/ifnet/tests/Makefile.in8
-rw-r--r--src/settings/plugins/ifnet/tests/net2
-rw-r--r--src/settings/plugins/ifnet/tests/test_all.c48
-rw-r--r--src/settings/plugins/ifnet/tests/wpa_supplicant.conf3
-rw-r--r--src/settings/plugins/ifupdown/Makefile.in8
-rw-r--r--src/settings/plugins/ifupdown/tests/Makefile.in8
-rw-r--r--src/settings/plugins/keyfile/Makefile.in8
-rw-r--r--src/settings/plugins/keyfile/reader.c176
-rw-r--r--src/settings/plugins/keyfile/tests/Makefile.in8
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Makefile.am2
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Makefile.in10
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Test_Intlike_SSID11
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Test_Intlike_SSID_211
-rw-r--r--src/settings/plugins/keyfile/tests/test-keyfile.c246
-rw-r--r--src/settings/plugins/keyfile/writer.c45
-rw-r--r--src/settings/tests/Makefile.in8
33 files changed, 960 insertions, 306 deletions
diff --git a/src/settings/Makefile.in b/src/settings/Makefile.in
index 980f35cb..09da869d 100644
--- a/src/settings/Makefile.in
+++ b/src/settings/Makefile.in
@@ -248,8 +248,16 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
+LIBNL1_CFLAGS = @LIBNL1_CFLAGS@
+LIBNL1_LIBS = @LIBNL1_LIBS@
+LIBNL2_CFLAGS = @LIBNL2_CFLAGS@
+LIBNL2_LIBS = @LIBNL2_LIBS@
+LIBNL3_CFLAGS = @LIBNL3_CFLAGS@
+LIBNL3_LIBS = @LIBNL3_LIBS@
 LIBNL_CFLAGS = @LIBNL_CFLAGS@
 LIBNL_LIBS = @LIBNL_LIBS@
+LIBNL_ROUTE3_CFLAGS = @LIBNL_ROUTE3_CFLAGS@
+LIBNL_ROUTE3_LIBS = @LIBNL_ROUTE3_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index 4b3a56cd..cdad8325 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -84,15 +84,27 @@ typedef struct {
 
 	NMDBusManager *dbus_mgr;
 	NMAgentManager *agent_mgr;
+	NMSessionMonitor *session_monitor;
+	guint session_changed_id;
 
 	GSList *pending_auths; /* List of pending authentication requests */
-	NMConnection *secrets;
 	gboolean visible; /* Is this connection is visible by some session? */
-
 	GSList *reqs;  /* in-progress secrets requests */
 
-	NMSessionMonitor *session_monitor;
-	guint session_changed_id;
+	/* Caches secrets from on-disk connections; were they not cached any
+	 * call to nm_connection_clear_secrets() wipes them out and we'd have
+	 * to re-read them from disk which defeats the purpose of having the
+	 * connection in-memory at all.
+	 */
+	NMConnection *system_secrets;
+
+	/* Caches secrets from agents during the activation process; if new system
+	 * secrets are returned from an agent, they get written out to disk,
+	 * triggering a re-read of the connection, which reads only system
+	 * secrets, and would wipe out any agent-owned or not-saved secrets the
+	 * agent also returned.
+	 */
+	NMConnection *agent_secrets;
 
 	guint64 timestamp;   /* Up-to-date timestamp of connection use */
 	GHashTable *seen_bssids; /* Up-to-date BSSIDs that's been seen for the connection */
@@ -294,57 +306,67 @@ nm_settings_connection_check_permission (NMSettingsConnection *self,
 
 /**************************************************************/
 
-static void
-only_system_secrets_cb (NMSetting *setting,
-                        const char *key,
-                        const GValue *value,
-                        GParamFlags flags,
-                        gpointer user_data)
+static gboolean
+secrets_filter_cb (NMSetting *setting,
+                   const char *secret,
+                   NMSettingSecretFlags flags,
+                   gpointer user_data)
 {
-	if (flags & NM_SETTING_PARAM_SECRET) {
-		NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+	NMSettingSecretFlags filter_flags = GPOINTER_TO_UINT (user_data);
 
-		/* VPNs are special; need to handle each secret separately */
-		if (NM_IS_SETTING_VPN (setting) && !strcmp (key, NM_SETTING_VPN_SECRETS)) {
-			GHashTableIter iter;
-			const char *secret_name = NULL;
+	/* Returns TRUE to remove the secret */
 
-			g_hash_table_iter_init (&iter, (GHashTable *) g_value_get_boxed (value));
-			while (g_hash_table_iter_next (&iter, (gpointer *) &secret_name, NULL)) {
-				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);
-			if (secret_flags != NM_SETTING_SECRET_FLAG_NONE)
-				g_object_set (G_OBJECT (setting), key, NULL, NULL);
-		}
-	}
+	/* Can't use bitops with SECRET_FLAG_NONE so handle that specifically */
+	if (   (flags == NM_SETTING_SECRET_FLAG_NONE)
+	    && (filter_flags == NM_SETTING_SECRET_FLAG_NONE))
+		return FALSE;
+
+	/* Otherwise if the secret has at least one of the desired flags keep it */
+	return (flags & filter_flags) ? FALSE : TRUE;
 }
 
 static void
-update_secrets_cache (NMSettingsConnection *self)
+update_system_secrets_cache (NMSettingsConnection *self)
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
-	if (priv->secrets)
-		g_object_unref (priv->secrets);
-	priv->secrets = nm_connection_duplicate (NM_CONNECTION (self));
+	if (priv->system_secrets)
+		g_object_unref (priv->system_secrets);
+	priv->system_secrets = nm_connection_duplicate (NM_CONNECTION (self));
 
 	/* Clear out non-system-owned and not-saved secrets */
-	nm_connection_for_each_setting_value (priv->secrets, only_system_secrets_cb, NULL);
+	nm_connection_clear_secrets_with_flags (priv->system_secrets,
+	                                        secrets_filter_cb,
+	                                        GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_NONE));
 }
 
-static gboolean
-clear_system_secrets (GHashTableIter *iter,
-                      NMSettingSecretFlags flags,
-                      gpointer user_data)
+static void
+update_agent_secrets_cache (NMSettingsConnection *self, NMConnection *new)
 {
-	if (flags == NM_SETTING_SECRET_FLAG_NONE)
-		g_hash_table_iter_remove (iter);
-	return TRUE;
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+	NMSettingSecretFlags filter_flags = NM_SETTING_SECRET_FLAG_NOT_SAVED | NM_SETTING_SECRET_FLAG_AGENT_OWNED;
+
+	if (priv->agent_secrets)
+		g_object_unref (priv->agent_secrets);
+	priv->agent_secrets = nm_connection_duplicate (new ? new : NM_CONNECTION (self));
+
+	/* Clear out non-system-owned secrets */
+	nm_connection_clear_secrets_with_flags (priv->agent_secrets,
+	                                        secrets_filter_cb,
+	                                        GUINT_TO_POINTER (filter_flags));
+}
+
+static void
+secrets_cleared_cb (NMSettingsConnection *self)
+{
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+
+	/* Clear agent secrets when connection's secrets are cleared since agent
+	 * secrets are transient.
+	 */
+	if (priv->agent_secrets)
+		g_object_unref (priv->agent_secrets);
+	priv->agent_secrets = NULL;
 }
 
 /* Update the settings of this connection to match that of 'new', taking care to
@@ -356,7 +378,7 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self,
                                          GError **error)
 {
 	NMSettingsConnectionPrivate *priv;
-	GHashTable *new_settings, *transient_secrets;
+	GHashTable *new_settings, *hash = NULL;
 	gboolean success = FALSE;
 
 	g_return_val_if_fail (self != NULL, FALSE);
@@ -366,37 +388,29 @@ 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)) {
-		/* Copy the connection to keep its secrets around even if NM
-		 * calls nm_connection_clear_secrets().
+		/* Cache the just-updated system secrets in case something calls
+		 * nm_connection_clear_secrets() and clears them.
 		 */
-		update_secrets_cache (self);
+		update_system_secrets_cache (self);
+		success = TRUE;
 
-		/* And add the transient secrets back */
-		if (transient_secrets)
-			nm_connection_update_secrets (NM_CONNECTION (self), NULL, transient_secrets, NULL);
+		/* 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) {
+				success = nm_connection_update_secrets (NM_CONNECTION (self), NULL, hash, error);
+				g_hash_table_destroy (hash);
+			}
+		}
 
 		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;
 }
 
@@ -550,6 +564,7 @@ do_delete (NMSettingsConnection *connection,
 	for_agents = nm_connection_duplicate (NM_CONNECTION (connection));
 	nm_connection_clear_secrets (for_agents);
 	nm_agent_manager_delete_secrets (priv->agent_mgr, for_agents, FALSE, 0);
+	g_object_unref (for_agents);
 
 	/* Remove timestamp from timestamps database file */
 	remove_entry_from_db (connection, "timestamps");
@@ -719,7 +734,7 @@ agent_secrets_done_cb (NMAgentManager *manager,
 
 	/* Update the connection with our existing secrets from backing storage */
 	nm_connection_clear_secrets (NM_CONNECTION (self));
-	hash = nm_connection_to_hash (priv->secrets, NM_SETTING_HASH_FLAG_ONLY_SECRETS);
+	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)) {
 		/* Update the connection with the agent's secrets; by this point if any
 		 * system-owned secrets exist in 'secrets' the agent that provided them
@@ -730,7 +745,8 @@ agent_secrets_done_cb (NMAgentManager *manager,
 			/* Now that all secrets are updated, copy and cache new secrets, 
 			 * then save them to backing storage.
 			 */
-			update_secrets_cache (self);
+			update_system_secrets_cache (self);
+			update_agent_secrets_cache (self, NULL);
 
 			/* Only save secrets to backing storage if the agent returned any
 			 * new system secrets.  If it didn't, then the secrets are agent-
@@ -807,11 +823,9 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
 	guint32 call_id = 0;
 
 	/* Use priv->secrets to work around the fact that nm_connection_clear_secrets()
-	 * will clear secrets on this object's settings.  priv->secrets should be
-	 * a complete copy of this object and kept in sync by
-	 * nm_settings_connection_replace_settings().
+	 * will clear secrets on this object's settings.
 	 */
-	if (!priv->secrets) {
+	if (!priv->system_secrets) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "%s.%d - Internal error; secrets cache invalid.",
 		             __FILE__, __LINE__);
@@ -826,7 +840,7 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
 		return 0;
 	}
 
-	existing_secrets = nm_connection_to_hash (priv->secrets, NM_SETTING_HASH_FLAG_ONLY_SECRETS);
+	existing_secrets = nm_connection_to_hash (priv->system_secrets, NM_SETTING_HASH_FLAG_ONLY_SECRETS);
 	call_id = nm_agent_manager_get_secrets (priv->agent_mgr,
 	                                        NM_CONNECTION (self),
 	                                        filter_by_uid,
@@ -1084,47 +1098,40 @@ impl_settings_connection_get_settings (NMSettingsConnection *self,
 	auth_start (self, context, NULL, get_settings_auth_cb, NULL);
 }
 
+typedef struct {
+	DBusGMethodInvocation *context;
+	NMAgentManager *agent_mgr;
+	gulong sender_uid;
+} UpdateInfo;
+
 static void
-con_update_cb (NMSettingsConnection *connection,
+con_update_cb (NMSettingsConnection *self,
                GError *error,
                gpointer user_data)
 {
-	DBusGMethodInvocation *context = user_data;
+	UpdateInfo *info = user_data;
+	NMConnection *for_agent;
 
 	if (error)
-		dbus_g_method_return_error (context, error);
-	else
-		dbus_g_method_return (context);
-}
-
-static void
-secrets_filter_cb (NMSetting *setting,
-                   const char *key,
-                   const GValue *value,
-                   GParamFlags flags,
-                   gpointer user_data)
-{
-	NMSettingSecretFlags filter_flags = GPOINTER_TO_UINT (user_data);
-	NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
-	const char *secret_name = NULL;
-	GHashTableIter iter;
+		dbus_g_method_return_error (info->context, error);
+	else {
+		/* Dupe the connection so we can clear out non-agent-owned secrets,
+		 * 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));
+		nm_connection_clear_secrets_with_flags (for_agent,
+		                                        secrets_filter_cb,
+		                                        GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED));
+		nm_agent_manager_save_secrets (info->agent_mgr, for_agent, TRUE, info->sender_uid);
+		g_object_unref (for_agent);
 
-	if (flags & NM_SETTING_PARAM_SECRET) {
-		if (NM_IS_SETTING_VPN (setting) && !strcmp (key, NM_SETTING_VPN_SECRETS)) {
-			/* 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)) {
-				secret_flags = NM_SETTING_SECRET_FLAG_NONE;
-				nm_setting_get_secret_flags (setting, secret_name, &secret_flags, NULL);
-				if (!(secret_flags & filter_flags))
-					nm_setting_vpn_remove_secret (NM_SETTING_VPN (setting), secret_name);
-			}
-		} else {
-			nm_setting_get_secret_flags (setting, key, &secret_flags, NULL);
-			if (!(secret_flags & filter_flags))
-				g_object_set (G_OBJECT (setting), key, NULL, NULL);
-		}
+		dbus_g_method_return (info->context);
 	}
+
+	g_object_unref (info->agent_mgr);
+	memset (info, 0, sizeof (*info));
+	g_free (info);
 }
 
 static void
@@ -1136,54 +1143,27 @@ update_auth_cb (NMSettingsConnection *self,
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 	NMConnection *new_settings = data;
-	NMConnection *for_agent, *dup;
-	NMSettingSecretFlags filter_flags;
-	GHashTable *hash;
-	GError *local = NULL;
+	UpdateInfo *info;
 
 	if (error)
 		dbus_g_method_return_error (context, error);
 	else {
-		/* Cache the new secrets since they may get overwritten by the replace
-		 * when transient secrets are copied back.
+		info = g_malloc0 (sizeof (*info));
+		info->context = context;
+		info->agent_mgr = g_object_ref (priv->agent_mgr);
+		info->sender_uid = sender_uid;
+
+		/* 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.
 		 */
-		dup = nm_connection_duplicate (new_settings);
+		update_agent_secrets_cache (self, new_settings);
 
 		/* Update and commit our settings. */
 		nm_settings_connection_replace_and_commit (self,
-		                                           new_settings,
-		                                           con_update_cb,
-		                                           context);
-
-		/* Copy new agent secrets back to the connection */
-		filter_flags = NM_SETTING_SECRET_FLAG_AGENT_OWNED | NM_SETTING_SECRET_FLAG_NOT_SAVED;
-		nm_connection_for_each_setting_value (dup,
-		                                      secrets_filter_cb,
-		                                      GUINT_TO_POINTER (filter_flags));
-		hash = nm_connection_to_hash (dup, NM_SETTING_HASH_FLAG_ONLY_SECRETS);
-		g_object_unref (dup);
-
-		if (hash) {
-			if (!nm_connection_update_secrets (NM_CONNECTION (self), NULL, hash, &local)) {
-				nm_log_warn (LOGD_SETTINGS, "Failed to update connection secrets: (%d) %s",
-				             local ? local->code : -1,
-				             local && local->message ? local->message : "(unknown)");
-				g_clear_error (&local);
-			}
-			g_hash_table_destroy (hash);
-		}
-
-		/* Dupe the connection and clear out non-agent-owned secrets so we can
-		 * send the agent-owned ones to agents to be saved.  Only send them to
-		 * agents of the same UID as the Update() request sender.
-		 */
-		for_agent = nm_connection_duplicate (NM_CONNECTION (self));
-		filter_flags = NM_SETTING_SECRET_FLAG_AGENT_OWNED;
-		nm_connection_for_each_setting_value (for_agent,
-		                                      secrets_filter_cb,
-		                                      GUINT_TO_POINTER (filter_flags));
-		nm_agent_manager_save_secrets (priv->agent_mgr, for_agent, TRUE, sender_uid);
-		g_object_unref (for_agent);
+			                                       new_settings,
+			                                       con_update_cb,
+			                                       info);
 	}
 
 	g_object_unref (new_settings);
@@ -1345,16 +1325,11 @@ dbus_get_agent_secrets_cb (NMSettingsConnection *self,
 	if (error)
 		dbus_g_method_return_error (context, error);
 	else {
-		/* The connection's secrets will have been updated by the agent manager,
-		 * so we want to refresh the secrets cache.  Note that we will never save
-		 * new secrets to backing storage here because D-Bus initated requests will
-		 * never ask for completely new secrets from agents.  Thus system-owned
-		 * secrets should not have changed from backing storage.  We also don't
-		 * send agent-owned secrets back out to be saved since we assume the agent
-		 * that provided the secrets saved them itself.
+		/* Return secrets from agent and backing storage to the D-Bus caller;
+		 * nm_settings_connection_get_secrets() will have updated itself with
+		 * secrets from backing storage and those returned from the agent
+		 * by the time we get here.
 		 */
-		update_secrets_cache (self);
-
 		hash = nm_connection_to_hash (NM_CONNECTION (self), NM_SETTING_HASH_FLAG_ONLY_SECRETS);
 		if (!hash)
 			hash = g_hash_table_new (NULL, NULL);
@@ -1726,6 +1701,8 @@ 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);
+
+	g_signal_connect (self, "secrets-cleared", G_CALLBACK (secrets_cleared_cb), NULL);
 }
 
 static void
@@ -1739,8 +1716,10 @@ dispose (GObject *object)
 		goto out;
 	priv->disposed = TRUE;
 
-	if (priv->secrets)
-		g_object_unref (priv->secrets);
+	if (priv->system_secrets)
+		g_object_unref (priv->system_secrets);
+	if (priv->agent_secrets)
+		g_object_unref (priv->agent_secrets);
 
 	/* Cancel PolicyKit requests */
 	for (iter = priv->pending_auths; iter; iter = g_slist_next (iter))
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index 733e9145..f0bfc162 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -27,6 +27,7 @@
 
 #include <unistd.h>
 #include <string.h>
+#include <ctype.h>
 #include <gmodule.h>
 #include <net/ethernet.h>
 #include <netinet/ether.h>
@@ -546,24 +547,23 @@ find_plugin (GSList *list, const char *pname)
 }
 
 static gboolean
-load_plugins (NMSettings *self, const char *plugins, GError **error)
+load_plugins (NMSettings *self, const char **plugins, GError **error)
 {
 	GSList *list = NULL;
-	char **plist;
-	char **iter;
+	const char **iter;
 	gboolean success = TRUE;
 
-	plist = g_strsplit (plugins, ",", 0);
-	if (!plist)
-		return FALSE;
-
-	for (iter = plist; *iter; iter++) {
+	for (iter = plugins; *iter; iter++) {
 		GModule *plugin;
 		char *full_name, *path;
-		const char *pname = g_strstrip (*iter);
+		const char *pname = *iter;
 		GObject *obj;
 		GObject * (*factory_func) (void);
 
+		/* strip leading spaces */
+		while (isblank (*pname))
+			pname++;
+
 		/* keyfile plugin built in now */
 		if (!strcmp (pname, "keyfile"))
 			continue;
@@ -616,8 +616,6 @@ load_plugins (NMSettings *self, const char *plugins, GError **error)
 		list = g_slist_append (list, obj);
 	}
 
-	g_strfreev (plist);
-
 	g_slist_foreach (list, (GFunc) g_object_unref, NULL);
 	g_slist_free (list);
 
@@ -634,6 +632,7 @@ connection_removed (NMSettingsConnection *obj, gpointer user_data)
 {
 	GObject *connection = G_OBJECT (obj);
 	guint id;
+
 	g_object_ref (connection);
 
 	/* Disconnect signal handlers, as plugins might still keep references
@@ -653,7 +652,7 @@ connection_removed (NMSettingsConnection *obj, gpointer user_data)
 	if (id)
 		g_signal_handler_disconnect (connection, id);
 
-	/* Forget about the connection internall */
+	/* Forget about the connection internally */
 	g_hash_table_remove (NM_SETTINGS_GET_PRIVATE (user_data)->connections,
 	                     (gpointer) nm_connection_get_path (NM_CONNECTION (connection)));
 
@@ -840,9 +839,6 @@ claim_connection (NMSettings *self,
 	}
 }
 
-// TODO it seems that this is only ever used to remove a
-// NMDefaultWiredConnection, and it probably needs to stay that way. So this
-// *needs* a better name!
 static void
 remove_default_wired_connection (NMSettings *self,
                                  NMSettingsConnection *connection,
@@ -852,7 +848,8 @@ remove_default_wired_connection (NMSettings *self,
 	const char *path = nm_connection_get_path (NM_CONNECTION (connection));
 
 	if (g_hash_table_lookup (priv->connections, path)) {
-		g_signal_emit_by_name (G_OBJECT (connection), NM_SETTINGS_CONNECTION_REMOVED);
+		if (do_signal)
+			g_signal_emit_by_name (G_OBJECT (connection), NM_SETTINGS_CONNECTION_REMOVED);
 		g_hash_table_remove (priv->connections, path);
 	}
 }
@@ -1378,7 +1375,7 @@ delete_cb (NMSettingsConnection *connection, GError *error, gpointer user_data)
 {
 }
 
-static gboolean
+static void
 default_wired_try_update (NMDefaultWiredConnection *wired,
                           NMSettings *self)
 {
@@ -1390,6 +1387,9 @@ default_wired_try_update (NMDefaultWiredConnection *wired,
 	 * persistent storage.
 	 */
 
+	/* Keep it alive over removal so we can re-add it if we need to */
+	g_object_ref (wired);
+
 	id = nm_connection_get_id (NM_CONNECTION (wired));
 	g_assert (id);
 
@@ -1402,21 +1402,21 @@ default_wired_try_update (NMDefaultWiredConnection *wired,
 		                   DEFAULT_WIRED_TAG,
 		                   NULL);
 		nm_log_info (LOGD_SETTINGS, "Saved default wired connection '%s' to persistent storage", id);
-		return FALSE;
+	} else {
+		nm_log_warn (LOGD_SETTINGS, "couldn't save default wired connection '%s': %d / %s",
+			         id,
+			         error ? error->code : -1,
+			         (error && error->message) ? error->message : "(unknown)");
+		g_clear_error (&error);
+
+		/* If there was an error, don't destroy the default wired connection,
+		 * but add it back to the system settings service. Connection is already
+		 * exported on the bus, don't export it again, thus do_export == FALSE.
+		 */
+		claim_connection (self, NM_SETTINGS_CONNECTION (wired), FALSE);
 	}
 
-	nm_log_warn (LOGD_SETTINGS, "couldn't save default wired connection '%s': %d / %s",
-	             id,
-	             error ? error->code : -1,
-	             (error && error->message) ? error->message : "(unknown)");
-	g_clear_error (&error);
-
-	/* If there was an error, don't destroy the default wired connection,
-	 * but add it back to the system settings service. Connection is already
-	 * exported on the bus, don't export it again, thus do_export == FALSE.
-	 */
-	claim_connection (self, NM_SETTINGS_CONNECTION (wired), FALSE);
-	return TRUE;
+	g_object_unref (wired);
 }
 
 void
@@ -1492,7 +1492,7 @@ nm_settings_device_removed (NMSettings *self, NMDevice *device)
 
 NMSettings *
 nm_settings_new (const char *config_file,
-                 const char *plugins,
+                 const char **plugins,
                  GError **error)
 {
 	NMSettings *self;
diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h
index 66d41cce..77485e19 100644
--- a/src/settings/nm-settings.h
+++ b/src/settings/nm-settings.h
@@ -77,7 +77,7 @@ typedef struct {
 GType nm_settings_get_type (void);
 
 NMSettings *nm_settings_new (const char *config_file,
-                             const char *plugins,
+                             const char **plugins,
                              GError **error);
 
 typedef void (*NMSettingsForEachFunc) (NMSettings *settings,
diff --git a/src/settings/plugins/Makefile.in b/src/settings/plugins/Makefile.in
index 148e9620..c23e3be0 100644
--- a/src/settings/plugins/Makefile.in
+++ b/src/settings/plugins/Makefile.in
@@ -197,8 +197,16 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
+LIBNL1_CFLAGS = @LIBNL1_CFLAGS@
+LIBNL1_LIBS = @LIBNL1_LIBS@
+LIBNL2_CFLAGS = @LIBNL2_CFLAGS@
+LIBNL2_LIBS = @LIBNL2_LIBS@
+LIBNL3_CFLAGS = @LIBNL3_CFLAGS@
+LIBNL3_LIBS = @LIBNL3_LIBS@
 LIBNL_CFLAGS = @LIBNL_CFLAGS@
 LIBNL_LIBS = @LIBNL_LIBS@
+LIBNL_ROUTE3_CFLAGS = @LIBNL_ROUTE3_CFLAGS@
+LIBNL_ROUTE3_LIBS = @LIBNL_ROUTE3_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
diff --git a/src/settings/plugins/ifcfg-rh/Makefile.in b/src/settings/plugins/ifcfg-rh/Makefile.in
index f02b6431..b904563a 100644
--- a/src/settings/plugins/ifcfg-rh/Makefile.in
+++ b/src/settings/plugins/ifcfg-rh/Makefile.in
@@ -267,8 +267,16 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
+LIBNL1_CFLAGS = @LIBNL1_CFLAGS@
+LIBNL1_LIBS = @LIBNL1_LIBS@
+LIBNL2_CFLAGS = @LIBNL2_CFLAGS@
+LIBNL2_LIBS = @LIBNL2_LIBS@
+LIBNL3_CFLAGS = @LIBNL3_CFLAGS@
+LIBNL3_LIBS = @LIBNL3_LIBS@
 LIBNL_CFLAGS = @LIBNL_CFLAGS@
 LIBNL_LIBS = @LIBNL_LIBS@
+LIBNL_ROUTE3_CFLAGS = @LIBNL_ROUTE3_CFLAGS@
+LIBNL_ROUTE3_LIBS = @LIBNL_ROUTE3_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
diff --git a/src/settings/plugins/ifcfg-rh/plugin.c b/src/settings/plugins/ifcfg-rh/plugin.c
index ed0dceca..b4be4cba 100644
--- a/src/settings/plugins/ifcfg-rh/plugin.c
+++ b/src/settings/plugins/ifcfg-rh/plugin.c
@@ -655,8 +655,10 @@ sc_plugin_ifcfg_init (SCPluginIfcfg *plugin)
 	}
 
 	if (!success) {
-		dbus_g_connection_unref (priv->bus);
-		priv->bus = NULL;
+		if (priv->bus) {
+			dbus_g_connection_unref (priv->bus);
+			priv->bus = NULL;
+		}
 	}
 }
 
diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c
index cdf5889e..910cca35 100644
--- a/src/settings/plugins/ifcfg-rh/reader.c
+++ b/src/settings/plugins/ifcfg-rh/reader.c
@@ -28,10 +28,10 @@
 #include <ctype.h>
 #include <sys/inotify.h>
 #include <errno.h>
-#include <net/if.h>
 #include <sys/ioctl.h>
 #include <unistd.h>
 #include <netinet/ether.h>
+#include <linux/if.h>
 
 #ifndef __user
 #define __user
@@ -3382,7 +3382,7 @@ connection_from_file (const char *filename,
 		network_file = SYSCONFDIR "/sysconfig/network";
 
 	if (!iscsiadm_path)
-		iscsiadm_path = SBINDIR "/iscsiadm";
+		iscsiadm_path = "/sbin/iscsiadm";
 
 	ifcfg_name = utils_get_ifcfg_name (filename, TRUE);
 	if (!ifcfg_name) {
diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c
index db084969..0aa8efc2 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/settings/plugins/ifcfg-rh/shvar.c
@@ -142,10 +142,11 @@ svUnescape(char *s) {
  */
 static const char escapees[] = "\"'\\$~`";		/* must be escaped */
 static const char spaces[] = " \t|&;()<>";		/* only require "" */
+static const char newlines[] = "\n\r";			/* will be removed */
 char *
 svEscape(const char *s) {
     char *new;
-    int i, j, mangle = 0, space = 0;
+    int i, j, mangle = 0, space = 0, newline = 0;
     int newlen, slen;
     static int esclen, splen;
 
@@ -156,23 +157,26 @@ svEscape(const char *s) {
     for (i = 0; i < slen; i++) {
 	if (strchr(escapees, s[i])) mangle++;
 	if (strchr(spaces, s[i])) space++;
+	if (strchr(newlines, s[i])) newline++;
     }
-    if (!mangle && !space) return strdup(s);
+    if (!mangle && !space && !newline) return strdup(s);
 
-    newlen = slen + mangle + 3;	/* 3 is extra ""\0 */
+    newlen = slen + mangle - newline + 3;	/* 3 is extra ""\0 */
     new = g_malloc0(newlen);
     if (!new) return NULL;
 
     j = 0;
     new[j++] = '"';
     for (i = 0; i < slen; i++) {
+	if (strchr(newlines, s[i]))
+	    continue;
 	if (strchr(escapees, s[i])) {
 	    new[j++] = '\\';
 	}
 	new[j++] = s[i];
     }
     new[j++] = '"';
-    g_assert(j == slen + mangle + 2); /* j is the index of the '\0' */
+    g_assert(j == slen + mangle - newline + 2); /* j is the index of the '\0' */
 
     return new;
 }
@@ -332,13 +336,13 @@ svSetValue(shvarFile *s, const char *key, const char *value, gboolean verbatim)
     }
 
 end:
-    if (newval) free(newval);
-    if (val1) free(val1);
-    if (val2) free(val2);
+    g_free(newval);
+    g_free(val1);
+    g_free(val2);
     return;
 
 bail:
-    if (keyValue) free (keyValue);
+    g_free (keyValue);
     goto end;
 }
 
diff --git a/src/settings/plugins/ifcfg-rh/tests/Makefile.in b/src/settings/plugins/ifcfg-rh/tests/Makefile.in
index cd37d0a7..6c509fd0 100644
--- a/src/settings/plugins/ifcfg-rh/tests/Makefile.in
+++ b/src/settings/plugins/ifcfg-rh/tests/Makefile.in
@@ -228,8 +228,16 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
+LIBNL1_CFLAGS = @LIBNL1_CFLAGS@
+LIBNL1_LIBS = @LIBNL1_LIBS@
+LIBNL2_CFLAGS = @LIBNL2_CFLAGS@
+LIBNL2_LIBS = @LIBNL2_LIBS@
+LIBNL3_CFLAGS = @LIBNL3_CFLAGS@
+LIBNL3_LIBS = @LIBNL3_LIBS@
 LIBNL_CFLAGS = @LIBNL_CFLAGS@
 LIBNL_LIBS = @LIBNL_LIBS@
+LIBNL_ROUTE3_CFLAGS = @LIBNL_ROUTE3_CFLAGS@
+LIBNL_ROUTE3_LIBS = @LIBNL_ROUTE3_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in b/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in
index a10cbb86..7a00bcfd 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in
@@ -152,8 +152,16 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
+LIBNL1_CFLAGS = @LIBNL1_CFLAGS@
+LIBNL1_LIBS = @LIBNL1_LIBS@
+LIBNL2_CFLAGS = @LIBNL2_CFLAGS@
+LIBNL2_LIBS = @LIBNL2_LIBS@
+LIBNL3_CFLAGS = @LIBNL3_CFLAGS@
+LIBNL3_LIBS = @LIBNL3_LIBS@
 LIBNL_CFLAGS = @LIBNL_CFLAGS@
 LIBNL_LIBS = @LIBNL_LIBS@
+LIBNL_ROUTE3_CFLAGS = @LIBNL_ROUTE3_CFLAGS@
+LIBNL_ROUTE3_LIBS = @LIBNL_ROUTE3_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
index d1f08aa9..e32266cb 100644
--- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
+++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
@@ -10141,6 +10141,207 @@ test_write_wifi_wpa_then_open (void)
 }
 
 static void
+test_write_wifi_wpa_then_wep_with_perms (void)
+{
+	NMConnection *connection;
+	NMConnection *reread;
+	NMSettingConnection *s_con;
+	NMSettingWireless *s_wifi;
+	NMSettingWirelessSecurity *s_wsec;
+	NMSettingIP4Config *s_ip4;
+	NMSettingIP6Config *s_ip6;
+	char *uuid;
+	gboolean success;
+	GError *error = NULL;
+	char *testfile = NULL;
+	char *unmanaged = NULL;
+	char *keyfile = NULL;
+	char *routefile = NULL;
+	char *route6file = NULL;
+	gboolean ignore_error = FALSE;
+	GByteArray *ssid;
+	GSList *perm_list = NULL;
+	const unsigned char ssid_data[] = "SomeSSID";
+
+	/* Test that writing out a WPA config then changing that to a WEP
+	 * config works and doesn't cause infinite loop or other issues.
+	 */
+
+	connection = nm_connection_new ();
+	g_assert (connection);
+
+	/* Connection setting */
+	s_con = (NMSettingConnection *) nm_setting_connection_new ();
+	g_assert (s_con);
+	nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+	uuid = nm_utils_uuid_generate ();
+	perm_list = g_slist_append (perm_list, "user:superman:");
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_ID, "random wifi connection 2",
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
+	              NM_SETTING_CONNECTION_PERMISSIONS, perm_list,
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME,
+	              NULL);
+	g_free (uuid);
+	g_slist_free (perm_list);
+	ASSERT (nm_setting_connection_get_num_permissions (s_con) == 1,
+                "test_write_wifi_wpa_then_wep_with_perms", "unexpected failure adding valid user permisson");
+
+	/* Wifi setting */
+	s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
+	g_assert (s_wifi);
+	nm_connection_add_setting (connection, NM_SETTING (s_wifi));
+
+	ssid = g_byte_array_sized_new (sizeof (ssid_data));
+	g_byte_array_append (ssid, ssid_data, sizeof (ssid_data));
+
+	g_object_set (s_wifi,
+	              NM_SETTING_WIRELESS_SSID, ssid,
+	              NM_SETTING_WIRELESS_MODE, "infrastructure",
+	              NM_SETTING_WIRELESS_SEC, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+	              NULL);
+
+	g_byte_array_free (ssid, TRUE);
+
+	/* Wireless security setting */
+	s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new ();
+	g_assert (s_wsec);
+	nm_connection_add_setting (connection, NM_SETTING (s_wsec));
+
+	g_object_set (s_wsec,
+	              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk",
+	              NM_SETTING_WIRELESS_SECURITY_PSK, "My cool PSK",
+	              NULL);
+
+	nm_setting_wireless_security_add_proto (s_wsec, "wpa");
+	nm_setting_wireless_security_add_pairwise (s_wsec, "tkip");
+	nm_setting_wireless_security_add_group (s_wsec, "tkip");
+
+	nm_setting_wireless_security_add_proto (s_wsec, "rsn");
+	nm_setting_wireless_security_add_pairwise (s_wsec, "ccmp");
+	nm_setting_wireless_security_add_group (s_wsec, "ccmp");
+
+	/* IP4 setting */
+	s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
+	g_assert (s_ip4);
+	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
+
+	g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL);
+
+	/* IP6 setting */
+	s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new ();
+	g_assert (s_ip6);
+	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
+
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
+
+	success = nm_connection_verify (connection, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	/* Save the ifcfg */
+	success = writer_new_connection (connection,
+	                                 TEST_SCRATCH_DIR "/network-scripts/",
+	                                 &testfile,
+	                                 &error);
+	g_assert_no_error (error);
+	g_assert (success);
+	g_assert (testfile);
+
+	/* re-read the connection for comparison */
+	reread = connection_from_file (testfile,
+	                               NULL,
+	                               TYPE_WIRELESS,
+	                               NULL,
+	                               &unmanaged,
+	                               &keyfile,
+	                               &routefile,
+	                               &route6file,
+	                               &error,
+	                               &ignore_error);
+	g_assert_no_error (error);
+	g_assert (reread);
+
+	success = nm_connection_verify (reread, &error);
+	g_assert_no_error (error);
+
+	success = nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT);
+	g_assert (success);
+
+	g_free (unmanaged);
+	unmanaged = NULL;
+	g_free (routefile);
+	routefile = NULL;
+	g_free (route6file);
+	route6file = NULL;
+	g_object_unref (reread);
+
+	/* Now change the connection to WEP and recheck */
+	s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new ();
+	g_assert (s_wsec);
+	nm_connection_add_setting (connection, NM_SETTING (s_wsec));
+
+	g_object_set (s_wsec,
+	              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none",
+	              NULL);
+	nm_setting_wireless_security_set_wep_key (s_wsec, 0, "abraka  dabra");
+
+	/* Write it back out */
+	success = writer_update_connection (connection,
+	                                    TEST_SCRATCH_DIR "/network-scripts/",
+	                                    testfile,
+	                                    keyfile,
+	                                    &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	g_free (keyfile);
+	keyfile = NULL;
+
+	/* re-read it for comparison */
+	reread = connection_from_file (testfile,
+	                               NULL,
+	                               TYPE_WIRELESS,
+	                               NULL,
+	                               &unmanaged,
+	                               &keyfile,
+	                               &routefile,
+	                               &route6file,
+	                               &error,
+	                               &ignore_error);
+	g_assert_no_error (error);
+
+	g_assert (reread);
+
+	success = nm_connection_verify (reread, &error);
+	g_assert_no_error (error);
+
+	success = nm_connection_compare (connection, reread,
+	                                 NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS |
+	                                 NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS);
+
+	ASSERT (success,
+	        "test_write_wifi_wpa_then_wep_with_perms", "failed to compare connections");
+
+	unlink (keyfile);
+	unlink (testfile);
+
+	g_free (testfile);
+	g_free (unmanaged);
+	g_free (keyfile);
+	g_free (routefile);
+	g_free (route6file);
+	g_object_unref (reread);
+
+	g_object_unref (connection);
+}
+
+static void
 test_write_wifi_dynamic_wep_leap (void)
 {
 	NMConnection *connection;
@@ -11713,6 +11914,7 @@ int main (int argc, char **argv)
 	test_write_wifi_wpa_eap_ttls_mschapv2 ();
 	test_write_wifi_dynamic_wep_leap ();
 	test_write_wifi_wpa_then_open ();
+	test_write_wifi_wpa_then_wep_with_perms ();
 	test_write_wired_qeth_dhcp ();
 	test_write_wired_ctc_dhcp ();
 	test_write_permissions ();
diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c
index 192226ac..068bcda2 100644
--- a/src/settings/plugins/ifcfg-rh/writer.c
+++ b/src/settings/plugins/ifcfg-rh/writer.c
@@ -569,6 +569,7 @@ write_wireless_security_setting (NMConnection *connection,
 	svSetValue (ifcfg, "DEFAULTKEY", NULL, FALSE);
 
 	if (!strcmp (key_mgmt, "none")) {
+		svSetValue (ifcfg, "KEY_MGMT", NULL, FALSE);
 		wep = TRUE;
 		*no_8021x = TRUE;
 	} else if (!strcmp (key_mgmt, "wpa-none") || !strcmp (key_mgmt, "wpa-psk")) {
@@ -1725,7 +1726,7 @@ write_connection (NMConnection *connection,
 
 			g_free (ifcfg_name);
 			while (idx++ < 500) {
-				ifcfg_name = g_strdup_printf ("%s/ifcfg-%s %u", ifcfg_dir, escaped, idx);
+				ifcfg_name = g_strdup_printf ("%s/ifcfg-%s-%u", ifcfg_dir, escaped, idx);
 				if (g_file_test (ifcfg_name, G_FILE_TEST_EXISTS) == FALSE)
 					break;
 				g_free (ifcfg_name);
diff --git a/src/settings/plugins/ifcfg-suse/Makefile.in b/src/settings/plugins/ifcfg-suse/Makefile.in
index 02e2247a..b8017e24 100644
--- a/src/settings/plugins/ifcfg-suse/Makefile.in
+++ b/src/settings/plugins/ifcfg-suse/Makefile.in
@@ -215,8 +215,16 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
+LIBNL1_CFLAGS = @LIBNL1_CFLAGS@
+LIBNL1_LIBS = @LIBNL1_LIBS@
+LIBNL2_CFLAGS = @LIBNL2_CFLAGS@
+LIBNL2_LIBS = @LIBNL2_LIBS@
+LIBNL3_CFLAGS = @LIBNL3_CFLAGS@
+LIBNL3_LIBS = @LIBNL3_LIBS@
 LIBNL_CFLAGS = @LIBNL_CFLAGS@
 LIBNL_LIBS = @LIBNL_LIBS@
+LIBNL_ROUTE3_CFLAGS = @LIBNL_ROUTE3_CFLAGS@
+LIBNL_ROUTE3_LIBS = @LIBNL_ROUTE3_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
diff --git a/src/settings/plugins/ifnet/Makefile.in b/src/settings/plugins/ifnet/Makefile.in
index 2bc7bc32..519e07c0 100644
--- a/src/settings/plugins/ifnet/Makefile.in
+++ b/src/settings/plugins/ifnet/Makefile.in
@@ -263,8 +263,16 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
+LIBNL1_CFLAGS = @LIBNL1_CFLAGS@
+LIBNL1_LIBS = @LIBNL1_LIBS@
+LIBNL2_CFLAGS = @LIBNL2_CFLAGS@
+LIBNL2_LIBS = @LIBNL2_LIBS@
+LIBNL3_CFLAGS = @LIBNL3_CFLAGS@
+LIBNL3_LIBS = @LIBNL3_LIBS@
 LIBNL_CFLAGS = @LIBNL_CFLAGS@
 LIBNL_LIBS = @LIBNL_LIBS@
+LIBNL_ROUTE3_CFLAGS = @LIBNL_ROUTE3_CFLAGS@
+LIBNL_ROUTE3_LIBS = @LIBNL_ROUTE3_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
diff --git a/src/settings/plugins/ifnet/connection_parser.c b/src/settings/plugins/ifnet/connection_parser.c
index 982f94f9..b4aaa8d2 100644
--- a/src/settings/plugins/ifnet/connection_parser.c
+++ b/src/settings/plugins/ifnet/connection_parser.c
@@ -1742,27 +1742,27 @@ ifnet_update_connection_from_config_block (const char *conn_name, GError **error
 
 	/* IPv4 setting */
 	make_ip4_setting (connection, conn_name, error);
-	if (error && *error)
-		PLUGIN_WARN (IFNET_PLUGIN_NAME,
-			     "Found error: %s", (*error)->message);
+	if (error && *error) {
+		PLUGIN_WARN (IFNET_PLUGIN_NAME, "Found error: %s", (*error)->message);
+		goto error;
+	}
 
 	/* IPv6 setting */
 	make_ip6_setting (connection, conn_name, error);
-	if (error && *error)
-		PLUGIN_WARN (IFNET_PLUGIN_NAME,
-			     "Found error: %s", (*error)->message);
+	if (error && *error) {
+		PLUGIN_WARN (IFNET_PLUGIN_NAME, "Found error: %s", (*error)->message);
+		goto error;
+	}
 
 	success = nm_connection_verify (connection, error);
 	if (error && *error)
-		PLUGIN_WARN (IFNET_PLUGIN_NAME,
-			     "Found error: %s", (*error)->message);
+		PLUGIN_WARN (IFNET_PLUGIN_NAME, "Found error: %s", (*error)->message);
 	PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Connection verified %s:%d", conn_name, success);
 	if (!success)
 		goto error;
 	return connection;
 
 error:
-	g_object_unref (setting);
 	g_object_unref (connection);
 	return NULL;
 }
diff --git a/src/settings/plugins/ifnet/net_utils.c b/src/settings/plugins/ifnet/net_utils.c
index 83f86ab5..b533ae3f 100644
--- a/src/settings/plugins/ifnet/net_utils.c
+++ b/src/settings/plugins/ifnet/net_utils.c
@@ -313,7 +313,7 @@ is_ip4_address (const char *in_address)
 	gboolean result = FALSE;
 	gchar *tmp;
 	GRegex *regex = g_regex_new (pattern, 0, 0, NULL);
-	GMatchInfo *match_info;
+	GMatchInfo *match_info = NULL;
 
 	if (!address)
 		goto done;
diff --git a/src/settings/plugins/ifnet/tests/Makefile.in b/src/settings/plugins/ifnet/tests/Makefile.in
index 17b80c36..d2cbb871 100644
--- a/src/settings/plugins/ifnet/tests/Makefile.in
+++ b/src/settings/plugins/ifnet/tests/Makefile.in
@@ -186,8 +186,16 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
+LIBNL1_CFLAGS = @LIBNL1_CFLAGS@
+LIBNL1_LIBS = @LIBNL1_LIBS@
+LIBNL2_CFLAGS = @LIBNL2_CFLAGS@
+LIBNL2_LIBS = @LIBNL2_LIBS@
+LIBNL3_CFLAGS = @LIBNL3_CFLAGS@
+LIBNL3_LIBS = @LIBNL3_LIBS@
 LIBNL_CFLAGS = @LIBNL_CFLAGS@
 LIBNL_LIBS = @LIBNL_LIBS@
+LIBNL_ROUTE3_CFLAGS = @LIBNL_ROUTE3_CFLAGS@
+LIBNL_ROUTE3_LIBS = @LIBNL_ROUTE3_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
diff --git a/src/settings/plugins/ifnet/tests/net b/src/settings/plugins/ifnet/tests/net
index e7550002..0eef399a 100644
--- a/src/settings/plugins/ifnet/tests/net
+++ b/src/settings/plugins/ifnet/tests/net
@@ -35,6 +35,8 @@ config_eth6=("192.168.4.{1..101}/24")
 config_eth7=( "dhcp" )
 auto_eth7="true"
 
+# missing config_eth8
+auto_eth8="true"
 
 config_myxjtu2=("202.117.16.121/24 brd 202.117.16.255")
 routes_myxjtu2=("default via 202.117.16.1")
diff --git a/src/settings/plugins/ifnet/tests/test_all.c b/src/settings/plugins/ifnet/tests/test_all.c
index d114bbba..52d9ce2a 100644
--- a/src/settings/plugins/ifnet/tests/test_all.c
+++ b/src/settings/plugins/ifnet/tests/test_all.c
@@ -252,25 +252,25 @@ test_is_unmanaged ()
 static void
 test_new_connection ()
 {
-	GError **error = NULL;
+	GError *error = NULL;
 	NMConnection *connection;
 
-	connection = ifnet_update_connection_from_config_block ("eth2", error);
+	connection = ifnet_update_connection_from_config_block ("eth2", &error);
 	ASSERT (connection != NULL, "new connection",
 		"new connection failed: %s",
-		error == NULL ? "None" : (*error)->message);
+		error ? error->message : "None");
 	g_object_unref (connection);
-	connection =
-	    ifnet_update_connection_from_config_block ("qiaomuf", error);
+
+	connection = ifnet_update_connection_from_config_block ("qiaomuf", &error);
 	ASSERT (connection != NULL, "new connection",
-		"new connection failed: %s", error
-		&& (*error) ? (*error)->message : "NONE");
+		"new connection failed: %s",
+		error ? error->message : "NONE");
 	g_object_unref (connection);
-	connection =
-	    ifnet_update_connection_from_config_block ("myxjtu2", error);
+
+	connection = ifnet_update_connection_from_config_block ("myxjtu2", &error);
 	ASSERT (connection != NULL, "new connection",
-		"new connection failed: %s", error
-		&& (*error) ? (*error)->message : "NONE");
+		"new connection failed: %s",
+		error ? error->message : "NONE");
 	g_object_unref (connection);
 }
 
@@ -280,32 +280,32 @@ test_new_connection ()
 static void
 test_update_connection ()
 {
-	GError **error = NULL;
+	GError *error = NULL;
 	NMConnection *connection;
 	gboolean success;
 
-	connection = ifnet_update_connection_from_config_block ("eth0", error);
+	connection = ifnet_update_connection_from_config_block ("eth0", &error);
 	ASSERT (connection != NULL, "get connection",
 		"get connection failed: %s",
-		error == NULL ? "None" : (*error)->message);
+		error ? error->message : "None");
 
 	success = ifnet_update_parsers_by_connection (connection, "eth0",
 	                                              NET_GEN_NAME,
 	                                              SUP_GEN_NAME,
 	                                              NULL,
-	                                              error);
+	                                              &error);
 	ASSERT (success, "update connection", "update connection failed %s", "eth0");
 	g_object_unref (connection);
 
-	connection = ifnet_update_connection_from_config_block ("0xab3ace", error);
+	connection = ifnet_update_connection_from_config_block ("0xab3ace", &error);
 	ASSERT (connection != NULL, "get connection", "get connection failed: %s",
-		error == NULL ? "None" : (*error)->message);
+		error ? error->message : "None");
 
 	success = ifnet_update_parsers_by_connection (connection, "0xab3ace",
 	                                              NET_GEN_NAME,
 	                                              SUP_GEN_NAME,
 	                                              NULL,
-	                                              error);
+	                                              &error);
 	ASSERT (success, "update connection", "update connection failed %s", "0xab3ace");
 	g_object_unref (connection);
 
@@ -357,6 +357,17 @@ test_delete_connection ()
 }
 
 static void
+test_missing_config ()
+{
+	GError *error = NULL;
+	NMConnection *connection;
+
+	connection = ifnet_update_connection_from_config_block ("eth8", &error);
+	ASSERT (connection == NULL && error != NULL, "get connection",
+	        "get connection should fail with 'Unknown config for eth8'");
+}
+
+static void
 run_all (gboolean run)
 {
 	if (run) {
@@ -378,6 +389,7 @@ run_all (gboolean run)
 		test_update_connection ();
 		test_add_connection ();
 		test_delete_connection ();
+		test_missing_config ();
 	}
 }
 
diff --git a/src/settings/plugins/ifnet/tests/wpa_supplicant.conf b/src/settings/plugins/ifnet/tests/wpa_supplicant.conf
index a2595d42..4f5f68cf 100644
--- a/src/settings/plugins/ifnet/tests/wpa_supplicant.conf
+++ b/src/settings/plugins/ifnet/tests/wpa_supplicant.conf
@@ -859,6 +859,7 @@ network={
 	identity="user@example.com"
 	anonymous_identity="anonymous@example.com"
 	password="foobar"
-	ca_cert="blob://exampleblob"
+	ca_cert="test_ca_cert.pem"
+	phase2="auth=CHAP"
 	priority=20
 }
diff --git a/src/settings/plugins/ifupdown/Makefile.in b/src/settings/plugins/ifupdown/Makefile.in
index 5f5a456b..b6896b59 100644
--- a/src/settings/plugins/ifupdown/Makefile.in
+++ b/src/settings/plugins/ifupdown/Makefile.in
@@ -262,8 +262,16 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
+LIBNL1_CFLAGS = @LIBNL1_CFLAGS@
+LIBNL1_LIBS = @LIBNL1_LIBS@
+LIBNL2_CFLAGS = @LIBNL2_CFLAGS@
+LIBNL2_LIBS = @LIBNL2_LIBS@
+LIBNL3_CFLAGS = @LIBNL3_CFLAGS@
+LIBNL3_LIBS = @LIBNL3_LIBS@
 LIBNL_CFLAGS = @LIBNL_CFLAGS@
 LIBNL_LIBS = @LIBNL_LIBS@
+LIBNL_ROUTE3_CFLAGS = @LIBNL_ROUTE3_CFLAGS@
+LIBNL_ROUTE3_LIBS = @LIBNL_ROUTE3_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
diff --git a/src/settings/plugins/ifupdown/tests/Makefile.in b/src/settings/plugins/ifupdown/tests/Makefile.in
index 7412e65d..3c8bf793 100644
--- a/src/settings/plugins/ifupdown/tests/Makefile.in
+++ b/src/settings/plugins/ifupdown/tests/Makefile.in
@@ -186,8 +186,16 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
+LIBNL1_CFLAGS = @LIBNL1_CFLAGS@
+LIBNL1_LIBS = @LIBNL1_LIBS@
+LIBNL2_CFLAGS = @LIBNL2_CFLAGS@
+LIBNL2_LIBS = @LIBNL2_LIBS@
+LIBNL3_CFLAGS = @LIBNL3_CFLAGS@
+LIBNL3_LIBS = @LIBNL3_LIBS@
 LIBNL_CFLAGS = @LIBNL_CFLAGS@
 LIBNL_LIBS = @LIBNL_LIBS@
+LIBNL_ROUTE3_CFLAGS = @LIBNL_ROUTE3_CFLAGS@
+LIBNL_ROUTE3_LIBS = @LIBNL_ROUTE3_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
diff --git a/src/settings/plugins/keyfile/Makefile.in b/src/settings/plugins/keyfile/Makefile.in
index a2a7d231..cf821e46 100644
--- a/src/settings/plugins/keyfile/Makefile.in
+++ b/src/settings/plugins/keyfile/Makefile.in
@@ -238,8 +238,16 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
+LIBNL1_CFLAGS = @LIBNL1_CFLAGS@
+LIBNL1_LIBS = @LIBNL1_LIBS@
+LIBNL2_CFLAGS = @LIBNL2_CFLAGS@
+LIBNL2_LIBS = @LIBNL2_LIBS@
+LIBNL3_CFLAGS = @LIBNL3_CFLAGS@
+LIBNL3_LIBS = @LIBNL3_LIBS@
 LIBNL_CFLAGS = @LIBNL_CFLAGS@
 LIBNL_LIBS = @LIBNL_LIBS@
+LIBNL_ROUTE3_CFLAGS = @LIBNL_ROUTE3_CFLAGS@
+LIBNL_ROUTE3_LIBS = @LIBNL_ROUTE3_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
diff --git a/src/settings/plugins/keyfile/reader.c b/src/settings/plugins/keyfile/reader.c
index c4136e05..4128b9f2 100644
--- a/src/settings/plugins/keyfile/reader.c
+++ b/src/settings/plugins/keyfile/reader.c
@@ -730,10 +730,27 @@ read_hash_of_string (GKeyFile *file, NMSetting *setting, const char *key)
 	g_strfreev (keys);
 }
 
+static void
+unescape_semicolons (char *str)
+{
+	int i;
+	gsize len = strlen (str);
+
+	for (i = 0; i < len; i++) {
+		if (str[i] == '\\' && str[i+1] == ';') {
+			memmove(str + i, str + i + 1, len - (i + 1));
+			len--;
+		}
+		str[len] = '\0';
+	}
+}
+
 static GByteArray *
 get_uchar_array (GKeyFile *keyfile,
                  const char *setting_name,
-                 const char *key)
+                 const char *key,
+                 gboolean zero_terminate,
+                 gboolean unescape_semicolon)
 {
 	GByteArray *array = NULL;
 	char *tmp_string;
@@ -742,26 +759,28 @@ get_uchar_array (GKeyFile *keyfile,
 	int i;
 
 	/* New format: just a string
-	 * Old format: integer list; e.g. 11;25;38
+	 * Old format: integer list; e.g. 11;25;38;
 	 */
 	tmp_string = g_key_file_get_string (keyfile, setting_name, key, NULL);
 	if (tmp_string) {
-		gboolean new_format = FALSE;
 		GRegex *regex;
 		GMatchInfo *match_info;
-		const char *pattern = "^[[:space:]]*[[:digit:]]{1,3}[[:space:]]*(;[[:space:]]*[[:digit:]]{1,3}[[:space:]]*)*(;[[:space:]]*)?$";
+		const char *pattern = "^[[:space:]]*[[:digit:]]{1,3}[[:space:]]*;([[:space:]]*[[:digit:]]{1,3}[[:space:]]*;)*([[:space:]]*)?$";
 
 		regex = g_regex_new (pattern, 0, 0, NULL);
 		g_regex_match (regex, tmp_string, 0, &match_info);
-		if (!g_match_info_matches (match_info))
-			new_format = TRUE;
+		if (!g_match_info_matches (match_info)) {
+			/* Handle as a simple string (ie, new format) */
+			if (unescape_semicolon)
+				unescape_semicolons (tmp_string);
+			length = strlen (tmp_string);
+			if (zero_terminate)
+				length++;
+			array = g_byte_array_sized_new (length);
+			g_byte_array_append (array, (guint8 *) tmp_string, length);
+		}
 		g_match_info_free (match_info);
 		g_regex_unref (regex);
-
-		if (new_format) {
-			array = g_byte_array_sized_new (strlen (tmp_string));
-			g_byte_array_append (array, (guint8 *) tmp_string, strlen (tmp_string));
-		}
 		g_free (tmp_string);
 	}
 
@@ -796,7 +815,7 @@ ssid_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char
 	const char *setting_name = nm_setting_get_name (setting);
 	GByteArray *array;
 
-	array = get_uchar_array (keyfile, setting_name, key);
+	array = get_uchar_array (keyfile, setting_name, key, FALSE, TRUE);
 	if (array) {
 		g_object_set (setting, key, array, NULL);
 		g_byte_array_free (array, TRUE);
@@ -837,21 +856,79 @@ get_cert_path (const char *keyfile_path, GByteArray *cert_path)
 static const char *certext[] = { ".pem", ".cert", ".crt", ".cer", ".p12", ".der", ".key" };
 
 static gboolean
-has_cert_ext (GByteArray *array)
+has_cert_ext (const char *path)
 {
 	int i;
 
 	for (i = 0; i < G_N_ELEMENTS (certext); i++) {
-		guint32 extlen = strlen (certext[i]);
-
-		if (array->len <= extlen)
-			continue;
-		if (memcmp (&array->data[array->len - extlen], certext[i], extlen) == 0)
+		if (g_str_has_suffix (path, certext[i]))
 			return TRUE;
 	}
 	return FALSE;
 }
 
+static gboolean
+handle_as_scheme (GByteArray *array, NMSetting *setting, const char *key)
+{
+	/* It's the PATH scheme, can just set plain data */
+	if (   (array->len > strlen (SCHEME_PATH))
+	    && g_str_has_prefix ((const char *) array->data, SCHEME_PATH)
+	    && (array->data[array->len - 1] == '\0')) {
+		g_object_set (setting, key, array, NULL);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+static gboolean
+handle_as_path (GByteArray *array,
+                NMSetting *setting,
+                const char *key,
+                const char *keyfile_path)
+{
+	gsize validate_len = array->len;
+	GByteArray *val;
+	char *path;
+	gboolean exists, success = FALSE;
+
+	if (array->len > 500 || array->len < 1)
+		return FALSE;
+
+	/* If there's a trailing NULL tell g_utf8_validate() to to until the NULL */
+	if (array->data[array->len - 1] == '\0')
+		validate_len = -1;
+
+	if (g_utf8_validate ((const char *) array->data, validate_len, NULL) == FALSE)
+		return FALSE;
+
+	/* Might be a bare path without the file:// prefix; in that case
+	 * if it's an absolute path, use that, otherwise treat it as a
+	 * relative path to the current directory.
+	 */
+
+	path = get_cert_path (keyfile_path, array);
+	exists = g_file_test (path, G_FILE_TEST_EXISTS);
+	if (   exists
+	    || memchr (array->data, '/', array->len)
+	    || has_cert_ext (path)) {
+		/* Construct the proper value as required for the PATH scheme */
+		val = g_byte_array_sized_new (strlen (SCHEME_PATH) + strlen (path) + 1);
+		g_byte_array_append (val, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH));
+		g_byte_array_append (val, (const guint8 *) path, strlen (path));
+		g_byte_array_append (val, (const guint8 *) "\0", 1);
+		g_object_set (setting, key, val, NULL);
+		g_byte_array_free (val, TRUE);
+		success = TRUE;
+
+		/* Warn if the certificate didn't exist */
+		if (exists == FALSE)
+			PLUGIN_WARN (KEYFILE_PLUGIN_NAME, "   certificate or key %s does not exist", path);
+	}
+	g_free (path);
+
+	return success;
+}
+
 static void
 cert_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
 {
@@ -859,62 +936,25 @@ cert_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char
 	GByteArray *array;
 	gboolean success = FALSE;
 
-	array = get_uchar_array (keyfile, setting_name, key);
-	if (array) {
-		/* Value could be either:
-		 * 1) the raw key/cert data as a blob
-		 * 2) a path scheme (ie, starts with "file://")
-		 * 3) a plain path
-		 */
-		if (   (array->len > strlen (SCHEME_PATH))
-		    && g_str_has_prefix ((const char *) array->data, SCHEME_PATH)
-		    && (array->data[array->len - 1] == '\0')) {
-			/* It's the PATH scheme, can just set plain data */
-			g_object_set (setting, key, array, NULL);
-			success = TRUE;
-		} else if (   (array->len < 500)
-		           && g_utf8_validate ((const char *) array->data, array->len, NULL)) {
-			GByteArray *val;
-			char *path;
-			gboolean exists;
-
-			/* Might be a bare path without the file:// prefix; in that case
-			 * if it's an absolute path, use that, otherwise treat it as a
-			 * relative path to the current directory.
-			 */
-
-			path = get_cert_path (keyfile_path, array);
-			exists = g_file_test (path, G_FILE_TEST_EXISTS);
-			if (   exists
-			    || memchr (array->data, '/', array->len)
-			    || has_cert_ext (array)) {
-				/* Construct the proper value as required for the PATH scheme */
-				val = g_byte_array_sized_new (strlen (SCHEME_PATH) + array->len + 1);
-				g_byte_array_append (val, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH));
-				g_byte_array_append (val, (const guint8 *) path, strlen (path));
-				g_byte_array_append (val, (const guint8 *) "\0", 1);
-				g_object_set (setting, key, val, NULL);
-				g_byte_array_free (val, TRUE);
-				success = TRUE;
-
-				/* Warn if the certificate didn't exist */
-				if (exists == FALSE) {
-					PLUGIN_WARN (KEYFILE_PLUGIN_NAME, "   certificate or key %s does not exist", path);
-				}
-			}
-			g_free (path);
-		}
+	array = get_uchar_array (keyfile, setting_name, key, TRUE, FALSE);
+	if (array && array->len > 0) {
+		/* Try as a path + scheme (ie, starts with "file://") */
+		success = handle_as_scheme (array, setting, key);
 
-		if (!success) {
-			/* Assume it's a simple blob value of the certificate or private key's data */
-			g_object_set (setting, key, array, NULL);
-		}
+		/* If not, it might be a plain path */
+		if (success == FALSE)
+			success = handle_as_path (array, setting, key, keyfile_path);
 
-		g_byte_array_free (array, TRUE);
+		/* If neither of those two, assume blob with certificate data */
+		if (success == FALSE)
+			g_object_set (setting, key, array, NULL);
 	} else {
 		g_warning ("%s: ignoring invalid key/cert value for %s / %s",
 		           __func__, setting_name, key);
 	}
+
+	if (array)
+		g_byte_array_free (array, TRUE);
 }
 
 typedef struct {
diff --git a/src/settings/plugins/keyfile/tests/Makefile.in b/src/settings/plugins/keyfile/tests/Makefile.in
index 05ed3a61..9d4f478d 100644
--- a/src/settings/plugins/keyfile/tests/Makefile.in
+++ b/src/settings/plugins/keyfile/tests/Makefile.in
@@ -224,8 +224,16 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
+LIBNL1_CFLAGS = @LIBNL1_CFLAGS@
+LIBNL1_LIBS = @LIBNL1_LIBS@
+LIBNL2_CFLAGS = @LIBNL2_CFLAGS@
+LIBNL2_LIBS = @LIBNL2_LIBS@
+LIBNL3_CFLAGS = @LIBNL3_CFLAGS@
+LIBNL3_LIBS = @LIBNL3_LIBS@
 LIBNL_CFLAGS = @LIBNL_CFLAGS@
 LIBNL_LIBS = @LIBNL_LIBS@
+LIBNL_ROUTE3_CFLAGS = @LIBNL_ROUTE3_CFLAGS@
+LIBNL_ROUTE3_LIBS = @LIBNL_ROUTE3_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.am b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.am
index 302db866..55dda7ee 100644
--- a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.am
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.am
@@ -8,6 +8,8 @@ KEYFILES = \
 	ATT_Data_Connect_Plain \
 	Test_String_SSID \
 	Test_Intlist_SSID \
+	Test_Intlike_SSID \
+	Test_Intlike_SSID_2 \
 	Test_Wired_TLS_Old \
 	Test_Wired_TLS_New \
 	Test_Wired_TLS_Blob \
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in
index 083615b0..3408cb15 100644
--- a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in
@@ -152,8 +152,16 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
+LIBNL1_CFLAGS = @LIBNL1_CFLAGS@
+LIBNL1_LIBS = @LIBNL1_LIBS@
+LIBNL2_CFLAGS = @LIBNL2_CFLAGS@
+LIBNL2_LIBS = @LIBNL2_LIBS@
+LIBNL3_CFLAGS = @LIBNL3_CFLAGS@
+LIBNL3_LIBS = @LIBNL3_LIBS@
 LIBNL_CFLAGS = @LIBNL_CFLAGS@
 LIBNL_LIBS = @LIBNL_LIBS@
+LIBNL_ROUTE3_CFLAGS = @LIBNL_ROUTE3_CFLAGS@
+LIBNL_ROUTE3_LIBS = @LIBNL_ROUTE3_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
@@ -278,6 +286,8 @@ KEYFILES = \
 	ATT_Data_Connect_Plain \
 	Test_String_SSID \
 	Test_Intlist_SSID \
+	Test_Intlike_SSID \
+	Test_Intlike_SSID_2 \
 	Test_Wired_TLS_Old \
 	Test_Wired_TLS_New \
 	Test_Wired_TLS_Blob \
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_Intlike_SSID b/src/settings/plugins/keyfile/tests/keyfiles/Test_Intlike_SSID
new file mode 100644
index 00000000..2bacb725
--- /dev/null
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_Intlike_SSID
@@ -0,0 +1,11 @@
+[connection]
+id=Test 
+uuid=2f962388-e5f3-45af-a62c-ac220b8f7baa
+type=802-11-wireless
+
+[802-11-wireless]
+ssid=101
+
+[ipv4]
+method=auto
+
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_Intlike_SSID_2 b/src/settings/plugins/keyfile/tests/keyfiles/Test_Intlike_SSID_2
new file mode 100644
index 00000000..20240251
--- /dev/null
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_Intlike_SSID_2
@@ -0,0 +1,11 @@
+[connection]
+id=Test SSID - escaping semicolon in string
+uuid=2f962388-e5f3-45af-a62c-ac220b8f7baa
+type=802-11-wireless
+
+[802-11-wireless]
+ssid=11\\;12\\;13\\;
+
+[ipv4]
+method=auto
+
diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c
index 3bbaaaec..2859cb3e 100644
--- a/src/settings/plugins/keyfile/tests/test-keyfile.c
+++ b/src/settings/plugins/keyfile/tests/test-keyfile.c
@@ -1563,6 +1563,246 @@ test_write_intlist_ssid (void)
 	g_object_unref (connection);
 }
 
+#define TEST_INTLIKE_SSID_FILE TEST_KEYFILES_DIR"/Test_Intlike_SSID"
+
+static void
+test_read_intlike_ssid (void)
+{
+	NMConnection *connection;
+	NMSettingWireless *s_wifi;
+	GError *error = NULL;
+	gboolean success;
+	const GByteArray *array;
+	const char *expected_ssid = "101";
+
+	connection = nm_keyfile_plugin_connection_from_file (TEST_INTLIKE_SSID_FILE, &error);
+	g_assert_no_error (error);
+	g_assert (connection);
+
+	success = nm_connection_verify (connection, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	/* SSID */
+	s_wifi = nm_connection_get_setting_wireless (connection);
+	g_assert (s_wifi);
+
+	array = nm_setting_wireless_get_ssid (s_wifi);
+	g_assert (array != NULL);
+	g_assert_cmpint (array->len, ==, strlen (expected_ssid));
+	g_assert_cmpint (memcmp (array->data, expected_ssid, strlen (expected_ssid)), ==, 0);
+
+	g_object_unref (connection);
+}
+
+#define TEST_INTLIKE_SSID_2_FILE TEST_KEYFILES_DIR"/Test_Intlike_SSID_2"
+
+static void
+test_read_intlike_ssid_2 (void)
+{
+	NMConnection *connection;
+	NMSettingWireless *s_wifi;
+	GError *error = NULL;
+	gboolean success;
+	const GByteArray *array;
+	const char *expected_ssid = "11;12;13;";
+
+	connection = nm_keyfile_plugin_connection_from_file (TEST_INTLIKE_SSID_2_FILE, &error);
+	g_assert_no_error (error);
+	g_assert (connection);
+
+	success = nm_connection_verify (connection, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	/* SSID */
+	s_wifi = nm_connection_get_setting_wireless (connection);
+	g_assert (s_wifi);
+
+	array = nm_setting_wireless_get_ssid (s_wifi);
+	g_assert (array != NULL);
+	g_assert_cmpint (array->len, ==, strlen (expected_ssid));
+	g_assert_cmpint (memcmp (array->data, expected_ssid, strlen (expected_ssid)), ==, 0);
+
+	g_object_unref (connection);
+}
+
+static void
+test_write_intlike_ssid (void)
+{
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingWireless *s_wifi;
+	NMSettingIP4Config *s_ip4;
+	char *uuid, *testfile = NULL;
+	GByteArray *ssid;
+	unsigned char tmpssid[] = { 49, 48, 49 };
+	gboolean success;
+	NMConnection *reread;
+	GError *error = NULL;
+	pid_t owner_grp;
+	uid_t owner_uid;
+	GKeyFile *keyfile;
+	char *tmp;
+
+	connection = nm_connection_new ();
+	g_assert (connection);
+
+	/* Connection setting */
+
+	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
+	g_assert (s_con);
+	nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+	uuid = nm_utils_uuid_generate ();
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_ID, "Intlike SSID Test",
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME,
+	              NULL);
+	g_free (uuid);
+
+	/* Wireless setting */
+	s_wifi = NM_SETTING_WIRELESS (nm_setting_wireless_new ());
+	g_assert (s_wifi);
+	nm_connection_add_setting (connection, NM_SETTING (s_wifi));
+
+	ssid = g_byte_array_sized_new (sizeof (tmpssid));
+	g_byte_array_append (ssid, &tmpssid[0], sizeof (tmpssid));
+	g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid, NULL);
+	g_byte_array_free (ssid, TRUE);
+
+	/* IP4 setting */
+	s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_ip4_config_new ());
+	g_assert (s_ip4);
+	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
+	g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL);
+
+	/* Write out the connection */
+	owner_uid = geteuid ();
+	owner_grp = getegid ();
+	success = nm_keyfile_plugin_write_test_connection (connection, TEST_SCRATCH_DIR, owner_uid, owner_grp, &testfile, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+	g_assert (testfile != NULL);
+
+	/* Ensure the SSID was written out as a plain "101" */
+	keyfile = g_key_file_new ();
+	success = g_key_file_load_from_file (keyfile, testfile, 0, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	tmp = g_key_file_get_string (keyfile, NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_SSID, &error);
+	g_assert_no_error (error);
+	g_assert (tmp);
+	g_assert_cmpstr (tmp, ==, "101");
+
+	g_key_file_free (keyfile);
+
+	/* Read the connection back in and compare it to the one we just wrote out */
+	reread = nm_keyfile_plugin_connection_from_file (testfile, &error);
+	g_assert_no_error (error);
+	g_assert (reread);
+
+	success = nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT);
+	g_assert (success);
+
+	g_clear_error (&error);
+	unlink (testfile);
+	g_free (testfile);
+
+	g_object_unref (reread);
+	g_object_unref (connection);
+}
+
+static void
+test_write_intlike_ssid_2 (void)
+{
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingWireless *s_wifi;
+	NMSettingIP4Config *s_ip4;
+	char *uuid, *testfile = NULL;
+	GByteArray *ssid;
+	unsigned char tmpssid[] = { 49, 49, 59, 49, 50, 59, 49, 51, 59};
+	gboolean success;
+	NMConnection *reread;
+	GError *error = NULL;
+	pid_t owner_grp;
+	uid_t owner_uid;
+	GKeyFile *keyfile;
+	char *tmp;
+
+	connection = nm_connection_new ();
+	g_assert (connection);
+
+	/* Connection setting */
+
+	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
+	g_assert (s_con);
+	nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+	uuid = nm_utils_uuid_generate ();
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_ID, "Intlike SSID Test 2",
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME,
+	              NULL);
+	g_free (uuid);
+
+	/* Wireless setting */
+	s_wifi = NM_SETTING_WIRELESS (nm_setting_wireless_new ());
+	g_assert (s_wifi);
+	nm_connection_add_setting (connection, NM_SETTING (s_wifi));
+
+	ssid = g_byte_array_sized_new (sizeof (tmpssid));
+	g_byte_array_append (ssid, &tmpssid[0], sizeof (tmpssid));
+	g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid, NULL);
+	g_byte_array_free (ssid, TRUE);
+
+	/* IP4 setting */
+	s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_ip4_config_new ());
+	g_assert (s_ip4);
+	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
+	g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL);
+
+	/* Write out the connection */
+	owner_uid = geteuid ();
+	owner_grp = getegid ();
+	success = nm_keyfile_plugin_write_test_connection (connection, TEST_SCRATCH_DIR, owner_uid, owner_grp, &testfile, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+	g_assert (testfile != NULL);
+
+	/* Ensure the SSID was written out as a plain "11;12;13;" */
+	keyfile = g_key_file_new ();
+	success = g_key_file_load_from_file (keyfile, testfile, 0, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	tmp = g_key_file_get_string (keyfile, NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_SSID, &error);
+	g_assert_no_error (error);
+	g_assert (tmp);
+	g_assert_cmpstr (tmp, ==, "11\\;12\\;13\\;");
+
+	g_key_file_free (keyfile);
+
+	/* Read the connection back in and compare it to the one we just wrote out */
+	reread = nm_keyfile_plugin_connection_from_file (testfile, &error);
+	g_assert_no_error (error);
+	g_assert (reread);
+
+	success = nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT);
+	g_assert (success);
+
+	g_clear_error (&error);
+	unlink (testfile);
+	g_free (testfile);
+
+	g_object_unref (reread);
+	g_object_unref (connection);
+}
+
 #define TEST_BT_DUN_FILE TEST_KEYFILES_DIR"/ATT_Data_Connect_BT"
 
 static void
@@ -2616,6 +2856,12 @@ int main (int argc, char **argv)
 	test_read_intlist_ssid ();
 	test_write_intlist_ssid ();
 
+	test_read_intlike_ssid ();
+	test_write_intlike_ssid ();
+
+	test_read_intlike_ssid_2 ();
+	test_write_intlike_ssid_2 ();
+
 	test_read_bt_dun_connection ();
 	test_write_bt_dun_connection ();
 
diff --git a/src/settings/plugins/keyfile/writer.c b/src/settings/plugins/keyfile/writer.c
index 060093ce..db43b233 100644
--- a/src/settings/plugins/keyfile/writer.c
+++ b/src/settings/plugins/keyfile/writer.c
@@ -44,6 +44,7 @@
 #include <ctype.h>
 
 #include "nm-dbus-glib-types.h"
+#include "nm-glib-compat.h"
 #include "writer.h"
 #include "common.h"
 
@@ -483,6 +484,7 @@ ssid_writer (GKeyFile *file,
 	GByteArray *array;
 	const char *setting_name = nm_setting_get_name (setting);
 	gboolean new_format = TRUE;
+	unsigned int semicolons = 0;
 	int i, *tmp_array;
 	char *ssid;
 
@@ -501,11 +503,24 @@ ssid_writer (GKeyFile *file,
 			new_format = FALSE;
 			break;
 		}
+		if (c == ';')
+			semicolons++;
 	}
 
 	if (new_format) {
-		ssid = g_malloc0 (array->len + 1);
-		memcpy (ssid, array->data, array->len);
+		ssid = g_malloc0 (array->len + semicolons + 1);
+		if (semicolons == 0)
+			memcpy (ssid, array->data, array->len);
+		else {
+			/* Escape semicolons with backslashes to make strings
+			 * containing ';', such as '16;17;' unambiguous */
+			int j = 0;
+			for (i = 0; i < array->len; i++) {
+				if (array->data[i] == ';')
+					ssid[j++] = '\\';
+				ssid[j++] = array->data[i];
+			}
+		}
 		g_key_file_set_string (file, setting_name, key, ssid);
 		g_free (ssid);
 	} else {
@@ -876,7 +891,7 @@ write_setting_value (NMSetting *setting,
 	} else if (type == G_TYPE_BOOLEAN) {
 		g_key_file_set_boolean (info->keyfile, setting_name, key, g_value_get_boolean (value));
 	} else if (type == G_TYPE_CHAR) {
-		g_key_file_set_integer (info->keyfile, setting_name, key, (int) g_value_get_char (value));
+		g_key_file_set_integer (info->keyfile, setting_name, key, (int) g_value_get_schar (value));
 	} else if (type == DBUS_TYPE_G_UCHAR_ARRAY) {
 		GByteArray *array;
 
@@ -993,15 +1008,27 @@ _internal_write_connection (NMConnection *connection,
 
 		path = g_strdup_printf ("%s/%s-%s", keyfile_dir, filename, nm_connection_get_uuid (connection));
 		if (g_file_test (path, G_FILE_TEST_EXISTS)) {
-			/* Hmm, this is odd. Give up. */
-			g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
-				         "%s.%d: could not find suitable keyfile file name (%s already used)",
-				         __FILE__, __LINE__, path);
-			g_free (path);
-			goto out;
+			if (existing_path == NULL || g_strcmp0 (path, existing_path) != 0) {
+				/* This should not happen. But, it actually occurs when
+				 * two connections have the same UUID, and one of the connections
+				 * is edited to contain the same ID as the other one.
+				 * Give up.
+				 */
+				g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
+				                    "%s.%d: could not find suitable keyfile file name (%s already used)",
+				                    __FILE__, __LINE__, path);
+				g_free (path);
+				goto out;
+			}
 		}
 	}
 
+	/* In case of updating the connection and changing the file path,
+	 * we need to remove the old one, not to end up with two connections.
+	 */
+	if (existing_path != NULL && strcmp (path, existing_path) != 0)
+		unlink (existing_path);
+
 	g_file_set_contents (path, data, len, error);
 	if (chown (path, owner_uid, owner_grp) < 0) {
 		g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
diff --git a/src/settings/tests/Makefile.in b/src/settings/tests/Makefile.in
index 33c4bd38..94ae4830 100644
--- a/src/settings/tests/Makefile.in
+++ b/src/settings/tests/Makefile.in
@@ -188,8 +188,16 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
+LIBNL1_CFLAGS = @LIBNL1_CFLAGS@
+LIBNL1_LIBS = @LIBNL1_LIBS@
+LIBNL2_CFLAGS = @LIBNL2_CFLAGS@
+LIBNL2_LIBS = @LIBNL2_LIBS@
+LIBNL3_CFLAGS = @LIBNL3_CFLAGS@
+LIBNL3_LIBS = @LIBNL3_LIBS@
 LIBNL_CFLAGS = @LIBNL_CFLAGS@
 LIBNL_LIBS = @LIBNL_LIBS@
+LIBNL_ROUTE3_CFLAGS = @LIBNL_ROUTE3_CFLAGS@
+LIBNL_ROUTE3_LIBS = @LIBNL_ROUTE3_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@