From e51d13d61d404ba813b3d0d300660c6f668dbeae Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Fri, 14 Apr 2023 21:07:51 +0200 Subject: [PATCH 01/11] test: check the colored --version output --- test/units/testsuite-01.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/units/testsuite-01.sh b/test/units/testsuite-01.sh index cd18a9fd74..91dd47ca14 100755 --- a/test/units/testsuite-01.sh +++ b/test/units/testsuite-01.sh @@ -3,6 +3,9 @@ set -eux set -o pipefail +# Check if the colored --version output behaves correctly +SYSTEMD_COLORS=256 systemctl --version + # Check if we properly differentiate between a full systemd setup and a "light" # version of it that's done during daemon-reexec # From 3a8b7e8b5f72a940a07938a8ed33f2c3283dd52b Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Fri, 14 Apr 2023 21:10:18 +0200 Subject: [PATCH 02/11] test: stop the test unit when it's not needed anymore Otherwise it keeps printing stuff to the journal/console, adding unnecessary noise. --- test/units/testsuite-04.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/test/units/testsuite-04.sh b/test/units/testsuite-04.sh index b7cbfeb75a..35d35c0e85 100755 --- a/test/units/testsuite-04.sh +++ b/test/units/testsuite-04.sh @@ -175,6 +175,7 @@ sleep 3 systemctl kill --signal=SIGKILL systemd-journald sleep 3 [[ ! -f "/i-lose-my-logs" ]] +systemctl stop forever-print-hola # https://github.com/systemd/systemd/issues/15528 journalctl --follow --file=/var/log/journal/*/* | head -n1 || [[ $? -eq 1 ]] From 1a127aa02bf9fb0c02e268744d6348c1992207b1 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Sat, 15 Apr 2023 13:12:43 +0200 Subject: [PATCH 03/11] docs: a couple of typo fixes & formatting tweaks --- docs/ENVIRONMENT.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index 445131b479..791ba8dd12 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -90,7 +90,7 @@ All tools: * `$SYSTEMD_UTF8=` — takes a boolean value, and overrides whether to generate non-ASCII special glyphs at various places (i.e. "→" instead of - "->"). Usually this is deterined automatically, based on $LC_CTYPE, but in + "->"). Usually this is determined automatically, based on `$LC_CTYPE`, but in scenarios where locale definitions are not installed it might make sense to override this check explicitly. @@ -343,7 +343,7 @@ the journal instead of only when logging in debug mode. * `SOURCE_DATE_EPOCH` — if unset, the field of the date of last password change in `/etc/shadow` will be the number of days from Jan 1, 1970 00:00 UTC until - today. If SOURCE_DATE_EPOCH is set to a valid UNIX epoch value in seconds, + today. If `$SOURCE_DATE_EPOCH` is set to a valid UNIX epoch value in seconds, then the field will be the number of days until that time instead. This is to support creating bit-by-bit reproducible system images by choosing a reproducible value for the field of the date of last password change in @@ -431,7 +431,7 @@ disk images with `--image=` or similar: specified defaults to something like: `ext4:btrfs:xfs:vfat:erofs:squashfs` * `$SYSTEMD_LOOP_DIRECT_IO` – takes a boolean, which controls whether to enable - LO_FLAGS_DIRECT_IO (i.e. direct IO + asynchronous IO) on loopback block + `LO_FLAGS_DIRECT_IO` (i.e. direct IO + asynchronous IO) on loopback block devices when opening them. Defaults to on, set this to "0" to disable this feature. From cb68860ece01406b51257ce65c065f44c4ab9aaf Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Sat, 15 Apr 2023 13:58:20 +0200 Subject: [PATCH 04/11] test: add a test case for table_dup_cell() Also, sneak in coverage for "less popular" cell types. --- src/test/test-format-table.c | 43 ++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/test/test-format-table.c b/src/test/test-format-table.c index a1bc8b99ac..7d544b18fd 100644 --- a/src/test/test-format-table.c +++ b/src/test/test-format-table.c @@ -583,6 +583,49 @@ TEST(path_basename) { assert_se(streq(formatted, "bar\nbar\nbaz\n")); } +TEST(dup_cell) { + _cleanup_(table_unrefp) Table *t = NULL; + _cleanup_free_ char *formatted = NULL; + + assert_se(t = table_new("foo", "bar", "x", "baz", ".", "%", "!", "~", "+")); + table_set_width(t, 75); + + assert_se(table_add_many(t, + TABLE_STRING, "hello", + TABLE_UINT8, UINT8_C(42), + TABLE_UINT16, UINT16_C(666), + TABLE_UINT32, UINT32_C(253), + TABLE_PERCENT, 0, + TABLE_PATH_BASENAME, "/foo/bar", + TABLE_STRING, "aaa", + TABLE_STRING, "bbb", + TABLE_STRING, "ccc") >= 0); + + /* Add the second row by duping cells */ + for (size_t i = 0; i < table_get_columns(t); i++) + assert_se(table_dup_cell(t, table_get_cell(t, 1, i)) >= 0); + + /* Another row, but dupe the last three strings from the same cell */ + assert_se(table_add_many(t, + TABLE_STRING, "aaa", + TABLE_UINT8, UINT8_C(0), + TABLE_UINT16, UINT16_C(65535), + TABLE_UINT32, UINT32_C(4294967295), + TABLE_PERCENT, 100, + TABLE_PATH_BASENAME, "../") >= 0); + + for (size_t i = 6; i < table_get_columns(t); i++) + assert_se(table_dup_cell(t, table_get_cell(t, 2, 0)) >= 0); + + assert_se(table_format(t, &formatted) >= 0); + printf("%s\n", formatted); + assert_se(streq(formatted, + "FOO BAR X BAZ . % ! ~ +\n" + "hello 42 666 253 0% bar aaa bbb ccc\n" + "hello 42 666 253 0% bar aaa bbb ccc\n" + "aaa 0 65535 4294967295 100% ../ hello hello hello\n")); +} + static int intro(void) { assert_se(setenv("SYSTEMD_COLORS", "0", 1) >= 0); assert_se(setenv("COLUMNS", "40", 1) >= 0); From 9f7fcf80ad2bd69d1267e782ac2c846e40bf2c7d Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Sat, 15 Apr 2023 18:02:10 +0200 Subject: [PATCH 05/11] test: add tests for uuid/uint64 specifiers They're used in repart, but are not part of the "common" specifier lists, so cover them explicitly. --- src/test/test-specifier.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/test/test-specifier.c b/src/test/test-specifier.c index 6d4093ec05..d2a7f922bd 100644 --- a/src/test/test-specifier.c +++ b/src/test/test-specifier.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "sd-id128.h" + #include "alloc-util.h" #include "log.h" #include "specifier.h" @@ -144,6 +146,31 @@ TEST(specifiers) { } } +/* Bunch of specifiers that are not part of the common lists */ +TEST(specifiers_assorted) { + const sd_id128_t id = SD_ID128_ALLF; + const uint64_t llu = UINT64_MAX; + const Specifier table[] = { + /* Used in src/partition/repart.c */ + { 'a', specifier_uuid, &id }, + { 'b', specifier_uint64, &llu }, + {} + }; + + for (const Specifier *s = table; s->specifier; s++) { + char spec[3]; + _cleanup_free_ char *resolved = NULL; + int r; + + xsprintf(spec, "%%%c", s->specifier); + + r = specifier_printf(spec, SIZE_MAX, table, NULL, NULL, &resolved); + assert_se(r >= 0); + + log_info("%%%c → %s", s->specifier, resolved); + } +} + TEST(specifiers_missing_data_ok) { _cleanup_free_ char *resolved = NULL; From 1b2719c2c5ce1349a1e48a093668fb90734e2e53 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Sat, 15 Apr 2023 18:24:13 +0200 Subject: [PATCH 06/11] shared: add a missing include --- src/shared/securebits-util.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/shared/securebits-util.h b/src/shared/securebits-util.h index f2e65cfcb0..caf8e6d593 100644 --- a/src/shared/securebits-util.h +++ b/src/shared/securebits-util.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include + #include "missing_securebits.h" int secure_bits_to_string_alloc(int i, char **s); From 10a9466135a32708f45f6f7fb10cd9f005c87eb6 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Sat, 15 Apr 2023 19:12:45 +0200 Subject: [PATCH 07/11] test: add a simple test for secure-bits stuff --- src/test/meson.build | 1 + src/test/test-secure-bits.c | 84 +++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 src/test/test-secure-bits.c diff --git a/src/test/meson.build b/src/test/meson.build index 85c3115e14..da843a7e47 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -141,6 +141,7 @@ simple_tests += files( 'test-rm-rf.c', 'test-sd-hwdb.c', 'test-sd-path.c', + 'test-secure-bits.c', 'test-selinux.c', 'test-serialize.c', 'test-set.c', diff --git a/src/test/test-secure-bits.c b/src/test/test-secure-bits.c new file mode 100644 index 0000000000..d44bef6a09 --- /dev/null +++ b/src/test/test-secure-bits.c @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "securebits-util.h" +#include "strv.h" +#include "tests.h" +#include "unit-file.h" + +static const char * const string_bits[] = { + "keep-caps", + "keep-caps-locked", + "no-setuid-fixup", + "no-setuid-fixup-locked", + "noroot", + "noroot-locked", + NULL +}; + +TEST(secure_bits_basic) { + _cleanup_free_ char *joined = NULL, *str = NULL; + int r; + + /* Check if converting each bit from string and back to string yields + * the same value */ + STRV_FOREACH(bit, string_bits) { + _cleanup_free_ char *s = NULL; + + r = secure_bits_from_string(*bit); + assert_se(r > 0); + assert_se(secure_bits_to_string_alloc(r, &s) >= 0); + printf("%s = 0x%x = %s\n", *bit, (unsigned)r, s); + assert_se(streq(*bit, s)); + } + + /* Ditto, but with all bits at once */ + joined = strv_join((char**)string_bits, " "); + assert_se(joined); + r = secure_bits_from_string(joined); + assert_se(r > 0); + assert_se(secure_bits_to_string_alloc(r, &str) >= 0); + printf("%s = 0x%x = %s\n", joined, (unsigned)r, str); + assert_se(streq(joined, str)); + + str = mfree(str); + + /* Empty string */ + assert_se(secure_bits_from_string("") == 0); + assert_se(secure_bits_from_string(" ") == 0); + + /* Only invalid entries */ + assert_se(secure_bits_from_string("foo bar baz") == 0); + + /* Empty secure bits */ + assert_se(secure_bits_to_string_alloc(0, &str) >= 0); + assert_se(isempty(str)); +} + +TEST(secure_bits_mix) { + static struct sbit_table { + const char *input; + const char *expected; + } sbit_table[] = { + { "keep-caps keep-caps keep-caps", "keep-caps" }, + { "keep-caps noroot keep-caps", "keep-caps noroot" }, + { "noroot foo bar baz noroot", "noroot" }, + { "noroot \"foo\" \"bar keep-caps", "noroot" }, + { "\"noroot foo\" bar keep-caps", "keep-caps" }, + {} + }; + + for (const struct sbit_table *s = sbit_table; s->input; s++) { + _cleanup_free_ char *str = NULL; + int r; + + r = secure_bits_from_string(s->input); + assert_se(r > 0); + assert_se(secure_bits_to_string_alloc(r, &str) >= 0); + printf("%s = 0x%x = %s\n", s->input, (unsigned)r, str); + assert_se(streq(s->expected, str)); + } +} + +DEFINE_TEST_MAIN(LOG_DEBUG); From a51ba8e31a8d68d3d27711d173e55b7cfc48e6d9 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Sat, 15 Apr 2023 19:51:44 +0200 Subject: [PATCH 08/11] test: add a couple of basic sanity tests for timedatectl --- test/units/testsuite-45.sh | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/units/testsuite-45.sh b/test/units/testsuite-45.sh index ebc6c7c144..73608756bf 100755 --- a/test/units/testsuite-45.sh +++ b/test/units/testsuite-45.sh @@ -7,6 +7,27 @@ set -o pipefail # shellcheck source=test/units/assert.sh . "$(dirname "$0")"/assert.sh +test_timedatectl() { + timedatectl --no-pager --help + timedatectl --version + + timedatectl + timedatectl --no-ask-password + timedatectl status --machine=testuser@.host + timedatectl status + timedatectl show + timedatectl show --all + timedatectl show -p NTP + timedatectl show -p NTP --value + timedatectl list-timezones + + if ! systemd-detect-virt -qc; then + systemctl enable --runtime --now systemd-timesyncd + timedatectl timesync-status + timedatectl show-timesync + fi +} + restore_timezone() { if [[ -f /tmp/timezone.bak ]]; then mv /tmp/timezone.bak /etc/timezone @@ -256,6 +277,7 @@ EOF : >/failed +test_timedatectl test_timezone test_adjtime test_ntp From f7f5657ece9d0afd5026edd835b12aa970f46547 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Sat, 15 Apr 2023 21:33:02 +0200 Subject: [PATCH 09/11] test: add a couple of basic sanity tests for the security verb --- test/units/testsuite-65.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/units/testsuite-65.sh b/test/units/testsuite-65.sh index 49e6b87a13..cf3a856413 100755 --- a/test/units/testsuite-65.sh +++ b/test/units/testsuite-65.sh @@ -150,6 +150,11 @@ systemd-analyze cat-config /etc/systemd/system.conf >/dev/null systemd-analyze cat-config systemd/system.conf systemd/journald.conf >/dev/null systemd-analyze cat-config systemd/system.conf foo/bar systemd/journald.conf >/dev/null systemd-analyze cat-config foo/bar +# security +systemd-analyze security +systemd-analyze security --json=off +systemd-analyze security --json=pretty | jq +systemd-analyze security --json=short | jq if [[ ! -v ASAN_OPTIONS ]]; then # check that systemd-analyze cat-config paths work in a chroot From 192242c986e2462c4d2ec5b3ecd6f1ac02f9c0ad Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Sat, 15 Apr 2023 22:04:37 +0200 Subject: [PATCH 10/11] test: add a simple test for getenv_path_list() --- src/test/test-env-util.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/test/test-env-util.c b/src/test/test-env-util.c index 8f2293b6c0..0f58d2fed0 100644 --- a/src/test/test-env-util.c +++ b/src/test/test-env-util.c @@ -484,4 +484,39 @@ TEST(strv_env_name_is_valid) { assert_se(!strv_env_name_is_valid(STRV_MAKE("HOME", "USER", "SHELL", "USER"))); } +TEST(getenv_path_list) { + _cleanup_strv_free_ char **path_list = NULL; + + /* Empty paths */ + FOREACH_STRING(s, "", ":", ":::::", " : ::: :: :") { + assert_se(setenv("TEST_GETENV_PATH_LIST", s, 1) >= 0); + assert_se(getenv_path_list("TEST_GETENV_PATH_LIST", &path_list) == -EINVAL); + assert_se(!path_list); + } + + /* Invalid paths */ + FOREACH_STRING(s, ".", "..", "/../", "/", "/foo/bar/baz/../foo", "foo/bar/baz") { + assert_se(setenv("TEST_GETENV_PATH_LIST", s, 1) >= 0); + assert_se(getenv_path_list("TEST_GETENV_PATH_LIST", &path_list) == -EINVAL); + assert_se(!path_list); + } + + /* Valid paths mixed with invalid ones */ + assert_se(setenv("TEST_GETENV_PATH_LIST", "/foo:/bar/baz:/../:/hello", 1) >= 0); + assert_se(getenv_path_list("TEST_GETENV_PATH_LIST", &path_list) == -EINVAL); + assert_se(!path_list); + + /* Finally some valid paths */ + assert_se(setenv("TEST_GETENV_PATH_LIST", "/foo:/bar/baz:/hello/world:/path with spaces:/final", 1) >= 0); + assert_se(getenv_path_list("TEST_GETENV_PATH_LIST", &path_list) >= 0); + assert_se(streq(path_list[0], "/foo")); + assert_se(streq(path_list[1], "/bar/baz")); + assert_se(streq(path_list[2], "/hello/world")); + assert_se(streq(path_list[3], "/path with spaces")); + assert_se(streq(path_list[4], "/final")); + assert_se(path_list[5] == NULL); + + assert_se(unsetenv("TEST_GETENV_PATH_LIST") >= 0); +} + DEFINE_TEST_MAIN(LOG_DEBUG); From 841834d9c358163308deb70642249e8b2ba76c1a Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Sat, 15 Apr 2023 22:22:56 +0200 Subject: [PATCH 11/11] test: add a couple of tests with invalid UTF-8 characters --- src/test/test-env-file.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/test/test-env-file.c b/src/test/test-env-file.c index 461a0f0810..c8ec0e2278 100644 --- a/src/test/test-env-file.c +++ b/src/test/test-env-file.c @@ -58,7 +58,6 @@ "d= \" \\n\\t\\$\\`\\\\\n" \ "\" \n" - TEST(load_env_file_1) { _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-load-env-file.XXXXXX"; assert_se(write_tmpfile(name, env_file_1) == 0); @@ -129,6 +128,25 @@ TEST(load_env_file_6) { assert_se(data[4] == NULL); } +TEST(load_env_file_invalid_utf8) { + /* Test out a couple of assignments where the key/value has an invalid + * UTF-8 character ("noncharacter") + * + * See: https://en.wikipedia.org/wiki/Universal_Character_Set_characters#Non-characters + */ + FOREACH_STRING(s, + "fo\ufffeo=bar", + "foo=b\uffffar", + "baz=hello world\ufffe") { + _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-load-env-file.XXXXXX"; + assert_se(write_tmpfile(name, s) == 0); + + _cleanup_strv_free_ char **data = NULL; + assert_se(load_env_file(NULL, name, &data) == -EINVAL); + assert_se(!data); + } +} + TEST(write_and_load_env_file) { /* Make sure that our writer, parser and the shell agree on what our env var files mean */