diff --git a/meson.build b/meson.build index 3bec86db81..23cf3e528a 100644 --- a/meson.build +++ b/meson.build @@ -1467,6 +1467,7 @@ foreach term : ['analyze', have = get_option(term) name = 'ENABLE_' + term.underscorify().to_upper() conf.set10(name, have) + substs.set10(name, have) endforeach enable_sysusers = conf.get('ENABLE_SYSUSERS') == 1 diff --git a/src/shared/user-record.c b/src/shared/user-record.c index 3ba78d455f..7e7b28eb55 100644 --- a/src/shared/user-record.c +++ b/src/shared/user-record.c @@ -37,21 +37,24 @@ static int parse_alloc_uid(const char *path, const char *name, const char *t, ui *ret_uid = uid; return 0; } +#endif -static int read_login_defs(UGIDAllocationRange *ret_defs, const char *path) { - _cleanup_fclose_ FILE *f = NULL; +int read_login_defs(UGIDAllocationRange *ret_defs, const char *path, const char *root) { UGIDAllocationRange defs = { .system_alloc_uid_min = SYSTEM_ALLOC_UID_MIN, .system_uid_max = SYSTEM_UID_MAX, .system_alloc_gid_min = SYSTEM_ALLOC_GID_MIN, .system_gid_max = SYSTEM_GID_MAX, }; + +#if ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES + _cleanup_fclose_ FILE *f = NULL; int r; if (!path) path = "/etc/login.defs"; - r = fopen_unlocked(path, "re", &f); + r = chase_symlinks_and_fopen_unlocked(path, root, CHASE_PREFIX_ROOT, "re", &f, NULL); if (r == -ENOENT) goto assign; if (r < 0) @@ -88,11 +91,11 @@ static int read_login_defs(UGIDAllocationRange *ret_defs, const char *path) { defs.system_alloc_gid_min = MIN(defs.system_gid_max - 1, (gid_t) SYSTEM_ALLOC_GID_MIN); /* Look at sys_gid_max to make sure sys_gid_min..sys_gid_max remains a valid range. */ } +#endif *ret_defs = defs; return 0; } -#endif const UGIDAllocationRange *acquire_ugid_allocation_range(void) { #if ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES @@ -114,7 +117,7 @@ const UGIDAllocationRange *acquire_ugid_allocation_range(void) { static thread_local bool initialized = false; if (!initialized) { - (void) read_login_defs(&defs, NULL); + (void) read_login_defs(&defs, NULL, NULL); initialized = true; } #endif diff --git a/src/shared/user-record.h b/src/shared/user-record.h index 1f87eff6d5..2e74b910c2 100644 --- a/src/shared/user-record.h +++ b/src/shared/user-record.h @@ -43,6 +43,7 @@ typedef struct UGIDAllocationRange { gid_t system_gid_max; } UGIDAllocationRange; +int read_login_defs(UGIDAllocationRange *ret_defs, const char *path, const char *root); const UGIDAllocationRange *acquire_ugid_allocation_range(void); typedef enum UserDisposition { diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 7349e9fcb9..987950d602 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -26,6 +26,7 @@ #include "strv.h" #include "tmpfile-util-label.h" #include "uid-range.h" +#include "user-record.h" #include "user-util.h" #include "utf8.h" #include "util.h" @@ -1949,10 +1950,25 @@ static int run(int argc, char *argv[]) { return log_error_errno(errno, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m"); if (!uid_range) { - /* Default to default range of 1..SYSTEM_UID_MAX */ - r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX); + /* Default to default range of SYSTEMD_UID_MIN..SYSTEM_UID_MAX. */ + UGIDAllocationRange defs; + + r = read_login_defs(&defs, NULL, arg_root); if (r < 0) - return log_oom(); + return log_error_errno(r, "Failed to read %s%s: %m", + strempty(arg_root), "/etc/login.defs"); + + /* We pick a range that very conservative: we look at compiled-in maximum and the value in + * /etc/login.defs. That way the uids/gids which we allocate will be interpreted correctly, + * even if /etc/login.defs is removed later. (The bottom bound doesn't matter much, since + * it's only used during allocation, so we use the configured value directly). */ + uid_t begin = defs.system_alloc_uid_min, + end = MIN3((uid_t) SYSTEM_UID_MAX, defs.system_uid_max, defs.system_gid_max); + if (begin < end) { + r = uid_range_add(&uid_range, &n_uid_range, begin, end - begin + 1); + if (r < 0) + return log_oom(); + } } r = add_implicit(); diff --git a/src/test/test-user-record.c b/src/test/test-user-record.c index fcab61d694..d623706648 100644 --- a/src/test/test-user-record.c +++ b/src/test/test-user-record.c @@ -3,10 +3,55 @@ #include #include +#include "fd-util.h" +#include "fileio.h" #include "format-util.h" +#include "fs-util.h" +#include "tmpfile-util.h" #include "tests.h" #include "user-record.h" +static void test_read_login_defs(const char *path) { + log_info("/* %s(\"%s\") */", __func__, path ?: ""); + + _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-user-record.XXXXXX"; + _cleanup_fclose_ FILE *f = NULL; + if (!path) { + assert_se(fmkostemp_safe(name, "r+", &f) == 0); + fprintf(f, + "SYS_UID_MIN "UID_FMT"\n" + "SYS_UID_MAX "UID_FMT"\n" + "SYS_GID_MIN "GID_FMT"\n" + "SYS_GID_MAX "GID_FMT"\n", + SYSTEM_ALLOC_UID_MIN + 5, + SYSTEM_UID_MAX + 5, + SYSTEM_ALLOC_GID_MIN + 5, + SYSTEM_GID_MAX + 5); + assert_se(fflush_and_check(f) >= 0); + } + + UGIDAllocationRange defs; + assert_se(read_login_defs(&defs, path ?: name, NULL) >= 0); + + log_info("system_alloc_uid_min="UID_FMT, defs.system_alloc_uid_min); + log_info("system_uid_max="UID_FMT, defs.system_uid_max); + log_info("system_alloc_gid_min="GID_FMT, defs.system_alloc_gid_min); + log_info("system_gid_max="GID_FMT, defs.system_gid_max); + + if (!path) { + uid_t offset = ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES ? 5 : 0; + assert_se(defs.system_alloc_uid_min == SYSTEM_ALLOC_UID_MIN + offset); + assert_se(defs.system_uid_max == SYSTEM_UID_MAX + offset); + assert_se(defs.system_alloc_gid_min == SYSTEM_ALLOC_GID_MIN + offset); + assert_se(defs.system_gid_max == SYSTEM_GID_MAX + offset); + } else if (streq(path, "/dev/null")) { + assert_se(defs.system_alloc_uid_min == SYSTEM_ALLOC_UID_MIN); + assert_se(defs.system_uid_max == SYSTEM_UID_MAX); + assert_se(defs.system_alloc_gid_min == SYSTEM_ALLOC_GID_MIN); + assert_se(defs.system_gid_max == SYSTEM_GID_MAX); + } +} + static void test_acquire_ugid_allocation_range(void) { log_info("/* %s */", __func__); @@ -48,6 +93,9 @@ static void test_gid_is_system(void) { int main(int argc, char *argv[]) { test_setup_logging(LOG_DEBUG); + test_read_login_defs("/dev/null"); + test_read_login_defs("/etc/login.defs"); + test_read_login_defs(NULL); test_acquire_ugid_allocation_range(); test_uid_is_system(); test_gid_is_system(); diff --git a/test/test-sysusers.sh.in b/test/test-sysusers.sh.in index 39d238c1f7..6e133cc841 100755 --- a/test/test-sysusers.sh.in +++ b/test/test-sysusers.sh.in @@ -20,19 +20,22 @@ prepare_testdir() { return 0 } +[ @SYSTEM_UID_MAX@ -lt @SYSTEM_GID_MAX@ ] && system_guid_max=@SYSTEM_UID_MAX@ || system_guid_max=@SYSTEM_GID_MAX@ + preprocess() { - sed -e "s/SYSTEM_UID_MAX/@SYSTEM_UID_MAX@/g" \ - -e "s/SYSTEM_GID_MAX/@SYSTEM_GID_MAX@/g" \ - -e "s#NOLOGIN#@NOLOGIN@#g" "$1" + m=${2:-$system_guid_max} + + sed -e "s/SYSTEM_UGID_MAX/$m/g; + s#NOLOGIN#@NOLOGIN@#g" "$1" } compare() { - if ! diff -u $TESTDIR/etc/passwd <(preprocess $1.expected-passwd); then + if ! diff -u $TESTDIR/etc/passwd <(preprocess $1.expected-passwd $3); then echo "**** Unexpected output for $f $2" exit 1 fi - if ! diff -u $TESTDIR/etc/group <(preprocess $1.expected-group); then + if ! diff -u $TESTDIR/etc/group <(preprocess $1.expected-group $3); then echo "**** Unexpected output for $f $2" exit 1 fi @@ -97,6 +100,52 @@ compare $SOURCE/inline "(--inline --replace=…)" rm -f $TESTDIR/etc/sysusers.d/* $TESTDIR/usr/lib/sysusers.d/* +cat >$TESTDIR/etc/login.defs <