diff options
| author | Michael Biebl <biebl@debian.org> | 2022-08-16 18:24:19 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2022-08-16 18:24:19 +0200 |
| commit | 0018d1f3cf71d680d7b6bceda55a5717244d8b26 (patch) | |
| tree | a058f1d106d172d3354179437ef034c9355cdf9c /src/libnm-systemd-shared | |
| parent | 6accbd3ec0e42d8633bbde4d47ed7bfe854e7e0b (diff) | |
New upstream version 1.39.90 upstream/1.39.90
Diffstat (limited to 'src/libnm-systemd-shared')
56 files changed, 1818 insertions, 1089 deletions
diff --git a/src/libnm-systemd-shared/meson.build b/src/libnm-systemd-shared/meson.build index 05c81faf..d8e7a31a 100644 --- a/src/libnm-systemd-shared/meson.build +++ b/src/libnm-systemd-shared/meson.build @@ -14,6 +14,7 @@ libnm_systemd_shared = static_library( 'src/basic/fileio.c', 'src/basic/format-util.c', 'src/basic/fs-util.c', + 'src/basic/glyph-util.c', 'src/basic/hash-funcs.c', 'src/basic/hashmap.c', 'src/basic/hexdecoct.c', @@ -21,6 +22,7 @@ libnm_systemd_shared = static_library( 'src/basic/in-addr-util.c', 'src/basic/inotify-util.c', 'src/basic/io-util.c', + 'src/basic/locale-util.c', 'src/basic/memory-util.c', 'src/basic/mempool.c', 'src/basic/ordered-set.c', @@ -41,6 +43,7 @@ libnm_systemd_shared = static_library( 'src/basic/tmpfile-util.c', 'src/basic/utf8.c', 'src/basic/util.c', + 'src/fundamental/sha256.c', 'src/fundamental/string-util-fundamental.c', 'src/shared/dns-domain.c', 'src/shared/web-util.c', diff --git a/src/libnm-systemd-shared/nm-sd-utils-shared.c b/src/libnm-systemd-shared/nm-sd-utils-shared.c index 0a35016a..dad21596 100644 --- a/src/libnm-systemd-shared/nm-sd-utils-shared.c +++ b/src/libnm-systemd-shared/nm-sd-utils-shared.c @@ -21,58 +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_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) { @@ -85,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 82a50678..fefb8a1c 100644 --- a/src/libnm-systemd-shared/nm-sd-utils-shared.h +++ b/src/libnm-systemd-shared/nm-sd-utils-shared.h @@ -8,25 +8,10 @@ /*****************************************************************************/ -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_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); -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); diff --git a/src/libnm-systemd-shared/sd-adapt-shared/locale-util.h b/src/libnm-systemd-shared/sd-adapt-shared/locale-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/libnm-systemd-shared/sd-adapt-shared/locale-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h b/src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h index 586c3a7e..ee5a0a24 100644 --- a/src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h +++ b/src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h @@ -51,8 +51,12 @@ #define LOG_TRACE 0 +#define WANT_LINUX_FS_H 0 + #define BUILD_MODE_DEVELOPER (NM_MORE_ASSERTS > 0) +#define LOG_MESSAGE_VERIFICATION (NM_MORE_ASSERTS > 0) + /*****************************************************************************/ /* systemd cannot be compiled with "-Wdeclaration-after-statement". In particular diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.h b/src/libnm-systemd-shared/src/basic/alloc-util.h index 9dde770d..b38db7d4 100644 --- a/src/libnm-systemd-shared/src/basic/alloc-util.h +++ b/src/libnm-systemd-shared/src/basic/alloc-util.h @@ -50,16 +50,30 @@ typedef void* (*mfree_func_t)(void *p); #define malloc0(n) (calloc(1, (n) ?: 1)) -#define free_and_replace(a, b) \ +#define free_and_replace_full(a, b, free_func) \ ({ \ typeof(a)* _a = &(a); \ typeof(b)* _b = &(b); \ - free(*_a); \ - (*_a) = (*_b); \ - (*_b) = NULL; \ + free_func(*_a); \ + *_a = *_b; \ + *_b = NULL; \ 0; \ }) +#define free_and_replace(a, b) \ + free_and_replace_full(a, b, free) + +/* This is similar to free_and_replace_full(), but NULL is not assigned to 'b', and its reference counter is + * increased. */ +#define unref_and_replace_full(a, b, ref_func, unref_func) \ + ({ \ + typeof(a)* _a = &(a); \ + typeof(b) _b = ref_func(b); \ + unref_func(*_a); \ + *_a = _b; \ + 0; \ + }) + void* memdup(const void *p, size_t l) _alloc_(2); void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, since we return a buffer one byte larger than the specified size */ @@ -174,23 +188,13 @@ void* greedy_realloc0(void **p, size_t need, size_t size); * is compatible with _FORTIFY_SOURCES. If _FORTIFY_SOURCES is used many memory operations will take the * object size as returned by __builtin_object_size() into account. Hence, let's return the smaller size of * malloc_usable_size() and __builtin_object_size() here, so that we definitely operate in safe territory by - * both the compiler's and libc's standards. Note that _FORTIFY_SOURCES=3 handles also dynamically allocated - * objects and thus it's safer using __builtin_dynamic_object_size if _FORTIFY_SOURCES=3 is used (#22801). - * Moreover, when NULL is passed malloc_usable_size() is documented to return zero, and + * both the compiler's and libc's standards. Note that __builtin_object_size() evaluates to SIZE_MAX if the + * size cannot be determined, hence the MIN() expression should be safe with dynamically sized memory, + * too. Moreover, when NULL is passed malloc_usable_size() is documented to return zero, and * __builtin_object_size() returns SIZE_MAX too, hence we also return a sensible value of 0 in this corner * case. */ - -#if defined __has_builtin -# if __has_builtin(__builtin_dynamic_object_size) -# define MALLOC_SIZEOF_SAFE(x) \ - MIN(malloc_usable_size(x), __builtin_dynamic_object_size(x, 0)) -# endif -#endif - -#ifndef MALLOC_SIZEOF_SAFE #define MALLOC_SIZEOF_SAFE(x) \ MIN(malloc_usable_size(x), __builtin_object_size(x, 0)) -#endif /* Inspired by ELEMENTSOF() but operates on malloc()'ed memory areas: typesafely returns the number of items * that fit into the specified memory block */ diff --git a/src/libnm-systemd-shared/src/basic/env-util.c b/src/libnm-systemd-shared/src/basic/env-util.c index b4ac6811..e869fad1 100644 --- a/src/libnm-systemd-shared/src/basic/env-util.c +++ b/src/libnm-systemd-shared/src/basic/env-util.c @@ -35,7 +35,7 @@ static bool env_name_is_valid_n(const char *e, size_t n) { if (n <= 0) return false; - if (e[0] >= '0' && e[0] <= '9') + if (ascii_isdigit(e[0])) return false; /* POSIX says the overall size of the environment block cannot diff --git a/src/libnm-systemd-shared/src/basic/ether-addr-util.c b/src/libnm-systemd-shared/src/basic/ether-addr-util.c index 4504b6be..7984ddf4 100644 --- a/src/libnm-systemd-shared/src/basic/ether-addr-util.c +++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.c @@ -35,6 +35,15 @@ char *hw_addr_to_string_full( return buffer; } +struct hw_addr_data *hw_addr_set(struct hw_addr_data *addr, const uint8_t *bytes, size_t length) { + assert(addr); + assert(length <= HW_ADDR_MAX_SIZE); + + addr->length = length; + memcpy_safe(addr->bytes, bytes, length); + return addr; +} + int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b) { int r; diff --git a/src/libnm-systemd-shared/src/basic/ether-addr-util.h b/src/libnm-systemd-shared/src/basic/ether-addr-util.h index 32f45fe8..83ed77d6 100644 --- a/src/libnm-systemd-shared/src/basic/ether-addr-util.h +++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.h @@ -52,6 +52,8 @@ static inline char *hw_addr_to_string(const struct hw_addr_data *addr, char buff #define HW_ADDR_NULL ((const struct hw_addr_data){}) +struct hw_addr_data *hw_addr_set(struct hw_addr_data *addr, const uint8_t *bytes, size_t length); + void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state); int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b); static inline bool hw_addr_equal(const struct hw_addr_data *a, const struct hw_addr_data *b) { diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c index 53efeb40..80295445 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.c +++ b/src/libnm-systemd-shared/src/basic/fd-util.c @@ -5,7 +5,9 @@ #include <errno.h> #include <fcntl.h> #include <linux/btrfs.h> +#if WANT_LINUX_FS_H #include <linux/fs.h> +#endif #include <linux/magic.h> #include <sys/ioctl.h> #include <sys/resource.h> diff --git a/src/libnm-systemd-shared/src/basic/fileio.c b/src/libnm-systemd-shared/src/basic/fileio.c index 0ad8ae03..d996bc02 100644 --- a/src/libnm-systemd-shared/src/basic/fileio.c +++ b/src/libnm-systemd-shared/src/basic/fileio.c @@ -767,8 +767,7 @@ int read_full_file_full( r = xfopenat(dir_fd, filename, "re", 0, &f); if (r < 0) { - _cleanup_close_ int dfd = -1, sk = -1; - union sockaddr_union sa; + _cleanup_close_ int sk = -1; /* ENXIO is what Linux returns if we open a node that is an AF_UNIX socket */ if (r != -ENXIO) @@ -780,23 +779,7 @@ int read_full_file_full( /* Seeking is not supported on AF_UNIX sockets */ if (offset != UINT64_MAX) - return -ESPIPE; - - if (dir_fd == AT_FDCWD) - r = sockaddr_un_set_path(&sa.un, filename); - else { - /* If we shall operate relative to some directory, then let's use O_PATH first to - * open the socket inode, and then connect to it via /proc/self/fd/. We have to do - * this since there's not connectat() that takes a directory fd as first arg. */ - - dfd = openat(dir_fd, filename, O_PATH|O_CLOEXEC); - if (dfd < 0) - return -errno; - - r = sockaddr_un_set_path(&sa.un, FORMAT_PROC_FD_PATH(dfd)); - } - if (r < 0) - return r; + return -ENXIO; sk = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); if (sk < 0) @@ -813,12 +796,14 @@ int read_full_file_full( return r; if (bind(sk, &bsa.sa, r) < 0) - return r; + return -errno; } - if (connect(sk, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) - return errno == ENOTSOCK ? -ENXIO : -errno; /* propagate original error if this is - * not a socket after all */ + r = connect_unix_path(sk, dir_fd, filename); + if (IN_SET(r, -ENOTSOCK, -EINVAL)) /* propagate original error if this is not a socket after all */ + return -ENXIO; + if (r < 0) + return r; if (shutdown(sk, SHUT_WR) < 0) return -errno; diff --git a/src/libnm-systemd-shared/src/basic/fs-util.c b/src/libnm-systemd-shared/src/basic/fs-util.c index dccfb27b..1a951bd5 100644 --- a/src/libnm-systemd-shared/src/basic/fs-util.c +++ b/src/libnm-systemd-shared/src/basic/fs-util.c @@ -160,24 +160,23 @@ int readlink_malloc(const char *p, char **ret) { #if 0 /* NM_IGNORED */ int readlink_value(const char *p, char **ret) { - _cleanup_free_ char *link = NULL; - char *value; + _cleanup_free_ char *link = NULL, *name = NULL; int r; + assert(p); + assert(ret); + r = readlink_malloc(p, &link); if (r < 0) return r; - value = basename(link); - if (!value) - return -ENOENT; - - value = strdup(value); - if (!value) - return -ENOMEM; - - *ret = value; + r = path_extract_filename(link, &name); + if (r < 0) + return r; + if (r == O_DIRECTORY) + return -EINVAL; + *ret = TAKE_PTR(name); return 0; } diff --git a/src/libnm-systemd-shared/src/basic/glyph-util.c b/src/libnm-systemd-shared/src/basic/glyph-util.c new file mode 100644 index 00000000..ec851e8b --- /dev/null +++ b/src/libnm-systemd-shared/src/basic/glyph-util.c @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "nm-sd-adapt-shared.h" + +#include "env-util.h" +#include "glyph-util.h" +#include "locale-util.h" +#include "strv.h" + +bool emoji_enabled(void) { + static int cached_emoji_enabled = -1; + + if (cached_emoji_enabled < 0) { + int val; + + val = getenv_bool("SYSTEMD_EMOJI"); + if (val < 0) + cached_emoji_enabled = + is_locale_utf8() && + !STRPTR_IN_SET(getenv("TERM"), "dumb", "linux"); + else + cached_emoji_enabled = val; + } + + return cached_emoji_enabled; +} + +const char *special_glyph(SpecialGlyph code) { + + /* A list of a number of interesting unicode glyphs we can use to decorate our output. It's probably wise to be + * conservative here, and primarily stick to the glyphs defined in the eurlatgr font, so that display still + * works reasonably well on the Linux console. For details see: + * + * http://git.altlinux.org/people/legion/packages/kbd.git?p=kbd.git;a=blob;f=data/consolefonts/README.eurlatgr + */ + + static const char* const draw_table[2][_SPECIAL_GLYPH_MAX] = { + /* ASCII fallback */ + [false] = { + [SPECIAL_GLYPH_TREE_VERTICAL] = "| ", + [SPECIAL_GLYPH_TREE_BRANCH] = "|-", + [SPECIAL_GLYPH_TREE_RIGHT] = "`-", + [SPECIAL_GLYPH_TREE_SPACE] = " ", + [SPECIAL_GLYPH_TREE_TOP] = ",-", + [SPECIAL_GLYPH_VERTICAL_DOTTED] = ":", + [SPECIAL_GLYPH_TRIANGULAR_BULLET] = ">", + [SPECIAL_GLYPH_BLACK_CIRCLE] = "*", + [SPECIAL_GLYPH_WHITE_CIRCLE] = "*", + [SPECIAL_GLYPH_MULTIPLICATION_SIGN] = "x", + [SPECIAL_GLYPH_CIRCLE_ARROW] = "*", + [SPECIAL_GLYPH_BULLET] = "*", + [SPECIAL_GLYPH_MU] = "u", + [SPECIAL_GLYPH_CHECK_MARK] = "+", + [SPECIAL_GLYPH_CROSS_MARK] = "-", + [SPECIAL_GLYPH_LIGHT_SHADE] = "-", + [SPECIAL_GLYPH_DARK_SHADE] = "X", + [SPECIAL_GLYPH_SIGMA] = "S", + [SPECIAL_GLYPH_ARROW_RIGHT] = "->", + [SPECIAL_GLYPH_ARROW_UP] = "^", + [SPECIAL_GLYPH_ARROW_DOWN] = "v", + [SPECIAL_GLYPH_ELLIPSIS] = "...", + [SPECIAL_GLYPH_EXTERNAL_LINK] = "[LNK]", + [SPECIAL_GLYPH_ECSTATIC_SMILEY] = ":-]", + [SPECIAL_GLYPH_HAPPY_SMILEY] = ":-}", + [SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY] = ":-)", + [SPECIAL_GLYPH_NEUTRAL_SMILEY] = ":-|", + [SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = ":-(", + [SPECIAL_GLYPH_UNHAPPY_SMILEY] = ":-{", + [SPECIAL_GLYPH_DEPRESSED_SMILEY] = ":-[", + [SPECIAL_GLYPH_LOCK_AND_KEY] = "o-,", + [SPECIAL_GLYPH_TOUCH] = "O=", /* Yeah, not very convincing, can you do it better? */ + [SPECIAL_GLYPH_RECYCLING] = "~", + [SPECIAL_GLYPH_DOWNLOAD] = "\\", + [SPECIAL_GLYPH_SPARKLES] = "*", + }, + + /* UTF-8 */ + [true] = { + /* The following are multiple glyphs in both ASCII and in UNICODE */ + [SPECIAL_GLYPH_TREE_VERTICAL] = u8"│ ", + [SPECIAL_GLYPH_TREE_BRANCH] = u8"├─", + [SPECIAL_GLYPH_TREE_RIGHT] = u8"└─", + [SPECIAL_GLYPH_TREE_SPACE] = u8" ", + [SPECIAL_GLYPH_TREE_TOP] = u8"┌─", + + /* Single glyphs in both cases */ + [SPECIAL_GLYPH_VERTICAL_DOTTED] = u8"┆", + [SPECIAL_GLYPH_TRIANGULAR_BULLET] = u8"‣", + [SPECIAL_GLYPH_BLACK_CIRCLE] = u8"●", + [SPECIAL_GLYPH_WHITE_CIRCLE] = u8"○", + [SPECIAL_GLYPH_MULTIPLICATION_SIGN] = u8"×", + [SPECIAL_GLYPH_CIRCLE_ARROW] = u8"↻", + [SPECIAL_GLYPH_BULLET] = u8"•", + [SPECIAL_GLYPH_MU] = u8"μ", /* actually called: GREEK SMALL LETTER MU */ + [SPECIAL_GLYPH_CHECK_MARK] = u8"✓", + [SPECIAL_GLYPH_CROSS_MARK] = u8"✗", /* actually called: BALLOT X */ + [SPECIAL_GLYPH_LIGHT_SHADE] = u8"░", + [SPECIAL_GLYPH_DARK_SHADE] = u8"▒", + [SPECIAL_GLYPH_SIGMA] = u8"Σ", + [SPECIAL_GLYPH_ARROW_UP] = u8"↑", /* actually called: UPWARDS ARROW */ + [SPECIAL_GLYPH_ARROW_DOWN] = u8"↓", /* actually called: DOWNWARDS ARROW */ + + /* Single glyph in Unicode, two in ASCII */ + [SPECIAL_GLYPH_ARROW_RIGHT] = u8"→", /* actually called: RIGHTWARDS ARROW */ + + /* Single glyph in Unicode, three in ASCII */ + [SPECIAL_GLYPH_ELLIPSIS] = u8"…", /* actually called: HORIZONTAL ELLIPSIS */ + + /* Three glyphs in Unicode, five in ASCII */ + [SPECIAL_GLYPH_EXTERNAL_LINK] = u8"[🡕]", /* actually called: NORTH EAST SANS-SERIF ARROW, enclosed in [] */ + + /* These smileys are a single glyph in Unicode, and three in ASCII */ + [SPECIAL_GLYPH_ECSTATIC_SMILEY] = u8"😇", /* actually called: SMILING FACE WITH HALO */ + [SPECIAL_GLYPH_HAPPY_SMILEY] = u8"😀", /* actually called: GRINNING FACE */ + [SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY] = u8"🙂", /* actually called: SLIGHTLY SMILING FACE */ + [SPECIAL_GLYPH_NEUTRAL_SMILEY] = u8"😐", /* actually called: NEUTRAL FACE */ + [SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = u8"🙁", /* actually called: SLIGHTLY FROWNING FACE */ + [SPECIAL_GLYPH_UNHAPPY_SMILEY] = u8"😨", /* actually called: FEARFUL FACE */ + [SPECIAL_GLYPH_DEPRESSED_SMILEY] = u8"🤢", /* actually called: NAUSEATED FACE */ + + /* This emoji is a single character cell glyph in Unicode, and three in ASCII */ + [SPECIAL_GLYPH_LOCK_AND_KEY] = u8"🔐", /* actually called: CLOSED LOCK WITH KEY */ + + /* This emoji is a single character cell glyph in Unicode, and two in ASCII */ + [SPECIAL_GLYPH_TOUCH] = u8"👆", /* actually called: BACKHAND INDEX POINTING UP */ + + /* These three emojis are single character cell glyphs in Unicode and also in ASCII. */ + [SPECIAL_GLYPH_RECYCLING] = u8"♻️", /* actually called: UNIVERSAL RECYCLNG SYMBOL */ + [SPECIAL_GLYPH_DOWNLOAD] = u8"⤵️", /* actually called: RIGHT ARROW CURVING DOWN */ + [SPECIAL_GLYPH_SPARKLES] = u8"✨", + }, + }; + + if (code < 0) + return NULL; + + assert(code < _SPECIAL_GLYPH_MAX); + return draw_table[code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8()][code]; +} diff --git a/src/libnm-systemd-shared/src/basic/glyph-util.h b/src/libnm-systemd-shared/src/basic/glyph-util.h new file mode 100644 index 00000000..065dde8a --- /dev/null +++ b/src/libnm-systemd-shared/src/basic/glyph-util.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <errno.h> +#include <stdbool.h> + +#include "macro.h" + +typedef enum SpecialGlyph { + SPECIAL_GLYPH_TREE_VERTICAL, + SPECIAL_GLYPH_TREE_BRANCH, + SPECIAL_GLYPH_TREE_RIGHT, + SPECIAL_GLYPH_TREE_SPACE, + SPECIAL_GLYPH_TREE_TOP, + SPECIAL_GLYPH_VERTICAL_DOTTED, + SPECIAL_GLYPH_TRIANGULAR_BULLET, + SPECIAL_GLYPH_BLACK_CIRCLE, + SPECIAL_GLYPH_WHITE_CIRCLE, + SPECIAL_GLYPH_MULTIPLICATION_SIGN, + SPECIAL_GLYPH_CIRCLE_ARROW, + SPECIAL_GLYPH_BULLET, + SPECIAL_GLYPH_MU, + SPECIAL_GLYPH_CHECK_MARK, + SPECIAL_GLYPH_CROSS_MARK, + SPECIAL_GLYPH_ARROW_RIGHT, + SPECIAL_GLYPH_ARROW_UP, + SPECIAL_GLYPH_ARROW_DOWN, + SPECIAL_GLYPH_ELLIPSIS, + SPECIAL_GLYPH_LIGHT_SHADE, + SPECIAL_GLYPH_DARK_SHADE, + SPECIAL_GLYPH_SIGMA, + SPECIAL_GLYPH_EXTERNAL_LINK, + _SPECIAL_GLYPH_FIRST_EMOJI, + SPECIAL_GLYPH_ECSTATIC_SMILEY = _SPECIAL_GLYPH_FIRST_EMOJI, + SPECIAL_GLYPH_HAPPY_SMILEY, + SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY, + SPECIAL_GLYPH_NEUTRAL_SMILEY, + SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY, + SPECIAL_GLYPH_UNHAPPY_SMILEY, + SPECIAL_GLYPH_DEPRESSED_SMILEY, + SPECIAL_GLYPH_LOCK_AND_KEY, + SPECIAL_GLYPH_TOUCH, + SPECIAL_GLYPH_RECYCLING, + SPECIAL_GLYPH_DOWNLOAD, + SPECIAL_GLYPH_SPARKLES, + _SPECIAL_GLYPH_MAX, + _SPECIAL_GLYPH_INVALID = -EINVAL, +} SpecialGlyph; + +const char *special_glyph(SpecialGlyph code) _const_; + +bool emoji_enabled(void); + +static inline const char *special_glyph_check_mark(bool b) { + return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : special_glyph(SPECIAL_GLYPH_CROSS_MARK); +} + +static inline const char *special_glyph_check_mark_space(bool b) { + return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : " "; +} diff --git a/src/libnm-systemd-shared/src/basic/hash-funcs.c b/src/libnm-systemd-shared/src/basic/hash-funcs.c index c1d28ff6..eed30f09 100644 --- a/src/libnm-systemd-shared/src/basic/hash-funcs.c +++ b/src/libnm-systemd-shared/src/basic/hash-funcs.c @@ -6,6 +6,7 @@ #include "hash-funcs.h" #include "path-util.h" +#include "strv.h" void string_hash_func(const char *p, struct siphash *state) { siphash24_compress(p, strlen(p) + 1, state); @@ -17,6 +18,9 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(string_hash_ops_free, DEFINE_HASH_OPS_FULL(string_hash_ops_free_free, char, string_hash_func, string_compare_func, free, void, free); +DEFINE_HASH_OPS_FULL(string_hash_ops_free_strv_free, + char, string_hash_func, string_compare_func, free, + char*, strv_free); #if 0 /* NM_IGNORED */ void path_hash_func(const char *q, struct siphash *state) { @@ -107,11 +111,17 @@ DEFINE_HASH_OPS(uint64_hash_ops, uint64_t, uint64_hash_func, uint64_compare_func void devt_hash_func(const dev_t *p, struct siphash *state) { siphash24_compress(p, sizeof(dev_t), state); } +#endif int devt_compare_func(const dev_t *a, const dev_t *b) { - return CMP(*a, *b); + int r; + + r = CMP(major(*a), major(*b)); + if (r != 0) + return r; + + return CMP(minor(*a), minor(*b)); } DEFINE_HASH_OPS(devt_hash_ops, dev_t, devt_hash_func, devt_compare_func); -#endif #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/hash-funcs.h b/src/libnm-systemd-shared/src/basic/hash-funcs.h index 023cfdf5..c14302ec 100644 --- a/src/libnm-systemd-shared/src/basic/hash-funcs.h +++ b/src/libnm-systemd-shared/src/basic/hash-funcs.h @@ -78,6 +78,7 @@ void string_hash_func(const char *p, struct siphash *state); extern const struct hash_ops string_hash_ops; extern const struct hash_ops string_hash_ops_free; extern const struct hash_ops string_hash_ops_free_free; +extern const struct hash_ops string_hash_ops_free_strv_free; void path_hash_func(const char *p, struct siphash *state); extern const struct hash_ops path_hash_ops; @@ -102,10 +103,9 @@ extern const struct hash_ops uint64_hash_ops; * 64bit archs. Yuck! */ #if SIZEOF_DEV_T != 8 void devt_hash_func(const dev_t *p, struct siphash *state) _pure_; -int devt_compare_func(const dev_t *a, const dev_t *b) _pure_; -extern const struct hash_ops devt_hash_ops; #else #define devt_hash_func uint64_hash_func -#define devt_compare_func uint64_compare_func -#define devt_hash_ops uint64_hash_ops #endif + +int devt_compare_func(const dev_t *a, const dev_t *b) _pure_; +extern const struct hash_ops devt_hash_ops; diff --git a/src/libnm-systemd-shared/src/basic/hashmap.c b/src/libnm-systemd-shared/src/basic/hashmap.c index e7dedc6d..5d0ae6aa 100644 --- a/src/libnm-systemd-shared/src/basic/hashmap.c +++ b/src/libnm-systemd-shared/src/basic/hashmap.c @@ -3,6 +3,7 @@ #include "nm-sd-adapt-shared.h" #include <errno.h> +#include <fnmatch.h> #include <pthread.h> #include <stdint.h> #include <stdlib.h> @@ -772,16 +773,15 @@ static void shared_hash_key_initialize(void) { static struct HashmapBase* hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) { HashmapBase *h; const struct hashmap_type_info *hi = &hashmap_type_info[type]; - bool up; - up = mempool_enabled(); + bool use_pool = mempool_enabled && mempool_enabled(); - h = up ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size); + h = use_pool ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size); if (!h) return NULL; h->type = type; - h->from_pool = up; + h->from_pool = use_pool; h->hash_ops = hash_ops ?: &trivial_hash_ops; if (type == HASHMAP_TYPE_ORDERED) { @@ -1845,7 +1845,7 @@ int _hashmap_put_strdup_full(Hashmap **h, const struct hash_ops *hash_ops, const } #endif /* NM_IGNORED */ -int _set_put_strdup_full(Set **s, const struct hash_ops *hash_ops, const char *p HASHMAP_DEBUG_PARAMS) { +int _set_put_strndup_full(Set **s, const struct hash_ops *hash_ops, const char *p, size_t n HASHMAP_DEBUG_PARAMS) { char *c; int r; @@ -1856,10 +1856,13 @@ int _set_put_strdup_full(Set **s, const struct hash_ops *hash_ops, const char *p if (r < 0) return r; - if (set_contains(*s, (char*) p)) - return 0; + if (n == SIZE_MAX) { + if (set_contains(*s, (char*) p)) + return 0; - c = strdup(p); + c = strdup(p); + } else + c = strndup(p, n); if (!c) return -ENOMEM; @@ -1872,7 +1875,7 @@ int _set_put_strdupv_full(Set **s, const struct hash_ops *hash_ops, char **l HA assert(s); STRV_FOREACH(i, l) { - r = _set_put_strdup_full(s, hash_ops, *i HASHMAP_DEBUG_PASS_ARGS); + r = _set_put_strndup_full(s, hash_ops, *i, SIZE_MAX HASHMAP_DEBUG_PASS_ARGS); if (r < 0) return r; @@ -2074,3 +2077,27 @@ bool set_equal(Set *a, Set *b) { return true; } + +static bool set_fnmatch_one(Set *patterns, const char *needle) { + const char *p; + + assert(needle); + + SET_FOREACH(p, patterns) + if (fnmatch(p, needle, 0) == 0) + return true; + + return false; +} + +bool set_fnmatch(Set *include_patterns, Set *exclude_patterns, const char *needle) { + assert(needle); + + if (set_fnmatch_one(exclude_patterns, needle)) + return false; + + if (set_isempty(include_patterns)) + return true; + + return set_fnmatch_one(include_patterns, needle); +} diff --git a/src/libnm-systemd-shared/src/basic/hashmap.h b/src/libnm-systemd-shared/src/basic/hashmap.h index eafc08f6..91b3fe86 100644 --- a/src/libnm-systemd-shared/src/basic/hashmap.h +++ b/src/libnm-systemd-shared/src/basic/hashmap.h @@ -90,12 +90,7 @@ OrderedHashmap* _ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DE #define ordered_hashmap_new(ops) _ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) #define hashmap_free_and_replace(a, b) \ - ({ \ - hashmap_free(a); \ - (a) = (b); \ - (b) = NULL; \ - 0; \ - }) + free_and_replace_full(a, b, hashmap_free) HashmapBase* _hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); static inline Hashmap* hashmap_free(Hashmap *h) { diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.c b/src/libnm-systemd-shared/src/basic/hostname-util.c index 692733ac..1e94ce13 100644 --- a/src/libnm-systemd-shared/src/basic/hostname-util.c +++ b/src/libnm-systemd-shared/src/basic/hostname-util.c @@ -79,10 +79,8 @@ int gethostname_full(GetHostnameFlags flags, char **ret) { bool 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') || + return ascii_isalpha(c) || + ascii_isdigit(c) || c == '-'; } diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.c b/src/libnm-systemd-shared/src/basic/in-addr-util.c index 51a6b165..dc4446ae 100644 --- a/src/libnm-systemd-shared/src/basic/in-addr-util.c +++ b/src/libnm-systemd-shared/src/basic/in-addr-util.c @@ -16,6 +16,7 @@ #include "macro.h" #include "parse-util.h" #include "random-util.h" +#include "stdio-util.h" #include "string-util.h" #include "strxcpyx.h" #include "util.h" @@ -50,6 +51,20 @@ bool in4_addr_is_link_local(const struct in_addr *a) { return (be32toh(a->s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16); } +bool in4_addr_is_link_local_dynamic(const struct in_addr *a) { + assert(a); + + if (!in4_addr_is_link_local(a)) + return false; + + /* 169.254.0.0/24 and 169.254.255.0/24 must not be used for the dynamic IPv4LL assignment. + * See RFC 3927 Section 2.1: + * The IPv4 prefix 169.254/16 is registered with the IANA for this purpose. The first 256 and last + * 256 addresses in the 169.254/16 prefix are reserved for future use and MUST NOT be selected by a + * host using this dynamic configuration mechanism. */ + return !IN_SET(be32toh(a->s_addr) & 0x0000FF00U, 0x0000U, 0xFF00U); +} + bool in6_addr_is_link_local(const struct in6_addr *a) { assert(a); @@ -450,7 +465,7 @@ int in_addr_to_string(int family, const union in_addr_union *u, char **ret) { return -ENOMEM; errno = 0; - if (!inet_ntop(family, u, x, l)) + if (!typesafe_inet_ntop(family, u, x, l)) return errno_or_else(EINVAL); *ret = TAKE_PTR(x); @@ -458,37 +473,26 @@ int in_addr_to_string(int family, const union in_addr_union *u, char **ret) { } #if 0 /* NM_IGNORED */ -int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret) { - _cleanup_free_ char *x = NULL; - char *p; - size_t l; +int in_addr_prefix_to_string( + int family, + const union in_addr_union *u, + unsigned prefixlen, + char *buf, + size_t buf_len) { assert(u); - assert(ret); + assert(buf); - if (family == AF_INET) - l = INET_ADDRSTRLEN + 3; - else if (family == AF_INET6) - l = INET6_ADDRSTRLEN + 4; - else + if (!IN_SET(family, AF_INET, AF_INET6)) return -EAFNOSUPPORT; - if (prefixlen > FAMILY_ADDRESS_SIZE(family) * 8) - return -EINVAL; - - x = new(char, l); - if (!x) - return -ENOMEM; - errno = 0; - if (!inet_ntop(family, u, x, l)) - return errno_or_else(EINVAL); + if (!typesafe_inet_ntop(family, u, buf, buf_len)) + return errno_or_else(ENOSPC); - p = x + strlen(x); - l -= strlen(x); - (void) strpcpyf(&p, l, "/%u", prefixlen); - - *ret = TAKE_PTR(x); + size_t l = strlen(buf); + if (!snprintf_ok(buf + l, buf_len - l, "/%u", prefixlen)) + return -ENOSPC; return 0; } #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.h b/src/libnm-systemd-shared/src/basic/in-addr-util.h index 5de87a95..fbc60436 100644 --- a/src/libnm-systemd-shared/src/basic/in-addr-util.h +++ b/src/libnm-systemd-shared/src/basic/in-addr-util.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include <arpa/inet.h> #include <netinet/in.h> #include <stddef.h> #include <sys/socket.h> @@ -43,6 +44,7 @@ static inline bool in_addr_data_is_set(const struct in_addr_data *a) { int in_addr_is_multicast(int family, const union in_addr_union *u); bool in4_addr_is_link_local(const struct in_addr *a); +bool in4_addr_is_link_local_dynamic(const struct in_addr *a); bool in6_addr_is_link_local(const struct in6_addr *a); int in_addr_is_link_local(int family, const union in_addr_union *u); bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a); @@ -68,14 +70,62 @@ int in_addr_prefix_range( unsigned prefixlen, union in_addr_union *ret_start, union in_addr_union *ret_end); + int in_addr_to_string(int family, const union in_addr_union *u, char **ret); static inline int in6_addr_to_string(const struct in6_addr *u, char **ret) { return in_addr_to_string(AF_INET6, (const union in_addr_union*) u, ret); } -int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret); -static inline int in6_addr_prefix_to_string(const struct in6_addr *u, unsigned prefixlen, char **ret) { - return in_addr_prefix_to_string(AF_INET6, (const union in_addr_union*) u, prefixlen, ret); + +static inline const char* typesafe_inet_ntop(int family, const union in_addr_union *a, char *buf, size_t len) { + return inet_ntop(family, a, buf, len); +} +static inline const char* typesafe_inet_ntop4(const struct in_addr *a, char *buf, size_t len) { + return inet_ntop(AF_INET, a, buf, len); +} +static inline const char* typesafe_inet_ntop6(const struct in6_addr *a, char *buf, size_t len) { + return inet_ntop(AF_INET6, a, buf, len); +} + +/* Note: the lifetime of the compound literal is the immediately surrounding block, + * see C11 §6.5.2.5, and + * https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */ +#define IN_ADDR_MAX CONST_MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) +#define IN_ADDR_TO_STRING(family, addr) typesafe_inet_ntop(family, addr, (char[IN_ADDR_MAX]){}, IN_ADDR_MAX) +#define IN4_ADDR_TO_STRING(addr) typesafe_inet_ntop4(addr, (char[INET_ADDRSTRLEN]){}, INET_ADDRSTRLEN) +#define IN6_ADDR_TO_STRING(addr) typesafe_inet_ntop6(addr, (char[INET6_ADDRSTRLEN]){}, INET6_ADDRSTRLEN) + +int in_addr_prefix_to_string( + int family, + const union in_addr_union *u, + unsigned prefixlen, + char *buf, + size_t buf_len); + +static inline const char* _in_addr_prefix_to_string( + int family, + const union in_addr_union *u, + unsigned prefixlen, + char *buf, + size_t buf_len) { + /* We assume that this is called with an appropriately sized buffer and can never fail. */ + assert_se(in_addr_prefix_to_string(family, u, prefixlen, buf, buf_len) == 0); + return buf; +} +static inline const char* _in4_addr_prefix_to_string(const struct in_addr *a, unsigned prefixlen, char *buf, size_t buf_len) { + return _in_addr_prefix_to_string(AF_INET, (const union in_addr_union *) a, prefixlen, buf, buf_len); +} +static inline const char* _in6_addr_prefix_to_string(const struct in6_addr *a, unsigned prefixlen, char *buf, size_t buf_len) { + return _in_addr_prefix_to_string(AF_INET6, (const union in_addr_union *) a, prefixlen, buf, buf_len); } + +#define PREFIX_SUFFIX_MAX (1 + DECIMAL_STR_MAX(unsigned)) +#define IN_ADDR_PREFIX_TO_STRING(family, addr, prefixlen) \ + _in_addr_prefix_to_string(family, addr, prefixlen, (char[IN_ADDR_MAX + PREFIX_SUFFIX_MAX]){}, IN_ADDR_MAX + PREFIX_SUFFIX_MAX) +#define IN4_ADDR_PREFIX_TO_STRING(addr, prefixlen) \ + _in4_addr_prefix_to_string(addr, prefixlen, (char[INET_ADDRSTRLEN + PREFIX_SUFFIX_MAX]){}, INET_ADDRSTRLEN + PREFIX_SUFFIX_MAX) +#define IN6_ADDR_PREFIX_TO_STRING(addr, prefixlen) \ + _in6_addr_prefix_to_string(addr, prefixlen, (char[INET6_ADDRSTRLEN + PREFIX_SUFFIX_MAX]){}, INET6_ADDRSTRLEN + PREFIX_SUFFIX_MAX) + int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret); static inline int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) { return in_addr_port_ifindex_name_to_string(family, u, 0, ifindex, NULL, ret); diff --git a/src/libnm-systemd-shared/src/basic/list.h b/src/libnm-systemd-shared/src/basic/list.h index 58e83a6c..ca300396 100644 --- a/src/libnm-systemd-shared/src/basic/list.h +++ b/src/libnm-systemd-shared/src/basic/list.h @@ -170,9 +170,6 @@ i != (p); \ i = i->name##_next ? i->name##_next : (head)) -#define LIST_IS_EMPTY(head) \ - (!(head)) - /* Join two lists tail to head: a->b, c->d to a->b->c->d and de-initialise second list */ #define LIST_JOIN(name,a,b) \ do { \ diff --git a/src/libnm-systemd-shared/src/basic/locale-util.c b/src/libnm-systemd-shared/src/basic/locale-util.c new file mode 100644 index 00000000..ae567345 --- /dev/null +++ b/src/libnm-systemd-shared/src/basic/locale-util.c @@ -0,0 +1,376 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "nm-sd-adapt-shared.h" + +#include <errno.h> +#include <fcntl.h> +#include <langinfo.h> +#include <libintl.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <sys/stat.h> + +#include "def.h" +#include "dirent-util.h" +#include "env-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "hashmap.h" +#include "locale-util.h" +#include "path-util.h" +#include "set.h" +#include "string-table.h" +#include "string-util.h" +#include "strv.h" +#include "utf8.h" + +#if 0 /* NM_IGNORED */ +static char *normalize_locale(const char *name) { + const char *e; + + /* Locale names are weird: glibc has some magic rules when looking for the charset name on disk: it + * lowercases everything, and removes most special chars. This means the official .UTF-8 suffix + * becomes .utf8 when looking things up on disk. When enumerating locales, let's do the reverse + * operation, and go back to ".UTF-8" which appears to be the more commonly accepted name. We only do + * that for UTF-8 however, since it's kinda the only charset that matters. */ + + e = endswith(name, ".utf8"); + if (e) { + _cleanup_free_ char *prefix = NULL; + + prefix = strndup(name, e - name); + if (!prefix) + return NULL; + + return strjoin(prefix, ".UTF-8"); + } + + e = strstr(name, ".utf8@"); + if (e) { + _cleanup_free_ char *prefix = NULL; + + prefix = strndup(name, e - name); + if (!prefix) + return NULL; + + return strjoin(prefix, ".UTF-8@", e + 6); + } + + return strdup(name); +} + +static int add_locales_from_archive(Set *locales) { + /* Stolen from glibc... */ + + struct locarhead { + uint32_t magic; + /* Serial number. */ + uint32_t serial; + /* Name hash table. */ + uint32_t namehash_offset; + uint32_t namehash_used; + uint32_t namehash_size; + /* String table. */ + uint32_t string_offset; + uint32_t string_used; + uint32_t string_size; + /* Table with locale records. */ + uint32_t locrectab_offset; + uint32_t locrectab_used; + uint32_t locrectab_size; + /* MD5 sum hash table. */ + uint32_t sumhash_offset; + uint32_t sumhash_used; + uint32_t sumhash_size; + }; + + struct namehashent { + /* Hash value of the name. */ + uint32_t hashval; + /* Offset of the name in the string table. */ + uint32_t name_offset; + /* Offset of the locale record. */ + uint32_t locrec_offset; + }; + + const struct locarhead *h; + const struct namehashent *e; + const void *p = MAP_FAILED; + _cleanup_close_ int fd = -1; + size_t sz = 0; + struct stat st; + int r; + + fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return errno == ENOENT ? 0 : -errno; + + if (fstat(fd, &st) < 0) + return -errno; + + if (!S_ISREG(st.st_mode)) + return -EBADMSG; + + if (st.st_size < (off_t) sizeof(struct locarhead)) + return -EBADMSG; + + if (file_offset_beyond_memory_size(st.st_size)) + return -EFBIG; + + p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (p == MAP_FAILED) + return -errno; + + h = (const struct locarhead *) p; + if (h->magic != 0xde020109 || + h->namehash_offset + h->namehash_size > st.st_size || + h->string_offset + h->string_size > st.st_size || + h->locrectab_offset + h->locrectab_size > st.st_size || + h->sumhash_offset + h->sumhash_size > st.st_size) { + r = -EBADMSG; + goto finish; + } + + e = (const struct namehashent*) ((const uint8_t*) p + h->namehash_offset); + for (size_t i = 0; i < h->namehash_size; i++) { + char *z; + + if (e[i].locrec_offset == 0) + continue; + + if (!utf8_is_valid((char*) p + e[i].name_offset)) + continue; + + z = normalize_locale((char*) p + e[i].name_offset); + if (!z) { + r = -ENOMEM; + goto finish; + } + + r = set_consume(locales, z); + if (r < 0) + goto finish; + } + + r = 0; + + finish: + if (p != MAP_FAILED) + munmap((void*) p, sz); + + return r; +} + +static int add_locales_from_libdir(Set *locales) { + _cleanup_closedir_ DIR *dir = NULL; + int r; + + dir = opendir("/usr/lib/locale"); + if (!dir) + return errno == ENOENT ? 0 : -errno; + + FOREACH_DIRENT(de, dir, return -errno) { + char *z; + + if (de->d_type != DT_DIR) + continue; + + z = normalize_locale(de->d_name); + if (!z) + return -ENOMEM; + + r = set_consume(locales, z); + if (r < 0 && r != -EEXIST) + return r; + } + + return 0; +} + +int get_locales(char ***ret) { + _cleanup_set_free_free_ Set *locales = NULL; + _cleanup_strv_free_ char **l = NULL; + int r; + + locales = set_new(&string_hash_ops); + if (!locales) + return -ENOMEM; + + r = add_locales_from_archive(locales); + if (r < 0 && r != -ENOENT) + return r; + + r = add_locales_from_libdir(locales); + if (r < 0) + return r; + + char *locale; + SET_FOREACH(locale, locales) { + r = locale_is_installed(locale); + if (r < 0) + return r; + if (r == 0) + free(set_remove(locales, locale)); + } + + l = set_get_strv(locales); + if (!l) + return -ENOMEM; + + /* Now, all elements are owned by strv 'l'. Hence, do not call set_free_free(). */ + locales = set_free(locales); + + r = getenv_bool("SYSTEMD_LIST_NON_UTF8_LOCALES"); + if (r == -ENXIO || r == 0) { + char **a, **b; + + /* Filter out non-UTF-8 locales, because it's 2019, by default */ + for (a = b = l; *a; a++) { + + if (endswith(*a, "UTF-8") || + strstr(*a, ".UTF-8@")) + *(b++) = *a; + else + free(*a); + } + + *b = NULL; + + } else if (r < 0) + log_debug_errno(r, "Failed to parse $SYSTEMD_LIST_NON_UTF8_LOCALES as boolean"); + + strv_sort(l); + + *ret = TAKE_PTR(l); + + return 0; +} + +bool locale_is_valid(const char *name) { + + if (isempty(name)) + return false; + + if (strlen(name) >= 128) + return false; + + if (!utf8_is_valid(name)) + return false; + + if (!filename_is_valid(name)) + return false; + + if (!string_is_safe(name)) + return false; + + return true; +} + +int locale_is_installed(const char *name) { + if (!locale_is_valid(name)) + return false; + + if (STR_IN_SET(name, "C", "POSIX")) /* These ones are always OK */ + return true; + + _cleanup_(freelocalep) locale_t loc = + newlocale(LC_ALL_MASK, name, 0); + if (loc == (locale_t) 0) + return errno == ENOMEM ? -ENOMEM : false; + + return true; +} + +void init_gettext(void) { + setlocale(LC_ALL, ""); + textdomain(GETTEXT_PACKAGE); +} +#endif /* NM_IGNORED */ + +bool is_locale_utf8(void) { + const char *set; + static int cached_answer = -1; + + /* Note that we default to 'true' here, since today UTF8 is + * pretty much supported everywhere. */ + + if (cached_answer >= 0) + goto out; + + if (!setlocale(LC_ALL, "")) { + cached_answer = true; + goto out; + } + + set = nl_langinfo(CODESET); + if (!set) { + cached_answer = true; + goto out; + } + + if (streq(set, "UTF-8")) { + cached_answer = true; + goto out; + } + + /* For LC_CTYPE=="C" return true, because CTYPE is effectively + * unset and everything can do to UTF-8 nowadays. */ + set = setlocale(LC_CTYPE, NULL); + if (!set) { + cached_answer = true; + goto out; + } + + /* Check result, but ignore the result if C was set + * explicitly. */ + cached_answer = + STR_IN_SET(set, "C", "POSIX") && + !getenv("LC_ALL") && + !getenv("LC_CTYPE") && + !getenv("LANG"); + +out: + return (bool) cached_answer; +} + +#if 0 /* NM_IGNORED */ +void locale_variables_free(char *l[_VARIABLE_LC_MAX]) { + if (!l) + return; + + for (LocaleVariable i = 0; i < _VARIABLE_LC_MAX; i++) + l[i] = mfree(l[i]); +} + +void locale_variables_simplify(char *l[_VARIABLE_LC_MAX]) { + assert(l); + + for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) { + if (p == VARIABLE_LANG) + continue; + if (isempty(l[p]) || streq_ptr(l[VARIABLE_LANG], l[p])) + l[p] = mfree(l[p]); + } +} + +static const char * const locale_variable_table[_VARIABLE_LC_MAX] = { + [VARIABLE_LANG] = "LANG", + [VARIABLE_LANGUAGE] = "LANGUAGE", + [VARIABLE_LC_CTYPE] = "LC_CTYPE", + [VARIABLE_LC_NUMERIC] = "LC_NUMERIC", + [VARIABLE_LC_TIME] = "LC_TIME", + [VARIABLE_LC_COLLATE] = "LC_COLLATE", + [VARIABLE_LC_MONETARY] = "LC_MONETARY", + [VARIABLE_LC_MESSAGES] = "LC_MESSAGES", + [VARIABLE_LC_PAPER] = "LC_PAPER", + [VARIABLE_LC_NAME] = "LC_NAME", + [VARIABLE_LC_ADDRESS] = "LC_ADDRESS", + [VARIABLE_LC_TELEPHONE] = "LC_TELEPHONE", + [VARIABLE_LC_MEASUREMENT] = "LC_MEASUREMENT", + [VARIABLE_LC_IDENTIFICATION] = "LC_IDENTIFICATION" +}; + +DEFINE_STRING_TABLE_LOOKUP(locale_variable, LocaleVariable); +#endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/locale-util.h b/src/libnm-systemd-shared/src/basic/locale-util.h new file mode 100644 index 00000000..8990cb6a --- /dev/null +++ b/src/libnm-systemd-shared/src/basic/locale-util.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <libintl.h> +#include <locale.h> +#include <stdbool.h> + +#include "macro.h" + +typedef enum LocaleVariable { + /* We don't list LC_ALL here on purpose. People should be + * using LANG instead. */ + + VARIABLE_LANG, + VARIABLE_LANGUAGE, + VARIABLE_LC_CTYPE, + VARIABLE_LC_NUMERIC, + VARIABLE_LC_TIME, + VARIABLE_LC_COLLATE, + VARIABLE_LC_MONETARY, + VARIABLE_LC_MESSAGES, + VARIABLE_LC_PAPER, + VARIABLE_LC_NAME, + VARIABLE_LC_ADDRESS, + VARIABLE_LC_TELEPHONE, + VARIABLE_LC_MEASUREMENT, + VARIABLE_LC_IDENTIFICATION, + _VARIABLE_LC_MAX, + _VARIABLE_LC_INVALID = -EINVAL, +} LocaleVariable; + +int get_locales(char ***l); +bool locale_is_valid(const char *name); +int locale_is_installed(const char *name); + +#define _(String) gettext(String) +#define N_(String) String +void init_gettext(void); + +bool is_locale_utf8(void); + +const char* locale_variable_to_string(LocaleVariable i) _const_; +LocaleVariable locale_variable_from_string(const char *s) _pure_; + +static inline void freelocalep(locale_t *p) { + if (*p == (locale_t) 0) + return; + + freelocale(*p); +} + +void locale_variables_free(char* l[_VARIABLE_LC_MAX]); +static inline void locale_variables_freep(char*(*l)[_VARIABLE_LC_MAX]) { + locale_variables_free(*l); +} +void locale_variables_simplify(char *l[_VARIABLE_LC_MAX]); diff --git a/src/libnm-systemd-shared/src/basic/log.h b/src/libnm-systemd-shared/src/basic/log.h index 6361eb0f..1bc84e46 100644 --- a/src/libnm-systemd-shared/src/basic/log.h +++ b/src/libnm-systemd-shared/src/basic/log.h @@ -434,8 +434,16 @@ int log_emergency_level(void); bool log_on_console(void) _pure_; -/* Helper to prepare various field for structured logging */ -#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__ +/* Helper to wrap the main message in structured logging. The macro doesn't do much, + * except to provide visual grouping of the format string and its arguments. */ +#if LOG_MESSAGE_VERIFICATION || defined(__COVERITY__) +/* Do a fake formatting of the message string to let the scanner verify the arguments against the format + * message. The variable will never be set to true, but we don't tell the compiler that :) */ +extern bool _log_message_dummy; +# define LOG_MESSAGE(fmt, ...) "MESSAGE=%.0d" fmt, (_log_message_dummy && printf(fmt, ##__VA_ARGS__)), ##__VA_ARGS__ +#else +# define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__ +#endif void log_received_signal(int level, const struct signalfd_siginfo *si); diff --git a/src/libnm-systemd-shared/src/basic/macro.h b/src/libnm-systemd-shared/src/basic/macro.h index 41d83b3e..7380da10 100644 --- a/src/libnm-systemd-shared/src/basic/macro.h +++ b/src/libnm-systemd-shared/src/basic/macro.h @@ -11,24 +11,6 @@ #include "macro-fundamental.h" -#define _printf_(a, b) __attribute__((__format__(printf, a, b))) -#ifdef __clang__ -# define _alloc_(...) -#else -# define _alloc_(...) __attribute__((__alloc_size__(__VA_ARGS__))) -#endif -#define _sentinel_ __attribute__((__sentinel__)) -#define _destructor_ __attribute__((__destructor__)) -#define _deprecated_ __attribute__((__deprecated__)) -#define _malloc_ __attribute__((__malloc__)) -#define _weak_ __attribute__((__weak__)) -#define _public_ __attribute__((__visibility__("default"))) -#define _hidden_ __attribute__((__visibility__("hidden"))) -#define _weakref_(x) __attribute__((__weakref__(#x))) -#define _alignas_(x) __attribute__((__aligned__(__alignof__(x)))) -#define _alignptr_ __attribute__((__aligned__(sizeof(void*)))) -#define _warn_unused_result_ __attribute__((__warn_unused_result__)) - #if !defined(HAS_FEATURE_MEMORY_SANITIZER) # if defined(__has_feature) # if __has_feature(memory_sanitizer) @@ -106,10 +88,6 @@ _Pragma("GCC diagnostic push") #endif -#define DISABLE_WARNING_FLOAT_EQUAL \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wfloat-equal\"") - #define DISABLE_WARNING_TYPE_LIMITS \ _Pragma("GCC diagnostic push"); \ _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") @@ -137,25 +115,6 @@ #error "neither int nor long are four bytes long?!?" #endif -/* Rounds up */ - -#define ALIGN4(l) (((l) + 3) & ~3) -#define ALIGN8(l) (((l) + 7) & ~7) - -#if __SIZEOF_POINTER__ == 8 -#define ALIGN(l) ALIGN8(l) -#elif __SIZEOF_POINTER__ == 4 -#define ALIGN(l) ALIGN4(l) -#else -#error "Wut? Pointers are neither 4 nor 8 bytes long?" -#endif - -#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) (p))) -#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) (p))) -#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p))) - -#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) (p), (ali))) - /* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */ static inline unsigned long ALIGN_POWER2(unsigned long u) { @@ -370,7 +329,7 @@ static inline int __coverity_check_and_return__(int condition) { #ifndef thread_local /* * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__ - * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769 + * see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769 */ #if __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16)) #define thread_local _Thread_local @@ -405,8 +364,12 @@ static inline int __coverity_check_and_return__(int condition) { if (!p) \ return NULL; \ \ - assert(p->n_ref > 0); \ - p->n_ref++; \ + /* For type check. */ \ + unsigned *q = &p->n_ref; \ + assert(*q > 0); \ + assert_se(*q < UINT_MAX); \ + \ + (*q)++; \ return p; \ } @@ -462,8 +425,15 @@ static inline int __coverity_check_and_return__(int condition) { _copy; \ }) +#define saturate_add(x, y, limit) \ + ({ \ + typeof(limit) _x = (x); \ + typeof(limit) _y = (y); \ + _x > (limit) || _y >= (limit) - _x ? (limit) : _x + _y; \ + }) + static inline size_t size_add(size_t x, size_t y) { - return y >= SIZE_MAX - x ? SIZE_MAX : x + y; + return saturate_add(x, y, SIZE_MAX); } typedef struct { diff --git a/src/libnm-systemd-shared/src/basic/mempool.c b/src/libnm-systemd-shared/src/basic/mempool.c index 46c44914..53a719e0 100644 --- a/src/libnm-systemd-shared/src/basic/mempool.c +++ b/src/libnm-systemd-shared/src/basic/mempool.c @@ -5,12 +5,9 @@ #include <stdint.h> #include <stdlib.h> -#include "env-util.h" #include "macro.h" #include "memory-util.h" #include "mempool.h" -#include "process-util.h" -#include "util.h" struct pool { struct pool *next; @@ -75,20 +72,6 @@ void mempool_free_tile(struct mempool *mp, void *p) { mp->freelist = p; } -bool mempool_enabled(void) { - static int b = -1; - - if (!is_main_thread()) - return false; - - if (!mempool_use_allowed) - b = false; - if (b < 0) - b = getenv_bool("SYSTEMD_MEMPOOL") != 0; - - return b; -} - #if VALGRIND void mempool_drop(struct mempool *mp) { struct pool *p = mp->first_pool; diff --git a/src/libnm-systemd-shared/src/basic/mempool.h b/src/libnm-systemd-shared/src/basic/mempool.h index 0fe2f278..539ccbdf 100644 --- a/src/libnm-systemd-shared/src/basic/mempool.h +++ b/src/libnm-systemd-shared/src/basic/mempool.h @@ -23,8 +23,7 @@ static struct mempool pool_name = { \ .at_least = alloc_at_least, \ } -extern const bool mempool_use_allowed; -bool mempool_enabled(void); +__attribute__((weak)) bool mempool_enabled(void); #if VALGRIND void mempool_drop(struct mempool *mp); diff --git a/src/libnm-systemd-shared/src/basic/missing_syscall.h b/src/libnm-systemd-shared/src/basic/missing_syscall.h index e5bdc0e1..107f4b01 100644 --- a/src/libnm-systemd-shared/src/basic/missing_syscall.h +++ b/src/libnm-systemd-shared/src/basic/missing_syscall.h @@ -76,6 +76,7 @@ static inline int missing_memfd_create(const char *name, unsigned int flags) { # define memfd_create missing_memfd_create #endif +#endif /* NM_IGNORED */ /* ======================================================================= */ @@ -95,6 +96,7 @@ static inline ssize_t missing_getrandom(void *buffer, size_t count, unsigned fla /* ======================================================================= */ +#if 0 /* NM_IGNORED */ /* The syscall has been defined since forever, but the glibc wrapper was missing. */ #if !HAVE_GETTID static inline pid_t missing_gettid(void) { diff --git a/src/libnm-systemd-shared/src/basic/ordered-set.h b/src/libnm-systemd-shared/src/basic/ordered-set.h index c0650e01..e73da205 100644 --- a/src/libnm-systemd-shared/src/basic/ordered-set.h +++ b/src/libnm-systemd-shared/src/basic/ordered-set.h @@ -74,6 +74,10 @@ static inline char** ordered_set_get_strv(OrderedSet *s) { return _hashmap_get_strv(HASHMAP_BASE((OrderedHashmap*) s)); } +static inline int ordered_set_reserve(OrderedSet *s, unsigned entries_add) { + return ordered_hashmap_reserve((OrderedHashmap*) s, entries_add); +} + int ordered_set_consume(OrderedSet *s, void *p); int _ordered_set_put_strdup(OrderedSet **s, const char *p HASHMAP_DEBUG_PARAMS); #define ordered_set_put_strdup(s, p) _ordered_set_put_strdup(s, p HASHMAP_DEBUG_SRC_ARGS) diff --git a/src/libnm-systemd-shared/src/basic/parse-util.c b/src/libnm-systemd-shared/src/basic/parse-util.c index 0a623c51..d5dcaae8 100644 --- a/src/libnm-systemd-shared/src/basic/parse-util.c +++ b/src/libnm-systemd-shared/src/basic/parse-util.c @@ -215,7 +215,7 @@ int parse_size(const char *t, uint64_t base, uint64_t *size) { e++; /* strtoull() itself would accept space/+/- */ - if (*e >= '0' && *e <= '9') { + if (ascii_isdigit(*e)) { unsigned long long l2; char *e2; @@ -606,7 +606,7 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) { /* accept any number of digits, strtoull is limited to 19 */ for (size_t i = 0; i < digits; i++,s++) { - if (*s < '0' || *s > '9') { + if (!ascii_isdigit(*s)) { if (i == 0) return -EINVAL; @@ -699,34 +699,6 @@ int parse_ip_prefix_length(const char *s, int *ret) { return 0; } -int parse_dev(const char *s, dev_t *ret) { - const char *major; - unsigned x, y; - size_t n; - int r; - - n = strspn(s, DIGITS); - if (n == 0) - return -EINVAL; - if (s[n] != ':') - return -EINVAL; - - major = strndupa_safe(s, n); - r = safe_atou(major, &x); - if (r < 0) - return r; - - r = safe_atou(s + n + 1, &y); - if (r < 0) - return r; - - if (!DEVICE_MAJOR_VALID(x) || !DEVICE_MINOR_VALID(y)) - return -ERANGE; - - *ret = makedev(x, y); - return 0; -} - int parse_oom_score_adjust(const char *s, int *ret) { int r, v; diff --git a/src/libnm-systemd-shared/src/basic/parse-util.h b/src/libnm-systemd-shared/src/basic/parse-util.h index 82731246..f2222dcf 100644 --- a/src/libnm-systemd-shared/src/basic/parse-util.h +++ b/src/libnm-systemd-shared/src/basic/parse-util.h @@ -12,7 +12,6 @@ typedef unsigned long loadavg_t; int parse_boolean(const char *v) _pure_; -int parse_dev(const char *s, dev_t *ret); int parse_pid(const char *s, pid_t* ret_pid); int parse_mode(const char *s, mode_t *ret); int parse_ifindex(const char *s); diff --git a/src/libnm-systemd-shared/src/basic/path-util.c b/src/libnm-systemd-shared/src/basic/path-util.c index f6088458..3ba3510f 100644 --- a/src/libnm-systemd-shared/src/basic/path-util.c +++ b/src/libnm-systemd-shared/src/basic/path-util.c @@ -19,17 +19,13 @@ #include "extract-word.h" #include "fd-util.h" #include "fs-util.h" -#include "glob-util.h" #include "log.h" #include "macro.h" -#include "nulstr-util.h" -#include "parse-util.h" #include "path-util.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" #include "time-util.h" -#include "utf8.h" #if 0 /* NM_IGNORED */ int path_split_and_make_absolute(const char *p, char ***ret) { @@ -380,54 +376,6 @@ char *path_simplify(char *path) { return path; } -#if 0 /* NM_IGNORED */ -int path_simplify_and_warn( - char *path, - unsigned flag, - const char *unit, - const char *filename, - unsigned line, - const char *lvalue) { - - bool fatal = flag & PATH_CHECK_FATAL; - - assert(!FLAGS_SET(flag, PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)); - - if (!utf8_is_valid(path)) - return log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, path); - - if (flag & (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)) { - bool absolute; - - absolute = path_is_absolute(path); - - if (!absolute && (flag & PATH_CHECK_ABSOLUTE)) - return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), - "%s= path is not absolute%s: %s", - lvalue, fatal ? "" : ", ignoring", path); - - if (absolute && (flag & PATH_CHECK_RELATIVE)) - return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), - "%s= path is absolute%s: %s", - lvalue, fatal ? "" : ", ignoring", path); - } - - path_simplify(path); - - if (!path_is_valid(path)) - return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), - "%s= path has invalid length (%zu bytes)%s.", - lvalue, strlen(path), fatal ? "" : ", ignoring"); - - if (!path_is_normalized(path)) - return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), - "%s= path is not normalized%s: %s", - lvalue, fatal ? "" : ", ignoring", path); - - return 0; -} -#endif /* NM_IGNORED */ - char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) { assert(path); assert(prefix); @@ -1113,7 +1061,7 @@ int path_extract_filename(const char *path, char **ret) { return -ENOMEM; *ret = TAKE_PTR(a); - return strlen(c) > (size_t)r ? O_DIRECTORY : 0; + return strlen(c) > (size_t) r ? O_DIRECTORY : 0; } int path_extract_directory(const char *path, char **ret) { @@ -1167,7 +1115,7 @@ bool filename_is_valid(const char *p) { if (isempty(p)) return false; - if (dot_or_dot_dot(p)) + if (dot_or_dot_dot(p)) /* Yes, in this context we consider "." and ".." invalid */ return false; e = strchrnul(p, '/'); @@ -1322,74 +1270,6 @@ bool valid_device_allow_pattern(const char *path) { return valid_device_node_path(path); } - -int systemd_installation_has_version(const char *root, unsigned minimal_version) { - const char *pattern; - int r; - - /* Try to guess if systemd installation is later than the specified version. This - * is hacky and likely to yield false negatives, particularly if the installation - * is non-standard. False positives should be relatively rare. - */ - - NULSTR_FOREACH(pattern, - /* /lib works for systems without usr-merge, and for systems with a sane - * usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessary - * for Gentoo which does a merge without making /lib a symlink. - */ - "lib/systemd/libsystemd-shared-*.so\0" - "lib64/systemd/libsystemd-shared-*.so\0" - "usr/lib/systemd/libsystemd-shared-*.so\0" - "usr/lib64/systemd/libsystemd-shared-*.so\0") { - - _cleanup_strv_free_ char **names = NULL; - _cleanup_free_ char *path = NULL; - char *c; - - path = path_join(root, pattern); - if (!path) - return -ENOMEM; - - r = glob_extend(&names, path, 0); - if (r == -ENOENT) - continue; - if (r < 0) - return r; - - assert_se(c = endswith(path, "*.so")); - *c = '\0'; /* truncate the glob part */ - - STRV_FOREACH(name, names) { - /* This is most likely to run only once, hence let's not optimize anything. */ - char *t, *t2; - unsigned version; - - t = startswith(*name, path); - if (!t) - continue; - - t2 = endswith(t, ".so"); - if (!t2) - continue; - - t2[0] = '\0'; /* truncate the suffix */ - - r = safe_atou(t, &version); - if (r < 0) { - log_debug_errno(r, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m", *name); - continue; - } - - log_debug("Found libsystemd shared at \"%s.so\", version %u (%s).", - *name, version, - version >= minimal_version ? "OK" : "too old"); - if (version >= minimal_version) - return true; - } - } - - return false; -} #endif /* NM_IGNORED */ bool dot_or_dot_dot(const char *path) { diff --git a/src/libnm-systemd-shared/src/basic/path-util.h b/src/libnm-systemd-shared/src/basic/path-util.h index fc41c869..c97c4540 100644 --- a/src/libnm-systemd-shared/src/basic/path-util.h +++ b/src/libnm-systemd-shared/src/basic/path-util.h @@ -45,12 +45,16 @@ #endif static inline bool is_path(const char *p) { - assert(p); + if (!p) /* A NULL pointer is definitely not a path */ + return false; + return strchr(p, '/'); } static inline bool path_is_absolute(const char *p) { - assert(p); + if (!p) /* A NULL pointer is definitely not an absolute path */ + return false; + return p[0] == '/'; } @@ -79,14 +83,6 @@ char* path_extend_internal(char **x, ...); char* path_simplify(char *path); -enum { - PATH_CHECK_FATAL = 1 << 0, /* If not set, then error message is appended with 'ignoring'. */ - PATH_CHECK_ABSOLUTE = 1 << 1, - PATH_CHECK_RELATIVE = 1 << 2, -}; - -int path_simplify_and_warn(char *path, unsigned flag, const char *unit, const char *filename, unsigned line, const char *lvalue); - static inline bool path_equal_ptr(const char *a, const char *b) { return !!a == !!b && (!a || path_equal(a, b)); } @@ -167,10 +163,10 @@ int path_extract_directory(const char *path, char **ret); bool filename_is_valid(const char *p) _pure_; bool path_is_valid_full(const char *p, bool accept_dot_dot) _pure_; static inline bool path_is_valid(const char *p) { - return path_is_valid_full(p, true); + return path_is_valid_full(p, /* accept_dot_dot= */ true); } static inline bool path_is_safe(const char *p) { - return path_is_valid_full(p, false); + return path_is_valid_full(p, /* accept_dot_dot= */ false); } bool path_is_normalized(const char *p) _pure_; @@ -183,8 +179,6 @@ bool is_device_path(const char *path); bool valid_device_node_path(const char *path); bool valid_device_allow_pattern(const char *path); -int systemd_installation_has_version(const char *root, unsigned minimal_version); - bool dot_or_dot_dot(const char *path); static inline const char *skip_dev_prefix(const char *p) { diff --git a/src/libnm-systemd-shared/src/basic/process-util.c b/src/libnm-systemd-shared/src/basic/process-util.c index 83e3a71e..1c23a2f8 100644 --- a/src/libnm-systemd-shared/src/basic/process-util.c +++ b/src/libnm-systemd-shared/src/basic/process-util.c @@ -1034,7 +1034,7 @@ bool oom_score_adjust_is_valid(int oa) { } unsigned long personality_from_string(const char *p) { - int architecture; + Architecture architecture; if (!p) return PERSONALITY_INVALID; @@ -1058,7 +1058,7 @@ unsigned long personality_from_string(const char *p) { } const char* personality_to_string(unsigned long p) { - int architecture = _ARCHITECTURE_INVALID; + Architecture architecture = _ARCHITECTURE_INVALID; if (p == PER_LINUX) architecture = native_architecture(); @@ -1156,7 +1156,7 @@ void reset_cached_pid(void) { pid_t getpid_cached(void) { static bool installed = false; - pid_t current_value; + pid_t current_value = CACHED_PID_UNSET; /* getpid_cached() is much like getpid(), but caches the value in local memory, to avoid having to invoke a * system call each time. This restores glibc behaviour from before 2.24, when getpid() was unconditionally @@ -1167,7 +1167,13 @@ pid_t getpid_cached(void) { * https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=c579f48edba88380635ab98cb612030e3ed8691e */ - current_value = __sync_val_compare_and_swap(&cached_pid, CACHED_PID_UNSET, CACHED_PID_BUSY); + __atomic_compare_exchange_n( + &cached_pid, + ¤t_value, + CACHED_PID_BUSY, + false, + __ATOMIC_SEQ_CST, + __ATOMIC_SEQ_CST); switch (current_value) { @@ -1589,6 +1595,30 @@ bool invoked_as(char *argv[], const char *token) { return strstr(last_path_component(argv[0]), token); } +bool invoked_by_systemd(void) { + int r; + + /* If the process is directly executed by PID1 (e.g. ExecStart= or generator), systemd-importd, + * or systemd-homed, then $SYSTEMD_EXEC_PID= is set, and read the command line. */ + const char *e = getenv("SYSTEMD_EXEC_PID"); + if (!e) + return false; + + if (streq(e, "*")) + /* For testing. */ + return true; + + pid_t p; + r = parse_pid(e, &p); + if (r < 0) { + /* We know that systemd sets the variable correctly. Something else must have set it. */ + log_debug_errno(r, "Failed to parse \"SYSTEMD_EXEC_PID=%s\", ignoring: %m", e); + return false; + } + + return getpid_cached() == p; +} + _noreturn_ void freeze(void) { log_close(); diff --git a/src/libnm-systemd-shared/src/basic/process-util.h b/src/libnm-systemd-shared/src/basic/process-util.h index c488c6a8..962f12d1 100644 --- a/src/libnm-systemd-shared/src/basic/process-util.h +++ b/src/libnm-systemd-shared/src/basic/process-util.h @@ -192,6 +192,8 @@ int setpriority_closest(int priority); bool invoked_as(char *argv[], const char *token); +bool invoked_by_systemd(void); + _noreturn_ void freeze(void); bool argv_looks_like_help(int argc, char **argv); diff --git a/src/libnm-systemd-shared/src/basic/random-util.c b/src/libnm-systemd-shared/src/basic/random-util.c index e3f4ee72..d576bff1 100644 --- a/src/libnm-systemd-shared/src/basic/random-util.c +++ b/src/libnm-systemd-shared/src/basic/random-util.c @@ -32,192 +32,142 @@ #include "missing_syscall.h" #include "parse-util.h" #include "random-util.h" -#include "siphash24.h" +#include "sha256.h" #include "time-util.h" -static bool srand_called = false; +/* This is a "best effort" kind of thing, but has no real security value. + * So, this should only be used by random_bytes(), which is not meant for + * crypto. This could be made better, but we're *not* trying to roll a + * userspace prng here, or even have forward secrecy, but rather just do + * the shortest thing that is at least better than libc rand(). */ +static void fallback_random_bytes(void *p, size_t n) { + static thread_local uint64_t fallback_counter = 0; + struct { + char label[32]; + uint64_t call_id, block_id; + usec_t stamp_mono, stamp_real; + pid_t pid, tid; + uint8_t auxval[16]; + } state = { + /* Arbitrary domain separation to prevent other usage of AT_RANDOM from clashing. */ + .label = "systemd fallback random bytes v1", + .call_id = fallback_counter++, + .stamp_mono = now(CLOCK_MONOTONIC), + .stamp_real = now(CLOCK_REALTIME), + .pid = getpid(), + .tid = gettid() + }; -int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { - static int have_syscall = -1; - _cleanup_close_ int fd = -1; - - /* Gathers some high-quality randomness from the kernel. This call won't block, unless the RANDOM_BLOCK - * flag is set. If it doesn't block, it will still always return some data from the kernel, regardless - * of whether the random pool is fully initialized or not. When creating cryptographic key material you - * should always use RANDOM_BLOCK. */ - - if (n == 0) - return 0; - - /* Use the getrandom() syscall unless we know we don't have it. */ - if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) { - for (;;) { -#if HAVE_GETRANDOM - ssize_t l = getrandom(p, n, FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_INSECURE); -#else - /* NetworkManager Note: systemd calls the syscall directly in this case. Don't add that workaround. - * If you don't compile against a libc that provides getrandom(), you don't get it. */ - ssize_t l = -1; - errno = ENOSYS; +#if HAVE_SYS_AUXV_H + memcpy(state.auxval, ULONG_TO_PTR(getauxval(AT_RANDOM)), sizeof(state.auxval)); #endif - if (l > 0) { - have_syscall = true; - - if ((size_t) l == n) - return 0; /* Yay, success! */ - - /* We didn't get enough data, so try again */ - assert((size_t) l < n); - p = (uint8_t*) p + l; - n -= l; - continue; - - } else if (l == 0) { - have_syscall = true; - return -EIO; - - } else if (ERRNO_IS_NOT_SUPPORTED(errno)) { - /* We lack the syscall, continue with reading from /dev/urandom. */ - have_syscall = false; - break; - - } else if (errno == EINVAL) { - /* If we previously passed GRND_INSECURE, and this flag isn't known, then - * we're likely running an old kernel which has getrandom() but not - * GRND_INSECURE. In this case, fall back to /dev/urandom. */ - if (!FLAGS_SET(flags, RANDOM_BLOCK)) - break; - - return -errno; - } else - return -errno; + while (n > 0) { + struct sha256_ctx ctx; + + sha256_init_ctx(&ctx); + sha256_process_bytes(&state, sizeof(state), &ctx); + if (n < SHA256_DIGEST_SIZE) { + uint8_t partial[SHA256_DIGEST_SIZE]; + sha256_finish_ctx(&ctx, partial); + memcpy(p, partial, n); + break; } + sha256_finish_ctx(&ctx, p); + p = (uint8_t *) p + SHA256_DIGEST_SIZE; + n -= SHA256_DIGEST_SIZE; + ++state.block_id; } - - fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - return errno == ENOENT ? -ENOSYS : -errno; - - return loop_read_exact(fd, p, n, true); } -static void clear_srand_initialization(void) { - srand_called = false; -} +void random_bytes(void *p, size_t n) { + static bool have_getrandom = true, have_grndinsecure = true; + _cleanup_close_ int fd = -1; -void initialize_srand(void) { - static bool pthread_atfork_registered = false; - unsigned x; -#if HAVE_SYS_AUXV_H - const void *auxv; -#endif - if (srand_called) + if (n == 0) return; -#if HAVE_SYS_AUXV_H - /* The kernel provides us with 16 bytes of entropy in auxv, so let's try to make use of that to seed - * the pseudo-random generator. It's better than nothing... But let's first hash it to make it harder - * to recover the original value by watching any pseudo-random bits we generate. After all the - * AT_RANDOM data might be used by other stuff too (in particular: ASLR), and we probably shouldn't - * leak the seed for that. */ - - auxv = ULONG_TO_PTR(getauxval(AT_RANDOM)); - if (auxv) { - static const uint8_t auxval_hash_key[16] = { - 0x92, 0x6e, 0xfe, 0x1b, 0xcf, 0x00, 0x52, 0x9c, 0xcc, 0x42, 0xcf, 0xdc, 0x94, 0x1f, 0x81, 0x0f - }; - - x = (unsigned) siphash24(auxv, 16, auxval_hash_key); - } else -#endif - x = 0; - - x ^= (unsigned) now(CLOCK_REALTIME); - x ^= (unsigned) gettid(); - - srand(x); - srand_called = true; - - if (!pthread_atfork_registered) { - (void) pthread_atfork(NULL, NULL, clear_srand_initialization); - pthread_atfork_registered = true; + for (;;) { + ssize_t l; + + if (!have_getrandom) + break; + + l = getrandom(p, n, have_grndinsecure ? GRND_INSECURE : GRND_NONBLOCK); + if (l > 0) { + if ((size_t) l == n) + return; /* Done reading, success. */ + p = (uint8_t *) p + l; + n -= l; + continue; /* Interrupted by a signal; keep going. */ + } else if (l == 0) + break; /* Weird, so fallback to /dev/urandom. */ + else if (ERRNO_IS_NOT_SUPPORTED(errno)) { + have_getrandom = false; + break; /* No syscall, so fallback to /dev/urandom. */ + } else if (errno == EINVAL && have_grndinsecure) { + have_grndinsecure = false; + continue; /* No GRND_INSECURE; fallback to GRND_NONBLOCK. */ + } else if (errno == EAGAIN && !have_grndinsecure) + break; /* Will block, but no GRND_INSECURE, so fallback to /dev/urandom. */ + + break; /* Unexpected, so just give up and fallback to /dev/urandom. */ } -} - -/* INT_MAX gives us only 31 bits, so use 24 out of that. */ -#if RAND_MAX >= INT_MAX -assert_cc(RAND_MAX >= 16777215); -# define RAND_STEP 3 -#else -/* SHORT_INT_MAX or lower gives at most 15 bits, we just use 8 out of that. */ -assert_cc(RAND_MAX >= 255); -# define RAND_STEP 1 -#endif -void pseudo_random_bytes(void *p, size_t n) { - uint8_t *q; - - /* This returns pseudo-random data using libc's rand() function. You probably never want to call this - * directly, because why would you use this if you can get better stuff cheaply? Use random_bytes() - * instead, see below: it will fall back to this function if there's nothing better to get, but only - * then. */ + fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd >= 0 && loop_read_exact(fd, p, n, false) == 0) + return; - initialize_srand(); + /* This is a terrible fallback. Oh well. */ + fallback_random_bytes(p, n); +} - for (q = p; q < (uint8_t*) p + n; q += RAND_STEP) { - unsigned rr; +int crypto_random_bytes(void *p, size_t n) { + static bool have_getrandom = true, seen_initialized = false; + _cleanup_close_ int fd = -1; - rr = (unsigned) rand(); + if (n == 0) + return 0; -#if RAND_STEP >= 3 - if ((size_t) (q - (uint8_t*) p + 2) < n) - q[2] = rr >> 16; -#endif -#if RAND_STEP >= 2 - if ((size_t) (q - (uint8_t*) p + 1) < n) - q[1] = rr >> 8; -#endif - q[0] = rr; + for (;;) { + ssize_t l; + + if (!have_getrandom) + break; + + l = getrandom(p, n, 0); + if (l > 0) { + if ((size_t) l == n) + return 0; /* Done reading, success. */ + p = (uint8_t *) p + l; + n -= l; + continue; /* Interrupted by a signal; keep going. */ + } else if (l == 0) + return -EIO; /* Weird, should never happen. */ + else if (ERRNO_IS_NOT_SUPPORTED(errno)) { + have_getrandom = false; + break; /* No syscall, so fallback to /dev/urandom. */ + } + return -errno; } -} -void random_bytes(void *p, size_t n) { + if (!seen_initialized) { + _cleanup_close_ int ready_fd = -1; + int r; - /* This returns high quality randomness if we can get it cheaply. If we can't because for some reason - * it is not available we'll try some crappy fallbacks. - * - * What this function will do: - * - * • Use getrandom(GRND_INSECURE) or /dev/urandom, to return high-quality random values if - * they are cheaply available, or less high-quality random values if they are not. - * - * • This function will return pseudo-random data, generated via libc rand() if nothing - * better is available. - * - * • This function will work fine in early boot - * - * • This function will always succeed - * - * What this function won't do: - * - * • This function will never fail: it will give you randomness no matter what. It might not - * be high quality, but it will return some, possibly generated via libc's rand() call. - * - * • This function will never block: if the only way to get good randomness is a blocking, - * synchronous getrandom() we'll instead provide you with pseudo-random data. - * - * This function is hence great for things like seeding hash tables, generating random numeric UNIX - * user IDs (that are checked for collisions before use) and such. - * - * This function is hence not useful for generating UUIDs or cryptographic key material. - */ - - if (genuine_random_bytes(p, n, 0) >= 0) - return; + ready_fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (ready_fd < 0) + return -errno; + r = fd_wait_for_event(ready_fd, POLLIN, USEC_INFINITY); + if (r < 0) + return r; + seen_initialized = true; + } - /* If for some reason some user made /dev/urandom unavailable to us, or the kernel has no entropy, use a PRNG instead. */ - pseudo_random_bytes(p, n); + fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return -errno; + return loop_read_exact(fd, p, n, false); } #if 0 /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/random-util.h b/src/libnm-systemd-shared/src/basic/random-util.h index ccee3279..2d998072 100644 --- a/src/libnm-systemd-shared/src/basic/random-util.h +++ b/src/libnm-systemd-shared/src/basic/random-util.h @@ -5,15 +5,8 @@ #include <stddef.h> #include <stdint.h> -typedef enum RandomFlags { - RANDOM_BLOCK = 1 << 0, /* Rather block than return crap randomness (only if the kernel supports that) */ -} RandomFlags; - -int genuine_random_bytes(void *p, size_t n, RandomFlags flags); /* returns "genuine" randomness, optionally filled up with pseudo random, if not enough is available */ -void pseudo_random_bytes(void *p, size_t n); /* returns only pseudo-randommess (but possibly seeded from something better) */ -void random_bytes(void *p, size_t n); /* returns genuine randomness if cheaply available, and pseudo randomness if not. */ - -void initialize_srand(void); +void random_bytes(void *p, size_t n); /* Returns random bytes suitable for most uses, but may be insecure sometimes. */ +int crypto_random_bytes(void *p, size_t n); /* Returns secure random bytes after waiting for the RNG to initialize. */ static inline uint64_t random_u64(void) { uint64_t u; diff --git a/src/libnm-systemd-shared/src/basic/set.h b/src/libnm-systemd-shared/src/basic/set.h index 5cae1316..618e7297 100644 --- a/src/libnm-systemd-shared/src/basic/set.h +++ b/src/libnm-systemd-shared/src/basic/set.h @@ -6,12 +6,7 @@ #include "macro.h" #define set_free_and_replace(a, b) \ - ({ \ - set_free(a); \ - (a) = (b); \ - (b) = NULL; \ - 0; \ - }) + free_and_replace_full(a, b, set_free) Set* _set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); #define set_new(ops) _set_new(ops HASHMAP_DEBUG_SRC_ARGS) @@ -127,9 +122,12 @@ int _set_ensure_consume(Set **s, const struct hash_ops *hash_ops, void *key HAS int set_consume(Set *s, void *value); -int _set_put_strdup_full(Set **s, const struct hash_ops *hash_ops, const char *p HASHMAP_DEBUG_PARAMS); -#define set_put_strdup_full(s, hash_ops, p) _set_put_strdup_full(s, hash_ops, p HASHMAP_DEBUG_SRC_ARGS) -#define set_put_strdup(s, p) set_put_strdup_full(s, &string_hash_ops_free, p) +int _set_put_strndup_full(Set **s, const struct hash_ops *hash_ops, const char *p, size_t n HASHMAP_DEBUG_PARAMS); +#define set_put_strndup_full(s, hash_ops, p, n) _set_put_strndup_full(s, hash_ops, p, n HASHMAP_DEBUG_SRC_ARGS) +#define set_put_strdup_full(s, hash_ops, p) set_put_strndup_full(s, hash_ops, p, SIZE_MAX) +#define set_put_strndup(s, p, n) set_put_strndup_full(s, &string_hash_ops_free, p, n) +#define set_put_strdup(s, p) set_put_strndup(s, p, SIZE_MAX) + int _set_put_strdupv_full(Set **s, const struct hash_ops *hash_ops, char **l HASHMAP_DEBUG_PARAMS); #define set_put_strdupv_full(s, hash_ops, l) _set_put_strdupv_full(s, hash_ops, l HASHMAP_DEBUG_SRC_ARGS) #define set_put_strdupv(s, l) set_put_strdupv_full(s, &string_hash_ops_free, l) @@ -153,3 +151,5 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free); int set_strjoin(Set *s, const char *separator, bool wrap_with_separator, char **ret); bool set_equal(Set *a, Set *b); + +bool set_fnmatch(Set *include_patterns, Set *exclude_patterns, const char *needle); diff --git a/src/libnm-systemd-shared/src/basic/socket-util.c b/src/libnm-systemd-shared/src/basic/socket-util.c index 6478fdee..c155e352 100644 --- a/src/libnm-systemd-shared/src/basic/socket-util.c +++ b/src/libnm-systemd-shared/src/basic/socket-util.c @@ -495,9 +495,7 @@ int sockaddr_pretty( if (r < 0) return -ENOMEM; } else { - char a[INET6_ADDRSTRLEN]; - - inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a)); + const char *a = IN6_ADDR_TO_STRING(&sa->in6.sin6_addr); if (include_port) { if (asprintf(&p, @@ -656,24 +654,24 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) } static const char* const netlink_family_table[] = { - [NETLINK_ROUTE] = "route", - [NETLINK_FIREWALL] = "firewall", - [NETLINK_INET_DIAG] = "inet-diag", - [NETLINK_NFLOG] = "nflog", - [NETLINK_XFRM] = "xfrm", - [NETLINK_SELINUX] = "selinux", - [NETLINK_ISCSI] = "iscsi", - [NETLINK_AUDIT] = "audit", - [NETLINK_FIB_LOOKUP] = "fib-lookup", - [NETLINK_CONNECTOR] = "connector", - [NETLINK_NETFILTER] = "netfilter", - [NETLINK_IP6_FW] = "ip6-fw", - [NETLINK_DNRTMSG] = "dnrtmsg", + [NETLINK_ROUTE] = "route", + [NETLINK_FIREWALL] = "firewall", + [NETLINK_INET_DIAG] = "inet-diag", + [NETLINK_NFLOG] = "nflog", + [NETLINK_XFRM] = "xfrm", + [NETLINK_SELINUX] = "selinux", + [NETLINK_ISCSI] = "iscsi", + [NETLINK_AUDIT] = "audit", + [NETLINK_FIB_LOOKUP] = "fib-lookup", + [NETLINK_CONNECTOR] = "connector", + [NETLINK_NETFILTER] = "netfilter", + [NETLINK_IP6_FW] = "ip6-fw", + [NETLINK_DNRTMSG] = "dnrtmsg", [NETLINK_KOBJECT_UEVENT] = "kobject-uevent", - [NETLINK_GENERIC] = "generic", - [NETLINK_SCSITRANSPORT] = "scsitransport", - [NETLINK_ECRYPTFS] = "ecryptfs", - [NETLINK_RDMA] = "rdma", + [NETLINK_GENERIC] = "generic", + [NETLINK_SCSITRANSPORT] = "scsitransport", + [NETLINK_ECRYPTFS] = "ecryptfs", + [NETLINK_RDMA] = "rdma", }; DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX); @@ -780,10 +778,10 @@ int fd_set_rcvbuf(int fd, size_t n, bool increase) { } static const char* const ip_tos_table[] = { - [IPTOS_LOWDELAY] = "low-delay", - [IPTOS_THROUGHPUT] = "throughput", + [IPTOS_LOWDELAY] = "low-delay", + [IPTOS_THROUGHPUT] = "throughput", [IPTOS_RELIABILITY] = "reliability", - [IPTOS_LOWCOST] = "low-cost", + [IPTOS_LOWCOST] = "low-cost", }; DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff); @@ -841,7 +839,7 @@ bool ifname_valid_full(const char *p, IfnameValidFlags flags) { if (!ifname_valid_char(*t)) return false; - numeric = numeric && (*t >= '0' && *t <= '9'); + numeric = numeric && ascii_isdigit(*t); } /* It's fully numeric but didn't parse as valid ifindex above? if so, it must be too large or zero or @@ -1248,7 +1246,10 @@ int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) { * addresses!), which the kernel doesn't. We do this to reduce chance of incompatibility with other apps that * do not expect non-NUL terminated file system path. */ if (l+1 > sizeof(ret->sun_path)) - return -EINVAL; + return path[0] == '@' ? -EINVAL : -ENAMETOOLONG; /* return a recognizable error if this is + * too long to fit into a sockaddr_un, but + * is a file system path, and thus might be + * connectible via O_PATH indirection. */ *ret = (struct sockaddr_un) { .sun_family = AF_UNIX, @@ -1437,3 +1438,51 @@ int socket_get_mtu(int fd, int af, size_t *ret) { return 0; } #endif /* NM_IGNORED */ + +int connect_unix_path(int fd, int dir_fd, const char *path) { + _cleanup_close_ int inode_fd = -1; + union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + }; + size_t path_len; + socklen_t salen; + + assert(fd >= 0); + assert(dir_fd == AT_FDCWD || dir_fd >= 0); + assert(path); + + /* Connects to the specified AF_UNIX socket in the file system. Works around the 108 byte size limit + * in sockaddr_un, by going via O_PATH if needed. This hence works for any kind of path. */ + + path_len = strlen(path); + + /* Refuse zero length path early, to make sure AF_UNIX stack won't mistake this for an abstract + * namespace path, since first char is NUL */ + if (path_len <= 0) + return -EINVAL; + + if (dir_fd == AT_FDCWD && path_len < sizeof(sa.un.sun_path)) { + memcpy(sa.un.sun_path, path, path_len + 1); + salen = offsetof(struct sockaddr_un, sun_path) + path_len + 1; + } else { + const char *proc; + size_t proc_len; + + /* If dir_fd is specified, then we need to go the indirect O_PATH route, because connectat() + * does not exist. If the path is too long, we also need to take the indirect route, since we + * can't fit this into a sockaddr_un directly. */ + + inode_fd = openat(dir_fd, path, O_PATH|O_CLOEXEC); + if (inode_fd < 0) + return -errno; + + proc = FORMAT_PROC_FD_PATH(inode_fd); + proc_len = strlen(proc); + + assert(proc_len < sizeof(sa.un.sun_path)); + memcpy(sa.un.sun_path, proc, proc_len + 1); + salen = offsetof(struct sockaddr_un, sun_path) + proc_len + 1; + } + + return RET_NERRNO(connect(fd, &sa.sa, salen)); +} diff --git a/src/libnm-systemd-shared/src/basic/socket-util.h b/src/libnm-systemd-shared/src/basic/socket-util.h index 709da206..cf9a0b35 100644 --- a/src/libnm-systemd-shared/src/basic/socket-util.h +++ b/src/libnm-systemd-shared/src/basic/socket-util.h @@ -131,7 +131,7 @@ static inline int fd_inc_sndbuf(int fd, size_t n) { return fd_set_sndbuf(fd, n, true); } int fd_set_rcvbuf(int fd, size_t n, bool increase); -static inline int fd_inc_rcvbuf(int fd, size_t n) { +static inline int fd_increase_rxbuf(int fd, size_t n) { return fd_set_rcvbuf(fd, n, true); } @@ -226,9 +226,9 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng strnlen(_sa->sun_path, sizeof(_sa->sun_path))+1); \ }) -#define SOCKADDR_LEN(sa) \ +#define SOCKADDR_LEN(saddr) \ ({ \ - const union sockaddr_union *__sa = &(sa); \ + const union sockaddr_union *__sa = &(saddr); \ size_t _len; \ switch (__sa->sa.sa_family) { \ case AF_INET: \ @@ -336,3 +336,5 @@ int socket_get_mtu(int fd, int af, size_t *ret); /* an initializer for struct ucred that initialized all fields to the invalid value appropriate for each */ #define UCRED_INVALID { .pid = 0, .uid = UID_INVALID, .gid = GID_INVALID } + +int connect_unix_path(int fd, int dir_fd, const char *path); diff --git a/src/libnm-systemd-shared/src/basic/stat-util.c b/src/libnm-systemd-shared/src/basic/stat-util.c index fd32f25d..8c5858ff 100644 --- a/src/libnm-systemd-shared/src/basic/stat-util.c +++ b/src/libnm-systemd-shared/src/basic/stat-util.c @@ -39,16 +39,17 @@ int is_symlink(const char *path) { } #endif /* NM_IGNORED */ -int is_dir(const char* path, bool follow) { +int is_dir_full(int atfd, const char* path, bool follow) { struct stat st; int r; - assert(path); + assert(atfd >= 0 || atfd == AT_FDCWD); + assert(atfd >= 0 || path); - if (follow) - r = stat(path, &st); + if (path) + r = fstatat(atfd, path, &st, follow ? 0 : AT_SYMLINK_NOFOLLOW); else - r = lstat(path, &st); + r = fstat(atfd, &st); if (r < 0) return -errno; @@ -56,15 +57,6 @@ int is_dir(const char* path, bool follow) { } #if 0 /* NM_IGNORED */ -int is_dir_fd(int fd) { - struct stat st; - - if (fstat(fd, &st) < 0) - return -errno; - - return !!S_ISDIR(st.st_mode); -} - int is_device_node(const char *path) { struct stat info; @@ -76,13 +68,10 @@ int is_device_node(const char *path) { return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode)); } -int dir_is_empty_at(int dir_fd, const char *path) { +int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup) { _cleanup_close_ int fd = -1; - /* Allocate space for at least 3 full dirents, since every dir has at least two entries ("." + - * ".."), and only once we have seen if there's a third we know whether the dir is empty or not. */ - DEFINE_DIRENT_BUFFER(buffer, 3); - struct dirent *de; - ssize_t n; + struct dirent *buf; + size_t m; if (path) { assert(dir_fd >= 0 || dir_fd == AT_FDCWD); @@ -104,15 +93,30 @@ int dir_is_empty_at(int dir_fd, const char *path) { return fd; } - n = getdents64(fd, &buffer, sizeof(buffer)); - if (n < 0) - return -errno; + /* Allocate space for at least 3 full dirents, since every dir has at least two entries ("." + + * ".."), and only once we have seen if there's a third we know whether the dir is empty or not. If + * 'ignore_hidden_or_backup' is true we'll allocate a bit more, since we might skip over a bunch of + * entries that we end up ignoring. */ + m = (ignore_hidden_or_backup ? 16 : 3) * DIRENT_SIZE_MAX; + buf = alloca(m); + + for (;;) { + struct dirent *de; + ssize_t n; + + n = getdents64(fd, buf, m); + if (n < 0) + return -errno; + if (n == 0) + break; - msan_unpoison(&buffer, n); + assert((size_t) n <= m); + msan_unpoison(buf, n); - FOREACH_DIRENT_IN_BUFFER(de, &buffer.de, n) - if (!dot_or_dot_dot(de->d_name)) - return 0; + FOREACH_DIRENT_IN_BUFFER(de, buf, n) + if (!(ignore_hidden_or_backup ? hidden_or_backup_file(de->d_name) : dot_or_dot_dot(de->d_name))) + return 0; + } return 1; } @@ -324,101 +328,6 @@ int fd_verify_directory(int fd) { return stat_verify_directory(&st); } - -int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret) { - const char *t; - - /* Generates the /dev/{char|block}/MAJOR:MINOR path for a dev_t */ - - if (S_ISCHR(mode)) - t = "char"; - else if (S_ISBLK(mode)) - t = "block"; - else - return -ENODEV; - - if (asprintf(ret, "/dev/%s/%u:%u", t, major(devno), minor(devno)) < 0) - return -ENOMEM; - - return 0; -} - -int device_path_make_canonical(mode_t mode, dev_t devno, char **ret) { - _cleanup_free_ char *p = NULL; - int r; - - /* Finds the canonical path for a device, i.e. resolves the /dev/{char|block}/MAJOR:MINOR path to the end. */ - - assert(ret); - - if (major(devno) == 0 && minor(devno) == 0) { - char *s; - - /* A special hack to make sure our 'inaccessible' device nodes work. They won't have symlinks in - * /dev/block/ and /dev/char/, hence we handle them specially here. */ - - if (S_ISCHR(mode)) - s = strdup("/run/systemd/inaccessible/chr"); - else if (S_ISBLK(mode)) - s = strdup("/run/systemd/inaccessible/blk"); - else - return -ENODEV; - - if (!s) - return -ENOMEM; - - *ret = s; - return 0; - } - - r = device_path_make_major_minor(mode, devno, &p); - if (r < 0) - return r; - - return chase_symlinks(p, NULL, 0, ret, NULL); -} - -int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno) { - mode_t mode; - dev_t devno; - int r; - - /* Tries to extract the major/minor directly from the device path if we can. Handles /dev/block/ and /dev/char/ - * paths, as well out synthetic inaccessible device nodes. Never goes to disk. Returns -ENODEV if the device - * path cannot be parsed like this. */ - - if (path_equal(path, "/run/systemd/inaccessible/chr")) { - mode = S_IFCHR; - devno = makedev(0, 0); - } else if (path_equal(path, "/run/systemd/inaccessible/blk")) { - mode = S_IFBLK; - devno = makedev(0, 0); - } else { - const char *w; - - w = path_startswith(path, "/dev/block/"); - if (w) - mode = S_IFBLK; - else { - w = path_startswith(path, "/dev/char/"); - if (!w) - return -ENODEV; - - mode = S_IFCHR; - } - - r = parse_dev(w, &devno); - if (r < 0) - return r; - } - - if (ret_mode) - *ret_mode = mode; - if (ret_devno) - *ret_devno = devno; - - return 0; -} #endif /* NM_IGNORED */ int proc_mounted(void) { diff --git a/src/libnm-systemd-shared/src/basic/stat-util.h b/src/libnm-systemd-shared/src/basic/stat-util.h index 37513a43..56f15534 100644 --- a/src/libnm-systemd-shared/src/basic/stat-util.h +++ b/src/libnm-systemd-shared/src/basic/stat-util.h @@ -13,21 +13,18 @@ #include "missing_stat.h" int is_symlink(const char *path); -int is_dir(const char *path, bool follow); -int is_dir_fd(int fd); -int is_device_node(const char *path); - -int dir_is_empty_at(int dir_fd, const char *path); -static inline int dir_is_empty(const char *path) { - return dir_is_empty_at(AT_FDCWD, path); +int is_dir_full(int atfd, const char *fname, bool follow); +static inline int is_dir(const char *path, bool follow) { + return is_dir_full(AT_FDCWD, path, follow); } +static inline int is_dir_fd(int fd) { + return is_dir_full(fd, NULL, false); +} +int is_device_node(const char *path); -static inline int dir_is_populated(const char *path) { - int r; - r = dir_is_empty(path); - if (r < 0) - return r; - return !r; +int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup); +static inline int dir_is_empty(const char *path, bool ignore_hidden_or_backup) { + return dir_is_empty_at(AT_FDCWD, path, ignore_hidden_or_backup); } bool null_or_empty(struct stat *st) _pure_; @@ -71,29 +68,6 @@ int fd_verify_regular(int fd); int stat_verify_directory(const struct stat *st); int fd_verify_directory(int fd); -/* glibc and the Linux kernel have different ideas about the major/minor size. These calls will check whether the - * specified major is valid by the Linux kernel's standards, not by glibc's. Linux has 20bits of minor, and 12 bits of - * major space. See MINORBITS in linux/kdev_t.h in the kernel sources. (If you wonder why we define _y here, instead of - * comparing directly >= 0: it's to trick out -Wtype-limits, which would otherwise complain if the type is unsigned, as - * such a test would be pointless in such a case.) */ - -#define DEVICE_MAJOR_VALID(x) \ - ({ \ - typeof(x) _x = (x), _y = 0; \ - _x >= _y && _x < (UINT32_C(1) << 12); \ - \ - }) - -#define DEVICE_MINOR_VALID(x) \ - ({ \ - typeof(x) _x = (x), _y = 0; \ - _x >= _y && _x < (UINT32_C(1) << 20); \ - }) - -int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret); -int device_path_make_canonical(mode_t mode, dev_t devno, char **ret); -int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno); - int proc_mounted(void); bool stat_inode_same(const struct stat *a, const struct stat *b); @@ -119,9 +93,3 @@ int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct s struct new_statx nsx; \ } var #endif - -static inline bool devid_set_and_equal(dev_t a, dev_t b) { - /* Returns true if a and b definitely refer to the same device. If either is zero, this means "don't - * know" and we'll return false */ - return a == b && a != 0; -} diff --git a/src/libnm-systemd-shared/src/basic/string-util.c b/src/libnm-systemd-shared/src/basic/string-util.c index fea22ec8..7cd4b055 100644 --- a/src/libnm-systemd-shared/src/basic/string-util.c +++ b/src/libnm-systemd-shared/src/basic/string-util.c @@ -1171,4 +1171,31 @@ bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok) { return in_charset(s1, ok) && in_charset(s2, ok); } + +char *string_replace_char(char *str, char old_char, char new_char) { + assert(str); + assert(old_char != '\0'); + assert(new_char != '\0'); + assert(old_char != new_char); + + for (char *p = strchr(str, old_char); p; p = strchr(p + 1, old_char)) + *p = new_char; + + return str; +} + +size_t strspn_from_end(const char *str, const char *accept) { + size_t n = 0; + + if (isempty(str)) + return 0; + + if (isempty(accept)) + return 0; + + for (const char *p = str + strlen(str); p > str && strchr(accept, p[-1]); p--) + n++; + + return n; +} #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/string-util.h b/src/libnm-systemd-shared/src/basic/string-util.h index a1d88fbb..1dd46f7f 100644 --- a/src/libnm-systemd-shared/src/basic/string-util.h +++ b/src/libnm-systemd-shared/src/basic/string-util.h @@ -10,17 +10,18 @@ #include "string-util-fundamental.h" /* What is interpreted as whitespace? */ -#define WHITESPACE " \t\n\r" -#define NEWLINE "\n\r" -#define QUOTES "\"\'" -#define COMMENTS "#;" -#define GLOB_CHARS "*?[" -#define DIGITS "0123456789" -#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz" -#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" -#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS -#define ALPHANUMERICAL LETTERS DIGITS -#define HEXDIGITS DIGITS "abcdefABCDEF" +#define WHITESPACE " \t\n\r" +#define NEWLINE "\n\r" +#define QUOTES "\"\'" +#define COMMENTS "#;" +#define GLOB_CHARS "*?[" +#define DIGITS "0123456789" +#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz" +#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS +#define ALPHANUMERICAL LETTERS DIGITS +#define HEXDIGITS DIGITS "abcdefABCDEF" +#define LOWERCASE_HEXDIGITS DIGITS "abcdef" static inline char* strstr_ptr(const char *haystack, const char *needle) { if (!haystack || !needle) @@ -28,10 +29,6 @@ static inline char* strstr_ptr(const char *haystack, const char *needle) { return strstr(haystack, needle); } -static inline const char* strempty(const char *s) { - return s ?: ""; -} - static inline const char* strnull(const char *s) { return s ?: "(null)"; } @@ -180,13 +177,6 @@ int free_and_strndup(char **p, const char *s, size_t l); bool string_is_safe(const char *p) _pure_; -static inline size_t strlen_ptr(const char *s) { - if (!s) - return 0; - - return strlen(s); -} - DISABLE_WARNING_STRINGOP_TRUNCATION; static inline void strncpy_exact(char *buf, const char *src, size_t buf_len) { strncpy(buf, src, buf_len); @@ -232,3 +222,7 @@ static inline int string_contains_word(const char *string, const char *separator } bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok); + +char *string_replace_char(char *str, char old_char, char new_char); + +size_t strspn_from_end(const char *str, const char *accept); diff --git a/src/libnm-systemd-shared/src/basic/strv.c b/src/libnm-systemd-shared/src/basic/strv.c index f2cb80fe..d9c58633 100644 --- a/src/libnm-systemd-shared/src/basic/strv.c +++ b/src/libnm-systemd-shared/src/basic/strv.c @@ -357,7 +357,7 @@ int strv_split_colon_pairs(char ***t, const char *s) { } #endif /* NM_IGNORED */ -char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) { +char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separator) { char *r, *e; size_t n, k, m; @@ -367,7 +367,7 @@ char* strv_join_full(char * const *l, const char *separator, const char *prefix, k = strlen(separator); m = strlen_ptr(prefix); - if (unescape_separators) /* If there separator is multi-char, we won't know how to escape it. */ + if (escape_separator) /* If the separator was multi-char, we wouldn't know how to escape it. */ assert(k == 1); n = 0; @@ -375,7 +375,7 @@ char* strv_join_full(char * const *l, const char *separator, const char *prefix, if (s != l) n += k; - bool needs_escaping = unescape_separators && strchr(*s, separator[0]); + bool needs_escaping = escape_separator && strchr(*s, *separator); n += m + strlen(*s) * (1 + needs_escaping); } @@ -392,11 +392,11 @@ char* strv_join_full(char * const *l, const char *separator, const char *prefix, if (prefix) e = stpcpy(e, prefix); - bool needs_escaping = unescape_separators && strchr(*s, separator[0]); + bool needs_escaping = escape_separator && strchr(*s, *separator); if (needs_escaping) for (size_t i = 0; (*s)[i]; i++) { - if ((*s)[i] == separator[0]) + if ((*s)[i] == *separator) *(e++) = '\\'; *(e++) = (*s)[i]; } @@ -409,27 +409,33 @@ char* strv_join_full(char * const *l, const char *separator, const char *prefix, return r; } -int strv_push(char ***l, char *value) { - char **c; - size_t n; +int strv_push_with_size(char ***l, size_t *n, char *value) { + /* n is a pointer to a variable to store the size of l. + * If not given (i.e. n is NULL or *n is SIZE_MAX), size will be calculated using strv_length(). + * If n is not NULL, the size after the push will be returned. + * If value is empty, no action is taken and *n is not set. */ if (!value) return 0; - n = strv_length(*l); + size_t size = n ? *n : SIZE_MAX; + if (size == SIZE_MAX) + size = strv_length(*l); /* Check for overflow */ - if (n > SIZE_MAX-2) + if (size > SIZE_MAX-2) return -ENOMEM; - c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(n + 2), sizeof(char*)); + char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(size + 2), sizeof(char*)); if (!c) return -ENOMEM; - c[n] = value; - c[n+1] = NULL; + c[size] = value; + c[size+1] = NULL; *l = c; + if (n) + *n = size + 1; return 0; } @@ -490,10 +496,10 @@ int strv_insert(char ***l, size_t position, char *value) { return free_and_replace(*l, c); } -int strv_consume(char ***l, char *value) { +int strv_consume_with_size(char ***l, size_t *n, char *value) { int r; - r = strv_push(l, value); + r = strv_push_with_size(l, n, value); if (r < 0) free(value); @@ -535,7 +541,7 @@ int strv_prepend(char ***l, const char *value) { return strv_consume_prepend(l, v); } -int strv_extend(char ***l, const char *value) { +int strv_extend_with_size(char ***l, size_t *n, const char *value) { char *v; if (!value) @@ -545,7 +551,7 @@ int strv_extend(char ***l, const char *value) { if (!v) return -ENOMEM; - return strv_consume(l, v); + return strv_consume_with_size(l, n, v); } int strv_extend_front(char ***l, const char *value) { diff --git a/src/libnm-systemd-shared/src/basic/strv.h b/src/libnm-systemd-shared/src/basic/strv.h index 2a858326..87ec6337 100644 --- a/src/libnm-systemd-shared/src/basic/strv.h +++ b/src/libnm-systemd-shared/src/basic/strv.h @@ -35,18 +35,36 @@ size_t strv_length(char * const *l) _pure_; int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates); int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix); int strv_prepend(char ***l, const char *value); -int strv_extend(char ***l, const char *value); + +/* _with_size() are lower-level functions where the size can be provided externally, + * which allows us to skip iterating over the strv to find the end, which saves + * a bit of time and reduces the complexity of appending from O(n²) to O(n). */ + +int strv_extend_with_size(char ***l, size_t *n, const char *value); +static inline int strv_extend(char ***l, const char *value) { + return strv_extend_with_size(l, NULL, value); +} + int strv_extendf(char ***l, const char *format, ...) _printf_(2,0); int strv_extend_front(char ***l, const char *value); -int strv_push(char ***l, char *value); + +int strv_push_with_size(char ***l, size_t *n, char *value); +static inline int strv_push(char ***l, char *value) { + return strv_push_with_size(l, NULL, value); +} int strv_push_pair(char ***l, char *a, char *b); + int strv_insert(char ***l, size_t position, char *value); static inline int strv_push_prepend(char ***l, char *value) { return strv_insert(l, 0, value); } -int strv_consume(char ***l, char *value); +int strv_consume_with_size(char ***l, size_t *n, char *value); +static inline int strv_consume(char ***l, char *value) { + return strv_consume_with_size(l, NULL, value); +} + int strv_consume_pair(char ***l, char *a, char *b); int strv_consume_prepend(char ***l, char *value); @@ -77,7 +95,7 @@ int strv_split_full(char ***t, const char *s, const char *separators, ExtractFla static inline char** strv_split(const char *s, const char *separators) { char **ret; - if (strv_split_full(&ret, s, separators, 0) < 0) + if (strv_split_full(&ret, s, separators, EXTRACT_RETAIN_ESCAPE) < 0) return NULL; return ret; @@ -101,7 +119,7 @@ static inline char** strv_split_newlines(const char *s) { * string in the vector is an empty string. */ int strv_split_colon_pairs(char ***t, const char *s); -char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separtor); +char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separator); static inline char *strv_join(char * const *l, const char *separator) { return strv_join_full(l, separator, NULL, false); } @@ -122,12 +140,6 @@ static inline int strv_from_nulstr(char ***a, const char *nulstr) { bool strv_overlap(char * const *a, char * const *b) _pure_; -#define _STRV_FOREACH(s, l, i) \ - for (typeof(*(l)) *s, *i = (l); (s = i) && *i; i++) - -#define STRV_FOREACH(s, l) \ - _STRV_FOREACH(s, l, UNIQ_T(i, UNIQ)) - #define _STRV_FOREACH_BACKWARDS(s, l, h, i) \ for (typeof(*(l)) *s, *h = (l), *i = ({ \ size_t _len = strv_length(h); \ @@ -246,14 +258,7 @@ int strv_extend_n(char ***l, const char *value, size_t n); int fputstrv(FILE *f, char * const *l, const char *separator, bool *space); #define strv_free_and_replace(a, b) \ - ({ \ - char ***_a = &(a); \ - char ***_b = &(b); \ - strv_free(*_a); \ - (*_a) = (*_b); \ - (*_b) = NULL; \ - 0; \ - }) + free_and_replace_full(a, b, strv_free) extern const struct hash_ops string_strv_hash_ops; int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS); diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c index 8476a650..0c8f4929 100644 --- a/src/libnm-systemd-shared/src/basic/time-util.c +++ b/src/libnm-systemd-shared/src/basic/time-util.c @@ -31,10 +31,10 @@ static clockid_t map_clock_id(clockid_t c) { - /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will - * fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is - * when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on - * those archs. */ + /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, + * clock_gettime() will fail for them. Since they are essentially the same as their non-ALARM + * pendants (their only difference is when timers are set on them), let's just map them + * accordingly. This way, we can get the correct time even on those archs. */ switch (c) { @@ -298,8 +298,8 @@ char *format_timestamp_style( usec_t t, TimestampStyle style) { - /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our - * generated timestamps may be parsed with parse_timestamp(), and always read the same. */ + /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that + * our generated timestamps may be parsed with parse_timestamp(), and always read the same. */ static const char * const weekdays[] = { [0] = "Sun", [1] = "Mon", @@ -386,8 +386,8 @@ char *format_timestamp_style( /* Append the timezone */ n = strlen(buf); if (utc) { - /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the - * obsolete "GMT" instead. */ + /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() + * normally uses the obsolete "GMT" instead. */ if (n + 5 > l) return NULL; /* "UTC" doesn't fit. */ @@ -402,12 +402,14 @@ char *format_timestamp_style( /* The full time zone does not fit in. Yuck. */ if (n + 1 + _POSIX_TZNAME_MAX + 1 > l) - return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */ - - /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX - * minimum time zone length. In this case suppress the timezone entirely, in order not to dump - * an overly long, hard to read string on the user. This should be safe, because the user will - * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */ + return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that + * case, complain that it doesn't fit. */ + + /* So the time zone doesn't fit in fully, but the caller passed enough space for the + * POSIX minimum time zone length. In this case suppress the timezone entirely, in + * order not to dump an overly long, hard to read string on the user. This should be + * safe, because the user will assume the local timezone anyway if none is shown. And + * so does parse_timestamp(). */ } else { buf[n++] = ' '; strcpy(buf + n, tm.tm_zone); @@ -593,7 +595,7 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { t = b; } - n = MIN((size_t) k, l); + n = MIN((size_t) k, l-1); l -= n; p += n; @@ -705,10 +707,11 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) { tzset(); - /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only - * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because - * there are no nice APIs available to cover this. By accepting the local time zone strings, we make - * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't + /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note + * that we only support the local timezones here, nothing else. Not because we + * wouldn't want to, but simply because there are no nice APIs available to cover + * this. By accepting the local time zone strings, we make sure that all timestamps + * written by format_timestamp() can be parsed correctly, even though we don't * support arbitrary timezone specifications. */ for (j = 0; j <= 1; j++) { @@ -1414,9 +1417,8 @@ int verify_timezone(const char *name, int log_level) { return -EINVAL; for (p = name; *p; p++) { - if (!(*p >= '0' && *p <= '9') && - !(*p >= 'a' && *p <= 'z') && - !(*p >= 'A' && *p <= 'Z') && + if (!ascii_isdigit(*p) && + !ascii_isalpha(*p) && !IN_SET(*p, '-', '_', '+', '/')) return -EINVAL; diff --git a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h index 1c198f6a..0ed99403 100644 --- a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h +++ b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h @@ -2,36 +2,50 @@ #pragma once #ifndef SD_BOOT -#include <assert.h> +# include <assert.h> #endif #include <limits.h> -#include "types-fundamental.h" +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> #define _align_(x) __attribute__((__aligned__(x))) +#define _alignas_(x) __attribute__((__aligned__(__alignof__(x)))) +#define _alignptr_ __attribute__((__aligned__(sizeof(void *)))) +#define _cleanup_(x) __attribute__((__cleanup__(x))) #define _const_ __attribute__((__const__)) -#define _pure_ __attribute__((__pure__)) -#define _section_(x) __attribute__((__section__(x))) +#define _deprecated_ __attribute__((__deprecated__)) +#define _destructor_ __attribute__((__destructor__)) +#define _hidden_ __attribute__((__visibility__("hidden"))) +#define _likely_(x) (__builtin_expect(!!(x), 1)) +#define _malloc_ __attribute__((__malloc__)) +#define _noreturn_ _Noreturn #define _packed_ __attribute__((__packed__)) +#define _printf_(a, b) __attribute__((__format__(printf, a, b))) +#define _public_ __attribute__((__visibility__("default"))) +#define _pure_ __attribute__((__pure__)) #define _retain_ __attribute__((__retain__)) -#define _used_ __attribute__((__used__)) -#define _unused_ __attribute__((__unused__)) -#define _cleanup_(x) __attribute__((__cleanup__(x))) -#define _likely_(x) (__builtin_expect(!!(x), 1)) +#define _returns_nonnull_ __attribute__((__returns_nonnull__)) +#define _section_(x) __attribute__((__section__(x))) +#define _sentinel_ __attribute__((__sentinel__)) #define _unlikely_(x) (__builtin_expect(!!(x), 0)) -#if __GNUC__ >= 7 -#define _fallthrough_ __attribute__((__fallthrough__)) +#define _unused_ __attribute__((__unused__)) +#define _used_ __attribute__((__used__)) +#define _warn_unused_result_ __attribute__((__warn_unused_result__)) +#define _weak_ __attribute__((__weak__)) +#define _weakref_(x) __attribute__((__weakref__(#x))) + +#ifdef __clang__ +# define _alloc_(...) #else -#define _fallthrough_ +# define _alloc_(...) __attribute__((__alloc_size__(__VA_ARGS__))) #endif -/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc - * compiler versions */ -#ifndef _noreturn_ -#if __STDC_VERSION__ >= 201112L -#define _noreturn_ _Noreturn + +#if __GNUC__ >= 7 || (defined(__clang__) && __clang_major__ >= 10) +# define _fallthrough_ __attribute__((__fallthrough__)) #else -#define _noreturn_ __attribute__((__noreturn__)) -#endif +# define _fallthrough_ #endif #define XSTRINGIFY(x) #x @@ -53,7 +67,7 @@ #define CONCATENATE(x, y) XCONCATENATE(x, y) #ifdef SD_BOOT - void efi_assert(const char *expr, const char *file, unsigned line, const char *function) _noreturn_; + _noreturn_ void efi_assert(const char *expr, const char *file, unsigned line, const char *function); #ifdef NDEBUG #define assert(expr) @@ -62,10 +76,8 @@ #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); }) #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __PRETTY_FUNCTION__) #endif + #define static_assert _Static_assert #define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); }) - - #define memcpy(a, b, c) CopyMem((a), (b), (c)) - #define free(a) FreePool(a) #endif /* This passes the argument through after (if asserts are enabled) checking that it is not null. */ @@ -83,15 +95,22 @@ _expr_; \ }) -#if defined(static_assert) -#define assert_cc(expr) \ - static_assert(expr, #expr) -#else -#define assert_cc(expr) \ - struct CONCATENATE(_assert_struct_, __COUNTER__) { \ - char x[(expr) ? 0 : -1]; \ - } -#endif +#define ASSERT_NONNEG(expr) \ + ({ \ + typeof(expr) _expr_ = (expr), _zero = 0; \ + assert(_expr_ >= _zero); \ + _expr_; \ + }) + +#define ASSERT_SE_NONNEG(expr) \ + ({ \ + typeof(expr) _expr_ = (expr), _zero = 0; \ + assert_se(_expr_ >= _zero); \ + _expr_; \ + }) + +#define assert_cc(expr) static_assert(expr, #expr) + #define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq)) #define UNIQ __COUNTER__ @@ -101,10 +120,10 @@ * on this macro will run concurrently to all other code conditionalized * the same way, there's no ordering or completion enforced. */ #define ONCE __ONCE(UNIQ_T(_once_, UNIQ)) -#define __ONCE(o) \ - ({ \ - static sd_bool (o) = sd_false; \ - __sync_bool_compare_and_swap(&(o), sd_false, sd_true); \ +#define __ONCE(o) \ + ({ \ + static bool (o) = false; \ + __atomic_exchange_n(&(o), true, __ATOMIC_SEQ_CST); \ }) #undef MAX @@ -181,6 +200,27 @@ MIN(_c, z); \ }) +/* Returns true if the passed integer is a positive power of two */ +#define CONST_ISPOWEROF2(x) \ + ((x) > 0 && ((x) & ((x) - 1)) == 0) + +#if 0 /* NM_IGNORED */ +#define ISPOWEROF2(x) \ + __builtin_choose_expr( \ + __builtin_constant_p(x), \ + CONST_ISPOWEROF2(x), \ + ({ \ + const typeof(x) _x = (x); \ + CONST_ISPOWEROF2(_x); \ + })) +#else /* NM_IGNORED */ +#define ISPOWEROF2(x) \ + ({ \ + const typeof(x) _x = (x); \ + CONST_ISPOWEROF2(_x); \ + }) +#endif /* NM_IGNORED */ + #define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b)) #define __LESS_BY(aq, a, bq, b) \ ({ \ @@ -254,7 +294,7 @@ #define IN_SET(x, ...) \ ({ \ - sd_bool _found = sd_false; \ + bool _found = false; \ /* If the build breaks in the line below, you need to extend the case macros. (We use "long double" as \ * type for the array, in the hope that checkers such as ubsan don't complain that the initializers for \ * the array are not representable by the base type. Ideally we'd use typeof(x) as base type, but that \ @@ -263,7 +303,7 @@ assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \ switch (x) { \ FOR_EACH_MAKE_CASE(__VA_ARGS__) \ - _found = sd_true; \ + _found = true; \ break; \ default: \ break; \ @@ -295,19 +335,7 @@ }) static inline size_t ALIGN_TO(size_t l, size_t ali) { - /* sd-boot uses UINTN for size_t, let's make sure SIZE_MAX is correct. */ - assert_cc(SIZE_MAX == ~(size_t)0); - - /* Check that alignment is exponent of 2 */ -#if SIZE_MAX == UINT_MAX - assert(__builtin_popcount(ali) == 1); -#elif SIZE_MAX == ULONG_MAX - assert(__builtin_popcountl(ali) == 1); -#elif SIZE_MAX == ULLONG_MAX - assert(__builtin_popcountll(ali) == 1); -#else - #error "Unexpected size_t" -#endif + assert(ISPOWEROF2(ali)); if (l > SIZE_MAX - (ali - 1)) return SIZE_MAX; /* indicate overflow */ @@ -315,12 +343,20 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) { return ((l + ali - 1) & ~(ali - 1)); } +#define ALIGN4(l) ALIGN_TO(l, 4) +#define ALIGN8(l) ALIGN_TO(l, 8) +#ifndef SD_BOOT +/* libefi also provides ALIGN, and we do not use them in sd-boot explicitly. */ +#define ALIGN(l) ALIGN_TO(l, sizeof(void*)) +#define ALIGN_PTR(p) ((void*) ALIGN((uintptr_t) (p))) +#endif + /* Same as ALIGN_TO but callable in constant contexts. */ #define CONST_ALIGN_TO(l, ali) \ __builtin_choose_expr( \ __builtin_constant_p(l) && \ __builtin_constant_p(ali) && \ - __builtin_popcountll(ali) == 1 && /* is power of 2? */ \ + CONST_ISPOWEROF2(ali) && \ (l <= SIZE_MAX - (ali - 1)), /* overflow? */ \ ((l) + (ali) - 1) & ~((ali) - 1), \ VOID_0) diff --git a/src/libnm-systemd-shared/src/fundamental/sha256.c b/src/libnm-systemd-shared/src/fundamental/sha256.c new file mode 100644 index 00000000..0f3872ae --- /dev/null +++ b/src/libnm-systemd-shared/src/fundamental/sha256.c @@ -0,0 +1,293 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "nm-sd-adapt-shared.h" + +/* Stolen from glibc and converted to our style. In glibc it comes with the following copyright blurb: */ + +/* Functions to compute SHA256 message digest of files or memory blocks. + according to the definition of SHA256 in FIPS 180-2. + Copyright (C) 2007-2022 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C 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.1 of the License, or (at your option) any later version. + + The GNU C 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 the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#include <stdbool.h> +#ifdef SD_BOOT +# include "efi-string.h" +#else +# include <string.h> +#endif + +#include "macro-fundamental.h" +#include "sha256.h" + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +# define SWAP64(n) \ + (((n) << 56) \ + | (((n) & 0xff00) << 40) \ + | (((n) & 0xff0000) << 24) \ + | (((n) & 0xff000000) << 8) \ + | (((n) >> 8) & 0xff000000) \ + | (((n) >> 24) & 0xff0000) \ + | (((n) >> 40) & 0xff00) \ + | ((n) >> 56)) +#else +# define SWAP(n) (n) +# define SWAP64(n) (n) +#endif + +/* The condition below is from glibc's string/string-inline.c. + * See definition of _STRING_INLINE_unaligned. */ +#if !defined(__mc68020__) && !defined(__s390__) && !defined(__i386__) +# define UNALIGNED_P(p) (((uintptr_t) p) % __alignof__(uint32_t) != 0) +#else +# define UNALIGNED_P(p) false +#endif + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (FIPS 180-2:5.1.1) */ +static const uint8_t fillbuf[64] = { + 0x80, 0 /* , 0, 0, ... */ +}; + +/* Constants for SHA256 from FIPS 180-2:4.2.2. */ +static const uint32_t K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +static void sha256_process_block(const void *, size_t, struct sha256_ctx *); + +/* Initialize structure containing state of computation. + (FIPS 180-2:5.3.2) */ +void sha256_init_ctx(struct sha256_ctx *ctx) { + assert(ctx); + + ctx->H[0] = 0x6a09e667; + ctx->H[1] = 0xbb67ae85; + ctx->H[2] = 0x3c6ef372; + ctx->H[3] = 0xa54ff53a; + ctx->H[4] = 0x510e527f; + ctx->H[5] = 0x9b05688c; + ctx->H[6] = 0x1f83d9ab; + ctx->H[7] = 0x5be0cd19; + + ctx->total64 = 0; + ctx->buflen = 0; +} + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. */ +void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf) { + /* Take yet unprocessed bytes into account. */ + uint32_t bytes = ctx->buflen; + size_t pad; + + assert(ctx); + assert(resbuf); + + /* Now count remaining bytes. */ + ctx->total64 += bytes; + + pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; + memcpy(&ctx->buffer[bytes], fillbuf, pad); + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + ctx->buffer32[(bytes + pad + 4) / 4] = SWAP(ctx->total[TOTAL64_low] << 3); + ctx->buffer32[(bytes + pad) / 4] = SWAP((ctx->total[TOTAL64_high] << 3) + | (ctx->total[TOTAL64_low] >> 29)); + + /* Process last bytes. */ + sha256_process_block(ctx->buffer, bytes + pad + 8, ctx); + + /* Put result from CTX in first 32 bytes following RESBUF. */ + for (size_t i = 0; i < 8; ++i) + if (UNALIGNED_P(resbuf)) + memcpy((uint8_t*) resbuf + i * sizeof(uint32_t), (uint32_t[]) { SWAP(ctx->H[i]) }, sizeof(uint32_t)); + else + ((uint32_t *) resbuf)[i] = SWAP(ctx->H[i]); + + return resbuf; +} + +void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx) { + assert(buffer); + assert(ctx); + + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + + if (ctx->buflen != 0) { + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + memcpy(&ctx->buffer[left_over], buffer, add); + ctx->buflen += add; + + if (ctx->buflen > 64) { + sha256_process_block(ctx->buffer, ctx->buflen & ~63, ctx); + + ctx->buflen &= 63; + /* The regions in the following copy operation cannot overlap. */ + memcpy(ctx->buffer, &ctx->buffer[(left_over + add) & ~63], + ctx->buflen); + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len >= 64) { + if (UNALIGNED_P(buffer)) + while (len > 64) { + memcpy(ctx->buffer, buffer, 64); + sha256_process_block(ctx->buffer, 64, ctx); + buffer = (const char *) buffer + 64; + len -= 64; + } + else { + sha256_process_block(buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } + } + + /* Move remaining bytes into internal buffer. */ + if (len > 0) { + size_t left_over = ctx->buflen; + + memcpy(&ctx->buffer[left_over], buffer, len); + left_over += len; + if (left_over >= 64) { + sha256_process_block(ctx->buffer, 64, ctx); + left_over -= 64; + memcpy(ctx->buffer, &ctx->buffer[64], left_over); + } + ctx->buflen = left_over; + } +} + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. */ +static void sha256_process_block(const void *buffer, size_t len, struct sha256_ctx *ctx) { + const uint32_t *words = buffer; + size_t nwords = len / sizeof(uint32_t); + + assert(buffer); + assert(ctx); + + uint32_t a = ctx->H[0]; + uint32_t b = ctx->H[1]; + uint32_t c = ctx->H[2]; + uint32_t d = ctx->H[3]; + uint32_t e = ctx->H[4]; + uint32_t f = ctx->H[5]; + uint32_t g = ctx->H[6]; + uint32_t h = ctx->H[7]; + + /* First increment the byte count. FIPS 180-2 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. */ + ctx->total64 += len; + + /* Process all bytes in the buffer with 64 bytes in each round of + the loop. */ + while (nwords > 0) { + uint32_t W[64]; + uint32_t a_save = a; + uint32_t b_save = b; + uint32_t c_save = c; + uint32_t d_save = d; + uint32_t e_save = e; + uint32_t f_save = f; + uint32_t g_save = g; + uint32_t h_save = h; + + /* Operators defined in FIPS 180-2:4.1.2. */ +#define Ch(x, y, z) ((x & y) ^ (~x & z)) +#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) +#define S0(x) (CYCLIC (x, 2) ^ CYCLIC (x, 13) ^ CYCLIC (x, 22)) +#define S1(x) (CYCLIC (x, 6) ^ CYCLIC (x, 11) ^ CYCLIC (x, 25)) +#define R0(x) (CYCLIC (x, 7) ^ CYCLIC (x, 18) ^ (x >> 3)) +#define R1(x) (CYCLIC (x, 17) ^ CYCLIC (x, 19) ^ (x >> 10)) + + /* It is unfortunate that C does not provide an operator for + cyclic rotation. Hope the C compiler is smart enough. */ +#define CYCLIC(w, s) ((w >> s) | (w << (32 - s))) + + /* Compute the message schedule according to FIPS 180-2:6.2.2 step 2. */ + for (size_t t = 0; t < 16; ++t) { + W[t] = SWAP (*words); + ++words; + } + for (size_t t = 16; t < 64; ++t) + W[t] = R1 (W[t - 2]) + W[t - 7] + R0 (W[t - 15]) + W[t - 16]; + + /* The actual computation according to FIPS 180-2:6.2.2 step 3. */ + for (size_t t = 0; t < 64; ++t) { + uint32_t T1 = h + S1 (e) + Ch (e, f, g) + K[t] + W[t]; + uint32_t T2 = S0 (a) + Maj (a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } + + /* Add the starting values of the context according to FIPS 180-2:6.2.2 + step 4. */ + a += a_save; + b += b_save; + c += c_save; + d += d_save; + e += e_save; + f += f_save; + g += g_save; + h += h_save; + + /* Prepare for the next round. */ + nwords -= 16; + } + + /* Put checksum in context given as argument. */ + ctx->H[0] = a; + ctx->H[1] = b; + ctx->H[2] = c; + ctx->H[3] = d; + ctx->H[4] = e; + ctx->H[5] = f; + ctx->H[6] = g; + ctx->H[7] = h; +} diff --git a/src/libnm-systemd-shared/src/fundamental/sha256.h b/src/libnm-systemd-shared/src/fundamental/sha256.h new file mode 100644 index 00000000..f296f76a --- /dev/null +++ b/src/libnm-systemd-shared/src/fundamental/sha256.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "stdint.h" + +#define SHA256_DIGEST_SIZE 32 + +struct sha256_ctx { + uint32_t H[8]; + + union { + uint64_t total64; +#define TOTAL64_low (1 - (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) +#define TOTAL64_high (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + uint32_t total[2]; + }; + + uint32_t buflen; + + union { + uint8_t buffer[128]; /* NB: always correctly aligned for UINT32. */ + uint32_t buffer32[32]; + uint64_t buffer64[16]; + }; +}; + +void sha256_init_ctx(struct sha256_ctx *ctx); +void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf); +void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx); diff --git a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c index 73569765..33f2d860 100644 --- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c +++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c @@ -3,10 +3,10 @@ #include "nm-sd-adapt-shared.h" #ifndef SD_BOOT -#include <ctype.h> - -#include "macro.h" +# include <ctype.h> #endif + +#include "macro-fundamental.h" #include "string-util-fundamental.h" sd_char *startswith(const sd_char *s, const sd_char *prefix) { @@ -80,36 +80,27 @@ sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) { } #if 0 /* NM_IGNORED */ -#ifdef SD_BOOT -static sd_bool isdigit(sd_char a) { - return a >= '0' && a <= '9'; -} -#endif - -static sd_bool is_alpha(sd_char a) { - /* Locale independent version of isalpha(). */ - return (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z'); -} - -static sd_bool is_valid_version_char(sd_char a) { - return isdigit(a) || is_alpha(a) || IN_SET(a, '~', '-', '^', '.'); +static bool is_valid_version_char(sd_char a) { + return ascii_isdigit(a) || ascii_isalpha(a) || IN_SET(a, '~', '-', '^', '.'); } -sd_int strverscmp_improved(const sd_char *a, const sd_char *b) { - - /* This is based on RPM's rpmvercmp(). But this explicitly handles '-' and '.', as we usually - * want to directly compare strings which contain both version and release; e.g. +int strverscmp_improved(const sd_char *a, const sd_char *b) { + /* This function is similar to strverscmp(3), but it treats '-' and '.' as separators. + * + * The logic is based on rpm's rpmvercmp(), but unlike rpmvercmp(), it distiguishes e.g. + * '123a' and '123.a', with '123a' being newer. + * + * It allows direct comparison of strings which contain both a version and a release; e.g. * '247.2-3.1.fc33.x86_64' or '5.11.0-0.rc5.20210128git76c057c84d28.137.fc34'. - * Unlike rpmvercmp(), this distiguishes e.g. 123a and 123.a, and 123a is newer. * - * This splits the input strings into segments. Each segment is numeric or alpha, and may be + * The input string is split into segments. Each segment is numeric or alphabetic, and may be * prefixed with the following: * '~' : used for pre-releases, a segment prefixed with this is the oldest, * '-' : used for the separator between version and release, * '^' : used for patched releases, a segment with this is newer than one with '-'. * '.' : used for point releases. - * Note, no prefix segment is the newest. All non-supported characters are dropped, and - * handled as a separator of segments, e.g., 123_a is equivalent to 123a. + * Note that no prefix segment is the newest. All non-supported characters are dropped, and + * handled as a separator of segments, e.g., '123_a' is equivalent to '123a'. * * By using this, version strings can be sorted like following: * (older) 122.1 @@ -126,12 +117,12 @@ sd_int strverscmp_improved(const sd_char *a, const sd_char *b) { * (newer) 124-1 */ - if (isempty(a) || isempty(b)) - return strcmp_ptr(a, b); + a = strempty(a); + b = strempty(b); for (;;) { const sd_char *aa, *bb; - sd_int r; + int r; /* Drop leading invalid characters. */ while (*a != '\0' && !is_valid_version_char(*a)) @@ -152,7 +143,7 @@ sd_int strverscmp_improved(const sd_char *a, const sd_char *b) { } /* If at least one string reaches the end, then longer is newer. - * Note that except for '~' prefixed segments, a string has more segments is newer. + * Note that except for '~' prefixed segments, a string which has more segments is newer. * So, this check must be after the '~' check. */ if (*a == '\0' || *b == '\0') return CMP(*a, *b); @@ -188,20 +179,25 @@ sd_int strverscmp_improved(const sd_char *a, const sd_char *b) { b++; } - if (isdigit(*a) || isdigit(*b)) { + if (ascii_isdigit(*a) || ascii_isdigit(*b)) { + /* Find the leading numeric segments. One may be an empty string. So, + * numeric segments are always newer than alpha segments. */ + for (aa = a; ascii_isdigit(*aa); aa++) + ; + for (bb = b; ascii_isdigit(*bb); bb++) + ; + + /* Check if one of the strings was empty, but the other not. */ + r = CMP(a != aa, b != bb); + if (r != 0) + return r; + /* Skip leading '0', to make 00123 equivalent to 123. */ while (*a == '0') a++; while (*b == '0') b++; - /* Find the leading numeric segments. One may be an empty string. So, - * numeric segments are always newer than alpha segments. */ - for (aa = a; isdigit(*aa); aa++) - ; - for (bb = b; isdigit(*bb); bb++) - ; - /* To compare numeric segments without parsing their values, first compare the * lengths of the segments. Eg. 12345 vs 123, longer is newer. */ r = CMP(aa - a, bb - b); @@ -209,18 +205,18 @@ sd_int strverscmp_improved(const sd_char *a, const sd_char *b) { return r; /* Then, compare them as strings. */ - r = strncmp(a, b, aa - a); + r = CMP(strncmp(a, b, aa - a), 0); if (r != 0) return r; } else { /* Find the leading non-numeric segments. */ - for (aa = a; is_alpha(*aa); aa++) + for (aa = a; ascii_isalpha(*aa); aa++) ; - for (bb = b; is_alpha(*bb); bb++) + for (bb = b; ascii_isalpha(*bb); bb++) ; /* Note that the segments are usually not NUL-terminated. */ - r = strncmp(a, b, MIN(aa - a, bb - b)); + r = CMP(strncmp(a, b, MIN(aa - a, bb - b)), 0); if (r != 0) return r; @@ -230,7 +226,7 @@ sd_int strverscmp_improved(const sd_char *a, const sd_char *b) { return r; } - /* The current segments are equivalent. Let's compare the next one. */ + /* The current segments are equivalent. Let's move to the next one. */ a = aa; b = bb; } diff --git a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h index dc0c1202..ecf32e51 100644 --- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h +++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h @@ -2,54 +2,62 @@ #pragma once #ifdef SD_BOOT -#include <efi.h> -#include <efilib.h> +# include <efi.h> +# include <efilib.h> +# include "efi-string.h" #else -#include <string.h> +# include <string.h> #endif #include "macro-fundamental.h" #ifdef SD_BOOT -#define strlen(a) StrLen((a)) -#define strcmp(a, b) StrCmp((a), (b)) -#define strncmp(a, b, n) StrnCmp((a), (b), (n)) -#define strcasecmp(a, b) StriCmp((a), (b)) -#define STR_C(str) (L ## str) -#define memcmp(a, b, n) CompareMem(a, b, n) +# define strlen strlen16 +# define strcmp strcmp16 +# define strncmp strncmp16 +# define strcasecmp strcasecmp16 +# define strncasecmp strncasecmp16 +# define STR_C(str) (L ## str) +typedef char16_t sd_char; #else -#define STR_C(str) (str) +# define STR_C(str) (str) +typedef char sd_char; #endif #define streq(a,b) (strcmp((a),(b)) == 0) #define strneq(a, b, n) (strncmp((a), (b), (n)) == 0) #define strcaseeq(a,b) (strcasecmp((a),(b)) == 0) -#ifndef SD_BOOT #define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0) -#endif -static inline sd_int strcmp_ptr(const sd_char *a, const sd_char *b) { +static inline int strcmp_ptr(const sd_char *a, const sd_char *b) { if (a && b) return strcmp(a, b); return CMP(a, b); } -static inline sd_int strcasecmp_ptr(const sd_char *a, const sd_char *b) { +static inline int strcasecmp_ptr(const sd_char *a, const sd_char *b) { if (a && b) return strcasecmp(a, b); return CMP(a, b); } -static inline sd_bool streq_ptr(const sd_char *a, const sd_char *b) { +static inline bool streq_ptr(const sd_char *a, const sd_char *b) { return strcmp_ptr(a, b) == 0; } -static inline sd_bool strcaseeq_ptr(const sd_char *a, const sd_char *b) { +static inline bool strcaseeq_ptr(const sd_char *a, const sd_char *b) { return strcasecmp_ptr(a, b) == 0; } +static inline size_t strlen_ptr(const sd_char *s) { + if (!s) + return 0; + + return strlen(s); +} + sd_char *startswith(const sd_char *s, const sd_char *prefix) _pure_; #ifndef SD_BOOT sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) _pure_; @@ -57,15 +65,23 @@ sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) _pure_; sd_char *endswith(const sd_char *s, const sd_char *postfix) _pure_; sd_char *endswith_no_case(const sd_char *s, const sd_char *postfix) _pure_; -static inline sd_bool isempty(const sd_char *a) { +static inline bool isempty(const sd_char *a) { return !a || a[0] == '\0'; } -static inline const sd_char *yes_no(sd_bool b) { +static inline const sd_char *strempty(const sd_char *s) { + return s ?: STR_C(""); +} + +static inline const sd_char *yes_no(bool b) { return b ? STR_C("yes") : STR_C("no"); } -sd_int strverscmp_improved(const sd_char *a, const sd_char *b); +static inline const sd_char* comparison_operator(int result) { + return result < 0 ? STR_C("<") : result > 0 ? STR_C(">") : STR_C("=="); +} + +int strverscmp_improved(const sd_char *a, const sd_char *b); /* Like startswith(), but operates on arbitrary memory blocks */ static inline void *memory_startswith(const void *p, size_t sz, const sd_char *token) { @@ -82,3 +98,19 @@ static inline void *memory_startswith(const void *p, size_t sz, const sd_char *t return (uint8_t*) p + n; } + +#define _STRV_FOREACH(s, l, i) \ + for (typeof(*(l)) *s, *i = (l); (s = i) && *i; i++) + +#define STRV_FOREACH(s, l) \ + _STRV_FOREACH(s, l, UNIQ_T(i, UNIQ)) + +static inline bool ascii_isdigit(sd_char a) { + /* A pure ASCII, locale independent version of isdigit() */ + return a >= '0' && a <= '9'; +} + +static inline bool ascii_isalpha(sd_char a) { + /* A pure ASCII, locale independent version of isalpha() */ + return (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z'); +} diff --git a/src/libnm-systemd-shared/src/fundamental/types-fundamental.h b/src/libnm-systemd-shared/src/fundamental/types-fundamental.h deleted file mode 100644 index 5977e40c..00000000 --- a/src/libnm-systemd-shared/src/fundamental/types-fundamental.h +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -/* This defines a number of basic types that are one thing in userspace and another in the UEFI environment, - * but mostly the same in concept and behaviour. - * - * Note: if the definition of these types/values has slightly different semantics in userspace and in the - * UEFI environment then please prefix its name with "sd_" to make clear these types have special semantics, - * and *we* defined them. Otherwise, if the types are effectively 100% identical in behaviour in userspace - * and UEFI environment you can omit the prefix. (Examples: sd_char is 8 bit in userspace and 16 bit in UEFI - * space hence it should have the sd_ prefix; but size_t in userspace and UINTN in UEFI environment are 100% - * defined the same way ultimately, hence it's OK to just define size_t as alias to UINTN in UEFI - * environment, so that size_t can be used everywhere, without any "sd_" prefix.) - * - * Note: we generally prefer the userspace names of types and concepts. i.e. if in doubt please name types - * after the userspace vocabulary, and let's keep UEFI vocabulary specific to the UEFI build environment. */ - -#ifdef SD_BOOT -#include <efi.h> - -typedef BOOLEAN sd_bool; -typedef CHAR16 sd_char; -typedef INTN sd_int; -typedef UINTN size_t; - -#define sd_true TRUE -#define sd_false FALSE -#else -#include <stdbool.h> -#include <stdint.h> - -typedef bool sd_bool; -typedef char sd_char; -typedef int sd_int; - -#define sd_true true -#define sd_false false - -#endif diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.c b/src/libnm-systemd-shared/src/shared/dns-domain.c index e932b693..7bba3b18 100644 --- a/src/libnm-systemd-shared/src/shared/dns-domain.c +++ b/src/libnm-systemd-shared/src/shared/dns-domain.c @@ -9,6 +9,7 @@ #include "alloc-util.h" #include "dns-domain.h" +#include "glyph-util.h" #include "hashmap.h" #include "hexdecoct.h" #include "hostname-util.h" @@ -239,9 +240,8 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) { sz -= 2; } else if (IN_SET(*p, '_', '-') || - (*p >= '0' && *p <= '9') || - (*p >= 'a' && *p <= 'z') || - (*p >= 'A' && *p <= 'Z')) { + ascii_isdigit(*p) || + ascii_isalpha(*p)) { /* Proper character */ @@ -456,12 +456,8 @@ int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_r return r; } - if (!first) - n++; - else - first = false; - - n += r; + n += r + !first; + first = false; } finish: @@ -919,15 +915,13 @@ static bool srv_type_label_is_valid(const char *label, size_t n) { return false; /* Second char must be a letter */ - if (!(label[1] >= 'A' && label[1] <= 'Z') && - !(label[1] >= 'a' && label[1] <= 'z')) + if (!ascii_isalpha(label[1])) return false; /* Third and further chars must be alphanumeric or a hyphen */ for (k = 2; k < n; k++) { - if (!(label[k] >= 'A' && label[k] <= 'Z') && - !(label[k] >= 'a' && label[k] <= 'z') && - !(label[k] >= '0' && label[k] <= '9') && + if (!ascii_isalpha(label[k]) && + !ascii_isdigit(label[k]) && label[k] != '-') return false; } @@ -1035,10 +1029,10 @@ static bool dns_service_name_label_is_valid(const char *label, size_t n) { return dns_service_name_is_valid(s); } -int dns_service_split(const char *joined, char **_name, char **_type, char **_domain) { +int dns_service_split(const char *joined, char **ret_name, char **ret_type, char **ret_domain) { _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL; - const char *p = joined, *q = NULL, *d = NULL; - char a[DNS_LABEL_MAX], b[DNS_LABEL_MAX], c[DNS_LABEL_MAX]; + const char *p = joined, *q = NULL, *d = joined; + char a[DNS_LABEL_MAX+1], b[DNS_LABEL_MAX+1], c[DNS_LABEL_MAX+1]; int an, bn, cn, r; unsigned x = 0; @@ -1058,6 +1052,9 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do return bn; if (bn > 0) { + if (!srv_type_label_is_valid(b, bn)) + goto finish; + x++; /* If there was a second label, try to get the third one */ @@ -1066,64 +1063,58 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do if (cn < 0) return cn; - if (cn > 0) + if (cn > 0 && srv_type_label_is_valid(c, cn)) x++; - } else - cn = 0; - } else - an = 0; - - if (x >= 2 && srv_type_label_is_valid(b, bn)) { + } + } - if (x >= 3 && srv_type_label_is_valid(c, cn)) { + switch (x) { + case 2: + if (!srv_type_label_is_valid(a, an)) + break; - if (dns_service_name_label_is_valid(a, an)) { - /* OK, got <name> . <type> . <type2> . <domain> */ + /* OK, got <type> . <type2> . <domain> */ - name = strndup(a, an); - if (!name) - return -ENOMEM; + name = NULL; - type = strjoin(b, ".", c); - if (!type) - return -ENOMEM; + type = strjoin(a, ".", b); + if (!type) + return -ENOMEM; - d = p; - goto finish; - } + d = q; + break; - } else if (srv_type_label_is_valid(a, an)) { + case 3: + if (!dns_service_name_label_is_valid(a, an)) + break; - /* OK, got <type> . <type2> . <domain> */ + /* OK, got <name> . <type> . <type2> . <domain> */ - name = NULL; + name = strndup(a, an); + if (!name) + return -ENOMEM; - type = strjoin(a, ".", b); - if (!type) - return -ENOMEM; + type = strjoin(b, ".", c); + if (!type) + return -ENOMEM; - d = q; - goto finish; - } + d = p; + break; } - name = NULL; - type = NULL; - d = joined; - finish: r = dns_name_normalize(d, 0, &domain); if (r < 0) return r; - if (_domain) - *_domain = TAKE_PTR(domain); + if (ret_domain) + *ret_domain = TAKE_PTR(domain); - if (_type) - *_type = TAKE_PTR(type); + if (ret_type) + *ret_type = TAKE_PTR(type); - if (_name) - *_name = TAKE_PTR(name); + if (ret_name) + *ret_name = TAKE_PTR(name); return 0; } @@ -1303,7 +1294,7 @@ int dns_name_apply_idna(const char *name, char **ret) { r = sym_idn2_lookup_u8((uint8_t*) name, (uint8_t**) &t, IDN2_NFC_INPUT | IDN2_TRANSITIONAL); - log_debug("idn2_lookup_u8: %s → %s", name, t); + log_debug("idn2_lookup_u8: %s %s %s", name, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), t); if (r == IDN2_OK) { if (!startswith(name, "xn--")) { _cleanup_free_ char *s = NULL; @@ -1317,8 +1308,9 @@ int dns_name_apply_idna(const char *name, char **ret) { } if (!streq_ptr(name, s)) { - log_debug("idn2 roundtrip failed: \"%s\" → \"%s\" → \"%s\", ignoring.", - name, t, s); + log_debug("idn2 roundtrip failed: \"%s\" %s \"%s\" %s \"%s\", ignoring.", + name, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), t, + special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), s); *ret = NULL; return 0; } diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.h b/src/libnm-systemd-shared/src/shared/dns-domain.h index ec174c0e..828e924d 100644 --- a/src/libnm-systemd-shared/src/shared/dns-domain.h +++ b/src/libnm-systemd-shared/src/shared/dns-domain.h @@ -42,8 +42,8 @@ static inline int dns_name_normalize(const char *s, DNSLabelFlags flags, char ** static inline int dns_name_is_valid(const char *s) { int r; - /* dns_name_normalize() verifies as a side effect */ - r = dns_name_normalize(s, 0, NULL); + /* dns_name_concat() verifies as a side effect */ + r = dns_name_concat(s, NULL, 0, NULL); if (r == -EINVAL) return 0; if (r < 0) @@ -90,7 +90,7 @@ bool dnssd_srv_type_is_valid(const char *name); bool dns_service_name_is_valid(const char *name); int dns_service_join(const char *name, const char *type, const char *domain, char **ret); -int dns_service_split(const char *joined, char **name, char **type, char **domain); +int dns_service_split(const char *joined, char **ret_name, char **ret_type, char **ret_domain); int dns_name_suffix(const char *name, unsigned n_labels, const char **ret); int dns_name_count_labels(const char *name); |