sysusers: look at login.defs when setting the default range to allocate users

Also, even if login.defs are not present, don't start allocating at 1, but at
SYSTEM_UID_MIN.

Fixes #9769.

The test is adjusted. Actually, it was busted before, because sysusers would
never use SYSTEM_GID_MIN, so if SYSTEM_GID_MIN was different than
SYSTEM_UID_MIN, the tests would fail. On all "normal" systems the two are
equal, so we didn't notice. Since sysusers now always uses the minimum of the
two, we only need to substitute one value.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2020-09-25 17:16:06 +02:00
parent 044df624aa
commit aa25270cb2
18 changed files with 143 additions and 25 deletions

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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();

View File

@ -3,10 +3,55 @@
#include <unistd.h>
#include <sys/types.h>
#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 ?: "<custom>");
_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();

View File

@ -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 <<EOF
SYS_UID_MIN abcd
SYS_UID_MAX abcd
SYS_GID_MIN abcd
SYS_GID_MAX abcd
SYS_UID_MIN 401
SYS_UID_MAX 555
SYS_GID_MIN 405
SYS_GID_MAX 666
SYS_UID_MIN abcd
SYS_UID_MAX abcd
SYS_GID_MIN abcd
SYS_GID_MAX abcd
SYS_UID_MIN999
SYS_UID_MAX999
SYS_GID_MIN999
SYS_GID_MAX999
EOF
for f in $(ls -1 $SOURCE/test-*.input | sort -V); do
echo "*** Running $f (with login.defs)"
prepare_testdir ${f%.input}
cp $f $TESTDIR/usr/lib/sysusers.d/test.conf
$SYSUSERS --root=$TESTDIR
[ @ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES@ = 1 ] && bound=555 || bound=$system_guid_max
compare ${f%.*} "(with login.defs)" $bound
done
rm -f $TESTDIR/etc/sysusers.d/* $TESTDIR/usr/lib/sysusers.d/*
mv $TESTDIR/etc/login.defs $TESTDIR/etc/login.defs.moved
ln -s ../../../../../etc/login.defs.moved $TESTDIR/etc/login.defs
for f in $(ls -1 $SOURCE/test-*.input | sort -V); do
echo "*** Running $f (with login.defs symlinked)"
prepare_testdir ${f%.input}
cp $f $TESTDIR/usr/lib/sysusers.d/test.conf
$SYSUSERS --root=$TESTDIR
[ @ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES@ = 1 ] && bound=555 || bound=$system_guid_max
compare ${f%.*} "(with login.defs symlinked)" $bound
done
rm -f $TESTDIR/etc/sysusers.d/* $TESTDIR/usr/lib/sysusers.d/*
# tests for error conditions
for f in $(ls -1 $SOURCE/unhappy-*.input | sort -V); do
echo "*** Running test $f"

View File

@ -1,2 +1,2 @@
u1:x:300:u2
u2:x:SYSTEM_UID_MAX:
u2:x:SYSTEM_UGID_MAX:

View File

@ -1,2 +1,2 @@
u1:x:300:300::/:NOLOGIN
u2:x:SYSTEM_UID_MAX:SYSTEM_UID_MAX::/:NOLOGIN
u2:x:SYSTEM_UGID_MAX:SYSTEM_UGID_MAX::/:NOLOGIN

View File

@ -1,5 +1,5 @@
hoge:x:300:
baz:x:302:
yyy:x:SYSTEM_GID_MAX:
yyy:x:SYSTEM_UGID_MAX:
foo:x:301:
ccc:x:305:

View File

@ -2,4 +2,4 @@ foo:x:301:301::/:NOLOGIN
aaa:x:303:302::/:NOLOGIN
bbb:x:304:302::/:NOLOGIN
ccc:x:305:305::/:NOLOGIN
zzz:x:306:SYSTEM_GID_MAX::/:NOLOGIN
zzz:x:306:SYSTEM_UGID_MAX::/:NOLOGIN

View File

@ -1 +1 @@
aaa:x:SYSTEM_UID_MAX:987::/:NOLOGIN
aaa:x:SYSTEM_UGID_MAX:987::/:NOLOGIN

View File

@ -1,4 +1,4 @@
u1:x:SYSTEM_UID_MAX:
u1:x:SYSTEM_UGID_MAX:
u2:x:777:
u3:x:778:
u4:x:779:

View File

@ -1,4 +1,4 @@
u1:x:SYSTEM_UID_MAX:SYSTEM_UID_MAX:some gecos:/random/dir:NOLOGIN
u1:x:SYSTEM_UGID_MAX:SYSTEM_UGID_MAX:some gecos:/random/dir:NOLOGIN
u2:x:777:777:some gecos:/random/dir:/bin/zsh
u3:x:778:778::/random/dir2:/bin/bash
u4:x:779:779::/:/bin/csh

View File

@ -1,4 +1,4 @@
# Test generation of ID dynamically based on SYSTEM_UID_MAX and
# Test generation of ID dynamically based on SYSTEM_UGID_MAX and
# replacement of all fields up to the login shell.
#
#Type Name ID GECOS homedir shell

View File

@ -1,2 +1,2 @@
g1:x:111:
u1:x:SYSTEM_UID_MAX:
u1:x:SYSTEM_UGID_MAX:

View File

@ -1 +1 @@
u1:x:SYSTEM_UID_MAX:SYSTEM_UID_MAX::/:NOLOGIN
u1:x:SYSTEM_UGID_MAX:SYSTEM_UGID_MAX::/:NOLOGIN

View File

@ -1 +1 @@
username:x:SYSTEM_UID_MAX:300::/:NOLOGIN
username:x:SYSTEM_UGID_MAX:300::/:NOLOGIN

View File

@ -1,2 +1,2 @@
user1:x:300:300::/:NOLOGIN
user2:x:SYSTEM_UID_MAX:300::/:NOLOGIN
user2:x:SYSTEM_UGID_MAX:300::/:NOLOGIN