From 7f3d60df818fb2b1dd99b12f893a50fd2402968f Mon Sep 17 00:00:00 2001 From: Thomas Haller 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 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 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 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 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 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. --> @@ -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. --> @@ -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. --> @@ -45,6 +54,8 @@ Driver: The driver handling the device. + Non-UTF-8 sequences are backslash escaped. Use g_strcompress() + to revert. --> @@ -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. --> @@ -59,6 +72,8 @@ FirmwareVersion: The firmware version for the device. + Non-UTF-8 sequences are backslash escaped. Use g_strcompress() + to revert. --> 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 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 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 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 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 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 #include +#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 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 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