From 0aa7d59557767f5e512caec3dfb6f72ba12ce1d1 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 11 Apr 2022 14:28:49 +0200 Subject: glib-aux: add nm_unbase64{char,mem,mem_full}() helpers These are taken from systemd code. We want to stop using systemd code, so we can eventually drop it. --- src/libnm-glib-aux/nm-shared-utils.c | 224 +++++++++++++++++++++++++ src/libnm-glib-aux/nm-shared-utils.h | 5 + src/libnm-glib-aux/tests/test-shared-general.c | 176 +++++++++++++++++++ 3 files changed, 405 insertions(+) diff --git a/src/libnm-glib-aux/nm-shared-utils.c b/src/libnm-glib-aux/nm-shared-utils.c index 1da2a68293..2e5a627ae1 100644 --- a/src/libnm-glib-aux/nm-shared-utils.c +++ b/src/libnm-glib-aux/nm-shared-utils.c @@ -6714,3 +6714,227 @@ nm_g_main_context_can_acquire(GMainContext *context) g_main_context_release(context); return TRUE; } + +/*****************************************************************************/ + +int +nm_unbase64char(char c) +{ + unsigned offset; + + /* copied from systemd's unbase64char(): + * https://github.com/systemd/systemd/blob/688efe7703328c5a0251fafac55757b8864a9f9a/src/basic/hexdecoct.c#L539 */ + + if (c >= 'A' && c <= 'Z') + return c - 'A'; + + offset = 'Z' - 'A' + 1; + + if (c >= 'a' && c <= 'z') + return c - 'a' + offset; + + offset += 'z' - 'a' + 1; + + if (c >= '0' && c <= '9') + return c - '0' + offset; + + offset += '9' - '0' + 1; + + if (c == '+') + return offset; + + offset++; + + if (c == '/') + return offset; + + return -EINVAL; +} + +#define WHITESPACE " \t\n\r" + +static int +unbase64_next(const char **p, size_t *l) +{ + int ret; + + assert(p); + assert(l); + + /* copied from systemd's unbase64_next(): + * https://github.com/systemd/systemd/blob/688efe7703328c5a0251fafac55757b8864a9f9a/src/basic/hexdecoct.c#L709 */ + + /* Find the next non-whitespace character, and decode it. If we find padding, we return it as INT_MAX. We + * greedily skip all preceding and all following whitespace. */ + + for (;;) { + if (*l == 0) + return -EPIPE; + + if (!strchr(WHITESPACE, **p)) + break; + + /* Skip leading whitespace */ + (*p)++, (*l)--; + } + + if (**p == '=') + ret = INT_MAX; /* return padding as INT_MAX */ + else { + ret = nm_unbase64char(**p); + if (ret < 0) + return ret; + } + + for (;;) { + (*p)++, (*l)--; + + if (*l == 0) + break; + if (!strchr(WHITESPACE, **p)) + break; + + /* Skip following whitespace */ + } + + return ret; +} + +/** + * nm_unbase64mem_full: + * @p: a valid base64 string. Whitespace is ignored, but invalid encodings + * will cause the function to fail. + * @l: the length of @p. @p is not treated as NUL terminated string but + * merely as a buffer of ascii characters. + * @secure: whether the temporary memory will be cleared to avoid leaving + * secrets in memory (see also nm_explicit_bzero()). + * @mem: (transfer full): the decoded buffer on success. + * @len: the length of @mem on success. + * + * glib provides g_base64_decode(), but that does not report any errors + * from invalid encodings. Our own implementation (based on systemd code) + * rejects invalid inputs. + * + * Returns: a non-negative code on success. Invalid encoding let the + * function fail. + */ +int +nm_unbase64mem_full(const char *p, gsize l, gboolean secure, guint8 **ret, gsize *ret_size) +{ + gs_free uint8_t *buf = NULL; + const char *x; + guint8 *z; + gsize len; + int r; + + /* copied from systemd's unbase64mem_full(): + * https://github.com/systemd/systemd/blob/688efe7703328c5a0251fafac55757b8864a9f9a/src/basic/hexdecoct.c#L751 */ + + assert(p || l == 0); + + if (l == SIZE_MAX) + l = strlen(p); + + /* A group of four input bytes needs three output bytes, in case of padding we need to add two or three extra + * bytes. Note that this calculation is an upper boundary, as we ignore whitespace while decoding */ + len = (l / 4) * 3 + (l % 4 != 0 ? (l % 4) - 1 : 0); + + buf = g_malloc(len + 1); + + for (x = p, z = buf;;) { + int a, b, c, d; /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */ + + a = unbase64_next(&x, &l); + if (a == -EPIPE) /* End of string */ + break; + if (a < 0) { + r = a; + goto on_failure; + } + if (a == INT_MAX) { /* Padding is not allowed at the beginning of a 4ch block */ + r = -EINVAL; + goto on_failure; + } + + b = unbase64_next(&x, &l); + if (b < 0) { + r = b; + goto on_failure; + } + if (b + == INT_MAX) { /* Padding is not allowed at the second character of a 4ch block either */ + r = -EINVAL; + goto on_failure; + } + + c = unbase64_next(&x, &l); + if (c < 0) { + r = c; + goto on_failure; + } + + d = unbase64_next(&x, &l); + if (d < 0) { + r = d; + goto on_failure; + } + + if (c == INT_MAX) { /* Padding at the third character */ + + if (d != INT_MAX) { /* If the third character is padding, the fourth must be too */ + r = -EINVAL; + goto on_failure; + } + + /* b == 00YY0000 */ + if (b & 15) { + r = -EINVAL; + goto on_failure; + } + + if (l > 0) { /* Trailing rubbish? */ + r = -ENAMETOOLONG; + goto on_failure; + } + + *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */ + break; + } + + if (d == INT_MAX) { + /* c == 00ZZZZ00 */ + if (c & 3) { + r = -EINVAL; + goto on_failure; + } + + if (l > 0) { /* Trailing rubbish? */ + r = -ENAMETOOLONG; + goto on_failure; + } + + *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ + *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ + break; + } + + *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ + *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ + *(z++) = (uint8_t) c << 6 | (uint8_t) d; /* ZZWWWWWW */ + } + + *z = 0; + + if (ret_size) + *ret_size = (size_t) (z - buf); + if (ret) + *ret = g_steal_pointer(&buf); + + return 0; + +on_failure: + if (secure) + nm_explicit_bzero(buf, len); + + return r; +} diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h index 0d2403ca66..959a2b5d24 100644 --- a/src/libnm-glib-aux/nm-shared-utils.h +++ b/src/libnm-glib-aux/nm-shared-utils.h @@ -3308,4 +3308,9 @@ gboolean nm_utils_validate_hostname(const char *hostname); void nm_utils_thread_local_register_destroy(gpointer tls_data, GDestroyNotify destroy_notify); +/*****************************************************************************/ + +int nm_unbase64char(char c); +int nm_unbase64mem_full(const char *p, gsize l, gboolean secure, guint8 **ret, gsize *ret_size); + #endif /* __NM_SHARED_UTILS_H__ */ diff --git a/src/libnm-glib-aux/tests/test-shared-general.c b/src/libnm-glib-aux/tests/test-shared-general.c index 4b537f55ed..35d0d1a2e5 100644 --- a/src/libnm-glib-aux/tests/test-shared-general.c +++ b/src/libnm-glib-aux/tests/test-shared-general.c @@ -1556,6 +1556,179 @@ test_parse_env_file(void) /*****************************************************************************/ +static void +test_unbase64char(void) +{ + static const int expected[128] = { + [0] = -1, [1] = -1, [2] = -1, [3] = -1, [4] = -1, [5] = -1, [6] = -1, + [7] = -1, [8] = -1, [9] = -1, [10] = -1, [11] = -1, [12] = -1, [13] = -1, + [14] = -1, [15] = -1, [16] = -1, [17] = -1, [18] = -1, [19] = -1, [20] = -1, + [21] = -1, [22] = -1, [23] = -1, [24] = -1, [25] = -1, [26] = -1, [27] = -1, + [28] = -1, [29] = -1, [30] = -1, [31] = -1, [32] = -1, [33] = -1, [34] = -1, + [35] = -1, [36] = -1, [37] = -1, [38] = -1, [39] = -1, [40] = -1, [41] = -1, + [42] = -1, ['+'] = 62, [44] = -1, [45] = -1, [46] = -1, ['/'] = 63, ['0'] = 52, + ['1'] = 53, ['2'] = 54, ['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59, + ['8'] = 60, ['9'] = 61, [58] = -1, [59] = -1, [60] = -1, [61] = -1, [62] = -1, + [63] = -1, [64] = -1, ['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, ['E'] = 4, + ['F'] = 5, ['G'] = 6, ['H'] = 7, ['I'] = 8, ['J'] = 9, ['K'] = 10, ['L'] = 11, + ['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15, ['Q'] = 16, ['R'] = 17, ['S'] = 18, + ['T'] = 19, ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23, ['Y'] = 24, ['Z'] = 25, + [91] = -1, [92] = -1, [93] = -1, [94] = -1, [95] = -1, [96] = -1, ['a'] = 26, + ['b'] = 27, ['c'] = 28, ['d'] = 29, ['e'] = 30, ['f'] = 31, ['g'] = 32, ['h'] = 33, + ['i'] = 34, ['j'] = 35, ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39, ['o'] = 40, + ['p'] = 41, ['q'] = 42, ['r'] = 43, ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47, + ['w'] = 48, ['x'] = 49, ['y'] = 50, ['z'] = 51, [123] = -1, [124] = -1, [125] = -1, + [126] = -1, [127] = -1, + }; + int i; + + /* Copied from systemd's TEST(unbase64char) + * https://github.com/systemd/systemd/blob/688efe7703328c5a0251fafac55757b8864a9f9a/src/test/test-hexdecoct.c#L44 */ + + g_assert_cmpint(nm_unbase64char('A'), ==, 0); + g_assert_cmpint(nm_unbase64char('Z'), ==, 25); + g_assert_cmpint(nm_unbase64char('a'), ==, 26); + g_assert_cmpint(nm_unbase64char('z'), ==, 51); + g_assert_cmpint(nm_unbase64char('0'), ==, 52); + g_assert_cmpint(nm_unbase64char('9'), ==, 61); + g_assert_cmpint(nm_unbase64char('+'), ==, 62); + g_assert_cmpint(nm_unbase64char('/'), ==, 63); + g_assert_cmpint(nm_unbase64char('='), ==, -EINVAL); + g_assert_cmpint(nm_unbase64char('\0'), ==, -EINVAL); + g_assert_cmpint(nm_unbase64char('\1'), ==, -EINVAL); + g_assert_cmpint(nm_unbase64char('\x7F'), ==, -EINVAL); + g_assert_cmpint(nm_unbase64char('\x80'), ==, -EINVAL); + g_assert_cmpint(nm_unbase64char('\xFF'), ==, -EINVAL); + + for (i = 0; i < 256; i++) { + int base64; + + base64 = nm_unbase64char((char) i); + + if (base64 < 0) { + g_assert_cmpint(base64, ==, -EINVAL); + base64 = -1; + } + + if (i >= G_N_ELEMENTS(expected)) { + g_assert_cmpint(base64, ==, -1); + continue; + } + g_assert_cmpint(base64, ==, expected[i]); + } +} + +/*****************************************************************************/ + +static void +test_unbase64mem1(void) +{ + nm_auto_str_buf NMStrBuf encoded_wrapped = NM_STR_BUF_INIT(400, FALSE); + uint8_t data[4096]; + int i_run; + + /* Copied from systemd's TEST(base64mem_linebreak) + * https://github.com/systemd/systemd/blob/688efe7703328c5a0251fafac55757b8864a9f9a/src/test/test-hexdecoct.c#L280 */ + + for (i_run = 0; i_run < 20; i_run++) { + gs_free char *encoded = NULL; + gs_free guint8 *decoded = NULL; + gsize decoded_size; + guint64 n; + guint64 m; + guint64 i; + guint64 j; + gssize l; + int r; + + /* Try a bunch of differently sized blobs */ + n = nmtst_get_rand_uint64() % sizeof(data); + nmtst_rand_buf(NULL, data, n); + + /* Break at various different columns */ + m = 1 + (nmtst_get_rand_uint64() % (n + 5)); + + encoded = g_base64_encode(data, n); + g_assert(encoded); + l = strlen(encoded); + + nm_str_buf_reset(&encoded_wrapped); + for (i = 0, j = 0; i < l; i++, j++) { + if (j == m) { + nm_str_buf_append_c(&encoded_wrapped, '\n'); + j = 0; + } + nm_str_buf_append_c(&encoded_wrapped, encoded[i]); + } + + g_assert_cmpint(strlen(nm_str_buf_get_str(&encoded_wrapped)), ==, encoded_wrapped.len); + + r = nm_unbase64mem_full(nm_str_buf_get_str(&encoded_wrapped), + nmtst_get_rand_bool() ? SIZE_MAX : encoded_wrapped.len, + nmtst_get_rand_bool(), + &decoded, + &decoded_size); + g_assert_cmpint(r, >=, 0); + g_assert_cmpmem(data, n, decoded, decoded_size); + + for (j = 0; j < encoded_wrapped.len; j++) + g_assert((nm_str_buf_get_str(&encoded_wrapped)[j] == '\n') == (j % (m + 1) == m)); + } +} + +/*****************************************************************************/ + +static void +_assert_unbase64mem(const char *input, const char *output, int ret) +{ + gs_free guint8 *buffer = NULL; + gsize size = 0; + int r; + + r = nm_unbase64mem_full(input, SIZE_MAX, nmtst_get_rand_bool(), &buffer, &size); + g_assert_cmpint(r, ==, ret); + + if (ret >= 0) { + g_assert_cmpmem(buffer, size, output, strlen(output)); + g_assert_cmpint(((const char *) buffer)[size], ==, '\0'); + } else { + g_assert(!buffer); + g_assert_cmpint(size, ==, 0); + } +} + +static void +test_unbase64mem2(void) +{ + /* Copied from systemd's TEST(unbase64mem) + * https://github.com/systemd/systemd/blob/688efe7703328c5a0251fafac55757b8864a9f9a/src/test/test-hexdecoct.c#L324 */ + + _assert_unbase64mem("", "", 0); + _assert_unbase64mem("Zg==", "f", 0); + _assert_unbase64mem("Zm8=", "fo", 0); + _assert_unbase64mem("Zm9v", "foo", 0); + _assert_unbase64mem("Zm9vYg==", "foob", 0); + _assert_unbase64mem("Zm9vYmE=", "fooba", 0); + _assert_unbase64mem("Zm9vYmFy", "foobar", 0); + + _assert_unbase64mem(" ", "", 0); + _assert_unbase64mem(" \n\r ", "", 0); + _assert_unbase64mem(" Zg\n== ", "f", 0); + _assert_unbase64mem(" Zm 8=\r", "fo", 0); + _assert_unbase64mem(" Zm9\n\r\r\nv ", "foo", 0); + _assert_unbase64mem(" Z m9vYg==\n\r", "foob", 0); + _assert_unbase64mem(" Zm 9vYmE= ", "fooba", 0); + _assert_unbase64mem(" Z m9v YmFy ", "foobar", 0); + + _assert_unbase64mem("A", NULL, -EPIPE); + _assert_unbase64mem("A====", NULL, -EINVAL); + _assert_unbase64mem("AAB==", NULL, -EINVAL); + _assert_unbase64mem(" A A A B = ", NULL, -EINVAL); + _assert_unbase64mem(" Z m 8 = q u u x ", NULL, -ENAMETOOLONG); +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -1590,6 +1763,9 @@ main(int argc, char **argv) g_test_add_func("/general/test_nm_g_source_sentinel", test_nm_g_source_sentinel); g_test_add_func("/general/test_nm_ascii", test_nm_ascii); g_test_add_func("/general/test_parse_env_file", test_parse_env_file); + g_test_add_func("/general/test_unbase64char", test_unbase64char); + g_test_add_func("/general/test_unbase64mem1", test_unbase64mem1); + g_test_add_func("/general/test_unbase64mem2", test_unbase64mem2); return g_test_run(); } -- cgit v1.2.1 From 63dcc5680bf58d9edd25c3eb83a89cf63c073219 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 11 Apr 2022 18:05:25 +0200 Subject: glib-aux/tests: add unbase64mem test This is copied and adjusted from "src/core/tests/test-systemd.c", where it currently tests the systemd implementation. --- src/libnm-glib-aux/tests/test-shared-general.c | 65 ++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/libnm-glib-aux/tests/test-shared-general.c b/src/libnm-glib-aux/tests/test-shared-general.c index 35d0d1a2e5..58ede68c78 100644 --- a/src/libnm-glib-aux/tests/test-shared-general.c +++ b/src/libnm-glib-aux/tests/test-shared-general.c @@ -1729,6 +1729,70 @@ test_unbase64mem2(void) /*****************************************************************************/ +static void +_test_unbase64mem_mem(const char *base64, const guint8 *expected_arr, gsize expected_len) +{ + gs_free char *expected_base64 = NULL; + int r; + nm_auto_free guint8 *exp2_arr = NULL; + nm_auto_free guint8 *exp3_arr = NULL; + gsize exp2_len; + gsize exp3_len; + + expected_base64 = g_base64_encode(expected_arr, expected_len); + + r = nm_unbase64mem_full(expected_base64, strlen(expected_base64), TRUE, &exp2_arr, &exp2_len); + g_assert_cmpint(r, ==, 0); + g_assert_cmpmem(expected_arr, expected_len, exp2_arr, exp2_len); + + if (!nm_streq(base64, expected_base64)) { + r = nm_unbase64mem_full(base64, strlen(base64), TRUE, &exp3_arr, &exp3_len); + g_assert_cmpint(r, ==, 0); + g_assert_cmpmem(expected_arr, expected_len, exp3_arr, exp3_len); + } +} + +#define _test_unbase64mem(base64, expected_str) \ + _test_unbase64mem_mem(base64, (const guint8 *) "" expected_str "", NM_STRLEN(expected_str)) + +static void +_test_unbase64mem_inval(const char *base64) +{ + gs_free guint8 *exp_arr = NULL; + gsize exp_len = 0; + int r; + + r = nm_unbase64mem_full(base64, strlen(base64), TRUE, &exp_arr, &exp_len); + g_assert_cmpint(r, <, 0); + g_assert(!exp_arr); + g_assert(exp_len == 0); +} + +static void +test_unbase64mem3(void) +{ + gs_free char *rnd_base64 = NULL; + guint8 rnd_buf[30]; + guint i, rnd_len; + + _test_unbase64mem("", ""); + _test_unbase64mem(" ", ""); + _test_unbase64mem(" Y Q == ", "a"); + _test_unbase64mem(" Y WJjZGV mZ 2g = ", "abcdefgh"); + _test_unbase64mem_inval(" Y %WJjZGV mZ 2g = "); + _test_unbase64mem_inval(" Y %WJjZGV mZ 2g = a"); + _test_unbase64mem("YQ==", "a"); + _test_unbase64mem_inval("YQ==a"); + + rnd_len = nmtst_get_rand_uint32() % sizeof(rnd_buf); + for (i = 0; i < rnd_len; i++) + rnd_buf[i] = nmtst_get_rand_uint32() % 256; + rnd_base64 = g_base64_encode(rnd_buf, rnd_len); + _test_unbase64mem_mem(rnd_base64, rnd_buf, rnd_len); +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -1766,6 +1830,7 @@ main(int argc, char **argv) g_test_add_func("/general/test_unbase64char", test_unbase64char); g_test_add_func("/general/test_unbase64mem1", test_unbase64mem1); g_test_add_func("/general/test_unbase64mem2", test_unbase64mem2); + g_test_add_func("/general/test_unbase64mem3", test_unbase64mem3); return g_test_run(); } -- cgit v1.2.1 From e3240781b1336be384cdc9fb5fc26fc45a36165b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 11 Apr 2022 14:47:10 +0200 Subject: glib-aux: use switch in nm_unbase64char() This seams easier to read. And as we have a unit test that covers all possible 256 input values, it's easy to refactor and ensure the code still works. --- src/libnm-glib-aux/nm-shared-utils.c | 38 ++++++++++++------------------------ 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/src/libnm-glib-aux/nm-shared-utils.c b/src/libnm-glib-aux/nm-shared-utils.c index 2e5a627ae1..c325e6c9e2 100644 --- a/src/libnm-glib-aux/nm-shared-utils.c +++ b/src/libnm-glib-aux/nm-shared-utils.c @@ -6720,35 +6720,23 @@ nm_g_main_context_can_acquire(GMainContext *context) int nm_unbase64char(char c) { - unsigned offset; - /* copied from systemd's unbase64char(): * https://github.com/systemd/systemd/blob/688efe7703328c5a0251fafac55757b8864a9f9a/src/basic/hexdecoct.c#L539 */ - if (c >= 'A' && c <= 'Z') + switch (c) { + case 'A' ... 'Z': return c - 'A'; - - offset = 'Z' - 'A' + 1; - - if (c >= 'a' && c <= 'z') - return c - 'a' + offset; - - offset += 'z' - 'a' + 1; - - if (c >= '0' && c <= '9') - return c - '0' + offset; - - offset += '9' - '0' + 1; - - if (c == '+') - return offset; - - offset++; - - if (c == '/') - return offset; - - return -EINVAL; + case 'a' ... 'z': + return (c - 'a') + ('Z' - 'A' + 1); + case '0' ... '9': + return (c - '0') + (('Z' - 'A' + 1) + ('z' - 'a' + 1)); + case '+': + return ('Z' - 'A' + 1) + ('z' - 'a' + 1) + ('9' - '0' + 1); + case '/': + return ('Z' - 'A' + 1) + ('z' - 'a' + 1) + ('9' - '0' + 1) + 1; + default: + return -EINVAL; + } } #define WHITESPACE " \t\n\r" -- cgit v1.2.1 From 3571292d97418d4de4b37d6fa88b68d722e4775d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 11 Apr 2022 14:54:00 +0200 Subject: glib-aux: treat '=' as special character in nm_unbase64char() This will be useful. --- src/core/tests/test-systemd.c | 2 ++ src/libnm-glib-aux/nm-shared-utils.c | 4 ++++ src/libnm-glib-aux/tests/test-shared-general.c | 7 +++++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/core/tests/test-systemd.c b/src/core/tests/test-systemd.c index 9c12032087..3d0a3f2fef 100644 --- a/src/core/tests/test-systemd.c +++ b/src/core/tests/test-systemd.c @@ -144,7 +144,9 @@ _test_unbase64char(char ch, gboolean maybe_invalid) g_assert(!maybe_invalid); g_assert_cmpint(r, <, 0); g_assert_cmpint(nm_sd_utils_unbase64char(ch, TRUE), ==, G_MAXINT); + g_assert_cmpint(nm_unbase64char(ch), ==, -ERANGE); } else { + g_assert_cmpint(nm_unbase64char(ch), ==, r); g_assert_cmpint(r, ==, nm_sd_utils_unbase64char(ch, TRUE)); if (r >= 0) g_assert_cmpint(r, <=, 255); diff --git a/src/libnm-glib-aux/nm-shared-utils.c b/src/libnm-glib-aux/nm-shared-utils.c index c325e6c9e2..d5acfd3594 100644 --- a/src/libnm-glib-aux/nm-shared-utils.c +++ b/src/libnm-glib-aux/nm-shared-utils.c @@ -6734,6 +6734,10 @@ nm_unbase64char(char c) return ('Z' - 'A' + 1) + ('z' - 'a' + 1) + ('9' - '0' + 1); case '/': return ('Z' - 'A' + 1) + ('z' - 'a' + 1) + ('9' - '0' + 1) + 1; + case '=': + /* The padding is a different kind of base64 character. Return + * a special error code for it. */ + return -ERANGE; default: return -EINVAL; } diff --git a/src/libnm-glib-aux/tests/test-shared-general.c b/src/libnm-glib-aux/tests/test-shared-general.c index 58ede68c78..783b771a0d 100644 --- a/src/libnm-glib-aux/tests/test-shared-general.c +++ b/src/libnm-glib-aux/tests/test-shared-general.c @@ -1593,7 +1593,7 @@ test_unbase64char(void) g_assert_cmpint(nm_unbase64char('9'), ==, 61); g_assert_cmpint(nm_unbase64char('+'), ==, 62); g_assert_cmpint(nm_unbase64char('/'), ==, 63); - g_assert_cmpint(nm_unbase64char('='), ==, -EINVAL); + g_assert_cmpint(nm_unbase64char('='), ==, -ERANGE); g_assert_cmpint(nm_unbase64char('\0'), ==, -EINVAL); g_assert_cmpint(nm_unbase64char('\1'), ==, -EINVAL); g_assert_cmpint(nm_unbase64char('\x7F'), ==, -EINVAL); @@ -1606,7 +1606,10 @@ test_unbase64char(void) base64 = nm_unbase64char((char) i); if (base64 < 0) { - g_assert_cmpint(base64, ==, -EINVAL); + if (((char) i) == '=') + g_assert_cmpint(base64, ==, -ERANGE); + else + g_assert_cmpint(base64, ==, -EINVAL); base64 = -1; } -- cgit v1.2.1 From 04fc191922b4197dc4f1c74bb187af56983f7826 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 11 Apr 2022 18:08:34 +0200 Subject: glib-aux: refactor nm_unbase64mem_full() Make the code more nm-like. --- src/libnm-glib-aux/nm-shared-utils.c | 85 +++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/src/libnm-glib-aux/nm-shared-utils.c b/src/libnm-glib-aux/nm-shared-utils.c index d5acfd3594..310609d2ac 100644 --- a/src/libnm-glib-aux/nm-shared-utils.c +++ b/src/libnm-glib-aux/nm-shared-utils.c @@ -6743,15 +6743,13 @@ nm_unbase64char(char c) } } -#define WHITESPACE " \t\n\r" - static int unbase64_next(const char **p, size_t *l) { int ret; - assert(p); - assert(l); + nm_assert(p); + nm_assert(l); /* copied from systemd's unbase64_next(): * https://github.com/systemd/systemd/blob/688efe7703328c5a0251fafac55757b8864a9f9a/src/basic/hexdecoct.c#L709 */ @@ -6763,32 +6761,34 @@ unbase64_next(const char **p, size_t *l) if (*l == 0) return -EPIPE; - if (!strchr(WHITESPACE, **p)) + if (!nm_ascii_is_whitespace(**p)) break; /* Skip leading whitespace */ - (*p)++, (*l)--; + (*p)++; + (*l)--; } - if (**p == '=') - ret = INT_MAX; /* return padding as INT_MAX */ - else { - ret = nm_unbase64char(**p); - if (ret < 0) + ret = nm_unbase64char(**p); + if (ret < 0) { + nm_assert(NM_IN_SET(ret, -EINVAL, -ERANGE)); + if (ret != -ERANGE) return ret; } for (;;) { - (*p)++, (*l)--; + (*p)++; + (*l)--; if (*l == 0) break; - if (!strchr(WHITESPACE, **p)) + if (!nm_ascii_is_whitespace(**p)) break; /* Skip following whitespace */ } + nm_assert(ret == -ERANGE || ret >= 0); return ret; } @@ -6822,9 +6822,9 @@ nm_unbase64mem_full(const char *p, gsize l, gboolean secure, guint8 **ret, gsize /* copied from systemd's unbase64mem_full(): * https://github.com/systemd/systemd/blob/688efe7703328c5a0251fafac55757b8864a9f9a/src/basic/hexdecoct.c#L751 */ - assert(p || l == 0); + nm_assert(p || l == 0); - if (l == SIZE_MAX) + if (l == G_MAXSIZE) l = strlen(p); /* A group of four input bytes needs three output bytes, in case of padding we need to add two or three extra @@ -6834,46 +6834,53 @@ nm_unbase64mem_full(const char *p, gsize l, gboolean secure, guint8 **ret, gsize buf = g_malloc(len + 1); for (x = p, z = buf;;) { - int a, b, c, d; /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */ + int a; /* a == 00XXXXXX */ + int b; /* b == 00YYYYYY */ + int c; /* c == 00ZZZZZZ */ + int d; /* d == 00WWWWWW */ a = unbase64_next(&x, &l); - if (a == -EPIPE) /* End of string */ - break; if (a < 0) { + if (a == -EPIPE) /* End of string */ + break; + if (a == -ERANGE) { /* Padding is not allowed at the beginning of a 4ch block */ + r = -EINVAL; + goto on_failure; + } r = a; goto on_failure; } - if (a == INT_MAX) { /* Padding is not allowed at the beginning of a 4ch block */ - r = -EINVAL; - goto on_failure; - } b = unbase64_next(&x, &l); if (b < 0) { + if (b == -ERANGE) { + /* Padding is not allowed at the second character of a 4ch block either */ + r = -EINVAL; + goto on_failure; + } r = b; goto on_failure; } - if (b - == INT_MAX) { /* Padding is not allowed at the second character of a 4ch block either */ - r = -EINVAL; - goto on_failure; - } c = unbase64_next(&x, &l); if (c < 0) { - r = c; - goto on_failure; + if (c != -ERANGE) { + r = c; + goto on_failure; + } } d = unbase64_next(&x, &l); if (d < 0) { - r = d; - goto on_failure; + if (d != -ERANGE) { + r = d; + goto on_failure; + } } - if (c == INT_MAX) { /* Padding at the third character */ + if (c == -ERANGE) { /* Padding at the third character */ - if (d != INT_MAX) { /* If the third character is padding, the fourth must be too */ + if (d != -ERANGE) { /* If the third character is padding, the fourth must be too */ r = -EINVAL; goto on_failure; } @@ -6893,7 +6900,7 @@ nm_unbase64mem_full(const char *p, gsize l, gboolean secure, guint8 **ret, gsize break; } - if (d == INT_MAX) { + if (d == -ERANGE) { /* c == 00ZZZZ00 */ if (c & 3) { r = -EINVAL; @@ -6915,18 +6922,14 @@ nm_unbase64mem_full(const char *p, gsize l, gboolean secure, guint8 **ret, gsize *(z++) = (uint8_t) c << 6 | (uint8_t) d; /* ZZWWWWWW */ } - *z = 0; - - if (ret_size) - *ret_size = (size_t) (z - buf); - if (ret) - *ret = g_steal_pointer(&buf); + *z = '\0'; + NM_SET_OUT(ret_size, (gsize) (z - buf)); + NM_SET_OUT(ret, g_steal_pointer(&buf)); return 0; on_failure: if (secure) nm_explicit_bzero(buf, len); - return r; } -- cgit v1.2.1 From bb0ba779f60971d432751b9cfc4b2565b2dcc3c9 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 11 Apr 2022 14:55:35 +0200 Subject: keyfile: use nm_unbase64char() instead of systemd code in _write_setting_wireguard() --- src/libnm-core-impl/nm-keyfile.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libnm-core-impl/nm-keyfile.c b/src/libnm-core-impl/nm-keyfile.c index 00fb8a335d..bbac7c1e23 100644 --- a/src/libnm-core-impl/nm-keyfile.c +++ b/src/libnm-core-impl/nm-keyfile.c @@ -21,7 +21,6 @@ #include "libnm-glib-aux/nm-uuid.h" #include "libnm-glib-aux/nm-str-buf.h" #include "libnm-glib-aux/nm-secret-utils.h" -#include "libnm-systemd-shared/nm-sd-utils-shared.h" #include "libnm-core-aux-intern/nm-common-macros.h" #include "libnm-core-aux-intern/nm-libnm-core-utils.h" #include "libnm-core-intern/nm-core-internal.h" @@ -4062,7 +4061,7 @@ _write_setting_wireguard(NMSetting *setting, KeyfileWriterInfo *info) public_key = nm_wireguard_peer_get_public_key(peer); if (!public_key || !public_key[0] - || !NM_STRCHAR_ALL(public_key, ch, nm_sd_utils_unbase64char(ch, TRUE) >= 0)) { + || !NM_STRCHAR_ALL(public_key, ch, nm_unbase64char(ch) != -EINVAL)) { /* invalid peer. Skip it */ continue; } -- cgit v1.2.1 From cdc3e3fa95f289f124bbc288b1d4642bf2ade2c1 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 11 Apr 2022 17:52:46 +0200 Subject: libnm: use own nm_unbase64mem_full() instead of systemd's in nm_utils_base64secret_decode() --- src/libnm-core-impl/nm-utils.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libnm-core-impl/nm-utils.c b/src/libnm-core-impl/nm-utils.c index d5d884f2e4..0d3f6a6fda 100644 --- a/src/libnm-core-impl/nm-utils.c +++ b/src/libnm-core-impl/nm-utils.c @@ -23,7 +23,6 @@ #include "libnm-glib-aux/nm-enum-utils.h" #include "libnm-glib-aux/nm-time-utils.h" #include "libnm-glib-aux/nm-secret-utils.h" -#include "libnm-systemd-shared/nm-sd-utils-shared.h" #include "libnm-core-aux-intern/nm-common-macros.h" #include "nm-utils-private.h" #include "nm-setting-private.h" @@ -5356,7 +5355,7 @@ nm_utils_base64secret_decode(const char *base64_key, gsize required_key_len, gui base64_key_len = strlen(base64_key); - r = nm_sd_utils_unbase64mem(base64_key, base64_key_len, TRUE, &bin_arr, &bin_len); + r = nm_unbase64mem_full(base64_key, base64_key_len, TRUE, &bin_arr, &bin_len); if (r < 0) return FALSE; if (bin_len != required_key_len) { -- cgit v1.2.1 From 82cac62fe2d6ecdc7f6fe455bab55fed1dc41107 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 11 Apr 2022 17:57:28 +0200 Subject: systemd: drop nm_sd_utils_unbase64{char,mem}() wrappers They are unused now. --- src/core/tests/test-systemd.c | 103 -------------------------- src/libnm-systemd-shared/nm-sd-utils-shared.c | 32 -------- src/libnm-systemd-shared/nm-sd-utils-shared.h | 6 -- 3 files changed, 141 deletions(-) diff --git a/src/core/tests/test-systemd.c b/src/core/tests/test-systemd.c index 3d0a3f2fef..ed0bdb4ff8 100644 --- a/src/core/tests/test-systemd.c +++ b/src/core/tests/test-systemd.c @@ -133,108 +133,6 @@ test_path_equal(void) /*****************************************************************************/ -static void -_test_unbase64char(char ch, gboolean maybe_invalid) -{ - int r; - - r = nm_sd_utils_unbase64char(ch, FALSE); - - if (ch == '=') { - g_assert(!maybe_invalid); - g_assert_cmpint(r, <, 0); - g_assert_cmpint(nm_sd_utils_unbase64char(ch, TRUE), ==, G_MAXINT); - g_assert_cmpint(nm_unbase64char(ch), ==, -ERANGE); - } else { - g_assert_cmpint(nm_unbase64char(ch), ==, r); - g_assert_cmpint(r, ==, nm_sd_utils_unbase64char(ch, TRUE)); - if (r >= 0) - g_assert_cmpint(r, <=, 255); - if (!maybe_invalid) - g_assert_cmpint(r, >=, 0); - } -} - -static void -_test_unbase64mem_mem(const char *base64, const guint8 *expected_arr, gsize expected_len) -{ - gs_free char *expected_base64 = NULL; - int r; - nm_auto_free guint8 *exp2_arr = NULL; - nm_auto_free guint8 *exp3_arr = NULL; - gsize exp2_len; - gsize exp3_len; - gsize i; - - expected_base64 = g_base64_encode(expected_arr, expected_len); - - for (i = 0; expected_base64[i]; i++) - _test_unbase64char(expected_base64[i], FALSE); - - r = nm_sd_utils_unbase64mem(expected_base64, - strlen(expected_base64), - TRUE, - &exp2_arr, - &exp2_len); - g_assert_cmpint(r, ==, 0); - g_assert_cmpmem(expected_arr, expected_len, exp2_arr, exp2_len); - - if (!nm_streq(base64, expected_base64)) { - r = nm_sd_utils_unbase64mem(base64, strlen(base64), TRUE, &exp3_arr, &exp3_len); - g_assert_cmpint(r, ==, 0); - g_assert_cmpmem(expected_arr, expected_len, exp3_arr, exp3_len); - } -} - -#define _test_unbase64mem(base64, expected_str) \ - _test_unbase64mem_mem(base64, (const guint8 *) "" expected_str "", NM_STRLEN(expected_str)) - -static void -_test_unbase64mem_inval(const char *base64) -{ - gs_free guint8 *exp_arr = NULL; - gsize exp_len = 0; - int r; - - r = nm_sd_utils_unbase64mem(base64, strlen(base64), TRUE, &exp_arr, &exp_len); - g_assert_cmpint(r, <, 0); - g_assert(!exp_arr); - g_assert(exp_len == 0); -} - -static void -test_nm_sd_utils_unbase64mem(void) -{ - gs_free char *rnd_base64 = NULL; - guint8 rnd_buf[30]; - guint i, rnd_len; - - _test_unbase64mem("", ""); - _test_unbase64mem(" ", ""); - _test_unbase64mem(" Y Q == ", "a"); - _test_unbase64mem(" Y WJjZGV mZ 2g = ", "abcdefgh"); - _test_unbase64mem_inval(" Y %WJjZGV mZ 2g = "); - _test_unbase64mem_inval(" Y %WJjZGV mZ 2g = a"); - _test_unbase64mem("YQ==", "a"); - _test_unbase64mem_inval("YQ==a"); - - rnd_len = nmtst_get_rand_uint32() % sizeof(rnd_buf); - for (i = 0; i < rnd_len; i++) - rnd_buf[i] = nmtst_get_rand_uint32() % 256; - rnd_base64 = g_base64_encode(rnd_buf, rnd_len); - _test_unbase64mem_mem(rnd_base64, rnd_buf, rnd_len); - - _test_unbase64char('=', FALSE); - for (i = 0; i < 10; i++) { - char ch = nmtst_get_rand_uint32() % 256; - - if (ch != '=') - _test_unbase64char(ch, TRUE); - } -} - -/*****************************************************************************/ - NMTST_DEFINE(); int @@ -245,7 +143,6 @@ main(int argc, char **argv) g_test_add_func("/systemd/lldp/create", test_lldp_create); g_test_add_func("/systemd/sd-event", test_sd_event); g_test_add_func("/systemd/test_path_equal", test_path_equal); - g_test_add_func("/systemd/test_nm_sd_utils_unbase64mem", test_nm_sd_utils_unbase64mem); return g_test_run(); } diff --git a/src/libnm-systemd-shared/nm-sd-utils-shared.c b/src/libnm-systemd-shared/nm-sd-utils-shared.c index 0a35016a40..0e50432c40 100644 --- a/src/libnm-systemd-shared/nm-sd-utils-shared.c +++ b/src/libnm-systemd-shared/nm-sd-utils-shared.c @@ -41,38 +41,6 @@ nm_sd_utils_path_startswith(const char *path, const char *prefix) /*****************************************************************************/ -int -nm_sd_utils_unbase64char(char ch, gboolean accept_padding_equal) -{ - if (ch == '=' && accept_padding_equal) - return G_MAXINT; - return unbase64char(ch); -} - -/** - * nm_sd_utils_unbase64mem: - * @p: a valid base64 string. Whitespace is ignored, but invalid encodings - * will cause the function to fail. - * @l: the length of @p. @p is not treated as NUL terminated string but - * merely as a buffer of ascii characters. - * @secure: whether the temporary memory will be cleared to avoid leaving - * secrets in memory (see also nm_explicit_bzero()). - * @mem: (transfer full): the decoded buffer on success. - * @len: the length of @mem on success. - * - * glib provides g_base64_decode(), but that does not report any errors - * from invalid encodings. Expose systemd's implementation which does - * reject invalid inputs. - * - * Returns: a non-negative code on success. Invalid encoding let the - * function fail. - */ -int -nm_sd_utils_unbase64mem(const char *p, size_t l, gboolean secure, guint8 **mem, size_t *len) -{ - return unbase64mem_full(p, l, secure, (void **) mem, len); -} - int nm_sd_dns_name_to_wire_format(const char *domain, guint8 *buffer, size_t len, gboolean canonical) { diff --git a/src/libnm-systemd-shared/nm-sd-utils-shared.h b/src/libnm-systemd-shared/nm-sd-utils-shared.h index 82a5067863..c09c889394 100644 --- a/src/libnm-systemd-shared/nm-sd-utils-shared.h +++ b/src/libnm-systemd-shared/nm-sd-utils-shared.h @@ -16,12 +16,6 @@ const char *nm_sd_utils_path_startswith(const char *path, const char *prefix); /*****************************************************************************/ -int nm_sd_utils_unbase64char(char ch, gboolean accept_padding_equal); - -int nm_sd_utils_unbase64mem(const char *p, size_t l, gboolean secure, guint8 **mem, size_t *len); - -/*****************************************************************************/ - int nm_sd_dns_name_to_wire_format(const char *domain, guint8 *buffer, size_t len, gboolean canonical); -- cgit v1.2.1 From b5f3d88e6fdd42424aaa96ff91c0588b721d3915 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 12 Apr 2022 15:32:38 +0200 Subject: glib-aux: add path-utils from systemd We use these functions, currently from our systemd fork. One day we want to stop importing systemd code, so we need them ourselves. Copy them, and adjust for NM style. --- src/libnm-glib-aux/nm-shared-utils.c | 242 +++++++++++++++++++ src/libnm-glib-aux/nm-shared-utils.h | 33 +++ src/libnm-glib-aux/tests/test-shared-general.c | 310 +++++++++++++++++++++++++ 3 files changed, 585 insertions(+) diff --git a/src/libnm-glib-aux/nm-shared-utils.c b/src/libnm-glib-aux/nm-shared-utils.c index 310609d2ac..9446cce80c 100644 --- a/src/libnm-glib-aux/nm-shared-utils.c +++ b/src/libnm-glib-aux/nm-shared-utils.c @@ -6933,3 +6933,245 @@ on_failure: nm_explicit_bzero(buf, len); return r; } + +/*****************************************************************************/ + +static const char * +skip_slash_or_dot(const char *p) +{ + for (; !nm_str_is_empty(p);) { + if (p[0] == '/') { + p += 1; + continue; + } + if (p[0] == '.' && p[1] == '/') { + p += 2; + continue; + } + break; + } + return p; +} + +int +nm_path_find_first_component(const char **p, gboolean accept_dot_dot, const char **ret) +{ + const char *q, *first, *end_first, *next; + size_t len; + + /* Copied from systemd's path_compare() + * https://github.com/systemd/systemd/blob/bc85f8b51d962597360e982811e674c126850f56/src/basic/path-util.c#L809 */ + + nm_assert(p); + + /* When a path is input, then returns the pointer to the first component and its length, and + * move the input pointer to the next component or nul. This skips both over any '/' + * immediately *before* and *after* the first component before returning. + * + * Examples + * Input: p: "//.//aaa///bbbbb/cc" + * Output: p: "bbbbb///cc" + * ret: "aaa///bbbbb/cc" + * return value: 3 (== strlen("aaa")) + * + * Input: p: "aaa//" + * Output: p: (pointer to NUL) + * ret: "aaa//" + * return value: 3 (== strlen("aaa")) + * + * Input: p: "/", ".", "" + * Output: p: (pointer to NUL) + * ret: NULL + * return value: 0 + * + * Input: p: NULL + * Output: p: NULL + * ret: NULL + * return value: 0 + * + * Input: p: "(too long component)" + * Output: return value: -EINVAL + * + * (when accept_dot_dot is false) + * Input: p: "//..//aaa///bbbbb/cc" + * Output: return value: -EINVAL + */ + + q = *p; + + first = skip_slash_or_dot(q); + if (nm_str_is_empty(first)) { + *p = first; + if (ret) + *ret = NULL; + return 0; + } + if (nm_streq(first, ".")) { + *p = first + 1; + if (ret) + *ret = NULL; + return 0; + } + + end_first = strchrnul(first, '/'); + len = end_first - first; + + if (len > NAME_MAX) + return -EINVAL; + if (!accept_dot_dot && len == 2 && first[0] == '.' && first[1] == '.') + return -EINVAL; + + next = skip_slash_or_dot(end_first); + + *p = next + (nm_streq(next, ".") ? 1 : 0); + if (ret) + *ret = first; + return len; +} + +int +nm_path_compare(const char *a, const char *b) +{ + /* Copied from systemd's path_compare() + * https://github.com/systemd/systemd/blob/bc85f8b51d962597360e982811e674c126850f56/src/basic/path-util.c#L415 */ + + /* Order NULL before non-NULL */ + NM_CMP_SELF(a, b); + + /* A relative path and an absolute path must not compare as equal. + * Which one is sorted before the other does not really matter. + * Here a relative path is ordered before an absolute path. */ + NM_CMP_DIRECT(nm_path_is_absolute(a), nm_path_is_absolute(b)); + + for (;;) { + const char *aa, *bb; + int j, k; + + j = nm_path_find_first_component(&a, TRUE, &aa); + k = nm_path_find_first_component(&b, TRUE, &bb); + + if (j < 0 || k < 0) { + /* When one of paths is invalid, order invalid path after valid one. */ + NM_CMP_DIRECT(j < 0, k < 0); + + /* fallback to use strcmp() if both paths are invalid. */ + NM_CMP_DIRECT_STRCMP(a, b); + return 0; + } + + /* Order prefixes first: "/foo" before "/foo/bar" */ + if (j == 0) { + if (k == 0) + return 0; + return -1; + } + if (k == 0) + return 1; + + /* Alphabetical sort: "/foo/aaa" before "/foo/b" */ + NM_CMP_DIRECT_MEMCMP(aa, bb, NM_MIN(j, k)); + + /* Sort "/foo/a" before "/foo/aaa" */ + NM_CMP_DIRECT(j, k); + } +} + +char * +nm_path_startswith_full(const char *path, const char *prefix, gboolean accept_dot_dot) +{ + /* Copied from systemd's path_startswith_full() + * https://github.com/systemd/systemd/blob/bc85f8b51d962597360e982811e674c126850f56/src/basic/path-util.c#L375 */ + + nm_assert(path); + nm_assert(prefix); + + /* Returns a pointer to the start of the first component after the parts matched by + * the prefix, iff + * - both paths are absolute or both paths are relative, + * and + * - each component in prefix in turn matches a component in path at the same position. + * An empty string will be returned when the prefix and path are equivalent. + * + * Returns NULL otherwise. + */ + + if ((path[0] == '/') != (prefix[0] == '/')) + return NULL; + + for (;;) { + const char *p, *q; + int r, k; + + r = nm_path_find_first_component(&path, accept_dot_dot, &p); + if (r < 0) + return NULL; + + k = nm_path_find_first_component(&prefix, accept_dot_dot, &q); + if (k < 0) + return NULL; + + if (k == 0) + return (char *) (p ?: path); + + if (r != k) + return NULL; + + if (strncmp(p, q, r) != 0) + return NULL; + } +} + +char * +nm_path_simplify(char *path) +{ + bool add_slash = false; + char *f = path; + int r; + + /* Copied from systemd's path_simplify() + * https://github.com/systemd/systemd/blob/bc85f8b51d962597360e982811e674c126850f56/src/basic/path-util.c#L325 */ + + nm_assert(path); + + /* Removes redundant inner and trailing slashes. Also removes unnecessary dots. + * Modifies the passed string in-place. + * + * ///foo//./bar/. becomes /foo/bar + * .//./foo//./bar/. becomes foo/bar + */ + + if (path[0] == '\0') + return path; + + if (nm_path_is_absolute(path)) + f++; + + for (const char *p = f;;) { + const char *e; + + r = nm_path_find_first_component(&p, TRUE, &e); + if (r == 0) + break; + + if (add_slash) + *f++ = '/'; + + if (r < 0) { + /* if path is invalid, then refuse to simplify remaining part. */ + memmove(f, p, strlen(p) + 1); + return path; + } + + memmove(f, e, r); + f += r; + + add_slash = TRUE; + } + + /* Special rule, if we stripped everything, we need a "." for the current directory. */ + if (f == path) + *f++ = '.'; + + *f = '\0'; + return path; +} diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h index 959a2b5d24..34936e0a36 100644 --- a/src/libnm-glib-aux/nm-shared-utils.h +++ b/src/libnm-glib-aux/nm-shared-utils.h @@ -3313,4 +3313,37 @@ void nm_utils_thread_local_register_destroy(gpointer tls_data, GDestroyNotify de int nm_unbase64char(char c); int nm_unbase64mem_full(const char *p, gsize l, gboolean secure, guint8 **ret, gsize *ret_size); +/*****************************************************************************/ + +static inline gboolean +nm_path_is_absolute(const char *p) +{ + /* Copied from systemd's path_is_absolute() + * https://github.com/systemd/systemd/blob/bc85f8b51d962597360e982811e674c126850f56/src/basic/path-util.h#L50 */ + + nm_assert(p); + return p[0] == '/'; +} + +int nm_path_find_first_component(const char **p, gboolean accept_dot_dot, const char **ret); + +int nm_path_compare(const char *a, const char *b); + +static inline gboolean +nm_path_equal(const char *a, const char *b) +{ + return nm_path_compare(a, b) == 0; +} + +char *nm_path_simplify(char *path); + +char * +nm_path_startswith_full(const char *path, const char *prefix, gboolean accept_dot_dot) _nm_pure; + +static inline char * +nm_path_startswith(const char *path, const char *prefix) +{ + return nm_path_startswith_full(path, prefix, TRUE); +} + #endif /* __NM_SHARED_UTILS_H__ */ diff --git a/src/libnm-glib-aux/tests/test-shared-general.c b/src/libnm-glib-aux/tests/test-shared-general.c index 783b771a0d..c3fa2559ed 100644 --- a/src/libnm-glib-aux/tests/test-shared-general.c +++ b/src/libnm-glib-aux/tests/test-shared-general.c @@ -1796,6 +1796,311 @@ test_unbase64mem3(void) /*****************************************************************************/ +static void +assert_path_compare(const char *a, const char *b, int expected) +{ + int r; + + g_assert(NM_IN_SET(expected, -1, 0, 1)); + + g_assert_cmpint(nm_path_compare(a, a), ==, 0); + g_assert_cmpint(nm_path_compare(b, b), ==, 0); + + r = nm_path_compare(a, b); + g_assert_cmpint(r, ==, expected); + r = nm_path_compare(b, a); + g_assert_cmpint(r, ==, -expected); + + g_assert(nm_path_equal(a, a) == 1); + g_assert(nm_path_equal(b, b) == 1); + g_assert(nm_path_equal(a, b) == (expected == 0)); + g_assert(nm_path_equal(b, a) == (expected == 0)); +} + +static void +test_path_compare(void) +{ + /* Copied from systemd. + * https://github.com/systemd/systemd/blob/bc85f8b51d962597360e982811e674c126850f56/src/test/test-path-util.c#L126 */ + + assert_path_compare("/goo", "/goo", 0); + assert_path_compare("/goo", "/goo", 0); + assert_path_compare("//goo", "/goo", 0); + assert_path_compare("//goo/////", "/goo", 0); + assert_path_compare("goo/////", "goo", 0); + assert_path_compare("/goo/boo", "/goo//boo", 0); + assert_path_compare("//goo/boo", "/goo/boo//", 0); + assert_path_compare("//goo/././//./boo//././//", "/goo/boo//.", 0); + assert_path_compare("/.", "//.///", 0); + assert_path_compare("/x", "x/", 1); + assert_path_compare("x/", "/", -1); + assert_path_compare("/x/./y", "x/y", 1); + assert_path_compare("/x/./y", "/x/y", 0); + assert_path_compare("/x/./././y", "/x/y/././.", 0); + assert_path_compare("./x/./././y", "./x/y/././.", 0); + assert_path_compare(".", "./.", 0); + assert_path_compare(".", "././.", 0); + assert_path_compare("./..", ".", 1); + assert_path_compare("x/.y", "x/y", -1); + assert_path_compare("foo", "/foo", -1); + assert_path_compare("/foo", "/foo/bar", -1); + assert_path_compare("/foo/aaa", "/foo/b", -1); + assert_path_compare("/foo/aaa", "/foo/b/a", -1); + assert_path_compare("/foo/a", "/foo/aaa", -1); + assert_path_compare("/foo/a/b", "/foo/aaa", -1); +} + +/*****************************************************************************/ + +static void +test_path_equal(void) +{ +#define _path_equal_check(path, expected) \ + G_STMT_START \ + { \ + const char *_path0 = (path); \ + const char *_expected = (expected); \ + gs_free char *_path = g_strdup(_path0); \ + const char *_path_result; \ + \ + _path_result = nm_path_simplify(_path); \ + g_assert(_path_result == _path); \ + g_assert_cmpstr(_path, ==, _expected); \ + } \ + G_STMT_END + + _path_equal_check("", ""); + _path_equal_check(".", "."); + _path_equal_check("..", ".."); + _path_equal_check("/..", "/.."); + _path_equal_check("//..", "/.."); + _path_equal_check("/.", "/"); + _path_equal_check("./", "."); + _path_equal_check("./.", "."); + _path_equal_check(".///.", "."); + _path_equal_check(".///./", "."); + _path_equal_check(".////", "."); + _path_equal_check("//..//foo/", "/../foo"); + _path_equal_check("///foo//./bar/.", "/foo/bar"); + _path_equal_check(".//./foo//./bar/.", "foo/bar"); +} + +/*****************************************************************************/ + +static void +assert_path_find_first_component(const char *path, + gboolean accept_dot_dot, + const char *const *expected, + int ret) +{ + const char *p; + + for (p = path;;) { + const char *e; + int r; + + r = nm_path_find_first_component(&p, accept_dot_dot, &e); + if (r <= 0) { + if (r == 0) { + if (path) + g_assert(p == path + strlen(path)); + else + g_assert(!p); + g_assert(!e); + } + g_assert(r == ret); + g_assert(!expected || !*expected); + return; + } + + g_assert(e); + g_assert(strcspn(e, "/") == (size_t) r); + g_assert(strlen(*expected) == (size_t) r); + g_assert(strncmp(e, *expected++, r) == 0); + } +} + +static void +test_path_find_first_component(void) +{ + gs_free char *hoge = NULL; + char foo[NAME_MAX * 2]; + + /* Copied from systemd. + * https://github.com/systemd/systemd/blob/bc85f8b51d962597360e982811e674c126850f56/src/test/test-path-util.c#L631 */ + + assert_path_find_first_component(NULL, false, NULL, 0); + assert_path_find_first_component("", false, NULL, 0); + assert_path_find_first_component("/", false, NULL, 0); + assert_path_find_first_component(".", false, NULL, 0); + assert_path_find_first_component("./", false, NULL, 0); + assert_path_find_first_component("./.", false, NULL, 0); + assert_path_find_first_component("..", false, NULL, -EINVAL); + assert_path_find_first_component("/..", false, NULL, -EINVAL); + assert_path_find_first_component("./..", false, NULL, -EINVAL); + assert_path_find_first_component("////./././//.", false, NULL, 0); + assert_path_find_first_component("a/b/c", false, NM_MAKE_STRV("a", "b", "c"), 0); + assert_path_find_first_component("././//.///aa/bbb//./ccc", + false, + NM_MAKE_STRV("aa", "bbb", "ccc"), + 0); + assert_path_find_first_component("././//.///aa/.../../bbb//./ccc/.", + false, + NM_MAKE_STRV("aa", "..."), + -EINVAL); + assert_path_find_first_component("//./aaa///.//./.bbb/..///c.//d.dd///..eeee/.", + false, + NM_MAKE_STRV("aaa", ".bbb"), + -EINVAL); + assert_path_find_first_component("a/foo./b", false, NM_MAKE_STRV("a", "foo.", "b"), 0); + + assert_path_find_first_component(NULL, true, NULL, 0); + assert_path_find_first_component("", true, NULL, 0); + assert_path_find_first_component("/", true, NULL, 0); + assert_path_find_first_component(".", true, NULL, 0); + assert_path_find_first_component("./", true, NULL, 0); + assert_path_find_first_component("./.", true, NULL, 0); + assert_path_find_first_component("..", true, NM_MAKE_STRV(".."), 0); + assert_path_find_first_component("/..", true, NM_MAKE_STRV(".."), 0); + assert_path_find_first_component("./..", true, NM_MAKE_STRV(".."), 0); + assert_path_find_first_component("////./././//.", true, NULL, 0); + assert_path_find_first_component("a/b/c", true, NM_MAKE_STRV("a", "b", "c"), 0); + assert_path_find_first_component("././//.///aa/bbb//./ccc", + true, + NM_MAKE_STRV("aa", "bbb", "ccc"), + 0); + assert_path_find_first_component("././//.///aa/.../../bbb//./ccc/.", + true, + NM_MAKE_STRV("aa", "...", "..", "bbb", "ccc"), + 0); + assert_path_find_first_component("//./aaa///.//./.bbb/..///c.//d.dd///..eeee/.", + true, + NM_MAKE_STRV("aaa", ".bbb", "..", "c.", "d.dd", "..eeee"), + 0); + assert_path_find_first_component("a/foo./b", true, NM_MAKE_STRV("a", "foo.", "b"), 0); + + memset(foo, 'a', sizeof(foo) - 1); + foo[sizeof(foo) - 1] = '\0'; + + assert_path_find_first_component(foo, false, NULL, -EINVAL); + assert_path_find_first_component(foo, true, NULL, -EINVAL); + + hoge = g_strjoin("", "a/b/c/", foo, "//d/e/.//f/", NULL); + g_assert(hoge); + + assert_path_find_first_component(hoge, false, NM_MAKE_STRV("a", "b", "c"), -EINVAL); + assert_path_find_first_component(hoge, true, NM_MAKE_STRV("a", "b", "c"), -EINVAL); +} + +/*****************************************************************************/ + +static void +assert_path_startswith(const char *path, + const char *prefix, + const char *skipped, + const char *expected) +{ + const char *p; + + p = nm_path_startswith(path, prefix); + g_assert_cmpstr(p, ==, expected); + if (p) { + gs_free char *q = NULL; + + g_assert(skipped); + q = g_strjoin("", skipped, p, NULL); + g_assert_cmpstr(q, ==, path); + g_assert(p == path + strlen(skipped)); + } else + g_assert(!skipped); +} + +static void +test_path_startswith(void) +{ + assert_path_startswith("/foo/bar/barfoo/", "/foo", "/foo/", "bar/barfoo/"); + assert_path_startswith("/foo/bar/barfoo/", "/foo/", "/foo/", "bar/barfoo/"); + assert_path_startswith("/foo/bar/barfoo/", "/", "/", "foo/bar/barfoo/"); + assert_path_startswith("/foo/bar/barfoo/", "////", "/", "foo/bar/barfoo/"); + assert_path_startswith("/foo/bar/barfoo/", "/foo//bar/////barfoo///", "/foo/bar/barfoo/", ""); + assert_path_startswith("/foo/bar/barfoo/", "/foo/bar/barfoo////", "/foo/bar/barfoo/", ""); + assert_path_startswith("/foo/bar/barfoo/", "/foo/bar///barfoo/", "/foo/bar/barfoo/", ""); + assert_path_startswith("/foo/bar/barfoo/", "/foo////bar/barfoo/", "/foo/bar/barfoo/", ""); + assert_path_startswith("/foo/bar/barfoo/", "////foo/bar/barfoo/", "/foo/bar/barfoo/", ""); + assert_path_startswith("/foo/bar/barfoo/", "/foo/bar/barfoo", "/foo/bar/barfoo/", ""); + + assert_path_startswith("/foo/bar/barfoo/", "/foo/bar/barfooa/", NULL, NULL); + assert_path_startswith("/foo/bar/barfoo/", "/foo/bar/barfooa", NULL, NULL); + assert_path_startswith("/foo/bar/barfoo/", "", NULL, NULL); + assert_path_startswith("/foo/bar/barfoo/", "/bar/foo", NULL, NULL); + assert_path_startswith("/foo/bar/barfoo/", "/f/b/b/", NULL, NULL); + assert_path_startswith("/foo/bar/barfoo/", "/foo/bar/barfo", NULL, NULL); + assert_path_startswith("/foo/bar/barfoo/", "/foo/bar/bar", NULL, NULL); + assert_path_startswith("/foo/bar/barfoo/", "/fo", NULL, NULL); +} + +/*****************************************************************************/ + +static void +assert_path_simplify(const char *in, const char *out) +{ + gs_free char *p = NULL; + + g_assert(in); + p = g_strdup(in); + nm_path_simplify(p); + g_assert_cmpstr(p, ==, out); +} + +static void +test_path_simplify(void) +{ + gs_free char *hoge = NULL; + gs_free char *hoge_out = NULL; + char foo[NAME_MAX * 2]; + + assert_path_simplify("", ""); + assert_path_simplify("aaa/bbb////ccc", "aaa/bbb/ccc"); + assert_path_simplify("//aaa/.////ccc", "/aaa/ccc"); + assert_path_simplify("///", "/"); + assert_path_simplify("///.//", "/"); + assert_path_simplify("///.//.///", "/"); + assert_path_simplify("////.././///../.", "/../.."); + assert_path_simplify(".", "."); + assert_path_simplify("./", "."); + assert_path_simplify(".///.//./.", "."); + assert_path_simplify(".///.//././/", "."); + assert_path_simplify("//./aaa///.//./.bbb/..///c.//d.dd///..eeee/.", + "/aaa/.bbb/../c./d.dd/..eeee"); + assert_path_simplify("//./aaa///.//./.bbb/..///c.//d.dd///..eeee/..", + "/aaa/.bbb/../c./d.dd/..eeee/.."); + assert_path_simplify(".//./aaa///.//./.bbb/..///c.//d.dd///..eeee/..", + "aaa/.bbb/../c./d.dd/..eeee/.."); + assert_path_simplify("..//./aaa///.//./.bbb/..///c.//d.dd///..eeee/..", + "../aaa/.bbb/../c./d.dd/..eeee/.."); + + memset(foo, 'a', sizeof(foo) - 1); + foo[sizeof(foo) - 1] = '\0'; + + assert_path_simplify(foo, foo); + + hoge = g_strjoin("", "/", foo, NULL); + g_assert(hoge); + assert_path_simplify(hoge, hoge); + nm_clear_g_free(&hoge); + + hoge = + g_strjoin("", "a////.//././//./b///././/./c/////././//./", foo, "//.//////d/e/.//f/", NULL); + g_assert(hoge); + + hoge_out = g_strjoin("", "a/b/c/", foo, "//.//////d/e/.//f/", NULL); + g_assert(hoge_out); + + assert_path_simplify(hoge, hoge_out); +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -1834,6 +2139,11 @@ main(int argc, char **argv) g_test_add_func("/general/test_unbase64mem1", test_unbase64mem1); g_test_add_func("/general/test_unbase64mem2", test_unbase64mem2); g_test_add_func("/general/test_unbase64mem3", test_unbase64mem3); + g_test_add_func("/general/test_path_compare", test_path_compare); + g_test_add_func("/general/test_path_equal", test_path_equal); + g_test_add_func("/general/test_path_find_first_component", test_path_find_first_component); + g_test_add_func("/general/test_path_startswith", test_path_startswith); + g_test_add_func("/general/test_path_simplify", test_path_simplify); return g_test_run(); } -- cgit v1.2.1 From f4c7b5b7b75e6619b069be6d566c848b9429a492 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 12 Apr 2022 16:26:47 +0200 Subject: all: avoid using systemd path utils --- src/core/NetworkManagerUtils.c | 3 +-- src/core/settings/plugins/keyfile/nms-keyfile-plugin.c | 10 ++++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/core/NetworkManagerUtils.c b/src/core/NetworkManagerUtils.c index 5727aac814..56f551060c 100644 --- a/src/core/NetworkManagerUtils.c +++ b/src/core/NetworkManagerUtils.c @@ -30,7 +30,6 @@ #include "libnm-platform/nm-linux-platform.h" #include "libnm-platform/nm-platform-utils.h" #include "nm-auth-utils.h" -#include "libnm-systemd-shared/nm-sd-utils-shared.h" /*****************************************************************************/ @@ -1155,7 +1154,7 @@ nm_utils_file_is_in_path(const char *abs_filename, const char *abs_path) g_return_val_if_fail(abs_filename && abs_filename[0] == '/', NULL); g_return_val_if_fail(abs_path && abs_path[0] == '/', NULL); - path = nm_sd_utils_path_startswith(abs_filename, abs_path); + path = nm_path_startswith(abs_filename, abs_path); if (!path) return NULL; diff --git a/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c index efb9bfce4f..1d7de8d24b 100644 --- a/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c +++ b/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c @@ -26,8 +26,6 @@ #include "libnm-core-intern/nm-core-internal.h" #include "libnm-core-intern/nm-keyfile-internal.h" -#include "libnm-systemd-shared/nm-sd-utils-shared.h" - #include "settings/nm-settings-plugin.h" #include "settings/nm-settings-storage.h" #include "settings/nm-settings-utils.h" @@ -1247,9 +1245,9 @@ nms_keyfile_plugin_init(NMSKeyfilePlugin *plugin) /* dirname_libs are a set of read-only directories with lower priority than /etc or /run. * There is nothing complicated about having multiple of such directories, so dirname_libs * is a list (which currently only has at most one directory). */ - priv->dirname_libs[0] = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_LIB)); + priv->dirname_libs[0] = nm_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_LIB)); priv->dirname_libs[1] = NULL; - priv->dirname_run = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_RUN)); + priv->dirname_run = nm_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_RUN)); priv->dirname_etc = nm_config_data_get_value(NM_CONFIG_GET_DATA_ORIG, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH, @@ -1262,9 +1260,9 @@ nms_keyfile_plugin_init(NMSKeyfilePlugin *plugin) } else if (!priv->dirname_etc || priv->dirname_etc[0] != '/') { /* either invalid path or unspecified. Use the default. */ g_free(priv->dirname_etc); - priv->dirname_etc = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_ETC_DEFAULT)); + priv->dirname_etc = nm_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_ETC_DEFAULT)); } else - nm_sd_utils_path_simplify(priv->dirname_etc); + nm_path_simplify(priv->dirname_etc); /* no duplicates */ if (NM_IN_STRSET(priv->dirname_libs[0], priv->dirname_etc, priv->dirname_run)) -- cgit v1.2.1 From 202d9c36c38eca098bca07483fc0d043e33110fd Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 12 Apr 2022 16:24:24 +0200 Subject: systemd: drop systemd path helpers from "nm-sd-utils-shared.h" adapter header They are now unused, and replaced by nm_path*() utils in glib-aux (which are forks of the systemd code). --- src/core/tests/test-systemd.c | 36 --------------------------- src/libnm-systemd-shared/nm-sd-utils-shared.c | 20 --------------- src/libnm-systemd-shared/nm-sd-utils-shared.h | 8 ------ 3 files changed, 64 deletions(-) diff --git a/src/core/tests/test-systemd.c b/src/core/tests/test-systemd.c index ed0bdb4ff8..cf31ec33cc 100644 --- a/src/core/tests/test-systemd.c +++ b/src/core/tests/test-systemd.c @@ -98,41 +98,6 @@ test_sd_event(void) /*****************************************************************************/ -static void -test_path_equal(void) -{ -#define _path_equal_check(path, expected) \ - G_STMT_START \ - { \ - const char *_path0 = (path); \ - const char *_expected = (expected); \ - gs_free char *_path = g_strdup(_path0); \ - const char *_path_result; \ - \ - _path_result = nm_sd_utils_path_simplify(_path); \ - g_assert(_path_result == _path); \ - g_assert_cmpstr(_path, ==, _expected); \ - } \ - G_STMT_END - - _path_equal_check("", ""); - _path_equal_check(".", "."); - _path_equal_check("..", ".."); - _path_equal_check("/..", "/.."); - _path_equal_check("//..", "/.."); - _path_equal_check("/.", "/"); - _path_equal_check("./", "."); - _path_equal_check("./.", "."); - _path_equal_check(".///.", "."); - _path_equal_check(".///./", "."); - _path_equal_check(".////", "."); - _path_equal_check("//..//foo/", "/../foo"); - _path_equal_check("///foo//./bar/.", "/foo/bar"); - _path_equal_check(".//./foo//./bar/.", "foo/bar"); -} - -/*****************************************************************************/ - NMTST_DEFINE(); int @@ -142,7 +107,6 @@ main(int argc, char **argv) g_test_add_func("/systemd/lldp/create", test_lldp_create); g_test_add_func("/systemd/sd-event", test_sd_event); - g_test_add_func("/systemd/test_path_equal", test_path_equal); return g_test_run(); } diff --git a/src/libnm-systemd-shared/nm-sd-utils-shared.c b/src/libnm-systemd-shared/nm-sd-utils-shared.c index 0e50432c40..c1c71c593d 100644 --- a/src/libnm-systemd-shared/nm-sd-utils-shared.c +++ b/src/libnm-systemd-shared/nm-sd-utils-shared.c @@ -21,26 +21,6 @@ const bool mempool_use_allowed = true; /*****************************************************************************/ -gboolean -nm_sd_utils_path_equal(const char *a, const char *b) -{ - return path_equal(a, b); -} - -char * -nm_sd_utils_path_simplify(char *path) -{ - return path_simplify(path); -} - -const char * -nm_sd_utils_path_startswith(const char *path, const char *prefix) -{ - return path_startswith(path, prefix); -} - -/*****************************************************************************/ - int nm_sd_dns_name_to_wire_format(const char *domain, guint8 *buffer, size_t len, gboolean canonical) { diff --git a/src/libnm-systemd-shared/nm-sd-utils-shared.h b/src/libnm-systemd-shared/nm-sd-utils-shared.h index c09c889394..731a49e20e 100644 --- a/src/libnm-systemd-shared/nm-sd-utils-shared.h +++ b/src/libnm-systemd-shared/nm-sd-utils-shared.h @@ -8,14 +8,6 @@ /*****************************************************************************/ -gboolean nm_sd_utils_path_equal(const char *a, const char *b); - -char *nm_sd_utils_path_simplify(char *path); - -const char *nm_sd_utils_path_startswith(const char *path, const char *prefix); - -/*****************************************************************************/ - int nm_sd_dns_name_to_wire_format(const char *domain, guint8 *buffer, size_t len, gboolean canonical); -- cgit v1.2.1 From 9ff1f666809ad5b773eec826cd07735562e720bf Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 12 Apr 2022 17:16:11 +0200 Subject: glib-aux: add nm_hostname_is_valid() helper from systemd --- src/libnm-glib-aux/nm-shared-utils.c | 76 ++++++++++++++++++++++++++ src/libnm-glib-aux/nm-shared-utils.h | 4 ++ src/libnm-glib-aux/tests/test-shared-general.c | 50 +++++++++++++++++ 3 files changed, 130 insertions(+) diff --git a/src/libnm-glib-aux/nm-shared-utils.c b/src/libnm-glib-aux/nm-shared-utils.c index 9446cce80c..0756bad45b 100644 --- a/src/libnm-glib-aux/nm-shared-utils.c +++ b/src/libnm-glib-aux/nm-shared-utils.c @@ -7175,3 +7175,79 @@ nm_path_simplify(char *path) *f = '\0'; return path; } + +/*****************************************************************************/ + +static gboolean +valid_ldh_char(char c) +{ + /* "LDH" → "Letters, digits, hyphens", as per RFC 5890, Section 2.3.1 */ + + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-'; +} + +/** + * nm_hostname_is_valid: + * @s: the hostname to check. + * @trailing_dot: Accept trailing dot on multi-label names. + * + * Return: %TRUE if valid. + */ +gboolean +nm_hostname_is_valid(const char *s, gboolean trailing_dot) +{ + unsigned n_dots = 0; + const char *p; + gboolean dot; + gboolean hyphen; + + /* Copied from systemd's hostname_is_valid() + * https://github.com/systemd/systemd/blob/bc85f8b51d962597360e982811e674c126850f56/src/basic/hostname-util.c#L85 */ + + /* Check if s looks like a valid hostname or FQDN. This does not do full DNS validation, but only + * checks if the name is composed of allowed characters and the length is not above the maximum + * allowed by Linux (c.f. dns_name_is_valid()). A trailing dot is allowed if + * VALID_HOSTNAME_TRAILING_DOT flag is set and at least two components are present in the name. Note + * that due to the restricted charset and length this call is substantially more conservative than + * dns_name_is_valid(). Doesn't accept empty hostnames, hostnames with leading dots, and hostnames + * with multiple dots in a sequence. Doesn't allow hyphens at the beginning or end of label. */ + + if (nm_str_is_empty(s)) + return FALSE; + + for (p = s, dot = hyphen = TRUE; *p; p++) + if (*p == '.') { + if (dot || hyphen) + return FALSE; + + dot = TRUE; + hyphen = FALSE; + n_dots++; + + } else if (*p == '-') { + if (dot) + return FALSE; + + dot = FALSE; + hyphen = TRUE; + + } else { + if (!valid_ldh_char(*p)) + return FALSE; + + dot = FALSE; + hyphen = FALSE; + } + + if (dot && (n_dots < 2 || !trailing_dot)) + return FALSE; + if (hyphen) + return FALSE; + + /* Note that HOST_NAME_MAX is 64 on Linux, but DNS allows domain names up to + * 255 characters */ + if (p - s > HOST_NAME_MAX) + return FALSE; + + return TRUE; +} diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h index 34936e0a36..14b6302662 100644 --- a/src/libnm-glib-aux/nm-shared-utils.h +++ b/src/libnm-glib-aux/nm-shared-utils.h @@ -3346,4 +3346,8 @@ nm_path_startswith(const char *path, const char *prefix) return nm_path_startswith_full(path, prefix, TRUE); } +/*****************************************************************************/ + +gboolean nm_hostname_is_valid(const char *s, gboolean trailing_dot); + #endif /* __NM_SHARED_UTILS_H__ */ diff --git a/src/libnm-glib-aux/tests/test-shared-general.c b/src/libnm-glib-aux/tests/test-shared-general.c index c3fa2559ed..0d588f17b7 100644 --- a/src/libnm-glib-aux/tests/test-shared-general.c +++ b/src/libnm-glib-aux/tests/test-shared-general.c @@ -2101,6 +2101,55 @@ test_path_simplify(void) /*****************************************************************************/ +static void +test_hostname_is_valid(void) +{ + g_assert(nm_hostname_is_valid("foobar", FALSE)); + g_assert(nm_hostname_is_valid("foobar.com", FALSE)); + g_assert(!nm_hostname_is_valid("foobar.com.", FALSE)); + g_assert(nm_hostname_is_valid("fooBAR", FALSE)); + g_assert(nm_hostname_is_valid("fooBAR.com", FALSE)); + g_assert(!nm_hostname_is_valid("fooBAR.", FALSE)); + g_assert(!nm_hostname_is_valid("fooBAR.com.", FALSE)); + g_assert(!nm_hostname_is_valid("fööbar", FALSE)); + g_assert(!nm_hostname_is_valid("", FALSE)); + g_assert(!nm_hostname_is_valid(".", FALSE)); + g_assert(!nm_hostname_is_valid("..", FALSE)); + g_assert(!nm_hostname_is_valid("foobar.", FALSE)); + g_assert(!nm_hostname_is_valid(".foobar", FALSE)); + g_assert(!nm_hostname_is_valid("foo..bar", FALSE)); + g_assert(!nm_hostname_is_valid("foo.bar..", FALSE)); + g_assert( + !nm_hostname_is_valid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + FALSE)); + g_assert(!nm_hostname_is_valid( + "au-xph5-rvgrdsb5hcxc-47et3a5vvkrc-server-wyoz4elpdpe3.openstack.local", + FALSE)); + + g_assert(nm_hostname_is_valid("foobar", TRUE)); + g_assert(nm_hostname_is_valid("foobar.com", TRUE)); + g_assert(nm_hostname_is_valid("foobar.com.", TRUE)); + g_assert(nm_hostname_is_valid("fooBAR", TRUE)); + g_assert(nm_hostname_is_valid("fooBAR.com", TRUE)); + g_assert(!nm_hostname_is_valid("fooBAR.", TRUE)); + g_assert(nm_hostname_is_valid("fooBAR.com.", TRUE)); + g_assert(!nm_hostname_is_valid("fööbar", TRUE)); + g_assert(!nm_hostname_is_valid("", TRUE)); + g_assert(!nm_hostname_is_valid(".", TRUE)); + g_assert(!nm_hostname_is_valid("..", TRUE)); + g_assert(!nm_hostname_is_valid("foobar.", TRUE)); + g_assert(!nm_hostname_is_valid(".foobar", TRUE)); + g_assert(!nm_hostname_is_valid("foo..bar", TRUE)); + g_assert(!nm_hostname_is_valid("foo.bar..", TRUE)); + g_assert( + !nm_hostname_is_valid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + TRUE)); +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -2144,6 +2193,7 @@ main(int argc, char **argv) g_test_add_func("/general/test_path_find_first_component", test_path_find_first_component); g_test_add_func("/general/test_path_startswith", test_path_startswith); g_test_add_func("/general/test_path_simplify", test_path_simplify); + g_test_add_func("/general/test_hostname_is_valid", test_hostname_is_valid); return g_test_run(); } -- cgit v1.2.1 From c4f51119208a424c080b1fbe332f8bcfa88454b5 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 12 Apr 2022 17:25:53 +0200 Subject: all: use nm_hostname_is_valid() instead of systemd code --- src/core/dhcp/nm-dhcp-client.c | 2 +- src/core/dns/nm-dns-manager.c | 3 +-- src/core/nm-core-utils.c | 5 ++--- src/nm-initrd-generator/nmi-cmdline-reader.c | 3 +-- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/core/dhcp/nm-dhcp-client.c b/src/core/dhcp/nm-dhcp-client.c index 2bfd7e01eb..29eeae186a 100644 --- a/src/core/dhcp/nm-dhcp-client.c +++ b/src/core/dhcp/nm-dhcp-client.c @@ -1080,7 +1080,7 @@ config_init(NMDhcpClientConfig *config, const NMDhcpClientConfig *src) if (!config->send_hostname) { nm_clear_g_free((gpointer *) &config->hostname); } else if ((config->use_fqdn && !nm_sd_dns_name_is_valid(config->hostname)) - || (!config->use_fqdn && !nm_sd_hostname_is_valid(config->hostname, FALSE))) { + || (!config->use_fqdn && !nm_hostname_is_valid(config->hostname, FALSE))) { nm_log_warn(LOGD_DHCP, "dhcp%c: %s '%s' is invalid, will be ignored", nm_utils_addr_family_to_char(config->addr_family), diff --git a/src/core/dns/nm-dns-manager.c b/src/core/dns/nm-dns-manager.c index afda300bd2..1e54452aa4 100644 --- a/src/core/dns/nm-dns-manager.c +++ b/src/core/dns/nm-dns-manager.c @@ -26,7 +26,6 @@ #include "libnm-core-intern/nm-core-internal.h" #include "libnm-glib-aux/nm-str-buf.h" -#include "libnm-systemd-shared/nm-sd-utils-shared.h" #include "NetworkManagerUtils.h" #include "devices/nm-device.h" @@ -2104,7 +2103,7 @@ nm_dns_manager_set_hostname(NMDnsManager *self, const char *hostname, gboolean s domain = hostname; } - if (!nm_sd_hostname_is_valid(domain, FALSE)) + if (!nm_hostname_is_valid(domain, FALSE)) domain = NULL; } } diff --git a/src/core/nm-core-utils.c b/src/core/nm-core-utils.c index c8b789b30a..085c806c12 100644 --- a/src/core/nm-core-utils.c +++ b/src/core/nm-core-utils.c @@ -30,7 +30,6 @@ #include "libnm-glib-aux/nm-secret-utils.h" #include "libnm-glib-aux/nm-time-utils.h" #include "libnm-glib-aux/nm-str-buf.h" -#include "libnm-systemd-shared/nm-sd-utils-shared.h" #include "nm-utils.h" #include "libnm-core-intern/nm-core-internal.h" #include "nm-setting-connection.h" @@ -5239,7 +5238,7 @@ nm_utils_shorten_hostname(const char *hostname, char **shortened) nm_assert(hostname); nm_assert(shortened); - if (nm_sd_hostname_is_valid(hostname, FALSE)) { + if (nm_hostname_is_valid(hostname, FALSE)) { *shortened = NULL; return TRUE; } @@ -5253,7 +5252,7 @@ nm_utils_shorten_hostname(const char *hostname, char **shortened) s = g_strndup(hostname, l); - if (!nm_sd_hostname_is_valid(s, FALSE)) { + if (!nm_hostname_is_valid(s, FALSE)) { *shortened = NULL; return FALSE; } diff --git a/src/nm-initrd-generator/nmi-cmdline-reader.c b/src/nm-initrd-generator/nmi-cmdline-reader.c index 412c751067..88760f7574 100644 --- a/src/nm-initrd-generator/nmi-cmdline-reader.c +++ b/src/nm-initrd-generator/nmi-cmdline-reader.c @@ -12,7 +12,6 @@ #include "libnm-log-core/nm-logging.h" #include "libnm-core-intern/nm-core-internal.h" #include "nm-initrd-generator.h" -#include "libnm-systemd-shared/nm-sd-utils-shared.h" /*****************************************************************************/ @@ -586,7 +585,7 @@ reader_parse_ip(Reader *reader, const char *sysfs_dir, char *argument) } } - if (client_hostname && !nm_sd_hostname_is_valid(client_hostname, FALSE)) + if (client_hostname && !nm_hostname_is_valid(client_hostname, FALSE)) client_hostname = NULL; if (client_hostname) { -- cgit v1.2.1 From e6f0c866d919873bc5914501458ff51e50eb287b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 12 Apr 2022 17:21:58 +0200 Subject: systemd: drop unused nm_sd_hostname_is_valid() --- src/libnm-systemd-shared/nm-sd-utils-shared.c | 8 -------- src/libnm-systemd-shared/nm-sd-utils-shared.h | 3 +-- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/libnm-systemd-shared/nm-sd-utils-shared.c b/src/libnm-systemd-shared/nm-sd-utils-shared.c index c1c71c593d..dad21596cf 100644 --- a/src/libnm-systemd-shared/nm-sd-utils-shared.c +++ b/src/libnm-systemd-shared/nm-sd-utils-shared.c @@ -33,14 +33,6 @@ nm_sd_dns_name_is_valid(const char *s) return dns_name_is_valid(s); } -gboolean -nm_sd_hostname_is_valid(const char *s, bool allow_trailing_dot) -{ - return hostname_is_valid(s, - allow_trailing_dot ? VALID_HOSTNAME_TRAILING_DOT - : (ValidHostnameFlags) 0); -} - char * nm_sd_dns_name_normalize(const char *s) { diff --git a/src/libnm-systemd-shared/nm-sd-utils-shared.h b/src/libnm-systemd-shared/nm-sd-utils-shared.h index 731a49e20e..fefb8a1cdf 100644 --- a/src/libnm-systemd-shared/nm-sd-utils-shared.h +++ b/src/libnm-systemd-shared/nm-sd-utils-shared.h @@ -11,8 +11,7 @@ int nm_sd_dns_name_to_wire_format(const char *domain, guint8 *buffer, size_t len, gboolean canonical); -int nm_sd_dns_name_is_valid(const char *s); -gboolean nm_sd_hostname_is_valid(const char *s, bool allow_trailing_dot); +int nm_sd_dns_name_is_valid(const char *s); char *nm_sd_dns_name_normalize(const char *s); -- cgit v1.2.1 From 747d7dcfe34f9e95049376134096a2cc8991dc25 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 12 Apr 2022 17:37:52 +0200 Subject: systemd: drop "nm-sd-utils-core.h" and nm_sd_utils_id128_get_machine() This was only for unit testing, to check whether our reader for "/etc/machine-id" agrees with systemd's. That unit test was anyway flawed, because it actually accesses the machine-id on the test system. Anyway. Drop this. Most likely our parser is good enough, and if we get a bug report with a defect, we can unit test against that. --- Makefile.am | 2 -- src/core/tests/test-core.c | 23 ----------------------- src/libnm-systemd-core/meson.build | 1 - src/libnm-systemd-core/nm-sd-utils-core.c | 27 --------------------------- src/libnm-systemd-core/nm-sd-utils-core.h | 17 ----------------- 5 files changed, 70 deletions(-) delete mode 100644 src/libnm-systemd-core/nm-sd-utils-core.c delete mode 100644 src/libnm-systemd-core/nm-sd-utils-core.h diff --git a/Makefile.am b/Makefile.am index b2c7e9a746..ba86583542 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2329,8 +2329,6 @@ src_libnm_systemd_core_libnm_systemd_core_la_libadd = \ src_libnm_systemd_core_libnm_systemd_core_la_SOURCES = \ src/libnm-systemd-core/nm-default-systemd-core.h \ - src/libnm-systemd-core/nm-sd-utils-core.c \ - src/libnm-systemd-core/nm-sd-utils-core.h \ src/libnm-systemd-core/nm-sd.c \ src/libnm-systemd-core/nm-sd.h \ src/libnm-systemd-core/sd-adapt-core/condition.h \ diff --git a/src/core/tests/test-core.c b/src/core/tests/test-core.c index b4e1c4d5f3..11a7f32349 100644 --- a/src/core/tests/test-core.c +++ b/src/core/tests/test-core.c @@ -15,7 +15,6 @@ #include "NetworkManagerUtils.h" #include "libnm-core-intern/nm-core-internal.h" #include "nm-core-utils.h" -#include "libnm-systemd-core/nm-sd-utils-core.h" #include "dns/nm-dns-manager.h" #include "nm-connectivity.h" @@ -2314,7 +2313,6 @@ test_dns_create_resolv_conf(void) static void test_machine_id_read(void) { - NMUuid machine_id_sd; const NMUuid *machine_id; char machine_id_str[33]; gpointer logstate; @@ -2346,27 +2344,6 @@ test_machine_id_read(void) == machine_id_str); g_assert(strlen(machine_id_str) == 32); g_assert_cmpstr(machine_id_str, ==, nm_utils_machine_id_str()); - - /* double check with systemd's implementation... */ - if (!nm_sd_utils_id128_get_machine(&machine_id_sd)) { - /* if systemd failed to read /etc/machine-id, the file likely - * is invalid. Our machine-id is fake, and we have nothing to - * compare against. */ - - if (g_file_test(LOCALSTATEDIR "/lib/dbus/machine-id", G_FILE_TEST_EXISTS)) { - /* Hm. So systemd failed to read /etc/machine-id, but we may have the one from D-Bus. - * With LOCALSTATEDIR"/lib/dbus/machine-id", we don't really know whether we - * parsed that file. Assume we don't know and skip the test on this system. */ - g_assert(!nm_utils_machine_id_is_fake()); - return; - } - - /* OK, in this case, our function should have generated a random machine ID. */ - g_assert(nm_utils_machine_id_is_fake()); - } else { - g_assert(!nm_utils_machine_id_is_fake()); - g_assert_cmpmem(&machine_id_sd, sizeof(NMUuid), machine_id, 16); - } } /*****************************************************************************/ diff --git a/src/libnm-systemd-core/meson.build b/src/libnm-systemd-core/meson.build index e1b52bc1bc..70293a6154 100644 --- a/src/libnm-systemd-core/meson.build +++ b/src/libnm-systemd-core/meson.build @@ -18,7 +18,6 @@ libnm_systemd_core = static_library( 'src/libsystemd/sd-id128/id128-util.c', 'src/libsystemd/sd-id128/sd-id128.c', 'nm-sd.c', - 'nm-sd-utils-core.c', 'sd-adapt-core/nm-sd-adapt-core.c', ), include_directories: [ diff --git a/src/libnm-systemd-core/nm-sd-utils-core.c b/src/libnm-systemd-core/nm-sd-utils-core.c deleted file mode 100644 index 21e8a3044e..0000000000 --- a/src/libnm-systemd-core/nm-sd-utils-core.c +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#include "libnm-systemd-core/nm-default-systemd-core.h" - -#include "nm-sd-utils-core.h" - -#include "libnm-glib-aux/nm-uuid.h" - -#include "nm-sd-adapt-core.h" - -#include "sd-id128.h" - -/*****************************************************************************/ - -NMUuid * -nm_sd_utils_id128_get_machine(NMUuid *out_uuid) -{ - g_assert(out_uuid); - - G_STATIC_ASSERT_EXPR(sizeof(*out_uuid) == sizeof(sd_id128_t)); - if (sd_id128_get_machine((sd_id128_t *) out_uuid) < 0) - return NULL; - return out_uuid; -} diff --git a/src/libnm-systemd-core/nm-sd-utils-core.h b/src/libnm-systemd-core/nm-sd-utils-core.h deleted file mode 100644 index ccad002989..0000000000 --- a/src/libnm-systemd-core/nm-sd-utils-core.h +++ /dev/null @@ -1,17 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#ifndef __NM_SD_UTILS_CORE_H__ -#define __NM_SD_UTILS_CORE_H__ - -/*****************************************************************************/ - -struct _NMUuid; - -struct _NMUuid *nm_sd_utils_id128_get_machine(struct _NMUuid *out_uuid); - -/*****************************************************************************/ - -#endif /* __NM_SD_UTILS_CORE_H__ */ -- cgit v1.2.1