mirror of
https://github.com/systemd/systemd.git
synced 2025-03-31 14:50:15 +03:00
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:
parent
044df624aa
commit
aa25270cb2
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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"
|
||||
|
@ -1,2 +1,2 @@
|
||||
u1:x:300:u2
|
||||
u2:x:SYSTEM_UID_MAX:
|
||||
u2:x:SYSTEM_UGID_MAX:
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -1 +1 @@
|
||||
aaa:x:SYSTEM_UID_MAX:987::/:NOLOGIN
|
||||
aaa:x:SYSTEM_UGID_MAX:987::/:NOLOGIN
|
||||
|
@ -1,4 +1,4 @@
|
||||
u1:x:SYSTEM_UID_MAX:
|
||||
u1:x:SYSTEM_UGID_MAX:
|
||||
u2:x:777:
|
||||
u3:x:778:
|
||||
u4:x:779:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,2 +1,2 @@
|
||||
g1:x:111:
|
||||
u1:x:SYSTEM_UID_MAX:
|
||||
u1:x:SYSTEM_UGID_MAX:
|
||||
|
@ -1 +1 @@
|
||||
u1:x:SYSTEM_UID_MAX:SYSTEM_UID_MAX::/:NOLOGIN
|
||||
u1:x:SYSTEM_UGID_MAX:SYSTEM_UGID_MAX::/:NOLOGIN
|
||||
|
@ -1 +1 @@
|
||||
username:x:SYSTEM_UID_MAX:300::/:NOLOGIN
|
||||
username:x:SYSTEM_UGID_MAX:300::/:NOLOGIN
|
||||
|
@ -1,2 +1,2 @@
|
||||
user1:x:300:300::/:NOLOGIN
|
||||
user2:x:SYSTEM_UID_MAX:300::/:NOLOGIN
|
||||
user2:x:SYSTEM_UGID_MAX:300::/:NOLOGIN
|
||||
|
Loading…
x
Reference in New Issue
Block a user