summary refs log tree commit diff
path: root/libnm-core/nm-keyfile.c
diff options
context:
space:
mode:
Diffstat (limited to 'libnm-core/nm-keyfile.c')
-rw-r--r--libnm-core/nm-keyfile.c455
1 files changed, 343 insertions, 112 deletions
diff --git a/libnm-core/nm-keyfile.c b/libnm-core/nm-keyfile.c
index 5c94c35c..c7d87869 100644
--- a/libnm-core/nm-keyfile.c
+++ b/libnm-core/nm-keyfile.c
@@ -33,6 +33,7 @@
 #include <string.h>
 #include <linux/pkt_sched.h>
 
+#include "nm-utils/nm-secret-utils.h"
 #include "nm-common-macros.h"
 #include "nm-core-internal.h"
 #include "nm-keyfile-utils.h"
@@ -119,6 +120,49 @@ setting_alias_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *k
 }
 
 static void
+sriov_vfs_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
+{
+	const char *setting_name = nm_setting_get_name (setting);
+	gs_unref_ptrarray GPtrArray *vfs = NULL;
+	gs_strfreev char **keys = NULL;
+	gsize n_keys = 0;
+	int i;
+
+	keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL);
+	if (!keys || n_keys == 0)
+		return;
+
+	vfs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_sriov_vf_unref);
+
+	for (i = 0; i < n_keys; i++) {
+		gs_free char *value = NULL;
+		NMSriovVF *vf;
+		const char *rest;
+
+		if (!g_str_has_prefix (keys[i], "vf."))
+			continue;
+
+		rest = &keys[i][3];
+
+		if (!NM_STRCHAR_ALL (rest, ch, g_ascii_isdigit (ch)))
+			continue;
+
+		value = nm_keyfile_plugin_kf_get_string (info->keyfile,
+		                                         setting_name,
+		                                         keys[i],
+		                                         NULL);
+
+		vf = _nm_utils_sriov_vf_from_strparts (rest, value, NULL);
+		if (vf)
+			g_ptr_array_add (vfs, vf);
+	}
+
+	g_object_set (G_OBJECT (setting),
+	              key, vfs,
+	              NULL);
+}
+
+static void
 read_array_of_uint (GKeyFile *file,
                     NMSetting *setting,
                     const char *key)
@@ -731,7 +775,7 @@ ip6_addr_gen_mode_parser (KeyfileReaderInfo *info, NMSetting *setting, const cha
 	} else
 		addr_gen_mode = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64;
 
-	g_object_set (G_OBJECT (setting), key, (gint) addr_gen_mode, NULL);
+	g_object_set (G_OBJECT (setting), key, (int) addr_gen_mode, NULL);
 }
 
 static void
@@ -896,7 +940,7 @@ unescape_semicolons (char *str)
 			i++;
 		str[j++] = str[i++];;
 	}
-	str[j] = '\0';
+	nm_explicit_bzero (&str[j], i - j);
 	return j;
 }
 
@@ -907,9 +951,10 @@ get_bytes (KeyfileReaderInfo *info,
            gboolean zero_terminate,
            gboolean unescape_semicolon)
 {
-	gs_free char *tmp_string = NULL;
+	nm_auto_free_secret char *tmp_string = NULL;
 	gboolean may_be_int_list = TRUE;
 	gsize length;
+	GBytes *result;
 
 	/* New format: just a string
 	 * Old format: integer list; e.g. 11;25;38;
@@ -926,7 +971,7 @@ get_bytes (KeyfileReaderInfo *info,
 		 * byte-array. The reason is that zero_terminate is there to terminate
 		 * *valid* strings. It's not there to terminated invalid (empty) strings.
 		 */
-		return g_bytes_new_take (tmp_string, 0);
+		return g_bytes_new_static ("", 0);
 	}
 
 	for (length = 0; tmp_string[length]; length++) {
@@ -943,12 +988,11 @@ get_bytes (KeyfileReaderInfo *info,
 
 	/* Try to parse the string as a integer list. */
 	if (may_be_int_list && length > 0) {
-		gs_free guint8 *bin_data = NULL;
+		nm_auto_free_secret_buf NMSecretBuf *bin = NULL;
 		const char *const s = tmp_string;
 		gsize i, d;
-		const gsize BIN_DATA_LEN = (length / 2 + 3);
 
-		bin_data = g_malloc (BIN_DATA_LEN);
+		bin = nm_secret_buf_new (length / 2 + 3);
 
 #define DIGIT(c) ((c) - '0')
 		i = 0;
@@ -981,8 +1025,8 @@ get_bytes (KeyfileReaderInfo *info,
 				break;
 			}
 
-			bin_data[d++] = n;
-			nm_assert (d < BIN_DATA_LEN);
+			nm_assert (d < bin->len);
+			bin->bin[d++] = n;
 
 			/* allow whitespace after the digit. */
 			while (g_ascii_isspace (s[i]))
@@ -1000,16 +1044,23 @@ get_bytes (KeyfileReaderInfo *info,
 		 * string format before. We expect that this conversion cannot fail. */
 		if (d > 0) {
 			/* note that @zero_terminate does not add a terminating '\0' to
-			 * binary data as an integer list.
-			 *
-			 * But we add a '\0' to the bin_data pointer, just to avoid somebody
-			 * (erronously!) reading the binary data as C-string.
+			 * binary data as an integer list. If the bytes are expressed as
+			 * an integer list, all potential NUL characters are supposed to
+			 * be included there explicitly.
 			 *
-			 * @d itself does not entail the '\0'. */
-			nm_assert (d + 1 <= BIN_DATA_LEN);
-			bin_data = g_realloc (bin_data, d + 1);
-			bin_data[d] = '\0';
-			return g_bytes_new_take (g_steal_pointer (&bin_data), d);
+			 * However, in the spirit of defensive programming, we do append a
+			 * NUL character to the buffer, although this character is hidden
+			 * and only a mitigation for bugs. */
+
+			if (d + 10 < bin->len) {
+				/* hm, too much unused memory. Copy the memory to a suitable
+				 * sized buffer. */
+				return nm_secret_copy_to_gbytes (bin->bin, d);
+			}
+
+			nm_assert (d < bin->len);
+			bin->bin[d] = '\0';
+			return nm_secret_buf_to_gbytes_take (g_steal_pointer (&bin), d);
 		}
 	}
 
@@ -1020,8 +1071,13 @@ get_bytes (KeyfileReaderInfo *info,
 		length++;
 	if (length == 0)
 		return NULL;
-	tmp_string = g_realloc (tmp_string, length + (zero_terminate ? 0 : 1));
-	return g_bytes_new_take (g_steal_pointer (&tmp_string), length);
+
+	result = g_bytes_new_with_free_func (tmp_string,
+	                                     length,
+	                                     (GDestroyNotify) nm_free_secret,
+	                                     tmp_string);
+	tmp_string = NULL;
+	return result;
 }
 
 static void
@@ -1065,12 +1121,12 @@ get_cert_path (const char *base_dir, const guint8 *cert_path, gsize cert_path_le
 	g_return_val_if_fail (base_dir != NULL, NULL);
 	g_return_val_if_fail (cert_path != NULL, NULL);
 
-	base = path = g_malloc0 (cert_path_len + 1);
-	memcpy (path, cert_path, cert_path_len);
+	path = g_strndup ((char *) cert_path, cert_path_len);
 
 	if (path[0] == '/')
 		return path;
 
+	base = path;
 	p = strrchr (path, '/');
 	if (p)
 		base = p + 1;
@@ -1104,8 +1160,9 @@ nm_keyfile_detect_unqualified_path_scheme (const char *base_dir,
 	const char *data = pdata;
 	gboolean exists = FALSE;
 	gsize validate_len;
+	gsize path_len, pathuri_len;
 	gs_free char *path = NULL;
-	GByteArray *tmp;
+	gs_free char *pathuri = NULL;
 
 	g_return_val_if_fail (base_dir && base_dir[0] == '/', NULL);
 
@@ -1148,18 +1205,16 @@ nm_keyfile_detect_unqualified_path_scheme (const char *base_dir,
 	 * When returning TRUE, we must also be sure that @data_len does not look like
 	 * the deprecated format of list of integers. With this implementation that is the
 	 * case, as long as @consider_exists is FALSE. */
-	tmp = g_byte_array_sized_new (strlen (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH) + strlen (path) + 1);
-	g_byte_array_append (tmp, (const guint8 *) NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, strlen (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH));
-	g_byte_array_append (tmp, (const guint8 *) path, strlen (path) + 1);
-	if (nm_setting_802_1x_check_cert_scheme (tmp->data, tmp->len, NULL) != NM_SETTING_802_1X_CK_SCHEME_PATH) {
-		g_byte_array_unref (tmp);
+	path_len = strlen (path);
+	pathuri_len = (NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH) + 1) + path_len;
+	pathuri = g_new (char, pathuri_len);
+	memcpy (pathuri, NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH));
+	memcpy (&pathuri[NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH)], path, path_len + 1);
+	if (nm_setting_802_1x_check_cert_scheme (pathuri, pathuri_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_PATH)
 		return NULL;
-	}
-	g_free (path);
-	path = (char *) g_byte_array_free (tmp, FALSE);
 
 	NM_SET_OUT (out_exists, exists);
-	return g_steal_pointer (&path);
+	return g_steal_pointer (&pathuri);
 }
 
 #define HAS_SCHEME_PREFIX(bin, bin_len, scheme) \
@@ -1393,7 +1448,7 @@ qdisc_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
 	const char *setting_name = nm_setting_get_name (setting);
 	GPtrArray *qdiscs;
-	gs_strfreev gchar **keys = NULL;
+	gs_strfreev char **keys = NULL;
 	gsize n_keys = 0;
 	int i;
 
@@ -1441,7 +1496,7 @@ tfilter_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
 	const char *setting_name = nm_setting_get_name (setting);
 	GPtrArray *tfilters;
-	gs_strfreev gchar **keys = NULL;
+	gs_strfreev char **keys = NULL;
 	gsize n_keys = 0;
 	int i;
 
@@ -1510,6 +1565,37 @@ setting_alias_writer (KeyfileWriterInfo *info,
 }
 
 static void
+sriov_vfs_writer (KeyfileWriterInfo *info,
+                  NMSetting *setting,
+                  const char *key,
+                  const GValue *value)
+{
+	GPtrArray *vfs;
+	guint i;
+
+	vfs = g_value_get_boxed (value);
+	if (!vfs)
+		return;
+
+	for (i = 0; i < vfs->len; i++) {
+		const NMSriovVF *vf = vfs->pdata[i];
+		gs_free char *kf_value = NULL;
+		char kf_key[32];
+
+		kf_value = nm_utils_sriov_vf_to_str (vf, TRUE, NULL);
+		if (!kf_value)
+			continue;
+
+		nm_sprintf_buf (kf_key, "vf.%u", nm_sriov_vf_get_index (vf));
+
+		nm_keyfile_plugin_kf_set_string (info->keyfile,
+		                                 nm_setting_get_name (setting),
+		                                 kf_key,
+		                                 kf_value);
+	}
+}
+
+static void
 write_array_of_uint (GKeyFile *file,
                      NMSetting *setting,
                      const char *key,
@@ -1525,7 +1611,7 @@ write_array_of_uint (GKeyFile *file,
 
 	g_return_if_fail (g_array_get_element_size (array) == sizeof (guint));
 
-	tmp_array = g_new (gint, array->len);
+	tmp_array = g_new (int, array->len);
 	for (i = 0; i < array->len; i++) {
 		guint v = g_array_index (array, guint, i);
 
@@ -2022,18 +2108,16 @@ typedef struct {
 	})
 
 typedef struct {
-	const char *setting_name;
 	const ParseInfoProperty*const*properties;
 } ParseInfoSetting;
 
-#define PARSE_INFO_SETTING(_setting_name, ...) \
-	{ \
-		.setting_name = _setting_name, \
+#define PARSE_INFO_SETTING(setting_type, ...) \
+	[setting_type] = (&((const ParseInfoSetting) { \
 		__VA_ARGS__ \
-	}
+	}))
 
-static const ParseInfoSetting parse_infos[] = {
-	PARSE_INFO_SETTING (NM_SETTING_WIRELESS_SETTING_NAME,
+static const ParseInfoSetting *const parse_infos[_NM_META_SETTING_TYPE_NUM] = {
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_WIRELESS,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_WIRELESS_BSSID,
 				.parser        = mac_address_parser_ETHER,
@@ -2050,7 +2134,7 @@ static const ParseInfoSetting parse_infos[] = {
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_802_1X_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_802_1X,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_802_1X_CA_CERT,
 				.parser        = cert_parser,
@@ -2082,7 +2166,7 @@ static const ParseInfoSetting parse_infos[] = {
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_WIRED_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_WIRED,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_WIRED_CLONED_MAC_ADDRESS,
 				.parser        = mac_address_parser_ETHER_cloned,
@@ -2092,28 +2176,28 @@ static const ParseInfoSetting parse_infos[] = {
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_BLUETOOTH_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_BLUETOOTH,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_BLUETOOTH_BDADDR,
 				.parser        = mac_address_parser_ETHER,
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_BOND_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_BOND,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_BOND_OPTIONS,
 				.parser_no_check_key = TRUE,
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_BRIDGE_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_BRIDGE,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_BRIDGE_MAC_ADDRESS,
 				.parser        = mac_address_parser_ETHER,
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_CONNECTION_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_CONNECTION,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_CONNECTION_READ_ONLY,
 				.parser_skip   = TRUE,
@@ -2125,14 +2209,14 @@ static const ParseInfoSetting parse_infos[] = {
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_INFINIBAND_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_INFINIBAND,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_INFINIBAND_MAC_ADDRESS,
 				.parser        = mac_address_parser_INFINIBAND,
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_IP4_CONFIG_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_IP4_CONFIG,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_ADDRESSES,
 				.parser_no_check_key = TRUE,
@@ -2154,7 +2238,7 @@ static const ParseInfoSetting parse_infos[] = {
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_IP6_CONFIG_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_IP6_CONFIG,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE,
 				.parser_no_check_key = TRUE,
@@ -2182,14 +2266,23 @@ static const ParseInfoSetting parse_infos[] = {
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_SERIAL_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_SERIAL,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_SERIAL_PARITY,
 				.parser        = parity_parser,
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_TC_CONFIG_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_SRIOV,
+		PARSE_INFO_PROPERTIES (
+			PARSE_INFO_PROPERTY (NM_SETTING_SRIOV_VFS,
+				.parser_no_check_key = TRUE,
+				.parser        = sriov_vfs_parser,
+				.writer        = sriov_vfs_writer,
+			),
+		),
+	),
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_TC_CONFIG,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_TC_CONFIG_QDISCS,
 				.parser_no_check_key = TRUE,
@@ -2203,7 +2296,7 @@ static const ParseInfoSetting parse_infos[] = {
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_TEAM_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_TEAM,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_CONFIG,
 				.parser        = team_config_parser,
@@ -2270,7 +2363,7 @@ static const ParseInfoSetting parse_infos[] = {
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_TEAM_PORT_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_TEAM_PORT,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_CONFIG,
 				.parser        = team_config_parser,
@@ -2301,21 +2394,21 @@ static const ParseInfoSetting parse_infos[] = {
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_USER_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_USER,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_USER_DATA,
 				.parser_no_check_key = TRUE,
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_VLAN_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_VLAN,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_VLAN_FLAGS,
 				.writer_persist_default = TRUE,
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_VPN_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_VPN,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_VPN_DATA,
 				.parser_no_check_key = TRUE,
@@ -2337,7 +2430,7 @@ static const ParseInfoSetting parse_infos[] = {
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_WIMAX_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_WIMAX,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_WIMAX_MAC_ADDRESS,
 				.parser        = mac_address_parser_ETHER,
@@ -2347,21 +2440,24 @@ static const ParseInfoSetting parse_infos[] = {
 };
 
 static const ParseInfoProperty *
-_parse_info_find (const char *setting_name, const char *property_name)
+_parse_info_find (NMSetting *setting,
+                  const char *property_name,
+                  const char **out_setting_name)
 {
+	const NMMetaSettingInfo *setting_info;
+	const ParseInfoSetting *pis;
 	gssize idx;
 
-#if NM_MORE_ASSERTS > 5
+#if NM_MORE_ASSERTS > 10
 	{
 		guint i, j;
 
 		for (i = 0; i < G_N_ELEMENTS (parse_infos); i++) {
-			const ParseInfoSetting *pis = &parse_infos[i];
+			pis = parse_infos[i];
+
+			if (!pis)
+				continue;
 
-			g_assert (pis->setting_name);
-			if (   i > 0
-				&& strcmp (pis[-1].setting_name, pis->setting_name) >= 0)
-				g_error ("Wrong order at index #%d: \"%s\" before \"%s\"", i - 1, pis[-1].setting_name, pis->setting_name);
 			g_assert (pis->properties);
 			g_assert (pis->properties[0]);
 			for (j = 0; pis->properties[j]; j++) {
@@ -2371,31 +2467,35 @@ _parse_info_find (const char *setting_name, const char *property_name)
 				g_assert (pip->property_name);
 				if (   j > 0
 				    && (pip0 = pis->properties[j - 1])
-				    && strcmp (pip0->property_name, pip->property_name) >= 0)
-					g_error ("Wrong order at index #%d.%d: \"%s.%s\" before \"%s.%s\"", i, j - 1, pis->setting_name, pip0->property_name, pis->setting_name, pip->property_name);
+				    && strcmp (pip0->property_name, pip->property_name) >= 0) {
+					g_error ("Wrong order at index #%d.%d: \"%s.%s\" before \"%s.%s\"",
+					         i, j - 1,
+					         nm_meta_setting_infos[i].setting_name, pip0->property_name,
+					         nm_meta_setting_infos[i].setting_name, pip->property_name);
+				}
 			}
 		}
 	}
 #endif
 
-	G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (ParseInfoSetting, setting_name) == 0);
-	idx = _nm_utils_array_find_binary_search (parse_infos,
-	                                          sizeof (ParseInfoSetting),
-	                                          G_N_ELEMENTS (parse_infos),
-	                                          &setting_name,
-	                                          nm_strcmp_p_with_data,
-	                                          NULL);
-	if (idx >= 0) {
-		const ParseInfoSetting *pis = &parse_infos[idx];
-
-		nm_assert (nm_streq (pis->setting_name, setting_name));
-		idx = _nm_utils_ptrarray_find_binary_search ((gconstpointer *) pis->properties,
-		                                             NM_PTRARRAY_LEN (pis->properties),
-		                                             &property_name,
-		                                             nm_strcmp_p_with_data,
-		                                             NULL,
-		                                             NULL,
-		                                             NULL);
+	if (   !NM_IS_SETTING (setting)
+	    || !(setting_info = NM_SETTING_GET_CLASS (setting)->setting_info)) {
+		/* handle invalid setting objects gracefully. */
+		*out_setting_name = NULL;
+		return NULL;
+	}
+
+	*out_setting_name = setting_info->setting_name;
+
+	if ((pis = parse_infos[setting_info->meta_type])) {
+		G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (ParseInfoProperty, property_name) == 0);
+		idx = nm_utils_ptrarray_find_binary_search ((gconstpointer *) pis->properties,
+		                                            NM_PTRARRAY_LEN (pis->properties),
+		                                            &property_name,
+		                                            nm_strcmp_p_with_data,
+		                                            NULL,
+		                                            NULL,
+		                                            NULL);
 		if (idx >= 0)
 			return pis->properties[idx];
 	}
@@ -2426,9 +2526,9 @@ read_one_setting_value (NMSetting *setting,
 	if (!(flags & G_PARAM_WRITABLE))
 		return;
 
-	setting_name = nm_setting_get_name (setting);
+	pip = _parse_info_find (setting, key, &setting_name);
 
-	pip = _parse_info_find (setting_name, key);
+	nm_assert (setting_name);
 
 	if (   !pip
 	    && nm_streq (key, NM_SETTING_NAME))
@@ -2521,7 +2621,7 @@ read_one_setting_value (NMSetting *setting,
 		} else
 			g_object_set (setting, key, int_val, NULL);
 	} else if (type == G_TYPE_BYTES) {
-		gint *tmp;
+		int *tmp;
 		GByteArray *array;
 		GBytes *bytes;
 		gsize length;
@@ -2554,7 +2654,7 @@ read_one_setting_value (NMSetting *setting,
 		g_bytes_unref (bytes);
 		g_free (tmp);
 	} else if (type == G_TYPE_STRV) {
-		gchar **sa;
+		char **sa;
 		gsize length;
 
 		sa = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_name, key, &length, NULL);
@@ -2580,11 +2680,11 @@ read_one_setting_value (NMSetting *setting,
 			}
 		}
 	} else if (G_VALUE_HOLDS_ENUM (value)) {
-		gint int_val;
+		int int_val;
 
 		int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, &err);
 		if (!err)
-			g_object_set (setting, key, (gint) int_val, NULL);
+			g_object_set (setting, key, (int) int_val, NULL);
 	} else {
 		if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
 		                 _("unhandled setting property type '%s'"),
@@ -2596,6 +2696,8 @@ read_one_setting_value (NMSetting *setting,
 static NMSetting *
 read_setting (KeyfileReaderInfo *info)
 {
+	const NMSettInfoSetting *sett_info;
+	gs_unref_object NMSetting *setting = NULL;
 	const char *alias;
 	GType type;
 
@@ -2604,22 +2706,92 @@ read_setting (KeyfileReaderInfo *info)
 		alias = info->group;
 
 	type = nm_setting_lookup_type (alias);
-	if (type) {
-		NMSetting *setting = g_object_new (type, NULL);
-
-		info->setting = setting;
-		nm_setting_enumerate_values (setting, read_one_setting_value, info);
-		info->setting = NULL;
-		if (!info->error)
-			return setting;
-
-		g_object_unref (setting);
-	} else {
+	if (!type) {
 		handle_warn (info, NULL, NM_KEYFILE_WARN_SEVERITY_WARN,
 		             _("invalid setting name '%s'"), info->group);
+		return NULL;
 	}
 
-	return NULL;
+	setting = g_object_new (type, NULL);
+
+	info->setting = setting;
+
+	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
+
+	if (sett_info->detail.gendata_info) {
+		gs_free char **keys = NULL;
+		gsize i, n_keys;
+
+		keys = g_key_file_get_keys (info->keyfile, info->group, &n_keys, NULL);
+		if (n_keys > 0) {
+			GHashTable *h = _nm_setting_gendata_hash (setting, TRUE);
+
+			nm_utils_strv_sort (keys, n_keys);
+			for (i = 0; i < n_keys; i++) {
+				gs_free char *key = keys[i];
+				gs_free_error GError *local = NULL;
+				const GVariantType *variant_type;
+				GVariant *variant;
+
+				/* a GKeyfile can return duplicate keys, there is just no API to make sense
+				 * of them. Skip them. */
+				if (   i + 1 < n_keys
+				    && nm_streq (key, keys[i + 1]))
+					continue;
+
+				/* currently, the API is very simple. The setting class just returns
+				 * the desired variant type, and keyfile reader will try to parse
+				 * it accordingly. Note, that this does currently not allow, that
+				 * a particular key can contain different variant types, nor is it
+				 * very flexible in general.
+				 *
+				 * We add flexibility when we need it. Keep it simple for now. */
+				variant_type = sett_info->detail.gendata_info->get_variant_type (sett_info,
+				                                                                 key,
+				                                                                 &local);
+				if (!variant_type) {
+					if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+					                  _("invalid key '%s.%s'"),
+					                  info->group, key))
+						break;
+					continue;
+				}
+
+				if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_BOOLEAN)) {
+					gboolean v;
+
+					v = g_key_file_get_boolean (info->keyfile,
+					                            info->group,
+					                            key,
+					                            &local);
+					if (local) {
+						if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+						                  _("key '%s.%s' is not boolean"),
+						                  info->group, key))
+							break;
+						continue;
+					}
+					variant = g_variant_new_boolean (v);
+				} else {
+					nm_assert_not_reached ();
+					continue;
+				}
+
+				g_hash_table_insert (h,
+				                     g_steal_pointer (&key),
+				                     g_variant_take_ref (variant));
+			}
+			for (; i < n_keys; i++)
+				g_free (keys[i]);
+		}
+	} else
+		nm_setting_enumerate_values (setting, read_one_setting_value, info);
+
+	info->setting = NULL;
+
+	if (info->error)
+		return NULL;
+	return g_steal_pointer (&setting);
 }
 
 static void
@@ -2674,7 +2846,7 @@ nm_keyfile_read (GKeyFile *keyfile,
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingConnection *s_con;
 	NMSetting *setting;
-	gchar **groups;
+	char **groups;
 	gsize length;
 	int i;
 	gboolean vpn_secrets = FALSE;
@@ -2694,7 +2866,7 @@ nm_keyfile_read (GKeyFile *keyfile,
 			base_dir = base_dir_free = g_get_current_dir ();
 		}
 	} else
-		g_return_val_if_fail ("/", NULL);
+		g_return_val_if_fail (base_dir[0] == '/', NULL);
 
 	connection = nm_simple_connection_new ();
 
@@ -2800,12 +2972,22 @@ write_setting_value (NMSetting *setting,
 	if (info->error)
 		return;
 
-	setting_name = nm_setting_get_name (setting);
-
 	pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key);
 	nm_assert (pspec);
 
-	pip = _parse_info_find (setting_name, key);
+	pip = _parse_info_find (setting, key, &setting_name);
+
+	if (!setting_name) {
+		/* the setting type is unknown. That is highly unexpected
+		 * (and as this is currently only called from NetworkManager
+		 * daemon, not possible).
+		 *
+		 * Still, handle it gracefully, because later keyfile writer will become
+		 * public API of libnm, where @setting is (untrusted) user input.
+		 *
+		 * Gracefully here just means: ignore the setting. */
+		return;
+	}
 
 	if (   !pip
 	    && nm_streq (key, NM_SETTING_NAME))
@@ -2879,7 +3061,7 @@ write_setting_value (NMSetting *setting,
 		char **array;
 
 		array = (char **) g_value_get_boxed (value);
-		nm_keyfile_plugin_kf_set_string_list (info->keyfile, setting_name, key, (const gchar **const) array, g_strv_length (array));
+		nm_keyfile_plugin_kf_set_string_list (info->keyfile, setting_name, key, (const char **const) array, g_strv_length (array));
 	} else if (type == G_TYPE_HASH_TABLE) {
 		write_hash_of_string (info->keyfile, setting, key, value);
 	} else if (type == G_TYPE_ARRAY) {
@@ -2888,7 +3070,7 @@ write_setting_value (NMSetting *setting,
 		/* Flags are guint but GKeyFile has no uint reader, just uint64 */
 		nm_keyfile_plugin_kf_set_uint64 (info->keyfile, setting_name, key, (guint64) g_value_get_flags (value));
 	} else if (G_VALUE_HOLDS_ENUM (value))
-		nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (gint) g_value_get_enum (value));
+		nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_enum (value));
 	else
 		g_warn_if_reached ();
 }
@@ -2900,6 +3082,8 @@ nm_keyfile_write (NMConnection *connection,
                   GError **error)
 {
 	KeyfileWriterInfo info = { 0 };
+	gs_free NMSetting **settings = NULL;
+	guint i, length = 0;
 
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
 	g_return_val_if_fail (!error || !*error, NULL);
@@ -2912,12 +3096,59 @@ nm_keyfile_write (NMConnection *connection,
 	info.error = NULL;
 	info.handler = handler;
 	info.user_data = user_data;
-	nm_connection_for_each_setting_value (connection, write_setting_value, &info);
+
+	settings = nm_connection_get_settings (connection, &length);
+	for (i = 0; i < length; i++) {
+		const NMSettInfoSetting *sett_info;
+		NMSetting *setting = settings[i];
+
+		sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
+
+		if (sett_info->detail.gendata_info) {
+			guint k, n_keys;
+			const char *const*keys;
+
+			nm_assert (!nm_keyfile_plugin_get_alias_for_setting_name (sett_info->setting_class->setting_info->setting_name));
+
+			n_keys = _nm_setting_gendata_get_all (setting, &keys, NULL);
+
+			if (n_keys > 0) {
+				const char *setting_name = sett_info->setting_class->setting_info->setting_name;
+				GHashTable *h = _nm_setting_gendata_hash (setting, FALSE);
+
+				for (k = 0; k < n_keys; k++) {
+					const char *key = keys[k];
+					GVariant *v;
+
+					v = g_hash_table_lookup (h, key);
+
+					if (g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN)) {
+						g_key_file_set_boolean (info.keyfile,
+						                        setting_name,
+						                        key,
+						                        g_variant_get_boolean (v));
+					} else {
+						/* BUG: The variant type is not implemented. Since the connection
+						 * verifies, this can only mean we either wrongly didn't reject
+						 * the connection as invalid, or we didn't properly implement the
+						 * variant type. */
+						nm_assert_not_reached ();
+						continue;
+					}
+				}
+			}
+		} else
+			nm_setting_enumerate_values (setting, write_setting_value, &info);
+
+		if (info.error)
+			break;
+	}
 
 	if (info.error) {
 		g_propagate_error (error, info.error);
 		g_key_file_unref (info.keyfile);
 		return NULL;
 	}
+
 	return info.keyfile;
 }