diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2022-03-09 22:29:19 +0100 |
---|---|---|
committer | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2022-03-29 16:17:56 +0200 |
commit | 172e9cc3ee3dcca288d04c744984a9a3b2a0d008 (patch) | |
tree | 146359ba4b1d7203810ae6a130bca8b046442502 /src | |
parent | 4a84db4c0c2eef6f40da35347c95dfa6b6e3d139 (diff) | |
download | systemd-172e9cc3ee3dcca288d04c744984a9a3b2a0d008.tar.gz |
shared/specifier: fix %u/%U/%g/%G when called as unprivileged user
We would resolve those specifiers to the calling user/group. This is mostly OK
when done in the manager, because the manager generally operates as root
in system mode, and a non-root in user mode. It would still be wrong if
called with --test though. But in systemctl, this would be generally wrong,
since we can call 'systemctl --system' as a normal user, either for testing
or even for actual operation with '--root=…'.
When operating in --global mode, %u/%U/%g/%G should return an error.
The information whether we're operating in system mode, user mode, or global
mode is passed as the data pointer to specifier_group_name(), specifier_user_name(),
specifier_group_id(), specifier_user_id(). We can't use userdata, because
it's already used for other things.
Diffstat (limited to 'src')
-rw-r--r-- | src/core/unit-printf.c | 4 | ||||
-rw-r--r-- | src/shared/install-printf.c | 9 | ||||
-rw-r--r-- | src/shared/install-printf.h | 7 | ||||
-rw-r--r-- | src/shared/install.c | 9 | ||||
-rw-r--r-- | src/shared/specifier.c | 44 | ||||
-rw-r--r-- | src/shared/specifier.h | 10 | ||||
-rw-r--r-- | src/test/test-load-fragment.c | 101 | ||||
-rw-r--r-- | src/test/test-specifier.c | 3 | ||||
-rw-r--r-- | src/tmpfiles/tmpfiles.c | 53 |
9 files changed, 147 insertions, 93 deletions
diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c index 4a86a3c0d6..0cdfcd6efd 100644 --- a/src/core/unit-printf.c +++ b/src/core/unit-printf.c @@ -183,7 +183,7 @@ int unit_name_printf(const Unit *u, const char* format, char **ret) { COMMON_SYSTEM_SPECIFIERS, - COMMON_CREDS_SPECIFIERS, + COMMON_CREDS_SPECIFIERS(u->manager->unit_file_scope), {} }; @@ -253,7 +253,7 @@ int unit_full_printf_full(const Unit *u, const char *format, size_t max_length, COMMON_SYSTEM_SPECIFIERS, - COMMON_CREDS_SPECIFIERS, + COMMON_CREDS_SPECIFIERS(u->manager->unit_file_scope), COMMON_TMP_SPECIFIERS, {} diff --git a/src/shared/install-printf.c b/src/shared/install-printf.c index 9c121cbaf7..571c4818e2 100644 --- a/src/shared/install-printf.c +++ b/src/shared/install-printf.c @@ -97,7 +97,12 @@ static int specifier_last_component(char specifier, const void *data, const char return 0; } -int install_name_printf(const UnitFileInstallInfo *i, const char *format, const char *root, char **ret) { +int install_name_printf( + UnitFileScope scope, + const UnitFileInstallInfo *i, + const char *format, + const char *root, + char **ret) { /* This is similar to unit_name_printf() */ const Specifier table[] = { @@ -109,7 +114,7 @@ int install_name_printf(const UnitFileInstallInfo *i, const char *format, const COMMON_SYSTEM_SPECIFIERS, - COMMON_CREDS_SPECIFIERS, + COMMON_CREDS_SPECIFIERS(scope), {} }; diff --git a/src/shared/install-printf.h b/src/shared/install-printf.h index 5ca9406797..d2cccdf66d 100644 --- a/src/shared/install-printf.h +++ b/src/shared/install-printf.h @@ -4,4 +4,9 @@ #include "install.h" #include "unit-name.h" -int install_name_printf(const UnitFileInstallInfo *i, const char *format, const char *root, char **ret); +int install_name_printf( + UnitFileScope scope, + const UnitFileInstallInfo *i, + const char *format, + const char *root, + char **ret); diff --git a/src/shared/install.c b/src/shared/install.c index bff1930828..4f43f190d6 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -1157,7 +1157,7 @@ static int config_parse_also( if (r == 0) break; - r = install_name_printf(info, word, info->root, &printed); + r = install_name_printf(ctx->scope, info, word, info->root, &printed); if (r < 0) return log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit name in Also=\"%s\": %m", word); @@ -1188,6 +1188,7 @@ static int config_parse_default_instance( void *data, void *userdata) { + InstallContext *ctx = ASSERT_PTR(data); UnitFileInstallInfo *info = ASSERT_PTR(userdata); _cleanup_free_ char *printed = NULL; int r; @@ -1205,7 +1206,7 @@ static int config_parse_default_instance( return log_syntax(unit, LOG_WARNING, filename, line, 0, "DefaultInstance= only makes sense for template units, ignoring."); - r = install_name_printf(info, rvalue, info->root, &printed); + r = install_name_printf(ctx->scope, info, rvalue, info->root, &printed); if (r < 0) return log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve instance name in DefaultInstance=\"%s\": %m", rvalue); @@ -1772,7 +1773,7 @@ static int install_info_symlink_alias( STRV_FOREACH(s, info->aliases) { _cleanup_free_ char *alias_path = NULL, *dst = NULL, *dst_updated = NULL; - q = install_name_printf(info, *s, info->root, &dst); + q = install_name_printf(scope, info, *s, info->root, &dst); if (q < 0) { unit_file_changes_add(changes, n_changes, q, *s, NULL); return q; @@ -1858,7 +1859,7 @@ static int install_info_symlink_wants( STRV_FOREACH(s, list) { _cleanup_free_ char *path = NULL, *dst = NULL; - q = install_name_printf(info, *s, info->root, &dst); + q = install_name_printf(scope, info, *s, info->root, &dst); if (q < 0) { unit_file_changes_add(changes, n_changes, q, *s, NULL); return q; diff --git a/src/shared/specifier.c b/src/shared/specifier.c index e994884196..777881b325 100644 --- a/src/shared/specifier.c +++ b/src/shared/specifier.c @@ -22,6 +22,7 @@ #include "specifier.h" #include "string-util.h" #include "strv.h" +#include "unit-file.h" #include "user-util.h" /* @@ -309,11 +310,15 @@ int specifier_os_image_version(char specifier, const void *data, const char *roo } int specifier_group_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) { + UnitFileScope scope = PTR_TO_INT(data); char *t; assert(ret); - t = gid_to_name(getgid()); + if (scope == UNIT_FILE_GLOBAL) + return -EINVAL; + + t = gid_to_name(scope == UNIT_FILE_USER ? getgid() : 0); if (!t) return -ENOMEM; @@ -322,27 +327,42 @@ int specifier_group_name(char specifier, const void *data, const char *root, con } int specifier_group_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) { + UnitFileScope scope = PTR_TO_INT(data); + gid_t gid; + assert(ret); - if (asprintf(ret, UID_FMT, getgid()) < 0) + if (scope == UNIT_FILE_GLOBAL) + return -EINVAL; + + gid = scope == UNIT_FILE_USER ? getgid() : 0; + + if (asprintf(ret, UID_FMT, gid) < 0) return -ENOMEM; return 0; } int specifier_user_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) { + UnitFileScope scope = PTR_TO_INT(data); + uid_t uid; char *t; assert(ret); - /* If we are UID 0 (root), this will not result in NSS, otherwise it might. This is good, as we want to be able - * to run this in PID 1, where our user ID is 0, but where NSS lookups are not allowed. + if (scope == UNIT_FILE_GLOBAL) + return -EINVAL; + + uid = scope == UNIT_FILE_USER ? getuid() : 0; - * We don't use getusername_malloc() here, because we don't want to look at $USER, to remain consistent with - * specifer_user_id() below. + /* If we are UID 0 (root), this will not result in NSS, otherwise it might. This is good, as we want + * to be able to run this in PID 1, where our user ID is 0, but where NSS lookups are not allowed. + + * We don't use getusername_malloc() here, because we don't want to look at $USER, to remain + * consistent with specifer_user_id() below. */ - t = uid_to_name(getuid()); + t = uid_to_name(uid); if (!t) return -ENOMEM; @@ -351,9 +371,17 @@ int specifier_user_name(char specifier, const void *data, const char *root, cons } int specifier_user_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) { + UnitFileScope scope = PTR_TO_INT(data); + uid_t uid; + assert(ret); - if (asprintf(ret, UID_FMT, getuid()) < 0) + if (scope == UNIT_FILE_GLOBAL) + return -EINVAL; + + uid = scope == UNIT_FILE_USER ? getuid() : 0; + + if (asprintf(ret, UID_FMT, uid) < 0) return -ENOMEM; return 0; diff --git a/src/shared/specifier.h b/src/shared/specifier.h index 2f2553cfdc..78b10f8467 100644 --- a/src/shared/specifier.h +++ b/src/shared/specifier.h @@ -84,11 +84,11 @@ int specifier_var_tmp_dir(char specifier, const void *data, const char *root, co { 'w', specifier_os_version_id, NULL }, \ { 'W', specifier_os_variant_id, NULL } -#define COMMON_CREDS_SPECIFIERS \ - { 'g', specifier_group_name, NULL }, \ - { 'G', specifier_group_id, NULL }, \ - { 'u', specifier_user_name, NULL }, \ - { 'U', specifier_user_id, NULL } +#define COMMON_CREDS_SPECIFIERS(scope) \ + { 'g', specifier_group_name, INT_TO_PTR(scope) }, \ + { 'G', specifier_group_id, INT_TO_PTR(scope) }, \ + { 'u', specifier_user_name, INT_TO_PTR(scope) }, \ + { 'U', specifier_user_id, INT_TO_PTR(scope) } #define COMMON_TMP_SPECIFIERS \ { 'T', specifier_tmp_dir, NULL }, \ diff --git a/src/test/test-load-fragment.c b/src/test/test-load-fragment.c index 52f6096eee..40cd7ea7e6 100644 --- a/src/test/test-load-fragment.c +++ b/src/test/test-load-fragment.c @@ -510,59 +510,74 @@ TEST(install_printf, .sd_booted = true) { assert_se(user = uid_to_name(getuid())); assert_se(asprintf(&uid, UID_FMT, getuid()) >= 0); -#define expect(src, pattern, result) \ +#define expect(scope, src, pattern, result) \ do { \ - _cleanup_free_ char *t = NULL; \ - _cleanup_free_ char \ - *d1 = strdup(i.name), \ - *d2 = strdup(i.path); \ - assert_se(install_name_printf(&src, pattern, NULL, &t) >= 0 || !result); \ + _cleanup_free_ char *t = NULL, \ + *d1 = ASSERT_PTR(strdup(i.name)), \ + *d2 = ASSERT_PTR(strdup(i.path)); \ + int r = install_name_printf(scope, &src, pattern, NULL, &t); \ + assert_se(result ? r >= 0 : r < 0); \ memzero(i.name, strlen(i.name)); \ memzero(i.path, strlen(i.path)); \ - assert_se(d1 && d2); \ if (result) { \ printf("%s\n", t); \ assert_se(streq(t, result)); \ - } else assert_se(t == NULL); \ + } else \ + assert_se(!t); \ strcpy(i.name, d1); \ strcpy(i.path, d2); \ } while (false) - expect(i, "%n", "name.service"); - expect(i, "%N", "name"); - expect(i, "%p", "name"); - expect(i, "%i", ""); - expect(i, "%j", "name"); - expect(i, "%g", group); - expect(i, "%G", gid); - expect(i, "%u", user); - expect(i, "%U", uid); - - expect(i, "%m", mid); - expect(i, "%b", bid); - expect(i, "%H", host); - - expect(i2, "%g", group); - expect(i2, "%G", gid); - expect(i2, "%u", user); - expect(i2, "%U", uid); - - expect(i3, "%n", "name@inst.service"); - expect(i3, "%N", "name@inst"); - expect(i3, "%p", "name"); - expect(i3, "%g", group); - expect(i3, "%G", gid); - expect(i3, "%u", user); - expect(i3, "%U", uid); - - expect(i3, "%m", mid); - expect(i3, "%b", bid); - expect(i3, "%H", host); - - expect(i4, "%g", group); - expect(i4, "%G", gid); - expect(i4, "%u", user); - expect(i4, "%U", uid); + expect(UNIT_FILE_SYSTEM, i, "%n", "name.service"); + expect(UNIT_FILE_SYSTEM, i, "%N", "name"); + expect(UNIT_FILE_SYSTEM, i, "%p", "name"); + expect(UNIT_FILE_SYSTEM, i, "%i", ""); + expect(UNIT_FILE_SYSTEM, i, "%j", "name"); + expect(UNIT_FILE_SYSTEM, i, "%g", "root"); + expect(UNIT_FILE_SYSTEM, i, "%G", "0"); + expect(UNIT_FILE_SYSTEM, i, "%u", "root"); + expect(UNIT_FILE_SYSTEM, i, "%U", "0"); + + expect(UNIT_FILE_SYSTEM, i, "%m", mid); + expect(UNIT_FILE_SYSTEM, i, "%b", bid); + expect(UNIT_FILE_SYSTEM, i, "%H", host); + + expect(UNIT_FILE_SYSTEM, i2, "%g", "root"); + expect(UNIT_FILE_SYSTEM, i2, "%G", "0"); + expect(UNIT_FILE_SYSTEM, i2, "%u", "root"); + expect(UNIT_FILE_SYSTEM, i2, "%U", "0"); + + expect(UNIT_FILE_USER, i2, "%g", group); + expect(UNIT_FILE_USER, i2, "%G", gid); + expect(UNIT_FILE_USER, i2, "%u", user); + expect(UNIT_FILE_USER, i2, "%U", uid); + + /* gcc-12.0.1-0.9.fc36.x86_64 insist that streq(…, NULL) is called, + * even though the call is inside of a conditional where the pointer is checked. :( */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull" + expect(UNIT_FILE_GLOBAL, i2, "%g", NULL); + expect(UNIT_FILE_GLOBAL, i2, "%G", NULL); + expect(UNIT_FILE_GLOBAL, i2, "%u", NULL); + expect(UNIT_FILE_GLOBAL, i2, "%U", NULL); +#pragma GCC diagnostic pop + + expect(UNIT_FILE_SYSTEM, i3, "%n", "name@inst.service"); + expect(UNIT_FILE_SYSTEM, i3, "%N", "name@inst"); + expect(UNIT_FILE_SYSTEM, i3, "%p", "name"); + expect(UNIT_FILE_USER, i3, "%g", group); + expect(UNIT_FILE_USER, i3, "%G", gid); + expect(UNIT_FILE_USER, i3, "%u", user); + expect(UNIT_FILE_USER, i3, "%U", uid); + + expect(UNIT_FILE_SYSTEM, i3, "%m", mid); + expect(UNIT_FILE_SYSTEM, i3, "%b", bid); + expect(UNIT_FILE_SYSTEM, i3, "%H", host); + + expect(UNIT_FILE_USER, i4, "%g", group); + expect(UNIT_FILE_USER, i4, "%G", gid); + expect(UNIT_FILE_USER, i4, "%u", user); + expect(UNIT_FILE_USER, i4, "%U", uid); } static uint64_t make_cap(int cap) { diff --git a/src/test/test-specifier.c b/src/test/test-specifier.c index bb1c651c2a..947a32acf7 100644 --- a/src/test/test-specifier.c +++ b/src/test/test-specifier.c @@ -8,6 +8,7 @@ #include "string-util.h" #include "strv.h" #include "tests.h" +#include "unit-file.h" static void test_specifier_escape_one(const char *a, const char *b) { _cleanup_free_ char *x = NULL; @@ -46,7 +47,7 @@ TEST(specifier_escape_strv) { static const Specifier specifier_table[] = { COMMON_SYSTEM_SPECIFIERS, - COMMON_CREDS_SPECIFIERS, + COMMON_CREDS_SPECIFIERS(UNIT_FILE_USER), { 'h', specifier_user_home, NULL }, COMMON_TMP_SPECIFIERS, diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index f0190f9b4c..17b9c6ab9a 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -204,31 +204,6 @@ STATIC_DESTRUCTOR_REGISTER(arg_image, freep); static int specifier_machine_id_safe(char specifier, const void *data, const char *root, const void *userdata, char **ret); static int specifier_directory(char specifier, const void *data, const char *root, const void *userdata, char **ret); -static const Specifier specifier_table[] = { - { 'a', specifier_architecture, NULL }, - { 'b', specifier_boot_id, NULL }, - { 'B', specifier_os_build_id, NULL }, - { 'H', specifier_host_name, NULL }, - { 'l', specifier_short_host_name, NULL }, - { 'm', specifier_machine_id_safe, NULL }, - { 'o', specifier_os_id, NULL }, - { 'v', specifier_kernel_release, NULL }, - { 'w', specifier_os_version_id, NULL }, - { 'W', specifier_os_variant_id, NULL }, - - { 'h', specifier_user_home, NULL }, - - { 'C', specifier_directory, UINT_TO_PTR(DIRECTORY_CACHE) }, - { 'L', specifier_directory, UINT_TO_PTR(DIRECTORY_LOGS) }, - { 'S', specifier_directory, UINT_TO_PTR(DIRECTORY_STATE) }, - { 't', specifier_directory, UINT_TO_PTR(DIRECTORY_RUNTIME) }, - - COMMON_CREDS_SPECIFIERS, - - COMMON_TMP_SPECIFIERS, - {} -}; - static int specifier_machine_id_safe(char specifier, const void *data, const char *root, const void *userdata, char **ret) { int r; @@ -2737,7 +2712,7 @@ static bool should_include_path(const char *path) { return false; } -static int specifier_expansion_from_arg(Item *i) { +static int specifier_expansion_from_arg(const Specifier *specifier_table, Item *i) { int r; assert(i); @@ -2944,6 +2919,30 @@ static int parse_line( assert(line >= 1); assert(buffer); + const Specifier specifier_table[] = { + { 'a', specifier_architecture, NULL }, + { 'b', specifier_boot_id, NULL }, + { 'B', specifier_os_build_id, NULL }, + { 'H', specifier_host_name, NULL }, + { 'l', specifier_short_host_name, NULL }, + { 'm', specifier_machine_id_safe, NULL }, + { 'o', specifier_os_id, NULL }, + { 'v', specifier_kernel_release, NULL }, + { 'w', specifier_os_version_id, NULL }, + { 'W', specifier_os_variant_id, NULL }, + + { 'h', specifier_user_home, NULL }, + + { 'C', specifier_directory, UINT_TO_PTR(DIRECTORY_CACHE) }, + { 'L', specifier_directory, UINT_TO_PTR(DIRECTORY_LOGS) }, + { 'S', specifier_directory, UINT_TO_PTR(DIRECTORY_STATE) }, + { 't', specifier_directory, UINT_TO_PTR(DIRECTORY_RUNTIME) }, + + COMMON_CREDS_SPECIFIERS(arg_user ? UNIT_FILE_USER : UNIT_FILE_SYSTEM), + COMMON_TMP_SPECIFIERS, + {} + }; + r = extract_many_words( &buffer, NULL, @@ -3148,7 +3147,7 @@ static int parse_line( if (!should_include_path(i.path)) return 0; - r = specifier_expansion_from_arg(&i); + r = specifier_expansion_from_arg(specifier_table, &i); if (r == -ENXIO) return log_unresolvable_specifier(fname, line); if (r < 0) { |