Skip to content

Commit aa25270

Browse files
committed
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 systemd#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.
1 parent 044df62 commit aa25270

18 files changed

+143
-25
lines changed

meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,6 +1467,7 @@ foreach term : ['analyze',
14671467
have = get_option(term)
14681468
name = 'ENABLE_' + term.underscorify().to_upper()
14691469
conf.set10(name, have)
1470+
substs.set10(name, have)
14701471
endforeach
14711472

14721473
enable_sysusers = conf.get('ENABLE_SYSUSERS') == 1

src/shared/user-record.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,24 @@ static int parse_alloc_uid(const char *path, const char *name, const char *t, ui
3737
*ret_uid = uid;
3838
return 0;
3939
}
40+
#endif
4041

41-
static int read_login_defs(UGIDAllocationRange *ret_defs, const char *path) {
42-
_cleanup_fclose_ FILE *f = NULL;
42+
int read_login_defs(UGIDAllocationRange *ret_defs, const char *path, const char *root) {
4343
UGIDAllocationRange defs = {
4444
.system_alloc_uid_min = SYSTEM_ALLOC_UID_MIN,
4545
.system_uid_max = SYSTEM_UID_MAX,
4646
.system_alloc_gid_min = SYSTEM_ALLOC_GID_MIN,
4747
.system_gid_max = SYSTEM_GID_MAX,
4848
};
49+
50+
#if ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES
51+
_cleanup_fclose_ FILE *f = NULL;
4952
int r;
5053

5154
if (!path)
5255
path = "/etc/login.defs";
5356

54-
r = fopen_unlocked(path, "re", &f);
57+
r = chase_symlinks_and_fopen_unlocked(path, root, CHASE_PREFIX_ROOT, "re", &f, NULL);
5558
if (r == -ENOENT)
5659
goto assign;
5760
if (r < 0)
@@ -88,11 +91,11 @@ static int read_login_defs(UGIDAllocationRange *ret_defs, const char *path) {
8891
defs.system_alloc_gid_min = MIN(defs.system_gid_max - 1, (gid_t) SYSTEM_ALLOC_GID_MIN);
8992
/* Look at sys_gid_max to make sure sys_gid_min..sys_gid_max remains a valid range. */
9093
}
94+
#endif
9195

9296
*ret_defs = defs;
9397
return 0;
9498
}
95-
#endif
9699

97100
const UGIDAllocationRange *acquire_ugid_allocation_range(void) {
98101
#if ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES
@@ -114,7 +117,7 @@ const UGIDAllocationRange *acquire_ugid_allocation_range(void) {
114117
static thread_local bool initialized = false;
115118

116119
if (!initialized) {
117-
(void) read_login_defs(&defs, NULL);
120+
(void) read_login_defs(&defs, NULL, NULL);
118121
initialized = true;
119122
}
120123
#endif

src/shared/user-record.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ typedef struct UGIDAllocationRange {
4343
gid_t system_gid_max;
4444
} UGIDAllocationRange;
4545

46+
int read_login_defs(UGIDAllocationRange *ret_defs, const char *path, const char *root);
4647
const UGIDAllocationRange *acquire_ugid_allocation_range(void);
4748

4849
typedef enum UserDisposition {

src/sysusers/sysusers.c

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "strv.h"
2727
#include "tmpfile-util-label.h"
2828
#include "uid-range.h"
29+
#include "user-record.h"
2930
#include "user-util.h"
3031
#include "utf8.h"
3132
#include "util.h"
@@ -1949,10 +1950,25 @@ static int run(int argc, char *argv[]) {
19491950
return log_error_errno(errno, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m");
19501951

19511952
if (!uid_range) {
1952-
/* Default to default range of 1..SYSTEM_UID_MAX */
1953-
r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX);
1953+
/* Default to default range of SYSTEMD_UID_MIN..SYSTEM_UID_MAX. */
1954+
UGIDAllocationRange defs;
1955+
1956+
r = read_login_defs(&defs, NULL, arg_root);
19541957
if (r < 0)
1955-
return log_oom();
1958+
return log_error_errno(r, "Failed to read %s%s: %m",
1959+
strempty(arg_root), "/etc/login.defs");
1960+
1961+
/* We pick a range that very conservative: we look at compiled-in maximum and the value in
1962+
* /etc/login.defs. That way the uids/gids which we allocate will be interpreted correctly,
1963+
* even if /etc/login.defs is removed later. (The bottom bound doesn't matter much, since
1964+
* it's only used during allocation, so we use the configured value directly). */
1965+
uid_t begin = defs.system_alloc_uid_min,
1966+
end = MIN3((uid_t) SYSTEM_UID_MAX, defs.system_uid_max, defs.system_gid_max);
1967+
if (begin < end) {
1968+
r = uid_range_add(&uid_range, &n_uid_range, begin, end - begin + 1);
1969+
if (r < 0)
1970+
return log_oom();
1971+
}
19561972
}
19571973

19581974
r = add_implicit();

src/test/test-user-record.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,55 @@
33
#include <unistd.h>
44
#include <sys/types.h>
55

6+
#include "fd-util.h"
7+
#include "fileio.h"
68
#include "format-util.h"
9+
#include "fs-util.h"
10+
#include "tmpfile-util.h"
711
#include "tests.h"
812
#include "user-record.h"
913

14+
static void test_read_login_defs(const char *path) {
15+
log_info("/* %s(\"%s\") */", __func__, path ?: "<custom>");
16+
17+
_cleanup_(unlink_tempfilep) char name[] = "/tmp/test-user-record.XXXXXX";
18+
_cleanup_fclose_ FILE *f = NULL;
19+
if (!path) {
20+
assert_se(fmkostemp_safe(name, "r+", &f) == 0);
21+
fprintf(f,
22+
"SYS_UID_MIN "UID_FMT"\n"
23+
"SYS_UID_MAX "UID_FMT"\n"
24+
"SYS_GID_MIN "GID_FMT"\n"
25+
"SYS_GID_MAX "GID_FMT"\n",
26+
SYSTEM_ALLOC_UID_MIN + 5,
27+
SYSTEM_UID_MAX + 5,
28+
SYSTEM_ALLOC_GID_MIN + 5,
29+
SYSTEM_GID_MAX + 5);
30+
assert_se(fflush_and_check(f) >= 0);
31+
}
32+
33+
UGIDAllocationRange defs;
34+
assert_se(read_login_defs(&defs, path ?: name, NULL) >= 0);
35+
36+
log_info("system_alloc_uid_min="UID_FMT, defs.system_alloc_uid_min);
37+
log_info("system_uid_max="UID_FMT, defs.system_uid_max);
38+
log_info("system_alloc_gid_min="GID_FMT, defs.system_alloc_gid_min);
39+
log_info("system_gid_max="GID_FMT, defs.system_gid_max);
40+
41+
if (!path) {
42+
uid_t offset = ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES ? 5 : 0;
43+
assert_se(defs.system_alloc_uid_min == SYSTEM_ALLOC_UID_MIN + offset);
44+
assert_se(defs.system_uid_max == SYSTEM_UID_MAX + offset);
45+
assert_se(defs.system_alloc_gid_min == SYSTEM_ALLOC_GID_MIN + offset);
46+
assert_se(defs.system_gid_max == SYSTEM_GID_MAX + offset);
47+
} else if (streq(path, "/dev/null")) {
48+
assert_se(defs.system_alloc_uid_min == SYSTEM_ALLOC_UID_MIN);
49+
assert_se(defs.system_uid_max == SYSTEM_UID_MAX);
50+
assert_se(defs.system_alloc_gid_min == SYSTEM_ALLOC_GID_MIN);
51+
assert_se(defs.system_gid_max == SYSTEM_GID_MAX);
52+
}
53+
}
54+
1055
static void test_acquire_ugid_allocation_range(void) {
1156
log_info("/* %s */", __func__);
1257

@@ -48,6 +93,9 @@ static void test_gid_is_system(void) {
4893
int main(int argc, char *argv[]) {
4994
test_setup_logging(LOG_DEBUG);
5095

96+
test_read_login_defs("/dev/null");
97+
test_read_login_defs("/etc/login.defs");
98+
test_read_login_defs(NULL);
5199
test_acquire_ugid_allocation_range();
52100
test_uid_is_system();
53101
test_gid_is_system();

test/test-sysusers.sh.in

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,22 @@ prepare_testdir() {
2020
return 0
2121
}
2222

23+
[ @SYSTEM_UID_MAX@ -lt @SYSTEM_GID_MAX@ ] && system_guid_max=@SYSTEM_UID_MAX@ || system_guid_max=@SYSTEM_GID_MAX@
24+
2325
preprocess() {
24-
sed -e "s/SYSTEM_UID_MAX/@SYSTEM_UID_MAX@/g" \
25-
-e "s/SYSTEM_GID_MAX/@SYSTEM_GID_MAX@/g" \
26-
-e "s#NOLOGIN#@NOLOGIN@#g" "$1"
26+
m=${2:-$system_guid_max}
27+
28+
sed -e "s/SYSTEM_UGID_MAX/$m/g;
29+
s#NOLOGIN#@NOLOGIN@#g" "$1"
2730
}
2831

2932
compare() {
30-
if ! diff -u $TESTDIR/etc/passwd <(preprocess $1.expected-passwd); then
33+
if ! diff -u $TESTDIR/etc/passwd <(preprocess $1.expected-passwd $3); then
3134
echo "**** Unexpected output for $f $2"
3235
exit 1
3336
fi
3437

35-
if ! diff -u $TESTDIR/etc/group <(preprocess $1.expected-group); then
38+
if ! diff -u $TESTDIR/etc/group <(preprocess $1.expected-group $3); then
3639
echo "**** Unexpected output for $f $2"
3740
exit 1
3841
fi
@@ -97,6 +100,52 @@ compare $SOURCE/inline "(--inline --replace=…)"
97100

98101
rm -f $TESTDIR/etc/sysusers.d/* $TESTDIR/usr/lib/sysusers.d/*
99102

103+
cat >$TESTDIR/etc/login.defs <<EOF
104+
SYS_UID_MIN abcd
105+
SYS_UID_MAX abcd
106+
SYS_GID_MIN abcd
107+
SYS_GID_MAX abcd
108+
SYS_UID_MIN 401
109+
SYS_UID_MAX 555
110+
SYS_GID_MIN 405
111+
SYS_GID_MAX 666
112+
SYS_UID_MIN abcd
113+
SYS_UID_MAX abcd
114+
SYS_GID_MIN abcd
115+
SYS_GID_MAX abcd
116+
SYS_UID_MIN999
117+
SYS_UID_MAX999
118+
SYS_GID_MIN999
119+
SYS_GID_MAX999
120+
EOF
121+
122+
for f in $(ls -1 $SOURCE/test-*.input | sort -V); do
123+
echo "*** Running $f (with login.defs)"
124+
prepare_testdir ${f%.input}
125+
cp $f $TESTDIR/usr/lib/sysusers.d/test.conf
126+
$SYSUSERS --root=$TESTDIR
127+
128+
[ @ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES@ = 1 ] && bound=555 || bound=$system_guid_max
129+
compare ${f%.*} "(with login.defs)" $bound
130+
done
131+
132+
rm -f $TESTDIR/etc/sysusers.d/* $TESTDIR/usr/lib/sysusers.d/*
133+
134+
mv $TESTDIR/etc/login.defs $TESTDIR/etc/login.defs.moved
135+
ln -s ../../../../../etc/login.defs.moved $TESTDIR/etc/login.defs
136+
137+
for f in $(ls -1 $SOURCE/test-*.input | sort -V); do
138+
echo "*** Running $f (with login.defs symlinked)"
139+
prepare_testdir ${f%.input}
140+
cp $f $TESTDIR/usr/lib/sysusers.d/test.conf
141+
$SYSUSERS --root=$TESTDIR
142+
143+
[ @ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES@ = 1 ] && bound=555 || bound=$system_guid_max
144+
compare ${f%.*} "(with login.defs symlinked)" $bound
145+
done
146+
147+
rm -f $TESTDIR/etc/sysusers.d/* $TESTDIR/usr/lib/sysusers.d/*
148+
100149
# tests for error conditions
101150
for f in $(ls -1 $SOURCE/unhappy-*.input | sort -V); do
102151
echo "*** Running test $f"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
u1:x:300:u2
2-
u2:x:SYSTEM_UID_MAX:
2+
u2:x:SYSTEM_UGID_MAX:
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
u1:x:300:300::/:NOLOGIN
2-
u2:x:SYSTEM_UID_MAX:SYSTEM_UID_MAX::/:NOLOGIN
2+
u2:x:SYSTEM_UGID_MAX:SYSTEM_UGID_MAX::/:NOLOGIN
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
hoge:x:300:
22
baz:x:302:
3-
yyy:x:SYSTEM_GID_MAX:
3+
yyy:x:SYSTEM_UGID_MAX:
44
foo:x:301:
55
ccc:x:305:

test/test-sysusers/test-13.expected-passwd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ foo:x:301:301::/:NOLOGIN
22
aaa:x:303:302::/:NOLOGIN
33
bbb:x:304:302::/:NOLOGIN
44
ccc:x:305:305::/:NOLOGIN
5-
zzz:x:306:SYSTEM_GID_MAX::/:NOLOGIN
5+
zzz:x:306:SYSTEM_UGID_MAX::/:NOLOGIN

0 commit comments

Comments
 (0)