about summary refs log tree commit diff
path: root/src/settings
diff options
context:
space:
mode:
Diffstat (limited to 'src/settings')
-rw-r--r--src/settings/nm-agent-manager.c1
-rw-r--r--src/settings/nm-settings-connection.c170
-rw-r--r--src/settings/nm-settings.c6
-rw-r--r--src/settings/plugins/ibft/nms-ibft-connection.c1
-rw-r--r--src/settings/plugins/ibft/nms-ibft-plugin.c2
-rw-r--r--src/settings/plugins/ibft/nms-ibft-reader.c2
-rw-r--r--src/settings/plugins/ibft/tests/test-ibft.c1
-rw-r--r--src/settings/plugins/ifcfg-rh/nm-inotify-helper.c8
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c1
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c2
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c50
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c1
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c6
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.c26
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-sae5
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-sae1
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c51
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-connection.c1
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c1
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-parser.c6
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-plugin.c7
-rw-r--r--src/settings/plugins/ifupdown/tests/test-ifupdown.c2
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-connection.c1
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-plugin.c2
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-reader.c1
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-utils.c5
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-writer.c73
-rw-r--r--src/settings/plugins/keyfile/tests/meson.build1
-rw-r--r--src/settings/plugins/keyfile/tests/test-keyfile.c10
29 files changed, 177 insertions, 267 deletions
diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c
index edadee14..8924c39f 100644
--- a/src/settings/nm-agent-manager.c
+++ b/src/settings/nm-agent-manager.c
@@ -22,7 +22,6 @@
 
 #include "nm-agent-manager.h"
 
-#include <string.h>
 #include <pwd.h>
 
 #include "nm-common-macros.h"
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index 0beb5ea7..8d1f9583 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -23,8 +23,6 @@
 
 #include "nm-settings-connection.h"
 
-#include <string.h>
-
 #include "c-list/src/c-list.h"
 
 #include "nm-common-macros.h"
@@ -208,141 +206,6 @@ nm_settings_connection_get_last_secret_agent_version_id (NMSettingsConnection *s
 
 /*****************************************************************************/
 
-/* Return TRUE to keep, FALSE to drop */
-typedef gboolean (*ForEachSecretFunc) (NMSettingSecretFlags flags,
-                                       gpointer user_data);
-
-/* Returns always a non-NULL, non-floating variant that must
- * be unrefed by the caller. */
-static GVariant *
-for_each_secret (NMConnection *self,
-                 GVariant *secrets,
-                 gboolean remove_non_secrets,
-                 ForEachSecretFunc callback,
-                 gpointer callback_data)
-{
-	GVariantBuilder secrets_builder, setting_builder;
-	GVariantIter secrets_iter, *setting_iter;
-	const char *setting_name;
-
-	/* This function, given a dict of dicts representing new secrets of
-	 * an NMConnection, walks through each toplevel dict (which represents a
-	 * NMSetting), and for each setting, walks through that setting dict's
-	 * properties.  For each property that's a secret, it will check that
-	 * secret's flags in the backing NMConnection object, and call a supplied
-	 * callback.
-	 *
-	 * The one complexity is that the VPN setting's 'secrets' property is
-	 * *also* a dict (since the key/value pairs are arbitrary and known
-	 * only to the VPN plugin itself).  That means we have three levels of
-	 * dicts that we potentially have to traverse here.  When we hit the
-	 * VPN setting's 'secrets' property, we special-case that and iterate over
-	 * each item in that 'secrets' dict, calling the supplied callback
-	 * each time.
-	 */
-
-	g_return_val_if_fail (callback, NULL);
-
-	g_variant_iter_init (&secrets_iter, secrets);
-	g_variant_builder_init (&secrets_builder, NM_VARIANT_TYPE_CONNECTION);
-	while (g_variant_iter_next (&secrets_iter, "{&sa{sv}}", &setting_name, &setting_iter)) {
-		NMSetting *setting;
-		const char *secret_name;
-		GVariant *val;
-
-		setting = nm_connection_get_setting_by_name (self, setting_name);
-		if (setting == NULL) {
-			g_variant_iter_free (setting_iter);
-			continue;
-		}
-
-		g_variant_builder_init (&setting_builder, NM_VARIANT_TYPE_SETTING);
-		while (g_variant_iter_next (setting_iter, "{&sv}", &secret_name, &val)) {
-			NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
-
-			/* VPN secrets need slightly different treatment here since the
-			 * "secrets" property is actually a hash table of secrets.
-			 */
-			if (NM_IS_SETTING_VPN (setting) && !g_strcmp0 (secret_name, NM_SETTING_VPN_SECRETS)) {
-				GVariantBuilder vpn_secrets_builder;
-				GVariantIter vpn_secrets_iter;
-				const char *vpn_secret_name, *secret;
-
-				/* Iterate through each secret from the VPN dict in the overall secrets dict */
-				g_variant_builder_init (&vpn_secrets_builder, G_VARIANT_TYPE ("a{ss}"));
-				g_variant_iter_init (&vpn_secrets_iter, val);
-				while (g_variant_iter_next (&vpn_secrets_iter, "{&s&s}", &vpn_secret_name, &secret)) {
-					if (!nm_setting_get_secret_flags (setting, vpn_secret_name, &secret_flags, NULL)) {
-						if (!remove_non_secrets)
-							g_variant_builder_add (&vpn_secrets_builder, "{ss}", vpn_secret_name, secret);
-						continue;
-					}
-
-					if (callback (secret_flags, callback_data))
-						g_variant_builder_add (&vpn_secrets_builder, "{ss}", vpn_secret_name, secret);
-				}
-
-				g_variant_builder_add (&setting_builder, "{sv}",
-				                       secret_name, g_variant_builder_end (&vpn_secrets_builder));
-			} else {
-				if (!nm_setting_get_secret_flags (setting, secret_name, &secret_flags, NULL)) {
-					if (!remove_non_secrets)
-						g_variant_builder_add (&setting_builder, "{sv}", secret_name, val);
-					continue;
-				}
-				if (callback (secret_flags, callback_data))
-					g_variant_builder_add (&setting_builder, "{sv}", secret_name, val);
-			}
-			g_variant_unref (val);
-		}
-
-		g_variant_iter_free (setting_iter);
-		g_variant_builder_add (&secrets_builder, "{sa{sv}}", setting_name, &setting_builder);
-	}
-
-	return g_variant_ref_sink (g_variant_builder_end (&secrets_builder));
-}
-
-typedef gboolean (*FindSecretFunc) (NMSettingSecretFlags flags,
-                                    gpointer user_data);
-
-typedef struct {
-	FindSecretFunc find_func;
-	gpointer find_func_data;
-	gboolean found;
-} FindSecretData;
-
-static gboolean
-find_secret_for_each_func (NMSettingSecretFlags flags,
-                           gpointer user_data)
-{
-	FindSecretData *data = user_data;
-
-	if (!data->found)
-		data->found = data->find_func (flags, data->find_func_data);
-	return FALSE;
-}
-
-static gboolean
-find_secret (NMConnection *self,
-             GVariant *secrets,
-             FindSecretFunc callback,
-             gpointer callback_data)
-{
-	FindSecretData data;
-	GVariant *dummy;
-
-	data.find_func = callback;
-	data.find_func_data = callback_data;
-	data.found = FALSE;
-
-	dummy = for_each_secret (self, secrets, FALSE, find_secret_for_each_func, &data);
-	g_variant_unref (dummy);
-	return data.found;
-}
-
-/*****************************************************************************/
-
 static void
 set_visible (NMSettingsConnection *self, gboolean new_visible)
 {
@@ -938,8 +801,8 @@ typedef struct {
 } ForEachSecretFlags;
 
 static gboolean
-validate_secret_flags (NMSettingSecretFlags flags,
-                       gpointer user_data)
+validate_secret_flags_cb (NMSettingSecretFlags flags,
+                          gpointer user_data)
 {
 	ForEachSecretFlags *cmp_flags = user_data;
 
@@ -950,6 +813,18 @@ validate_secret_flags (NMSettingSecretFlags flags,
 	return TRUE;
 }
 
+static GVariant *
+validate_secret_flags (NMConnection *connection,
+                       GVariant *secrets,
+                       ForEachSecretFlags *cmp_flags)
+{
+	return g_variant_ref_sink (_nm_connection_for_each_secret (connection,
+	                                                           secrets,
+	                                                           TRUE,
+	                                                           validate_secret_flags_cb,
+	                                                           cmp_flags));
+}
+
 static gboolean
 secret_is_system_owned (NMSettingSecretFlags flags,
                         gpointer user_data)
@@ -992,7 +867,7 @@ get_cmp_flags (NMSettingsConnection *self, /* only needed for logging */
 		 * save those system-owned secrets.  If not, discard them and use the
 		 * existing secrets, or fail the connection.
 		 */
-		*agent_had_system = find_secret (connection, secrets, secret_is_system_owned, NULL);
+		*agent_had_system = _nm_connection_find_secret (connection, secrets, secret_is_system_owned, NULL);
 		if (*agent_had_system) {
 			if (flags == NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE) {
 				/* No user interaction was allowed when requesting secrets; the
@@ -1151,14 +1026,14 @@ get_secrets_done_cb (NMAgentManager *manager,
 	/* Update the connection with our existing secrets from backing storage */
 	nm_connection_clear_secrets (nm_settings_connection_get_connection (self));
 	if (!dict || nm_connection_update_secrets (nm_settings_connection_get_connection (self), setting_name, dict, &local)) {
-		GVariant *filtered_secrets;
+		gs_unref_variant GVariant *filtered_secrets = NULL;
 
 		/* 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.
 		 */
-		filtered_secrets = for_each_secret (nm_settings_connection_get_connection (self), secrets, TRUE, validate_secret_flags, &cmp_flags);
+		filtered_secrets = validate_secret_flags (nm_settings_connection_get_connection (self), secrets, &cmp_flags);
 		if (nm_connection_update_secrets (nm_settings_connection_get_connection (self), setting_name, filtered_secrets, &local)) {
 			/* Now that all secrets are updated, copy and cache new secrets,
 			 * then save them to backing storage.
@@ -1194,7 +1069,6 @@ get_secrets_done_cb (NMAgentManager *manager,
 			       call_id,
 			       local->message);
 		}
-		g_variant_unref (filtered_secrets);
 	} else {
 		_LOGD ("(%s:%p) failed to update with existing secrets: %s",
 		       setting_name,
@@ -1218,11 +1092,10 @@ get_secrets_done_cb (NMAgentManager *manager,
 		nm_connection_clear_secrets (applied_connection);
 
 		if (!dict || nm_connection_update_secrets (applied_connection, setting_name, dict, NULL)) {
-			GVariant *filtered_secrets;
+			gs_unref_variant GVariant *filtered_secrets = NULL;
 
-			filtered_secrets = for_each_secret (applied_connection, secrets, TRUE, validate_secret_flags, &cmp_flags);
+			filtered_secrets = validate_secret_flags (applied_connection, secrets, &cmp_flags);
 			nm_connection_update_secrets (applied_connection, setting_name, filtered_secrets, NULL);
-			g_variant_unref (filtered_secrets);
 		}
 	}
 
@@ -1878,6 +1751,9 @@ settings_connection_update (NMSettingsConnection *self,
 			                                           &error);
 			if (!tmp)
 				goto error;
+
+			if (!nm_connection_verify_secrets (tmp, &error))
+				goto error;
 		}
 	}
 
@@ -2777,7 +2653,7 @@ _autoconnect_retries_set (NMSettingsConnection *self,
 		/* NOTE: the blocked time must be identical for all connections, otherwise
 		 * the tracking of resetting the retry count in NMPolicy needs adjustment
 		 * in _connection_autoconnect_retries_set() (as it would need to re-evaluate
-		 * the next-timeout everytime a connection gets blocked). */
+		 * the next-timeout every time a connection gets blocked). */
 		priv->autoconnect_retries_blocked_until = nm_utils_get_monotonic_timestamp_s () + AUTOCONNECT_RESET_RETRIES_TIMER;
 	}
 }
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index 74de6cc2..fd1d316a 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -29,8 +29,6 @@
 
 #include <unistd.h>
 #include <sys/stat.h>
-#include <errno.h>
-#include <string.h>
 #include <gmodule.h>
 #include <pwd.h>
 
@@ -375,7 +373,7 @@ _clear_connections_cached_list (NMSettingsPrivate *priv)
 /**
  * nm_settings_get_connections:
  * @self: the #NMSettings
- * @out_len: (out): (allow-none): returns the number of returned
+ * @out_len: (out) (allow-none): returns the number of returned
  *   connections.
  *
  * Returns: (transfer none): a list of NMSettingsConnections. The list is
@@ -638,7 +636,7 @@ add_plugin_load_file (NMSettings *self, const char *pname, GError **error)
 
 	if (stat (path, &st) != 0) {
 		errsv = errno;
-		_LOGW ("could not load plugin '%s' from file '%s': %s", pname, path, strerror (errsv));
+		_LOGW ("could not load plugin '%s' from file '%s': %s", pname, path, nm_strerror_native (errsv));
 		return TRUE;
 	}
 	if (!S_ISREG (st.st_mode)) {
diff --git a/src/settings/plugins/ibft/nms-ibft-connection.c b/src/settings/plugins/ibft/nms-ibft-connection.c
index fb7f18f8..a36d8a31 100644
--- a/src/settings/plugins/ibft/nms-ibft-connection.c
+++ b/src/settings/plugins/ibft/nms-ibft-connection.c
@@ -22,7 +22,6 @@
 
 #include "nms-ibft-connection.h"
 
-#include <string.h>
 #include <net/ethernet.h>
 #include <netinet/ether.h>
 #include <glib/gstdio.h>
diff --git a/src/settings/plugins/ibft/nms-ibft-plugin.c b/src/settings/plugins/ibft/nms-ibft-plugin.c
index 69dd3733..00b25068 100644
--- a/src/settings/plugins/ibft/nms-ibft-plugin.c
+++ b/src/settings/plugins/ibft/nms-ibft-plugin.c
@@ -22,9 +22,7 @@
 
 #include "nms-ibft-plugin.h"
 
-#include <string.h>
 #include <unistd.h>
-#include <errno.h>
 #include <gmodule.h>
 
 #include "nm-setting-connection.h"
diff --git a/src/settings/plugins/ibft/nms-ibft-reader.c b/src/settings/plugins/ibft/nms-ibft-reader.c
index ac5824a1..c6c14376 100644
--- a/src/settings/plugins/ibft/nms-ibft-reader.c
+++ b/src/settings/plugins/ibft/nms-ibft-reader.c
@@ -23,13 +23,11 @@
 #include "nms-ibft-reader.h"
 
 #include <stdlib.h>
-#include <string.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <arpa/inet.h>
 #include <sys/wait.h>
 #include <sys/inotify.h>
-#include <errno.h>
 #include <sys/ioctl.h>
 #include <unistd.h>
 
diff --git a/src/settings/plugins/ibft/tests/test-ibft.c b/src/settings/plugins/ibft/tests/test-ibft.c
index 5e46be2e..4c45f574 100644
--- a/src/settings/plugins/ibft/tests/test-ibft.c
+++ b/src/settings/plugins/ibft/tests/test-ibft.c
@@ -23,7 +23,6 @@
 #include <stdio.h>
 #include <stdarg.h>
 #include <unistd.h>
-#include <string.h>
 #include <netinet/ether.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
diff --git a/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c b/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c
index e7a74a1a..04cbb5bc 100644
--- a/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c
+++ b/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c
@@ -23,9 +23,7 @@
 #include "nm-inotify-helper.h"
 
 #include <unistd.h>
-#include <string.h>
 #include <sys/inotify.h>
-#include <errno.h>
 
 #include "NetworkManagerUtils.h"
 
@@ -143,12 +141,12 @@ init_inotify (NMInotifyHelper *self)
 {
 	NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self);
 	GIOChannel *channel;
+	int errsv;
 
 	priv->ifd = inotify_init1 (IN_CLOEXEC);
 	if (priv->ifd == -1) {
-		int errsv = errno;
-
-		nm_log_warn (LOGD_SETTINGS, "couldn't initialize inotify: %s (%d)", strerror (errsv), errsv);
+		errsv = errno;
+		nm_log_warn (LOGD_SETTINGS, "couldn't initialize inotify: %s (%d)", nm_strerror_native (errsv), errsv);
 		return FALSE;
 	}
 
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c
index ca319ddc..4f769c5f 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c
@@ -22,7 +22,6 @@
 
 #include "nms-ifcfg-rh-connection.h"
 
-#include <string.h>
 #include <sys/inotify.h>
 #include <glib/gstdio.h>
 
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
index 05d4d738..89272edb 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
@@ -25,9 +25,7 @@
 
 #include "nms-ifcfg-rh-plugin.h"
 
-#include <string.h>
 #include <unistd.h>
-#include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <gmodule.h>
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
index 6eb99d3b..7c1db225 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
@@ -23,13 +23,11 @@
 #include "nms-ifcfg-rh-reader.h"
 
 #include <stdlib.h>
-#include <string.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <arpa/inet.h>
 #include <sys/wait.h>
 #include <sys/inotify.h>
-#include <errno.h>
 #include <sys/ioctl.h>
 #include <unistd.h>
 
@@ -189,7 +187,7 @@ _secret_password_raw_to_bytes (const char *ifcfg_key,
 		password_raw += 2;
 
 	secret = nm_secret_buf_new (strlen (password_raw) / 2 + 3);
-	if (!_nm_utils_hexstr2bin_full (password_raw, FALSE, FALSE, ":", 0, secret->bin, secret->len, &len)) {
+	if (!nm_utils_hexstr2bin_full (password_raw, FALSE, FALSE, ":", 0, secret->bin, secret->len, &len)) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Invalid hex password in %s",
 		             ifcfg_key);
@@ -801,7 +799,7 @@ enum {
  * @options_route: (in-out): when line is from the OPTIONS setting, this is a pre-created
  *   route object that is completed with the settings from options. Otherwise,
  *   it shall point to %NULL and a new route is created and returned.
- * @out_route: (out): (transfer-full): (allow-none): the parsed %NMIPRoute instance.
+ * @out_route: (out) (transfer-full) (allow-none): the parsed %NMIPRoute instance.
  *   In case a @options_route is passed in, it returns the input route that was modified
  *   in-place. But the caller must unref the returned route in either case.
  * @error: the failure description.
@@ -1427,8 +1425,8 @@ make_user_setting (shvarFile *ifcfg)
 			has_user_data = TRUE;
 	}
 
-	return has_user_data
-	       ? g_steal_pointer (&s_user)
+	return   has_user_data
+	       ? NM_SETTING (g_steal_pointer (&s_user))
 	       : NULL;
 }
 
@@ -1613,7 +1611,7 @@ make_ip4_setting (shvarFile *ifcfg,
 	              NULL);
 
 	if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED))
-		return g_steal_pointer (&s_ip4);
+		return NM_SETTING (g_steal_pointer (&s_ip4));
 
 	/* Handle DHCP settings */
 	nm_clear_g_free (&value);
@@ -1804,7 +1802,7 @@ make_ip4_setting (shvarFile *ifcfg,
 	}
 	g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DAD_TIMEOUT, (int) timeout, NULL);
 
-	return g_steal_pointer (&s_ip4);
+	return NM_SETTING (g_steal_pointer (&s_ip4));
 }
 
 static void
@@ -2946,7 +2944,7 @@ make_wep_setting (shvarFile *ifcfg,
 		return NULL;
 	}
 
-	return g_steal_pointer (&s_wsec);
+	return NM_SETTING (g_steal_pointer (&s_wsec));
 }
 
 static gboolean
@@ -3612,7 +3610,7 @@ make_wpa_setting (shvarFile *ifcfg,
 	gs_unref_object NMSettingWirelessSecurity *wsec = NULL;
 	gs_free char *value = NULL;
 	const char *v;
-	gboolean wpa_psk = FALSE, wpa_eap = FALSE, ieee8021x = FALSE;
+	gboolean wpa_psk = FALSE, wpa_sae = FALSE, wpa_eap = FALSE, ieee8021x = FALSE;
 	int i_val;
 	GError *local = NULL;
 
@@ -3620,9 +3618,10 @@ make_wpa_setting (shvarFile *ifcfg,
 
 	v = svGetValueStr (ifcfg, "KEY_MGMT", &value);
 	wpa_psk = nm_streq0 (v, "WPA-PSK");
+	wpa_sae = nm_streq0 (v, "SAE");
 	wpa_eap = nm_streq0 (v, "WPA-EAP");
 	ieee8021x = nm_streq0 (v, "IEEE8021X");
-	if (!wpa_psk && !wpa_eap && !ieee8021x)
+	if (!wpa_psk && !wpa_sae && !wpa_eap && !ieee8021x)
 		return NULL; /* Not WPA or Dynamic WEP */
 
 	/* WPS */
@@ -3636,7 +3635,7 @@ make_wpa_setting (shvarFile *ifcfg,
 	              NULL);
 
 	/* Pairwise and Group ciphers (only relevant for WPA/RSN) */
-	if (wpa_psk || wpa_eap) {
+	if (wpa_psk || wpa_sae || wpa_eap) {
 		fill_wpa_ciphers (ifcfg, wsec, FALSE, adhoc);
 		fill_wpa_ciphers (ifcfg, wsec, TRUE, adhoc);
 	}
@@ -3659,7 +3658,7 @@ make_wpa_setting (shvarFile *ifcfg,
 			nm_setting_wireless_security_add_proto (wsec, "rsn");
 	}
 
-	if (wpa_psk) {
+	if (wpa_psk || wpa_sae) {
 		NMSettingSecretFlags psk_flags;
 
 		psk_flags = _secret_read_ifcfg_flags (ifcfg, "WPA_PSK_FLAGS");
@@ -3680,8 +3679,12 @@ make_wpa_setting (shvarFile *ifcfg,
 
 		if (adhoc)
 			g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-none", NULL);
-		else
+		else if (wpa_psk)
 			g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", NULL);
+		else if (wpa_sae)
+			g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "sae", NULL);
+		else
+			g_assert_not_reached ();
 	} else if (wpa_eap || ieee8021x) {
 		/* Adhoc mode is mutually exclusive with any 802.1x-based authentication */
 		if (adhoc) {
@@ -3946,9 +3949,8 @@ make_wireless_setting (shvarFile *ifcfg,
 
 	value = svGetValueStr_cp (ifcfg, "CHANNEL");
 	if (value) {
-		errno = 0;
 		chan = _nm_utils_ascii_str_to_int64 (value, 10, 1, 196, 0);
-		if (errno || (chan == 0)) {
+		if (chan == 0) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Invalid wireless channel '%s'", value);
 			g_free (value);
@@ -4992,7 +4994,7 @@ handle_bridge_option (NMSetting *setting,
 			} else {
 				v = _nm_utils_ascii_str_to_int64 (value, 10, 0, 1, -1);
 				if (v == -1) {
-					error_message = g_strerror (errno);
+					error_message = nm_strerror_native (errno);
 					goto warn;
 				}
 			}
@@ -5004,7 +5006,7 @@ handle_bridge_option (NMSetting *setting,
 		case G_TYPE_UINT:
 			v = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT, -1);
 			if (v == -1) {
-				error_message = g_strerror (errno);
+				error_message = nm_strerror_native (errno);
 				goto warn;
 			}
 			if (!nm_g_object_set_property_uint (G_OBJECT (setting), m[i].property_name, v, NULL)) {
@@ -5229,16 +5231,14 @@ is_vlan_device (const char *name, shvarFile *parsed)
 static gboolean
 is_wifi_device (const char *name, shvarFile *parsed)
 {
-	int ifindex;
+	const NMPlatformLink *pllink;
 
 	g_return_val_if_fail (name != NULL, FALSE);
 	g_return_val_if_fail (parsed != NULL, FALSE);
 
-	ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, name);
-	if (ifindex == 0)
-		return FALSE;
-
-	return nm_platform_link_get_type (NM_PLATFORM_GET, ifindex) == NM_LINK_TYPE_WIFI;
+	pllink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, name);
+	return    pllink
+	       && pllink->type == NM_LINK_TYPE_WIFI;
 }
 
 static void
@@ -5384,7 +5384,7 @@ make_vlan_setting (shvarFile *ifcfg,
 	parse_prio_map_list (s_vlan, ifcfg, "VLAN_INGRESS_PRIORITY_MAP", NM_VLAN_INGRESS_MAP);
 	parse_prio_map_list (s_vlan, ifcfg, "VLAN_EGRESS_PRIORITY_MAP", NM_VLAN_EGRESS_MAP);
 
-	return g_steal_pointer (&s_vlan);
+	return NM_SETTING (g_steal_pointer (&s_vlan));
 }
 
 static NMConnection *
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
index 49096d26..22c9061b 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
@@ -23,7 +23,6 @@
 #include "nms-ifcfg-rh-utils.h"
 
 #include <stdlib.h>
-#include <string.h>
 
 #include "nm-core-internal.h"
 #include "NetworkManagerUtils.h"
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
index f5be7520..ee7fd161 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
@@ -22,11 +22,9 @@
 
 #include "nms-ifcfg-rh-writer.h"
 
-#include <string.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <arpa/inet.h>
-#include <errno.h>
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -603,6 +601,10 @@ write_wireless_security_setting (NMConnection *connection,
 		svSetValueStr (ifcfg, "KEY_MGMT", "WPA-PSK");
 		wpa = TRUE;
 		*no_8021x = TRUE;
+	} else if (!strcmp (key_mgmt, "sae")) {
+		svSetValueStr (ifcfg, "KEY_MGMT", "SAE");
+		wpa = TRUE;
+		*no_8021x = TRUE;
 	} else if (!strcmp (key_mgmt, "ieee8021x")) {
 		svSetValueStr (ifcfg, "KEY_MGMT", "IEEE8021X");
 		dynamic_wep = TRUE;
diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c
index 3259d936..f3d58e26 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/settings/plugins/ifcfg-rh/shvar.c
@@ -27,11 +27,9 @@
 
 #include "shvar.h"
 
-#include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -215,9 +213,9 @@ _escape_ansic (const char *source)
 
 /*****************************************************************************/
 
-#define _char_req_escape(ch)        NM_IN_SET (ch,      '\"', '\\',       '$', '`')
-#define _char_req_escape_old(ch)    NM_IN_SET (ch,      '\"', '\\', '\'', '$', '`', '~')
-#define _char_req_quotes(ch)        NM_IN_SET (ch, ' ',             '\'',           '~', '\t', '|', '&', ';', '(', ')', '<', '>')
+#define _char_req_escape(ch)        NM_IN_SET (ch,      '"', '\\',       '$', '`')
+#define _char_req_escape_old(ch)    NM_IN_SET (ch,      '"', '\\', '\'', '$', '`', '~')
+#define _char_req_quotes(ch)        NM_IN_SET (ch, ' ',            '\'',           '~', '\t', '|', '&', ';', '(', ')', '<', '>')
 
 const char *
 svEscape (const char *s, char **to_free)
@@ -815,7 +813,7 @@ svOpenFileInternal (const char *name, gboolean create, GError **error)
 
 		g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errsv),
 		             "Could not read file '%s': %s",
-		             name, strerror (errsv));
+		             name, nm_strerror_native (errsv));
 		return NULL;
 	}
 
@@ -1317,34 +1315,32 @@ svWriteFile (shvarFile *s, int mode, GError **error)
 	FILE *f;
 	int tmpfd;
 	CList *current;
+	int errsv;
 
 	if (s->modified) {
 		if (s->fd == -1)
 			s->fd = open (s->fileName, O_WRONLY | O_CREAT | O_CLOEXEC, mode);
 		if (s->fd == -1) {
-			int errsv = errno;
-
+			errsv = errno;
 			g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errsv),
 			             "Could not open file '%s' for writing: %s",
-			             s->fileName, strerror (errsv));
+			             s->fileName, nm_strerror_native (errsv));
 			return FALSE;
 		}
 		if (ftruncate (s->fd, 0) < 0) {
-			int errsv = errno;
-
+			errsv = errno;
 			g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errsv),
 			             "Could not overwrite file '%s': %s",
-			             s->fileName, strerror (errsv));
+			             s->fileName, nm_strerror_native (errsv));
 			return FALSE;
 		}
 
 		tmpfd = fcntl (s->fd, F_DUPFD_CLOEXEC, 0);
 		if (tmpfd == -1) {
-			int errsv = errno;
-
+			errsv = errno;
 			g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errsv),
 			             "Internal error writing file '%s': %s",
-			             s->fileName, strerror (errsv));
+			             s->fileName, nm_strerror_native (errsv));
 			return FALSE;
 		}
 		f = fdopen (tmpfd, "w");
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-sae b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-sae
new file mode 100644
index 00000000..68afbe97
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-sae
@@ -0,0 +1,5 @@
+TYPE=Wireless
+DEVICE=wlan1
+ESSID=blahblah
+MODE=Managed
+KEY_MGMT=SAE
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-sae b/src/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-sae
new file mode 100644
index 00000000..5a9569ed
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-sae
@@ -0,0 +1 @@
+WPA_PSK="The king is dead."
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 d135ea43..b352fbfc 100644
--- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
+++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
@@ -23,7 +23,6 @@
 #include <stdio.h>
 #include <stdarg.h>
 #include <unistd.h>
-#include <string.h>
 #include <linux/pkt_sched.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -123,7 +122,7 @@ _assert_reread_same_FIXME (NMConnection *connection, NMConnection *reread)
 
 /* dummy path for an "expected" file, meaning: don't check for expected
  * written ifcfg file. */
-static const char const NO_EXPECTED[1];
+static const char NO_EXPECTED[1];
 
 static void
 _assert_expected_content (NMConnection *connection, const char *filename, const char *expected)
@@ -2967,6 +2966,45 @@ test_read_wifi_wpa_psk (void)
 }
 
 static void
+test_read_wifi_sae (void)
+{
+	gs_unref_object NMConnection *connection = NULL;
+	NMSettingConnection *s_con;
+	NMSettingWireless *s_wireless;
+	NMSettingWirelessSecurity *s_wsec;
+	GBytes *ssid;
+	const char *expected_ssid = "blahblah";
+
+	connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-sae",
+	                                    NULL, TYPE_WIRELESS, NULL);
+
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+	g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "System blahblah (test-wifi-sae)");
+
+	g_assert_cmpint (nm_setting_connection_get_timestamp (s_con), ==, 0);
+	g_assert (nm_setting_connection_get_autoconnect (s_con));
+
+	s_wireless = nm_connection_get_setting_wireless (connection);
+	g_assert (s_wireless);
+
+	g_assert_cmpint (nm_setting_wireless_get_mtu (s_wireless), ==, 0);
+
+	ssid = nm_setting_wireless_get_ssid (s_wireless);
+	g_assert (ssid);
+	g_assert_cmpmem (g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid), expected_ssid, strlen (expected_ssid));
+
+	g_assert (!nm_setting_wireless_get_bssid (s_wireless));
+	g_assert_cmpstr (nm_setting_wireless_get_mode (s_wireless), ==, "infrastructure");
+
+	s_wsec = nm_connection_get_setting_wireless_security (connection);
+	g_assert (s_wsec);
+	g_assert_cmpstr (nm_setting_wireless_security_get_key_mgmt (s_wsec), ==, "sae");
+	g_assert_cmpstr (nm_setting_wireless_security_get_psk (s_wsec), ==, "The king is dead.");
+	g_assert (!nm_setting_wireless_security_get_auth_alg (s_wsec));
+}
+
+static void
 test_read_wifi_wpa_psk_2 (void)
 {
 	NMConnection *connection;
@@ -9981,10 +10019,14 @@ NMTST_DEFINE ();
 
 int main (int argc, char **argv)
 {
+	int errsv;
+
 	nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT");
 
-	if (g_mkdir_with_parents (TEST_SCRATCH_DIR_TMP, 0755) != 0)
-		g_error ("failure to create test directory \"%s\": %s", TEST_SCRATCH_DIR_TMP, g_strerror (errno));
+	if (g_mkdir_with_parents (TEST_SCRATCH_DIR_TMP, 0755) != 0) {
+		errsv = errno;
+		g_error ("failure to create test directory \"%s\": %s", TEST_SCRATCH_DIR_TMP, nm_strerror_native (errsv));
+	}
 
 	g_test_add_func (TPATH "svUnescape", test_svUnescape);
 
@@ -10081,6 +10123,7 @@ int main (int argc, char **argv)
 	g_test_add_func (TPATH "wifi/read/wpa-psk/unquoted2", test_read_wifi_wpa_psk_unquoted2);
 	g_test_add_func (TPATH "wifi/read/wpa-psk/adhoc", test_read_wifi_wpa_psk_adhoc);
 	g_test_add_func (TPATH "wifi/read/wpa-psk/hex", test_read_wifi_wpa_psk_hex);
+	g_test_add_func (TPATH "wifi/read/sae", test_read_wifi_sae);
 	g_test_add_func (TPATH "wifi/read/dynamic-wep/leap", test_read_wifi_dynamic_wep_leap);
 	g_test_add_func (TPATH "wifi/read/wpa/eap/tls", test_read_wifi_wpa_eap_tls);
 	g_test_add_func (TPATH "wifi/read/wpa/eap/ttls/tls", test_read_wifi_wpa_eap_ttls_tls);
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-connection.c b/src/settings/plugins/ifupdown/nms-ifupdown-connection.c
index 1b817044..d06078a9 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-connection.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-connection.c
@@ -24,7 +24,6 @@
 
 #include "nms-ifupdown-connection.h"
 
-#include <string.h>
 #include <glib/gstdio.h>
 
 #include "nm-dbus-interface.h"
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c
index 73ecc2f9..6587fc84 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c
@@ -26,7 +26,6 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 #include <wordexp.h>
 #include <libgen.h>
 
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
index 369fa70d..fd5561ae 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
@@ -25,10 +25,8 @@
 
 #include "nms-ifupdown-parser.h"
 
-#include <string.h>
 #include <arpa/inet.h>
 #include <stdlib.h>
-#include <errno.h>
 #include <ctype.h>
 
 #include "nm-core-internal.h"
@@ -63,7 +61,7 @@ _ifupdownplugin_guess_connection_type (if_block *block)
 {
 	const char *ret_type = NULL;
 
-	if(nm_streq0 (ifparser_getkey (block, "inet"), "ppp"))
+	if (nm_streq0 (ifparser_getkey (block, "inet"), "ppp"))
 		ret_type = NM_SETTING_PPP_SETTING_NAME;
 	else {
 		if_data *ifb;
@@ -75,7 +73,7 @@ _ifupdownplugin_guess_connection_type (if_block *block)
 				break;
 			}
 		}
-		if(!ret_type)
+		if (!ret_type)
 			ret_type = NM_SETTING_WIRED_SETTING_NAME;
 	}
 
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
index b66eedc7..99a59477 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
@@ -26,7 +26,6 @@
 
 #include "nms-ifupdown-plugin.h"
 
-#include <string.h>
 #include <arpa/inet.h>
 #include <gmodule.h>
 
@@ -282,8 +281,10 @@ initialize (NMSettingsPlugin *plugin)
 
 		g_hash_table_iter_init (&iter, priv->eni_ifaces);
 		while (g_hash_table_iter_next (&iter, NULL, (gpointer *) conn)) {
-			_nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self),
-			                                                  NM_SETTINGS_CONNECTION (conn));
+			if (conn) {
+				_nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self),
+				                                                  NM_SETTINGS_CONNECTION (conn));
+			}
 		}
 	}
 }
diff --git a/src/settings/plugins/ifupdown/tests/test-ifupdown.c b/src/settings/plugins/ifupdown/tests/test-ifupdown.c
index 82ee1c4a..674cb19c 100644
--- a/src/settings/plugins/ifupdown/tests/test-ifupdown.c
+++ b/src/settings/plugins/ifupdown/tests/test-ifupdown.c
@@ -20,8 +20,6 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-
 #include "nm-core-internal.h"
 
 #include "settings/plugins/ifupdown/nms-ifupdown-interface-parser.h"
diff --git a/src/settings/plugins/keyfile/nms-keyfile-connection.c b/src/settings/plugins/keyfile/nms-keyfile-connection.c
index 7511f206..3b362978 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-connection.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-connection.c
@@ -23,7 +23,6 @@
 
 #include "nms-keyfile-connection.h"
 
-#include <string.h>
 #include <glib/gstdio.h>
 
 #include "nm-dbus-interface.h"
diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
index ae9bea13..c13cc1ff 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
@@ -26,8 +26,6 @@
 #include <sys/stat.h>
 #include <unistd.h>
 #include <sys/types.h>
-#include <string.h>
-
 #include <glib/gstdio.h>
 
 #include "nm-connection.h"
diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.c b/src/settings/plugins/keyfile/nms-keyfile-reader.c
index 314b1033..5778f13c 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-reader.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-reader.c
@@ -23,7 +23,6 @@
 #include "nms-keyfile-reader.h"
 
 #include <sys/stat.h>
-#include <string.h>
 
 #include "nm-keyfile-internal.h"
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.c b/src/settings/plugins/keyfile/nms-keyfile-utils.c
index 8d4ec943..3c4b0288 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-utils.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-utils.c
@@ -23,7 +23,6 @@
 #include "nms-keyfile-utils.h"
 
 #include <stdlib.h>
-#include <string.h>
 #include <sys/stat.h>
 
 #include "nm-keyfile-internal.h"
@@ -277,14 +276,14 @@ nms_keyfile_utils_check_file_permissions (NMSKeyfileFiletype filetype,
 		if (stat (filename, &st) != 0) {
 			errsv = errno;
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-			             "cannot access file: %s", g_strerror (errsv));
+			             "cannot access file: %s", nm_strerror_native (errsv));
 			return FALSE;
 		}
 	} else if (filetype == NMS_KEYFILE_FILETYPE_NMLOADED) {
 		if (lstat (filename, &st) != 0) {
 			errsv = errno;
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-			             "cannot access file: %s", g_strerror (errsv));
+			             "cannot access file: %s", nm_strerror_native (errsv));
 			return FALSE;
 		}
 	} else
diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.c b/src/settings/plugins/keyfile/nms-keyfile-writer.c
index 23a6a77c..8c75d8c7 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-writer.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-writer.c
@@ -26,8 +26,6 @@
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <unistd.h>
-#include <errno.h>
-#include <string.h>
 
 #include "nm-keyfile-internal.h"
 
@@ -177,27 +175,30 @@ _internal_write_connection (NMConnection *connection,
                             uid_t owner_uid,
                             pid_t owner_grp,
                             const char *existing_path,
+                            gboolean existing_path_read_only,
                             gboolean force_rename,
                             char **out_path,
                             NMConnection **out_reread,
                             gboolean *out_reread_same,
                             GError **error)
 {
-	gs_unref_keyfile GKeyFile *key_file = NULL;
-	gs_free char *data = NULL;
-	gsize len;
+	gs_unref_keyfile GKeyFile *kf_file = NULL;
+	gs_free char *kf_content_buf = NULL;
+	gsize kf_content_len;
 	gs_free char *path = NULL;
 	const char *id;
 	WriteInfo info = { 0 };
 	GError *local_err = NULL;
 	int errsv;
-	gboolean rename = force_rename;
+	gboolean rename;
 
 	g_return_val_if_fail (!out_path || !*out_path, FALSE);
 	g_return_val_if_fail (keyfile_dir && keyfile_dir[0] == '/', FALSE);
 
-	if (existing_path && !g_str_has_prefix (existing_path, keyfile_dir))
-		rename = TRUE;
+	rename =    force_rename
+	         || existing_path_read_only
+	         || (   existing_path
+	             && !nm_utils_file_is_in_path (existing_path, keyfile_dir));
 
 	switch (_nm_connection_verify (connection, error)) {
 	case NM_SETTING_VERIFY_NORMALIZABLE:
@@ -214,11 +215,11 @@ _internal_write_connection (NMConnection *connection,
 
 	info.keyfile_dir = keyfile_dir;
 
-	key_file = nm_keyfile_write (connection, _handler_write, &info, error);
-	if (!key_file)
+	kf_file = nm_keyfile_write (connection, _handler_write, &info, error);
+	if (!kf_file)
 		return FALSE;
-	data = g_key_file_to_data (key_file, &len, error);
-	if (!data)
+	kf_content_buf = g_key_file_to_data (kf_file, &kf_content_len, error);
+	if (!kf_content_buf)
 		return FALSE;
 
 	if (!g_file_test (keyfile_dir, G_FILE_TEST_IS_DIR))
@@ -227,13 +228,14 @@ _internal_write_connection (NMConnection *connection,
 	/* If we have existing file path, use it. Else generate one from
 	 * connection's ID.
 	 */
-	if (existing_path != NULL && !rename) {
+	if (   existing_path
+	    && !rename)
 		path = g_strdup (existing_path);
-	} else {
-		char *filename_escaped = nm_keyfile_utils_create_filename (id, with_extension);
+	else {
+		gs_free char *filename_escaped = NULL;
 
+		filename_escaped = nm_keyfile_utils_create_filename (id, with_extension);
 		path = g_build_filename (keyfile_dir, filename_escaped, NULL);
-		g_free (filename_escaped);
 	}
 
 	/* If a file with this path already exists (but isn't the existing path
@@ -243,13 +245,15 @@ _internal_write_connection (NMConnection *connection,
 	 * there's a race here, but there's not a lot we can do about it, and
 	 * we shouldn't get more than one connection with the same UUID either.
 	 */
-	if (g_strcmp0 (path, existing_path) != 0 && g_file_test (path, G_FILE_TEST_EXISTS)) {
+	if (   !nm_streq0 (path, existing_path)
+	    && g_file_test (path, G_FILE_TEST_EXISTS)) {
 		guint i;
 		gboolean name_found = FALSE;
 
 		/* A keyfile with this connection's ID already exists. Pick another name. */
 		for (i = 0; i < 100; i++) {
-			char *filename, *filename_escaped;
+			gs_free char *filename_escaped = NULL;
+			gs_free char *filename = NULL;
 
 			if (i == 0)
 				filename = g_strdup_printf ("%s-%s", id, nm_connection_get_uuid (connection));
@@ -260,15 +264,15 @@ _internal_write_connection (NMConnection *connection,
 
 			g_free (path);
 			path = g_strdup_printf ("%s/%s", keyfile_dir, filename_escaped);
-			g_free (filename);
-			g_free (filename_escaped);
-			if (g_strcmp0 (path, existing_path) == 0 || !g_file_test (path, G_FILE_TEST_EXISTS)) {
+
+			if (   nm_streq0 (path, existing_path)
+			    || !g_file_test (path, G_FILE_TEST_EXISTS)) {
 				name_found = TRUE;
 				break;
 			}
 		}
 		if (!name_found) {
-			if (existing_path == NULL) {
+			if (existing_path_read_only || !existing_path) {
 				/* this really should not happen, we tried hard to find an unused name... bail out. */
 				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 				                    "could not find suitable keyfile file name (%s already used)", path);
@@ -281,13 +285,7 @@ _internal_write_connection (NMConnection *connection,
 		}
 	}
 
-	/* 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);
-
-	nm_utils_file_set_contents (path, data, len, 0600, &local_err);
+	nm_utils_file_set_contents (path, kf_content_buf, kf_content_len, 0600, &local_err);
 	if (local_err) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		             "error writing to file '%s': %s",
@@ -300,17 +298,24 @@ _internal_write_connection (NMConnection *connection,
 		errsv = errno;
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		             "error chowning '%s': %s (%d)",
-		             path, g_strerror (errsv), errsv);
+		             path, nm_strerror_native (errsv), errsv);
 		unlink (path);
 		return FALSE;
 	}
 
-	if (out_reread || out_reread_same)
-	{
+	/* 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
+	    && !existing_path_read_only
+	    && !nm_streq (path, existing_path))
+		unlink (existing_path);
+
+	if (out_reread || out_reread_same) {
 		gs_unref_object NMConnection *reread = NULL;
 		gboolean reread_same = FALSE;
 
-		reread = nms_keyfile_reader_from_keyfile (key_file, path, NULL, profile_dir, FALSE, NULL);
+		reread = nms_keyfile_reader_from_keyfile (kf_file, path, NULL, profile_dir, FALSE, NULL);
 
 		nm_assert (NM_IS_CONNECTION (reread));
 
@@ -365,6 +370,7 @@ nms_keyfile_writer_connection (NMConnection *connection,
 	                                   0,
 	                                   0,
 	                                   existing_path,
+	                                   FALSE,
 	                                   force_rename,
 	                                   out_path,
 	                                   out_reread,
@@ -390,6 +396,7 @@ nms_keyfile_writer_test_connection (NMConnection *connection,
 	                                   owner_grp,
 	                                   NULL,
 	                                   FALSE,
+	                                   FALSE,
 	                                   out_path,
 	                                   out_reread,
 	                                   out_reread_same,
diff --git a/src/settings/plugins/keyfile/tests/meson.build b/src/settings/plugins/keyfile/tests/meson.build
index 4253fe3c..752b6d7b 100644
--- a/src/settings/plugins/keyfile/tests/meson.build
+++ b/src/settings/plugins/keyfile/tests/meson.build
@@ -12,4 +12,5 @@ test(
   'keyfile/' + test_unit,
   test_script,
   args: test_args + [exe.full_path()],
+  timeout: default_test_timeout,
 )
diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c
index cdc9bfb0..baecac13 100644
--- a/src/settings/plugins/keyfile/tests/test-keyfile.c
+++ b/src/settings/plugins/keyfile/tests/test-keyfile.c
@@ -23,7 +23,6 @@
 #include <stdio.h>
 #include <stdarg.h>
 #include <unistd.h>
-#include <string.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <sys/socket.h>
@@ -2615,11 +2614,16 @@ NMTST_DEFINE ();
 
 int main (int argc, char **argv)
 {
+	int errsv;
+
 	_nm_utils_set_testing (NM_UTILS_TEST_NO_KEYFILE_OWNER_CHECK);
+
 	nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT");
 
-	if (g_mkdir_with_parents (TEST_SCRATCH_DIR, 0755) != 0)
-		g_error ("failure to create test directory \"%s\": %s", TEST_SCRATCH_DIR, g_strerror (errno));
+	if (g_mkdir_with_parents (TEST_SCRATCH_DIR, 0755) != 0) {
+		errsv = errno;
+		g_error ("failure to create test directory \"%s\": %s", TEST_SCRATCH_DIR, nm_strerror_native (errsv));
+	}
 
 	/* The tests */
 	g_test_add_func ("/keyfile/test_read_valid_wired_connection", test_read_valid_wired_connection);