Blob Blame History Raw
From 7f3d60df818fb2b1dd99b12f893a50fd2402968f Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@redhat.com>
Date: Wed, 17 May 2017 16:45:46 +0200
Subject: [PATCH 01/13] libnm: fix unterminated NUL string when parsing UDev
 properties

This can result in trailing garbage (which fails UTF-8 validation
checks) or even worse, in read-out-of-bounds.

Fixes: 6808bf8195d427975638610781f8c5384228218d

https://bugzilla.redhat.com/show_bug.cgi?id=1443114
https://bugzilla.redhat.com/show_bug.cgi?id=1451160
https://bugzilla.redhat.com/show_bug.cgi?id=1451286
(cherry picked from commit 9594ee6e6921c3e37615a572de7e986274a68500)
(cherry picked from commit 5eb11aa8ec4e402b3e5795723519cdaab1cfb828)
---
 shared/nm-utils/nm-udev-utils.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/shared/nm-utils/nm-udev-utils.c b/shared/nm-utils/nm-udev-utils.c
index bf0ad5b..1f1811c 100644
--- a/shared/nm-utils/nm-udev-utils.c
+++ b/shared/nm-utils/nm-udev-utils.c
@@ -89,6 +89,7 @@ nm_udev_utils_property_decode (const char *uproperty, char **to_free)
 		return uproperty;
 	}
 
+	*n++ = '\0';
 	return (*to_free = unescaped);
 }
 
-- 
2.9.4


From be2a87e07f7a75e568d68afed1532b24edbee414 Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@redhat.com>
Date: Wed, 17 May 2017 17:04:13 +0200
Subject: [PATCH 02/13] libnm: don't cunescape \x00 encoding in
 nm_udev_utils_property_decode()

UDev never creates such invalid escape sequences. Anyway,
we cannot accept a NUL character at this point. Just take
the ill escape verbatim -- it should never happen anyway.

(cherry picked from commit c15eae92c0c5fc12017dd84a66ee0bbb9638b270)
(cherry picked from commit 822282754d1653ac24f6e5a9bf616fc74f957050)
---
 shared/nm-utils/nm-udev-utils.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/shared/nm-utils/nm-udev-utils.c b/shared/nm-utils/nm-udev-utils.c
index 1f1811c..79d4426 100644
--- a/shared/nm-utils/nm-udev-utils.c
+++ b/shared/nm-utils/nm-udev-utils.c
@@ -67,8 +67,9 @@ nm_udev_utils_property_decode (const char *uproperty, char **to_free)
 		if (   p[0] == '\\'
 		    && p[1] == 'x'
 		    && (a = g_ascii_xdigit_value (p[2])) >= 0
-		    && (b = g_ascii_xdigit_value (p[3])) >= 0) {
-			if (!unescaped) {
+		    && (b = g_ascii_xdigit_value (p[3])) >= 0
+		    && (a || b)) {
+			if (!n) {
 				gssize l = p - uproperty;
 
 				unescaped = g_malloc (l + strlen (p) + 1 - 3);
@@ -84,7 +85,7 @@ nm_udev_utils_property_decode (const char *uproperty, char **to_free)
 		}
 	}
 
-	if (!unescaped) {
+	if (!n) {
 		*to_free = NULL;
 		return uproperty;
 	}
-- 
2.9.4


From e1679a6a48ba7367528927f3e7f0021be71e37d6 Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@redhat.com>
Date: Wed, 17 May 2017 15:18:20 +0200
Subject: [PATCH 03/13] device: fix setting device's UDI property

Fixes: e8139f56c26ae3bcc5e14abdb29970ae07e93299
(cherry picked from commit 5eac18b58d2be9b5b611f4b4e356b2ac59e46bce)
(cherry picked from commit 4ae14d3677609ef3c702c1a7a706b6bc38030958)
---
 src/devices/nm-device.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index b14dc49..62cb5dd 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -2460,7 +2460,7 @@ device_link_changed (NMDevice *self)
 	info = *pllink;
 
 	udi = nm_platform_link_get_udi (nm_device_get_platform (self), info.ifindex);
-	if (udi && g_strcmp0 (udi, priv->udi)) {
+	if (udi && !nm_streq0 (udi, priv->udi)) {
 		/* Update UDI to what udev gives us */
 		g_free (priv->udi);
 		priv->udi = g_strdup (udi);
@@ -2841,7 +2841,7 @@ update_device_from_platform_link (NMDevice *self, const NMPlatformLink *plink)
 	g_return_if_fail (plink != NULL);
 
 	udi = nm_platform_link_get_udi (nm_device_get_platform (self), plink->ifindex);
-	if (udi && !g_strcmp0 (udi, priv->udi)) {
+	if (udi && !nm_streq0 (udi, priv->udi)) {
 		g_free (priv->udi);
 		priv->udi = g_strdup (udi);
 		_notify (self, PROP_UDI);
-- 
2.9.4


From 84b1c78c2474d9e579955fa0ebdfc6a8c0761548 Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@redhat.com>
Date: Wed, 17 May 2017 15:01:10 +0200
Subject: [PATCH 04/13] device: make UDI property construct-only

(cherry picked from commit e216d5eac0768b5936f3415cde7808982c74f0ac)
(cherry picked from commit c3b180198fe10d1fe84fea8b9944834be4f4ad56)
---
 src/devices/nm-device.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 62cb5dd..ed9bc49 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -13894,14 +13894,11 @@ set_property (GObject *object, guint prop_id,
 
 	switch (prop_id) {
 	case PROP_UDI:
-		if (g_value_get_string (value)) {
-			g_free (priv->udi);
-			priv->udi = g_value_dup_string (value);
-		}
+		/* construct-only */
+		priv->udi = g_value_dup_string (value);
 		break;
 	case PROP_IFACE:
 		/* construct-only */
-		g_return_if_fail (!priv->iface);
 		priv->iface = g_value_dup_string (value);
 		break;
 	case PROP_DRIVER:
@@ -14212,7 +14209,7 @@ nm_device_class_init (NMDeviceClass *klass)
 	obj_properties[PROP_UDI] =
 	    g_param_spec_string (NM_DEVICE_UDI, "", "",
 	                         NULL,
-	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
 	                         G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_IFACE] =
 	    g_param_spec_string (NM_DEVICE_IFACE, "", "",
-- 
2.9.4


From c63800b21cb46b7fbdc318205ed6af93aac9aeac Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@redhat.com>
Date: Tue, 16 May 2017 18:50:21 +0200
Subject: [PATCH 05/13] shared: add nm_utils_str_utf8safe_*() API to sanitize
 UTF-8 strings

Use C-style backslash escaping to sanitize non-UTF-8 strings.
The functions are compatible with glib's g_strcompress() and
g_strescape().

The difference is only that g_strescape() escapes all non-printable,
non ASCII character as well, while nm_utils_str_utf8safe_escape()
-- depending on the flags -- preserves valid UTF-8 sequence except
backslash.

The flags allow to optionally escape ASCII control characters and
all non-ASCII (valid UTF-8) characters. But the option to preserve
valid UTF-8 (non-ASCII) characters verbatim, is what distinguishes
from g_strescape().

(cherry picked from commit df6d27b33a86e2ecdc5a8e1deff275d19b2cbde1)
(cherry picked from commit 52105f27df974715a8481fb240f98f73a6a8be08)
---
 libnm-core/tests/test-general.c   |  95 ++++++++++++++++++++++++++
 shared/nm-utils/nm-shared-utils.c | 138 ++++++++++++++++++++++++++++++++++++++
 shared/nm-utils/nm-shared-utils.h |  16 +++++
 3 files changed, 249 insertions(+)

diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c
index 7ecd681..fbcfa7d 100644
--- a/libnm-core/tests/test-general.c
+++ b/libnm-core/tests/test-general.c
@@ -5248,6 +5248,100 @@ static void test_nm_utils_enum (void)
 
 /*****************************************************************************/
 
+static void
+do_test_utils_str_utf8safe (const char *str, const char *expected, NMUtilsStrUtf8SafeFlags flags)
+{
+	const char *str_safe, *s;
+	gs_free char *str2 = NULL;
+	gs_free char *str3 = NULL;
+
+	str_safe = nm_utils_str_utf8safe_escape (str, flags, &str2);
+
+	str3 = nm_utils_str_utf8safe_escape_cp (str, flags);
+	g_assert_cmpstr (str3, ==, str_safe);
+	g_assert ((!str && !str3) || (str != str3));
+	g_clear_pointer (&str3, g_free);
+
+	if (expected == NULL) {
+		g_assert (str_safe == str);
+		g_assert (!str2);
+		if (str) {
+			g_assert (!strchr (str, '\\'));
+			g_assert (g_utf8_validate (str, -1, NULL));
+		}
+
+		g_assert (str == nm_utils_str_utf8safe_unescape (str_safe, &str3));
+		g_assert (!str3);
+
+		str3 = nm_utils_str_utf8safe_unescape_cp (str_safe);
+		if (str) {
+			g_assert (str3 != str);
+			g_assert_cmpstr (str3, ==, str);
+		} else
+			g_assert (!str3);
+		g_clear_pointer (&str3, g_free);
+		return;
+	}
+
+	g_assert (str);
+	g_assert (str_safe != str);
+	g_assert (str_safe == str2);
+	g_assert (   strchr (str, '\\')
+	          || !g_utf8_validate (str, -1, NULL)
+	          || (   NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII)
+	              && NM_STRCHAR_ANY (str, ch, (guchar) ch >= 127))
+	          || (   NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL)
+	              && NM_STRCHAR_ANY (str, ch, (guchar) ch < ' ')));
+	g_assert (g_utf8_validate (str_safe, -1, NULL));
+
+	str3 = g_strcompress (str_safe);
+	g_assert_cmpstr (str, ==, str3);
+	g_clear_pointer (&str3, g_free);
+
+	str3 = nm_utils_str_utf8safe_unescape_cp (str_safe);
+	g_assert (str3 != str);
+	g_assert_cmpstr (str3, ==, str);
+	g_clear_pointer (&str3, g_free);
+
+	s = nm_utils_str_utf8safe_unescape (str_safe, &str3);
+	g_assert (str3 != str);
+	g_assert (s == str3);
+	g_assert_cmpstr (str3, ==, str);
+	g_clear_pointer (&str3, g_free);
+
+	g_assert_cmpstr (str_safe, ==, expected);
+}
+
+static void
+test_utils_str_utf8safe (void)
+{
+	do_test_utils_str_utf8safe (NULL, NULL,                                       NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("", NULL,                                         NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("\314", "\\314",                                  NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("\314\315x\315\315x", "\\314\\315x\\315\\315x",   NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("\314\315xx", "\\314\\315xx",                     NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("\314xx", "\\314xx",                              NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("\xa0", "\\240",                                  NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("\xe2\x91\xa0", NULL,                             NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("\xe2\xe2\x91\xa0", "\\342\xe2\x91\xa0",          NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("\xe2\xe2\x91\xa0\xa0", "\\342\xe2\x91\xa0\\240", NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("a", NULL,                                        NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("ab", NULL,                                       NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("ab\314", "ab\\314",                              NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("ab\314adsf", "ab\\314adsf",                      NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("abadsf", NULL,                                   NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("abäb", NULL,                                     NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("x\xa0", "x\\240",                                NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("Ä\304ab\\äb", "Ä\\304ab\\\\äb",                  NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("Äab\\äb", "Äab\\\\äb",                           NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("ÄÄab\\äb", "ÄÄab\\\\äb",                         NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("㈞abä㈞b", NULL,                                 NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("abäb", "ab\\303\\244b",                          NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII);
+	do_test_utils_str_utf8safe ("ab\ab", "ab\\007b",                              NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL);
+}
+
+/*****************************************************************************/
+
 static int
 _test_nm_in_set_get (int *call_counter, gboolean allow_called, int value)
 {
@@ -5605,6 +5699,7 @@ int main (int argc, char **argv)
 	nmtst_init (&argc, &argv, TRUE);
 
 	/* The tests */
+	g_test_add_func ("/core/general/test_utils_str_utf8safe", test_utils_str_utf8safe);
 	g_test_add_func ("/core/general/test_nm_in_set", test_nm_in_set);
 	g_test_add_func ("/core/general/test_nm_in_strset", test_nm_in_strset);
 	g_test_add_func ("/core/general/test_setting_vpn_items", test_setting_vpn_items);
diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c
index 413526d..e7f31cb 100644
--- a/shared/nm-utils/nm-shared-utils.c
+++ b/shared/nm-utils/nm-shared-utils.c
@@ -364,3 +364,141 @@ nm_g_object_set_property (GObject *object,
 }
 
 /*****************************************************************************/
+
+static void
+_str_append_escape (GString *s, char ch)
+{
+	g_string_append_c (s, '\\');
+	g_string_append_c (s, '0' + ((((guchar) ch) >> 6) & 07));
+	g_string_append_c (s, '0' + ((((guchar) ch) >> 3) & 07));
+	g_string_append_c (s, '0' + ( ((guchar) ch)       & 07));
+}
+
+/**
+ * nm_utils_str_utf8safe_escape:
+ * @str: NUL terminated input string, possibly in utf-8 encoding
+ * @flags: #NMUtilsStrUtf8SafeFlags flags
+ * @to_free: (out): return the pointer location of the string
+ *   if a copying was necessary.
+ *
+ * Returns the possible non-UTF-8 NUL terminated string @str
+ * and uses backslash escaping (C escaping, like g_strescape())
+ * to sanitize non UTF-8 characters. The result is valid
+ * UTF-8.
+ *
+ * The operation can be reverted with g_strcompress() or
+ * nm_utils_str_utf8safe_unescape().
+ *
+ * Depending on @flags, valid UTF-8 characters are not escaped at all
+ * (except the escape character '\\'). This is the difference to g_strescape(),
+ * which escapes all non-ASCII characters. This allows to pass on
+ * valid UTF-8 characters as-is and can be directly shown to the user
+ * as UTF-8 -- with exception of the backslash escape character,
+ * invalid UTF-8 sequences, and other (depending on @flags).
+ *
+ * Returns: the escaped input string, as valid UTF-8. If no escaping
+ *   is necessary, it returns the input @str. Otherwise, an allocated
+ *   string @to_free is returned which must be freed by the caller
+ *   with g_free. The escaping can be reverted by g_strcompress().
+ **/
+const char *
+nm_utils_str_utf8safe_escape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free)
+{
+	const char *p = NULL;
+	GString *s;
+
+	g_return_val_if_fail (to_free, NULL);
+
+	*to_free = NULL;
+	if (!str || !str[0])
+		return str;
+
+	if (   g_utf8_validate (str, -1, &p)
+	    && !NM_STRCHAR_ANY (str, ch,
+	                        (   ch == '\\' \
+	                         || (   NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) \
+	                             && ch < ' ') \
+	                         || (   NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) \
+	                             && ((guchar) ch) >= 127))))
+		return str;
+
+	s = g_string_sized_new ((p - str) + strlen (p) + 5);
+
+	do {
+		for (; str < p; str++) {
+			char ch = str[0];
+
+			if (ch == '\\')
+				g_string_append (s, "\\\\");
+			else if (   (   NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) \
+			             && ch < ' ') \
+			         || (   NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) \
+			             && ((guchar) ch) >= 127))
+				_str_append_escape (s, ch);
+			else
+				g_string_append_c (s, ch);
+		}
+
+		if (p[0] == '\0')
+			break;
+		_str_append_escape (s, p[0]);
+
+		str = &p[1];
+		g_utf8_validate (str, -1, &p);
+	} while (TRUE);
+
+	*to_free = g_string_free (s, FALSE);
+	return *to_free;
+}
+
+const char *
+nm_utils_str_utf8safe_unescape (const char *str, char **to_free)
+{
+	g_return_val_if_fail (to_free, NULL);
+
+	if (!str || !strchr (str, '\\')) {
+		*to_free = NULL;
+		return str;
+	}
+	return (*to_free = g_strcompress (str));
+}
+
+/**
+ * nm_utils_str_utf8safe_escape_cp:
+ * @str: NUL terminated input string, possibly in utf-8 encoding
+ * @flags: #NMUtilsStrUtf8SafeFlags flags
+ *
+ * Like nm_utils_str_utf8safe_escape(), except the returned value
+ * is always a copy of the input and must be freed by the caller.
+ *
+ * Returns: the escaped input string in UTF-8 encoding. The returned
+ *   value should be freed with g_free().
+ *   The escaping can be reverted by g_strcompress().
+ **/
+char *
+nm_utils_str_utf8safe_escape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags)
+{
+	char *s;
+
+	nm_utils_str_utf8safe_escape (str, flags, &s);
+	return s ?: g_strdup (str);
+}
+
+char *
+nm_utils_str_utf8safe_unescape_cp (const char *str)
+{
+	return str ? g_strcompress (str) : NULL;
+}
+
+char *
+nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags)
+{
+	char *str_to_free;
+
+	nm_utils_str_utf8safe_escape (str, flags, &str_to_free);
+	if (str_to_free) {
+		g_free (str);
+		return str_to_free;
+	}
+	return str;
+}
diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h
index f1f9f51..69f9533 100644
--- a/shared/nm-utils/nm-shared-utils.h
+++ b/shared/nm-utils/nm-shared-utils.h
@@ -86,4 +86,20 @@ gboolean nm_g_object_set_property (GObject *object,
 
 /*****************************************************************************/
 
+typedef enum {
+	NM_UTILS_STR_UTF8_SAFE_FLAG_NONE                = 0,
+	NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL         = 0x0001,
+	NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII    = 0x0002,
+} NMUtilsStrUtf8SafeFlags;
+
+const char *nm_utils_str_utf8safe_escape   (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free);
+const char *nm_utils_str_utf8safe_unescape (const char *str, char **to_free);
+
+char *nm_utils_str_utf8safe_escape_cp   (const char *str, NMUtilsStrUtf8SafeFlags flags);
+char *nm_utils_str_utf8safe_unescape_cp (const char *str);
+
+char *nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags);
+
+/*****************************************************************************/
+
 #endif /* __NM_SHARED_UTILS_H__ */
-- 
2.9.4


From e9eee9c65818e7dd85e79c1db6ad4c32b4c7fb52 Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@redhat.com>
Date: Tue, 16 May 2017 14:11:07 +0200
Subject: [PATCH 06/13] device: sanitze UTF-8 values for D-Bus

  ip link add name $'d\xccf\\c' type dummy

Use nm_utils_str_utf8safe_escape() to sanitize non UTF-8 sequences
before exposing them on D-Bus. The operation can be reverted client
side via nm_utils_str_utf8safe_unescape() or simply g_strcompress().

Note that this preserves all valid UTF-8 sequences as-is, with exception
of the backslash escape character and ASCII control characters. Thus, this
is a change in behavior for strings that contain such characters.

Note that nmcli is not changed to somehow unescape the string before
printing. As the string is not valid UTF-8 (or contains ASCII characters
that need escaping), they are not printable as-is, so unescaping before
printing makes little sense.

(cherry picked from commit 0870906540506d0157f305df32b6b1f65b10ee85)
(cherry picked from commit 3a96772918a391ec8186183b5c14c9b165322d3a)
---
 .../org.freedesktop.NetworkManager.Device.xml      | 15 +++++++++++
 src/devices/nm-device.c                            | 31 ++++++++++++++++------
 2 files changed, 38 insertions(+), 8 deletions(-)

diff --git a/introspection/org.freedesktop.NetworkManager.Device.xml b/introspection/org.freedesktop.NetworkManager.Device.xml
index ee42410..be0a612 100644
--- a/introspection/org.freedesktop.NetworkManager.Device.xml
+++ b/introspection/org.freedesktop.NetworkManager.Device.xml
@@ -21,6 +21,9 @@
         each device in your application, use the object path. If you're looking
         for a way to track a specific piece of hardware across reboot or hotplug,
         use a MAC address or USB serial number.
+
+        Note that non-UTF-8 characters are backslash escaped. Use g_strcompress()
+        to obtain the true (non-UTF-8) string.
     -->
     <property name="Udi" type="s" access="read"/>
 
@@ -28,6 +31,9 @@
         Interface:
 
         The name of the device's control (and often data) interface.
+        Note that non UTF-8 characters are backslash escaped, so the
+        resulting name may be longer then 15 characters. Use g_strcompress()
+        to revert the escaping.
     -->
     <property name="Interface" type="s" access="read"/>
 
@@ -38,6 +44,9 @@
         not refer to the actual data interface until the device has successfully
         established a data connection, indicated by the device's State becoming
         ACTIVATED.
+        Note that non UTF-8 characters are backslash escaped, so the
+        resulting name may be longer then 15 characters. Use g_strcompress()
+        to revert the escaping.
     -->
     <property name="IpInterface" type="s" access="read"/>
 
@@ -45,6 +54,8 @@
         Driver:
 
         The driver handling the device.
+        Non-UTF-8 sequences are backslash escaped. Use g_strcompress()
+        to revert.
     -->
     <property name="Driver" type="s" access="read"/>
 
@@ -52,6 +63,8 @@
         DriverVersion:
 
         The version of the driver handling the device.
+        Non-UTF-8 sequences are backslash escaped. Use g_strcompress()
+        to revert.
     -->
     <property name="DriverVersion" type="s" access="read"/>
 
@@ -59,6 +72,8 @@
         FirmwareVersion:
 
         The firmware version for the device.
+        Non-UTF-8 sequences are backslash escaped. Use g_strcompress()
+        to revert.
     -->
     <property name="FirmwareVersion" type="s" access="read"/>
 
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index ed9bc49..e61bde3 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -13993,28 +13993,43 @@ get_property (GObject *object, guint prop_id,
 
 	switch (prop_id) {
 	case PROP_UDI:
-		g_value_set_string (value, priv->udi);
+		/* UDI is (depending on the device type) a path to sysfs and can contain
+		 * non-UTF-8.
+		 *   ip link add name $'d\xccf\\c' type dummy  */
+		g_value_take_string (value,
+		                     nm_utils_str_utf8safe_escape_cp (priv->udi,
+		                                                      NM_UTILS_STR_UTF8_SAFE_FLAG_NONE));
 		break;
 	case PROP_IFACE:
-		g_value_set_string (value, priv->iface);
+		g_value_take_string (value,
+		                     nm_utils_str_utf8safe_escape_cp (priv->iface,
+		                                                      NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL));
 		break;
 	case PROP_IP_IFACE:
-		if (ip_config_valid (priv->state))
-			g_value_set_string (value, nm_device_get_ip_iface (self));
-		else
+		if (ip_config_valid (priv->state)) {
+			g_value_take_string (value,
+			                     nm_utils_str_utf8safe_escape_cp (nm_device_get_ip_iface (self),
+			                                                      NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL));
+		} else
 			g_value_set_string (value, NULL);
 		break;
 	case PROP_IFINDEX:
 		g_value_set_int (value, priv->ifindex);
 		break;
 	case PROP_DRIVER:
-		g_value_set_string (value, priv->driver);
+		g_value_take_string (value,
+		                     nm_utils_str_utf8safe_escape_cp (priv->driver,
+		                                                      NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL));
 		break;
 	case PROP_DRIVER_VERSION:
-		g_value_set_string (value, priv->driver_version);
+		g_value_take_string (value,
+		                     nm_utils_str_utf8safe_escape_cp (priv->driver_version,
+		                                                      NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL));
 		break;
 	case PROP_FIRMWARE_VERSION:
-		g_value_set_string (value, priv->firmware_version);
+		g_value_take_string (value,
+		                     nm_utils_str_utf8safe_escape_cp (priv->firmware_version,
+		                                                      NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL));
 		break;
 	case PROP_CAPABILITIES:
 		g_value_set_uint (value, (priv->capabilities & ~NM_DEVICE_CAP_INTERNAL_MASK));
-- 
2.9.4


From 0b8676b887f63a708e6444f6b3d19003a2f9b4c8 Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@redhat.com>
Date: Wed, 17 May 2017 11:48:53 +0200
Subject: [PATCH 07/13] libnm: UTF-8 sanitize strings from UDev in NMDevice

(cherry picked from commit b9e9f7616556f693e2642ed433f3289f9c6da452)
(cherry picked from commit d7b184d99257a6e6e59b22709007675430ec308b)
---
 libnm/nm-device.c | 273 ++++++++++++++++++++++++++++++------------------------
 1 file changed, 152 insertions(+), 121 deletions(-)

diff --git a/libnm/nm-device.c b/libnm/nm-device.c
index 9cbdbd0..7e8feb1 100644
--- a/libnm/nm-device.c
+++ b/libnm/nm-device.c
@@ -81,7 +81,7 @@ typedef struct {
 	GPtrArray *available_connections;
 
 	struct udev *udev;
-	char *product, *short_product;
+	char *product;
 	char *vendor, *short_vendor;
 	char *description, *bus_name;
 
@@ -320,7 +320,6 @@ finalize (GObject *object)
 	g_free (priv->driver_version);
 	g_free (priv->firmware_version);
 	g_free (priv->product);
-	g_free (priv->short_product);
 	g_free (priv->vendor);
 	g_free (priv->short_vendor);
 	g_free (priv->description);
@@ -1357,6 +1356,17 @@ _get_udev_property (NMDevice *device,
 	return db_value;
 }
 
+static char *
+_get_udev_property_utf8safe (NMDevice *device,
+                             const char *enc_prop,  /* ID_XXX_ENC */
+                             const char *db_prop)   /* ID_XXX_FROM_DATABASE */
+{
+	return nm_utils_str_utf8safe_escape_take (_get_udev_property (device,
+	                                                              enc_prop,
+	                                                              db_prop),
+	                                          NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL);
+}
+
 /**
  * nm_device_get_product:
  * @device: a #NMDevice
@@ -1365,6 +1375,9 @@ _get_udev_property (NMDevice *device,
  *
  * Returns: the product name of the device. This is the internal string used by the
  * device, and must not be modified.
+ *
+ * The string is backslash escaped (C escaping) for invalid characters. The escaping
+ * can be reverted with g_strcompress(), however the result may not be valid UTF-8.
  **/
 const char *
 nm_device_get_product (NMDevice *device)
@@ -1374,15 +1387,16 @@ nm_device_get_product (NMDevice *device)
 	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
 
 	priv = NM_DEVICE_GET_PRIVATE (device);
-	if (!priv->product)
-		priv->product = _get_udev_property (device, "ID_MODEL_ENC", "ID_MODEL_FROM_DATABASE");
+	if (!priv->product) {
+		priv->product = _get_udev_property_utf8safe (device, "ID_MODEL_ENC", "ID_MODEL_FROM_DATABASE");
 
-	/* Sometimes ID_PRODUCT_FROM_DATABASE is used? */
-	if (!priv->product)
-		priv->product = _get_udev_property (device, "ID_MODEL_ENC", "ID_PRODUCT_FROM_DATABASE");
+		/* Sometimes ID_PRODUCT_FROM_DATABASE is used? */
+		if (!priv->product)
+			priv->product = _get_udev_property_utf8safe (device, "ID_MODEL_ENC", "ID_PRODUCT_FROM_DATABASE");
 
-	if (!priv->product)
-		priv->product = g_strdup ("");
+		if (!priv->product)
+			priv->product = g_strdup ("");
+	}
 
 	return priv->product;
 }
@@ -1395,6 +1409,9 @@ nm_device_get_product (NMDevice *device)
  *
  * Returns: the vendor name of the device. This is the internal string used by the
  * device, and must not be modified.
+ *
+ * The string is backslash escaped (C escaping) for invalid characters. The escaping
+ * can be reverted with g_strcompress(), however the result may not be valid UTF-8.
  **/
 const char *
 nm_device_get_vendor (NMDevice *device)
@@ -1406,7 +1423,7 @@ nm_device_get_vendor (NMDevice *device)
 	priv = NM_DEVICE_GET_PRIVATE (device);
 
 	if (!priv->vendor)
-		priv->vendor = _get_udev_property (device, "ID_VENDOR_ENC", "ID_VENDOR_FROM_DATABASE");
+		priv->vendor = _get_udev_property_utf8safe (device, "ID_VENDOR_ENC", "ID_VENDOR_FROM_DATABASE");
 
 	if (!priv->vendor)
 		priv->vendor = g_strdup ("");
@@ -1414,128 +1431,146 @@ nm_device_get_vendor (NMDevice *device)
 	return priv->vendor;
 }
 
-static const char * const ignored_words[] = {
-	"Semiconductor",
-	"Components",
-	"Corporation",
-	"Communications",
-	"Company",
-	"Corp.",
-	"Corp",
-	"Co.",
-	"Inc.",
-	"Inc",
-	"Incorporated",
-	"Ltd.",
-	"Limited.",
-	"Intel?",
-	"chipset",
-	"adapter",
-	"[hex]",
-	"NDIS",
-	"Module",
-	NULL
-};
-
-static const char * const ignored_phrases[] = {
-	"Multiprotocol MAC/baseband processor",
-	"Wireless LAN Controller",
-	"Wireless LAN Adapter",
-	"Wireless Adapter",
-	"Network Connection",
-	"Wireless Cardbus Adapter",
-	"Wireless CardBus Adapter",
-	"54 Mbps Wireless PC Card",
-	"Wireless PC Card",
-	"Wireless PC",
-	"PC Card with XJACK(r) Antenna",
-	"Wireless cardbus",
-	"Wireless LAN PC Card",
-	"Technology Group Ltd.",
-	"Communication S.p.A.",
-	"Business Mobile Networks BV",
-	"Mobile Broadband Minicard Composite Device",
-	"Mobile Communications AB",
-	"(PC-Suite Mode)",
-	NULL
-};
-
 static char *
 fixup_desc_string (const char *desc)
 {
-	char *p, *temp;
-	char **words, **item;
-	GString *str;
+	static const char *const IGNORED_PHRASES[] = {
+		"Multiprotocol MAC/baseband processor",
+		"Wireless LAN Controller",
+		"Wireless LAN Adapter",
+		"Wireless Adapter",
+		"Network Connection",
+		"Wireless Cardbus Adapter",
+		"Wireless CardBus Adapter",
+		"54 Mbps Wireless PC Card",
+		"Wireless PC Card",
+		"Wireless PC",
+		"PC Card with XJACK(r) Antenna",
+		"Wireless cardbus",
+		"Wireless LAN PC Card",
+		"Technology Group Ltd.",
+		"Communication S.p.A.",
+		"Business Mobile Networks BV",
+		"Mobile Broadband Minicard Composite Device",
+		"Mobile Communications AB",
+		"(PC-Suite Mode)",
+	};
+	static const char *const IGNORED_WORDS[] = {
+		"Semiconductor",
+		"Components",
+		"Corporation",
+		"Communications",
+		"Company",
+		"Corp.",
+		"Corp",
+		"Co.",
+		"Inc.",
+		"Inc",
+		"Incorporated",
+		"Ltd.",
+		"Limited.",
+		"Intel?",
+		"chipset",
+		"adapter",
+		"[hex]",
+		"NDIS",
+		"Module",
+	};
+	char *desc_full;
+	char *p, *q;
 	int i;
 
-	if (!desc)
+	if (!desc || !desc[0])
 		return NULL;
 
-	p = temp = g_strdup (desc);
-	while (*p) {
-		if (*p == '_' || *p == ',')
+	/* restore original non-UTF-8-safe text. */
+	desc_full = nm_utils_str_utf8safe_unescape_cp (desc);
+
+	/* replace all invalid UTF-8 bytes with space. */
+	p = desc_full;
+	while (!g_utf8_validate (p, -1, (const char **) &q)) {
+		/* the byte is invalid UTF-8. Replace it with space and proceed. */
+		*q = ' ';
+		p = q + 1;
+	}
+
+	/* replace '_', ',', and ASCII controll characters with space. */
+	for (p = desc_full; p[0]; p++) {
+		if (   NM_IN_SET (*p, '_', ',')
+		    || *p < ' ')
 			*p = ' ';
-		p++;
 	}
 
 	/* Attempt to shorten ID by ignoring certain phrases */
-	for (i = 0; ignored_phrases[i]; i++) {
-		p = strstr (temp, ignored_phrases[i]);
+	for (i = 0; i < G_N_ELEMENTS (IGNORED_PHRASES); i++) {
+		p = strstr (desc_full, IGNORED_PHRASES[i]);
 		if (p) {
-			guint32 ignored_len = strlen (ignored_phrases[i]);
+			const char *eow = &p[strlen (IGNORED_PHRASES[i])];
 
-			memmove (p, p + ignored_len, strlen (p + ignored_len) + 1); /* +1 for the \0 */
+			memmove (p, eow, strlen (eow) + 1); /* +1 for the \0 */
 		}
 	}
 
-	/* Attempt to shorten ID by ignoring certain individual words */
-	words = g_strsplit (temp, " ", 0);
-	str = g_string_new_len (NULL, strlen (temp));
-	g_free (temp);
-
-	for (item = words; *item; item++) {
-		gboolean ignore = FALSE;
-
-		if (**item == '\0')
-			continue;
-
-		for (i = 0; ignored_words[i]; i++) {
-			if (!strcmp (*item, ignored_words[i])) {
-				ignore = TRUE;
-				break;
-			}
+	/* Attempt to shorten ID by ignoring certain individual words.
+	 * - word-split the description at spaces
+	 * - coalesce multiple spaces
+	 * - skip over IGNORED_WORDS */
+	p = desc_full;
+	q = desc_full;
+	for (;;) {
+		char *eow;
+		gsize l;
+
+		/* skip leading spaces. */
+		while (p[0] == ' ')
+			p++;
+
+		if (!p[0])
+			break;
+
+		/* split leading word on first space */
+		eow = strchr (p, ' ');
+		if (eow)
+			*eow = '\0';
+
+		if (nm_utils_strv_find_first ((char **) IGNORED_WORDS,
+		                              G_N_ELEMENTS (IGNORED_WORDS),
+		                              p) < 0)
+			goto next;
+
+		l = strlen (p);
+		if (q != p) {
+			if (q != desc_full)
+				*q++ = ' ';
+			memmove (q, p, l);
 		}
+		q += l;
 
-		if (!ignore) {
-			if (str->len)
-				g_string_append_c (str, ' ');
-			g_string_append (str, *item);
-		}
+next:
+		if (!eow)
+			break;
+		p = eow + 1;
 	}
-	g_strfreev (words);
 
-	temp = str->str;
-	g_string_free (str, FALSE);
+	*q++ = '\0';
 
-	return temp;
+	if (!desc_full[0]) {
+		g_free (desc_full);
+		return NULL;
+	}
+
+	nm_assert (g_utf8_validate (desc_full, -1, NULL));
+	return desc_full;
 }
 
 static void
-get_description (NMDevice *device)
+ensure_description (NMDevice *device)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
-	const char *dev_product;
-	const char *dev_vendor;
-	char *pdown;
-	char *vdown;
-	GString *str;
 	GParamSpec *name_prop;
+	gs_free char *short_product = NULL;
 
-	dev_product = nm_device_get_product (device);
-	priv->short_product = fixup_desc_string (dev_product);
-
-	dev_vendor = nm_device_get_vendor (device);
-	priv->short_vendor = fixup_desc_string (dev_vendor);
+	priv->short_vendor = nm_str_realloc (fixup_desc_string (nm_device_get_vendor (device)));
 
 	/* Grab device's preferred name, if any */
 	name_prop = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (device)), "name");
@@ -1546,28 +1581,24 @@ get_description (NMDevice *device)
 		g_clear_pointer (&priv->description, g_free);
 	}
 
-	if (!dev_product || !dev_vendor) {
-		priv->description = g_strdup (nm_device_get_iface (device));
+	if (   !priv->short_vendor
+	    || !(short_product = fixup_desc_string (nm_device_get_product (device)))) {
+		priv->description = g_strdup (nm_device_get_iface (device) ?: "");
 		return;
 	}
 
-	str = g_string_new_len (NULL, strlen (priv->short_vendor) + strlen (priv->short_product) + 1);
-
 	/* Another quick hack; if all of the fixed up vendor string
 	 * is found in product, ignore the vendor.
 	 */
-	pdown = g_ascii_strdown (priv->short_product, -1);
-	vdown = g_ascii_strdown (priv->short_vendor, -1);
-	if (!strstr (pdown, vdown)) {
-		g_string_append (str, priv->short_vendor);
-		g_string_append_c (str, ' ');
+	{
+		gs_free char *pdown = g_ascii_strdown (short_product, -1);
+		gs_free char *vdown = g_ascii_strdown (priv->short_vendor, -1);
+
+		if (!strstr (pdown, vdown))
+			priv->description = g_strconcat (priv->short_vendor, " ", short_product, NULL);
+		else
+			priv->description = g_steal_pointer (&short_product);
 	}
-	g_free (pdown);
-	g_free (vdown);
-
-	g_string_append (str, priv->short_product);
-
-	priv->description = g_string_free (str, FALSE);
 }
 
 static const char *
@@ -1580,7 +1611,7 @@ get_short_vendor (NMDevice *device)
 	priv = NM_DEVICE_GET_PRIVATE (device);
 
 	if (!priv->description)
-		get_description (device);
+		ensure_description (device);
 
 	return priv->short_vendor;
 }
@@ -1604,7 +1635,7 @@ nm_device_get_description (NMDevice *device)
 	priv = NM_DEVICE_GET_PRIVATE (device);
 
 	if (!priv->description)
-		get_description (device);
+		ensure_description (device);
 
 	return priv->description;
 }
-- 
2.9.4


From aee0c50ffa840e67642749189bd323620fff8d93 Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@redhat.com>
Date: Fri, 19 May 2017 10:18:59 +0200
Subject: [PATCH 08/13] libnm: fix device description in fixup_desc_string()

Fixes: b9e9f7616556f693e2642ed433f3289f9c6da452
(cherry picked from commit 12c881ad40c4829eb430bd0148fa5dbbdfd0ec01)
(cherry picked from commit c22075dc98fc79021e9a4847b57188f09b489ddd)
---
 libnm/nm-device.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libnm/nm-device.c b/libnm/nm-device.c
index 7e8feb1..6c27190 100644
--- a/libnm/nm-device.c
+++ b/libnm/nm-device.c
@@ -1535,7 +1535,7 @@ fixup_desc_string (const char *desc)
 
 		if (nm_utils_strv_find_first ((char **) IGNORED_WORDS,
 		                              G_N_ELEMENTS (IGNORED_WORDS),
-		                              p) < 0)
+		                              p) >= 0)
 			goto next;
 
 		l = strlen (p);
-- 
2.9.4


From 47527d343339cf0df13fa852ef73c5d0e4713890 Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@redhat.com>
Date: Fri, 19 May 2017 11:01:23 +0200
Subject: [PATCH 09/13] libnm: ignore phrases in fixup device description only
 when delimited by space

(cherry picked from commit 72104ea10a26d4b4ff245ed60c5ccd5c043c5fe0)
(cherry picked from commit aa60b77146641db5f5d670356d8244f46dd90f81)
---
 libnm/nm-device.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/libnm/nm-device.c b/libnm/nm-device.c
index 6c27190..3a6052b 100644
--- a/libnm/nm-device.c
+++ b/libnm/nm-device.c
@@ -1507,7 +1507,11 @@ fixup_desc_string (const char *desc)
 		if (p) {
 			const char *eow = &p[strlen (IGNORED_PHRASES[i])];
 
-			memmove (p, eow, strlen (eow) + 1); /* +1 for the \0 */
+			/* require that the phrase is delimited by space, or
+			 * at the beginning or end of the description. */
+			if (   (p == desc_full || p[-1] == ' ')
+			    && NM_IN_SET (eow[0], '\0', ' '))
+				memmove (p, eow, strlen (eow) + 1); /* +1 for the \0 */
 		}
 	}
 
-- 
2.9.4


From 6bc607bd944e83eb4e1c668bba5f750ac98ea451 Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@redhat.com>
Date: Fri, 19 May 2017 10:19:25 +0200
Subject: [PATCH 10/13] libnm: add testable libnm/nm-libnm-utils.c file

Previously, internal parts of libnm were not testable.
Instead, add "libnm/nm-libnm-utils.c" and "libnm/libnm-utils.la"
to contain code that can be statically linked with a new
test "libnm/tests/test-general".

(cherry picked from commit 8df944c7e495d18bfecaf9d8316ef7783039c94b)
(cherry picked from commit 1ebac60d2219dd29634e5d375f487d5ffa624266)
---
 Makefile.am                | 45 ++++++++++++++++++++++++++++++++++++++-------
 libnm/nm-libnm-utils.c     | 27 +++++++++++++++++++++++++++
 libnm/nm-libnm-utils.h     | 26 ++++++++++++++++++++++++++
 libnm/tests/test-general.c | 34 ++++++++++++++++++++++++++++++++++
 4 files changed, 125 insertions(+), 7 deletions(-)
 create mode 100644 libnm/nm-libnm-utils.c
 create mode 100644 libnm/nm-libnm-utils.h
 create mode 100644 libnm/tests/test-general.c

diff --git a/Makefile.am b/Makefile.am
index 709d79b..a6776e9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -734,6 +734,7 @@ libnm_lib_h_pub_nointrospect = \
 libnm_lib_h_pub_mkenums = \
 	libnm/nm-enum-types.h
 libnm_lib_h_priv = \
+	libnm/nm-libnm-utils.h \
 	libnm/nm-dbus-helpers.h \
 	libnm/nm-device-private.h \
 	libnm/nm-dhcp4-config.h \
@@ -790,6 +791,14 @@ libnm_lib_c_real = \
 libnm_lib_c_mkenums = \
 	libnm/nm-enum-types.c
 
+libnm_lib_cppflags = \
+	$(dflt_cppflags_libnm_core) \
+	-I$(srcdir)/libnm \
+	-I$(builddir)/libnm \
+	-DG_LOG_DOMAIN=\""libnm"\" \
+	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_LIB \
+	-DNMRUNDIR=\"$(nmrundir)\"
+
 libnminclude_HEADERS += \
 	$(libnm_lib_h_pub_real) \
 	$(libnm_lib_h_pub_nointrospect)
@@ -799,6 +808,23 @@ nodist_libnminclude_HEADERS += \
 
 ###############################################################################
 
+lib_LTLIBRARIES += libnm/libnm-utils.la
+
+libnm_libnm_utils_la_CPPFLAGS = \
+	$(libnm_lib_cppflags)
+
+libnm_libnm_utils_la_SOURCES = \
+	libnm/nm-libnm-utils.c
+
+libnm_libnm_utils_la_LIBADD = \
+	libnm-core/libnm-core.la \
+	introspection/libnmdbus.la \
+	$(GLIB_LIBS)
+
+$(libnm_libnm_utils_la_OBJECTS) : $(libnm_lib_h_pub_mkenums)
+
+###############################################################################
+
 lib_LTLIBRARIES += libnm/libnm.la
 
 GLIB_GENERATED += \
@@ -817,13 +843,8 @@ $(libnm_libnm_la_OBJECTS):                              $(libnm_lib_h_pub_mkenum
 $(libnm_tests_libnm_vpn_plugin_utils_test_la_OBJECTS):  $(libnm_core_lib_h_pub_mkenums)
 
 libnm_libnm_la_CPPFLAGS = \
-	$(dflt_cppflags_libnm_core) \
-	-I$(srcdir)/libnm \
-	-I$(builddir)/libnm \
-	$(LIBUDEV_CFLAGS) \
-	-DG_LOG_DOMAIN=\""libnm"\" \
-	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_LIB \
-	-DNMRUNDIR=\"$(nmrundir)\"
+	$(libnm_lib_cppflags) \
+	$(LIBUDEV_CFLAGS)
 
 libnm_libnm_la_SOURCES = \
 	$(libnm_lib_h_pub_real) \
@@ -841,6 +862,7 @@ EXTRA_libnm_libnm_la_DEPENDENCIES = \
 libnm_libnm_la_LIBADD = \
 	libnm-core/libnm-core.la \
 	introspection/libnmdbus.la \
+	libnm/libnm-utils.la \
 	$(DL_LIBS) \
 	$(GLIB_LIBS) \
 	$(UUID_LIBS) \
@@ -947,6 +969,7 @@ EXTRA_DIST += \
 ###############################################################################
 
 libnm_tests_programs = \
+	libnm/tests/test-general \
 	libnm/tests/test-nm-client \
 	libnm/tests/test-remote-settings-client \
 	libnm/tests/test-secret-agent
@@ -964,10 +987,14 @@ libnm_tests_ldadd = \
 	libnm/libnm.la \
 	$(GLIB_LIBS)
 
+libnm_tests_test_general_CPPFLAGS = $(libnm_tests_cppflags)
 libnm_tests_test_nm_client_CPPFLAGS = $(libnm_tests_cppflags)
 libnm_tests_test_remote_settings_client_CPPFLAGS = $(libnm_tests_cppflags)
 libnm_tests_test_secret_agent_CPPFLAGS = $(libnm_tests_cppflags)
 
+libnm_tests_test_general_SOURCES = \
+	libnm/tests/test-general.c
+
 libnm_tests_test_nm_client_SOURCES = \
 	shared/nm-test-utils-impl.c \
 	shared/nm-test-libnm-utils.h \
@@ -983,10 +1010,14 @@ libnm_tests_test_secret_agent_SOURCES = \
 	shared/nm-test-libnm-utils.h \
 	libnm/tests/test-secret-agent.c
 
+libnm_tests_test_general_LDADD = \
+	libnm/libnm-utils.la \
+	$(libnm_tests_ldadd)
 libnm_tests_test_nm_client_LDADD = $(libnm_tests_ldadd)
 libnm_tests_test_remote_settings_client_LDADD = $(libnm_tests_ldadd)
 libnm_tests_test_secret_agent_LDADD = $(libnm_tests_ldadd)
 
+$(libnm_tests_test_general_OBJECTS): $(libnm_core_lib_h_pub_mkenums)
 $(libnm_tests_test_nm_client_OBJECTS): $(libnm_core_lib_h_pub_mkenums)
 $(libnm_tests_test_remote_settings_client_OBJECTS): $(libnm_core_lib_h_pub_mkenums)
 $(libnm_tests_test_secret_agent_OBJECTS): $(libnm_core_lib_h_pub_mkenums)
diff --git a/libnm/nm-libnm-utils.c b/libnm/nm-libnm-utils.c
new file mode 100644
index 0000000..8cea276
--- /dev/null
+++ b/libnm/nm-libnm-utils.c
@@ -0,0 +1,27 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2017 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-libnm-utils.h"
+
+/*****************************************************************************/
+
diff --git a/libnm/nm-libnm-utils.h b/libnm/nm-libnm-utils.h
new file mode 100644
index 0000000..356b2f9
--- /dev/null
+++ b/libnm/nm-libnm-utils.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2017 Red Hat, Inc.
+ */
+
+#ifndef __NM_LIBNM_UTILS_H__
+#define __NM_LIBNM_UTILS_H__
+
+
+
+#endif /* __NM_LIBNM_UTILS_H__ */
diff --git a/libnm/tests/test-general.c b/libnm/tests/test-general.c
new file mode 100644
index 0000000..2653cb9
--- /dev/null
+++ b/libnm/tests/test-general.c
@@ -0,0 +1,34 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT SC WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2017 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-utils/nm-test-utils.h"
+
+/*****************************************************************************/
+
+NMTST_DEFINE ();
+
+int main (int argc, char **argv)
+{
+	nmtst_init (&argc, &argv, TRUE);
+
+	return g_test_run ();
+}
-- 
2.9.4


From 3ac56f2ad81a4104f0bc3b95ebcc648fe8445c1d Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@redhat.com>
Date: Fri, 19 May 2017 10:32:13 +0200
Subject: [PATCH 11/13] libnm: move fixup_desc_string() to nm-libnm-utils.c

(cherry picked from commit e255ad2a03c84f806f9606b420fa12757bbd883f)
(cherry picked from commit ea0fd21428bd3b287a6537d9238a53f174c8854c)
---
 libnm/nm-device.c          | 141 +--------------------------------------------
 libnm/nm-libnm-utils.c     | 135 +++++++++++++++++++++++++++++++++++++++++++
 libnm/nm-libnm-utils.h     |   2 +-
 libnm/tests/test-general.c |  35 +++++++++++
 4 files changed, 174 insertions(+), 139 deletions(-)

diff --git a/libnm/nm-device.c b/libnm/nm-device.c
index 3a6052b..969d08a 100644
--- a/libnm/nm-device.c
+++ b/libnm/nm-device.c
@@ -26,6 +26,7 @@
 #include <string.h>
 #include <libudev.h>
 
+#include "nm-libnm-utils.h"
 #include "nm-dbus-interface.h"
 #include "nm-active-connection.h"
 #include "nm-device-bt.h"
@@ -1431,142 +1432,6 @@ nm_device_get_vendor (NMDevice *device)
 	return priv->vendor;
 }
 
-static char *
-fixup_desc_string (const char *desc)
-{
-	static const char *const IGNORED_PHRASES[] = {
-		"Multiprotocol MAC/baseband processor",
-		"Wireless LAN Controller",
-		"Wireless LAN Adapter",
-		"Wireless Adapter",
-		"Network Connection",
-		"Wireless Cardbus Adapter",
-		"Wireless CardBus Adapter",
-		"54 Mbps Wireless PC Card",
-		"Wireless PC Card",
-		"Wireless PC",
-		"PC Card with XJACK(r) Antenna",
-		"Wireless cardbus",
-		"Wireless LAN PC Card",
-		"Technology Group Ltd.",
-		"Communication S.p.A.",
-		"Business Mobile Networks BV",
-		"Mobile Broadband Minicard Composite Device",
-		"Mobile Communications AB",
-		"(PC-Suite Mode)",
-	};
-	static const char *const IGNORED_WORDS[] = {
-		"Semiconductor",
-		"Components",
-		"Corporation",
-		"Communications",
-		"Company",
-		"Corp.",
-		"Corp",
-		"Co.",
-		"Inc.",
-		"Inc",
-		"Incorporated",
-		"Ltd.",
-		"Limited.",
-		"Intel?",
-		"chipset",
-		"adapter",
-		"[hex]",
-		"NDIS",
-		"Module",
-	};
-	char *desc_full;
-	char *p, *q;
-	int i;
-
-	if (!desc || !desc[0])
-		return NULL;
-
-	/* restore original non-UTF-8-safe text. */
-	desc_full = nm_utils_str_utf8safe_unescape_cp (desc);
-
-	/* replace all invalid UTF-8 bytes with space. */
-	p = desc_full;
-	while (!g_utf8_validate (p, -1, (const char **) &q)) {
-		/* the byte is invalid UTF-8. Replace it with space and proceed. */
-		*q = ' ';
-		p = q + 1;
-	}
-
-	/* replace '_', ',', and ASCII controll characters with space. */
-	for (p = desc_full; p[0]; p++) {
-		if (   NM_IN_SET (*p, '_', ',')
-		    || *p < ' ')
-			*p = ' ';
-	}
-
-	/* Attempt to shorten ID by ignoring certain phrases */
-	for (i = 0; i < G_N_ELEMENTS (IGNORED_PHRASES); i++) {
-		p = strstr (desc_full, IGNORED_PHRASES[i]);
-		if (p) {
-			const char *eow = &p[strlen (IGNORED_PHRASES[i])];
-
-			/* require that the phrase is delimited by space, or
-			 * at the beginning or end of the description. */
-			if (   (p == desc_full || p[-1] == ' ')
-			    && NM_IN_SET (eow[0], '\0', ' '))
-				memmove (p, eow, strlen (eow) + 1); /* +1 for the \0 */
-		}
-	}
-
-	/* Attempt to shorten ID by ignoring certain individual words.
-	 * - word-split the description at spaces
-	 * - coalesce multiple spaces
-	 * - skip over IGNORED_WORDS */
-	p = desc_full;
-	q = desc_full;
-	for (;;) {
-		char *eow;
-		gsize l;
-
-		/* skip leading spaces. */
-		while (p[0] == ' ')
-			p++;
-
-		if (!p[0])
-			break;
-
-		/* split leading word on first space */
-		eow = strchr (p, ' ');
-		if (eow)
-			*eow = '\0';
-
-		if (nm_utils_strv_find_first ((char **) IGNORED_WORDS,
-		                              G_N_ELEMENTS (IGNORED_WORDS),
-		                              p) >= 0)
-			goto next;
-
-		l = strlen (p);
-		if (q != p) {
-			if (q != desc_full)
-				*q++ = ' ';
-			memmove (q, p, l);
-		}
-		q += l;
-
-next:
-		if (!eow)
-			break;
-		p = eow + 1;
-	}
-
-	*q++ = '\0';
-
-	if (!desc_full[0]) {
-		g_free (desc_full);
-		return NULL;
-	}
-
-	nm_assert (g_utf8_validate (desc_full, -1, NULL));
-	return desc_full;
-}
-
 static void
 ensure_description (NMDevice *device)
 {
@@ -1574,7 +1439,7 @@ ensure_description (NMDevice *device)
 	GParamSpec *name_prop;
 	gs_free char *short_product = NULL;
 
-	priv->short_vendor = nm_str_realloc (fixup_desc_string (nm_device_get_vendor (device)));
+	priv->short_vendor = nm_str_realloc (nm_utils_fixup_desc_string (nm_device_get_vendor (device)));
 
 	/* Grab device's preferred name, if any */
 	name_prop = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (device)), "name");
@@ -1586,7 +1451,7 @@ ensure_description (NMDevice *device)
 	}
 
 	if (   !priv->short_vendor
-	    || !(short_product = fixup_desc_string (nm_device_get_product (device)))) {
+	    || !(short_product = nm_utils_fixup_desc_string (nm_device_get_product (device)))) {
 		priv->description = g_strdup (nm_device_get_iface (device) ?: "");
 		return;
 	}
diff --git a/libnm/nm-libnm-utils.c b/libnm/nm-libnm-utils.c
index 8cea276..fbbfe2c 100644
--- a/libnm/nm-libnm-utils.c
+++ b/libnm/nm-libnm-utils.c
@@ -25,3 +25,138 @@
 
 /*****************************************************************************/
 
+char *
+nm_utils_fixup_desc_string (const char *desc)
+{
+	static const char *const IGNORED_PHRASES[] = {
+		"Multiprotocol MAC/baseband processor",
+		"Wireless LAN Controller",
+		"Wireless LAN Adapter",
+		"Wireless Adapter",
+		"Network Connection",
+		"Wireless Cardbus Adapter",
+		"Wireless CardBus Adapter",
+		"54 Mbps Wireless PC Card",
+		"Wireless PC Card",
+		"Wireless PC",
+		"PC Card with XJACK(r) Antenna",
+		"Wireless cardbus",
+		"Wireless LAN PC Card",
+		"Technology Group Ltd.",
+		"Communication S.p.A.",
+		"Business Mobile Networks BV",
+		"Mobile Broadband Minicard Composite Device",
+		"Mobile Communications AB",
+		"(PC-Suite Mode)",
+	};
+	static const char *const IGNORED_WORDS[] = {
+		"Semiconductor",
+		"Components",
+		"Corporation",
+		"Communications",
+		"Company",
+		"Corp.",
+		"Corp",
+		"Co.",
+		"Inc.",
+		"Inc",
+		"Incorporated",
+		"Ltd.",
+		"Limited.",
+		"Intel?",
+		"chipset",
+		"adapter",
+		"[hex]",
+		"NDIS",
+		"Module",
+	};
+	char *desc_full;
+	char *p, *q;
+	int i;
+
+	if (!desc || !desc[0])
+		return NULL;
+
+	/* restore original non-UTF-8-safe text. */
+	desc_full = nm_utils_str_utf8safe_unescape_cp (desc);
+
+	/* replace all invalid UTF-8 bytes with space. */
+	p = desc_full;
+	while (!g_utf8_validate (p, -1, (const char **) &q)) {
+		/* the byte is invalid UTF-8. Replace it with space and proceed. */
+		*q = ' ';
+		p = q + 1;
+	}
+
+	/* replace '_', ',', and ASCII controll characters with space. */
+	for (p = desc_full; p[0]; p++) {
+		if (   NM_IN_SET (*p, '_', ',')
+		    || *p < ' ')
+			*p = ' ';
+	}
+
+	/* Attempt to shorten ID by ignoring certain phrases */
+	for (i = 0; i < G_N_ELEMENTS (IGNORED_PHRASES); i++) {
+		p = strstr (desc_full, IGNORED_PHRASES[i]);
+		if (p) {
+			const char *eow = &p[strlen (IGNORED_PHRASES[i])];
+
+			/* require that the phrase is delimited by space, or
+			 * at the beginning or end of the description. */
+			if (   (p == desc_full || p[-1] == ' ')
+			    && NM_IN_SET (eow[0], '\0', ' '))
+				memmove (p, eow, strlen (eow) + 1); /* +1 for the \0 */
+		}
+	}
+
+	/* Attempt to shorten ID by ignoring certain individual words.
+	 * - word-split the description at spaces
+	 * - coalesce multiple spaces
+	 * - skip over IGNORED_WORDS */
+	p = desc_full;
+	q = desc_full;
+	for (;;) {
+		char *eow;
+		gsize l;
+
+		/* skip leading spaces. */
+		while (p[0] == ' ')
+			p++;
+
+		if (!p[0])
+			break;
+
+		/* split leading word on first space */
+		eow = strchr (p, ' ');
+		if (eow)
+			*eow = '\0';
+
+		if (nm_utils_strv_find_first ((char **) IGNORED_WORDS,
+		                              G_N_ELEMENTS (IGNORED_WORDS),
+		                              p) >= 0)
+			goto next;
+
+		l = strlen (p);
+		if (q != p) {
+			if (q != desc_full)
+				*q++ = ' ';
+			memmove (q, p, l);
+		}
+		q += l;
+
+next:
+		if (!eow)
+			break;
+		p = eow + 1;
+	}
+
+	*q++ = '\0';
+
+	if (!desc_full[0]) {
+		g_free (desc_full);
+		return NULL;
+	}
+
+	nm_assert (g_utf8_validate (desc_full, -1, NULL));
+	return desc_full;
+}
diff --git a/libnm/nm-libnm-utils.h b/libnm/nm-libnm-utils.h
index 356b2f9..4a5a361 100644
--- a/libnm/nm-libnm-utils.h
+++ b/libnm/nm-libnm-utils.h
@@ -21,6 +21,6 @@
 #ifndef __NM_LIBNM_UTILS_H__
 #define __NM_LIBNM_UTILS_H__
 
-
+char *nm_utils_fixup_desc_string (const char *desc);
 
 #endif /* __NM_LIBNM_UTILS_H__ */
diff --git a/libnm/tests/test-general.c b/libnm/tests/test-general.c
index 2653cb9..7e0b7bb 100644
--- a/libnm/tests/test-general.c
+++ b/libnm/tests/test-general.c
@@ -20,15 +20,50 @@
 
 #include "nm-default.h"
 
+#include "nm-libnm-utils.h"
+
 #include "nm-utils/nm-test-utils.h"
 
 /*****************************************************************************/
 
+static void
+do_test_fixup_desc_string (const char *desc, const char *expected)
+{
+	gs_free char *result = NULL;
+
+	result = nm_utils_fixup_desc_string (desc);
+	g_assert_cmpstr (result, ==, expected);
+}
+
+#define do_test_fixup_desc_string_same(desc) (do_test_fixup_desc_string (""desc"", ""desc""))
+
+static void
+test_fixup_desc_string (void)
+{
+	do_test_fixup_desc_string (NULL, NULL);
+	do_test_fixup_desc_string ("", NULL);
+	do_test_fixup_desc_string_same ("a");
+	do_test_fixup_desc_string_same ("a b");
+	do_test_fixup_desc_string ("a b ", "a b");
+	do_test_fixup_desc_string ("  a   bbc ", "a bbc");
+	do_test_fixup_desc_string ("  a \xcc  bbc ", "a bbc");
+	do_test_fixup_desc_string ("  a\xcc  bbc ", "a bbc");
+	do_test_fixup_desc_string ("  a\xcc""bbc Wireless PC", "a bbc");
+	do_test_fixup_desc_string ("  a\xcc""bbc Wireless PC ", "a bbc");
+	do_test_fixup_desc_string ("  a\xcc""bbcWireless PC ", "a bbcWireless PC");
+	do_test_fixup_desc_string ("  a\xcc""bbc Wireless PCx", "a bbc Wireless PCx");
+	do_test_fixup_desc_string ("  a\xcc""bbc Inc Wireless PC ", "a bbc");
+}
+
+/*****************************************************************************/
+
 NMTST_DEFINE ();
 
 int main (int argc, char **argv)
 {
 	nmtst_init (&argc, &argv, TRUE);
 
+	g_test_add_func ("/libnm/general/fixup_desc_string", test_fixup_desc_string);
+
 	return g_test_run ();
 }
-- 
2.9.4


From 1e5a95c89c35fadbefd6db21c20fcc4a7750063a Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@redhat.com>
Date: Fri, 19 May 2017 14:13:37 +0200
Subject: [PATCH 12/13] build: don't install intermediate library
 libnm/libnm-utils.la

Fixes: 8df944c7e495d18bfecaf9d8316ef7783039c94b
(cherry picked from commit 733160c862452721821c508465429ffbbda203ae)
(cherry picked from commit 23b5bdd8435bfaf729488e1d3d8821cce948e92e)
---
 Makefile.am | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Makefile.am b/Makefile.am
index a6776e9..7ba3603 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -808,7 +808,7 @@ nodist_libnminclude_HEADERS += \
 
 ###############################################################################
 
-lib_LTLIBRARIES += libnm/libnm-utils.la
+noinst_LTLIBRARIES += libnm/libnm-utils.la
 
 libnm_libnm_utils_la_CPPFLAGS = \
 	$(libnm_lib_cppflags)
-- 
2.9.4


From 32ec4059fd17a30003571c6d97c9859e84de92ed Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@redhat.com>
Date: Fri, 19 May 2017 14:24:14 +0200
Subject: [PATCH 13/13] build: don't link static libraries multiple times

libnm-core.a should only be linked once in libnm.so. Previously,
it was linked twice, once as part of libnm-utils.a and once
directly in libnm.so.

Fixes: 8df944c7e495d18bfecaf9d8316ef7783039c94b
(cherry picked from commit 5a67130e1548bd9314fbd007e131ef378d8b51c7)
(cherry picked from commit da2a02138dc7f0c5aff41019a0517854f379a44c)
---
 Makefile.am | 2 --
 1 file changed, 2 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 7ba3603..909847e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -860,8 +860,6 @@ EXTRA_libnm_libnm_la_DEPENDENCIES = \
 	libnm/libnm.ver
 
 libnm_libnm_la_LIBADD = \
-	libnm-core/libnm-core.la \
-	introspection/libnmdbus.la \
 	libnm/libnm-utils.la \
 	$(DL_LIBS) \
 	$(GLIB_LIBS) \
-- 
2.9.4