about summary refs log tree commit diff
path: root/src/settings/plugins/keyfile
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/plugins/keyfile
parent263bf4c0c89bb88dc995acd9a6a2de9095fbd461 (diff)
Imported Upstream version 0.9.1.95 upstream/0.9.1.95
Diffstat (limited to 'src/settings/plugins/keyfile')
-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
9 files changed, 440 insertions, 77 deletions
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,