]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sysusers: look at login.defs when setting the default range to allocate users
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Fri, 25 Sep 2020 15:16:06 +0000 (17:16 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 1 Oct 2020 17:53:45 +0000 (19:53 +0200)
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.

18 files changed:
meson.build
src/shared/user-record.c
src/shared/user-record.h
src/sysusers/sysusers.c
src/test/test-user-record.c
test/test-sysusers.sh.in
test/test-sysusers/test-10.expected-group
test/test-sysusers/test-10.expected-passwd
test/test-sysusers/test-13.expected-group
test/test-sysusers/test-13.expected-passwd
test/test-sysusers/test-14.expected-passwd
test/test-sysusers/test-2.expected-group
test/test-sysusers/test-2.expected-passwd
test/test-sysusers/test-2.input
test/test-sysusers/test-6.expected-group
test/test-sysusers/test-6.expected-passwd
test/test-sysusers/test-8.expected-passwd
test/test-sysusers/test-9.expected-passwd

index 3bec86db813a6a7e3331f7d16481dd20e4a36185..23cf3e528a03f7e79cfff82c600774262fd9b190 100644 (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
index 3ba78d455f77f71275283b3c13283f3a099503c6..7e7b28eb55023b1cb12e6ebe89577eb9232ba11e 100644 (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
index 1f87eff6d5ba06d385f6a2a1624bd79a51c143bd..2e74b910c2756a54811471d87d8f4560acb49ff5 100644 (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 {
index 7349e9fcb9ca535b205fd0969d4407e113188478..987950d60217555b624561c3613acfffae14945b 100644 (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();
index fcab61d6943e7eb84ca7b0b9149f9e8732002476..d623706648b8ae6e95db2b21dd7ac53909c5f2cb 100644 (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();
index 39d238c1f7821f2bc0a1244694299aa3a72b750a..6e133cc841ba5198d8958cd413d82ac1e02d56c5 100755 (executable)
@@ -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"
index 1c921587205479e5e9bf13e25313256fe841cca1..c94a8322d2f045dad697db08f49d9eeec6c91fde 100644 (file)
@@ -1,2 +1,2 @@
 u1:x:300:u2
-u2:x:SYSTEM_UID_MAX:
+u2:x:SYSTEM_UGID_MAX:
index ca2d764ea0686798520981307c0957aa543a1540..e5f2a699938481af4c2f295a4a3814934ccd8cca 100644 (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
index c78ea54bd30cad5ce3950793d137e369acf6bf09..1677d41b45b8cd90fbe93201d3e363dfbfb28dc3 100644 (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:
index ffc20a8193cc123b4890f93ce48bb26843a1482c..4a2c34b4cbe3c9bd145f5d11b28177ca0403b4af 100644 (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
index 62ed4f50af95b638b476a90678f1393b19ee201e..3c3bef2c8e5f024cf6cc096a7492034d64b5730f 100644 (file)
@@ -1 +1 @@
-aaa:x:SYSTEM_UID_MAX:987::/:NOLOGIN
+aaa:x:SYSTEM_UGID_MAX:987::/:NOLOGIN
index 8fcc03f4e9fd36113ad6c8a53936a47410779dc4..fa216d79dec138367788438a014055e3a1912c20 100644 (file)
@@ -1,4 +1,4 @@
-u1:x:SYSTEM_UID_MAX:
+u1:x:SYSTEM_UGID_MAX:
 u2:x:777:
 u3:x:778:
 u4:x:779:
index af8068813b6978fb820eca5a78a30a813724d900..ce49e84aad8041bb3b62e2ce4c15707bfbd4bda2 100644 (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
index cedea9e401946b0a7358144e960e7dc4aa013412..773d9e5da1268b304331ebb75c85fb3eff7f2381 100644 (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
index 499c9008ce7fbff989c29c25b18f3b1f6faefee4..2ef661a4a11abc0f971da941f59c106f916e9aca 100644 (file)
@@ -1,2 +1,2 @@
 g1:x:111:
-u1:x:SYSTEM_UID_MAX:
+u1:x:SYSTEM_UGID_MAX:
index ba55a13e18ed2da292852327b2f0741cfc88c68a..d589e2ede6de20a9e4d7351d547a8fc1f0f860cb 100644 (file)
@@ -1 +1 @@
-u1:x:SYSTEM_UID_MAX:SYSTEM_UID_MAX::/:NOLOGIN
+u1:x:SYSTEM_UGID_MAX:SYSTEM_UGID_MAX::/:NOLOGIN
index 23e99f0513b51a73abcdc0e1d90f8babf1021498..b5b8fac4372313c460ad5b6cc0e92a2f81bdcd25 100644 (file)
@@ -1 +1 @@
-username:x:SYSTEM_UID_MAX:300::/:NOLOGIN
+username:x:SYSTEM_UGID_MAX:300::/:NOLOGIN
index 0bffbcd9c7bdd372d12eab093c1bb3ee43f7a18b..fc2a060d69cf149d4d2b38f51e4172fd2a9c430e 100644 (file)
@@ -1,2 +1,2 @@
 user1:x:300:300::/:NOLOGIN
-user2:x:SYSTEM_UID_MAX:300::/:NOLOGIN
+user2:x:SYSTEM_UGID_MAX:300::/:NOLOGIN