Compare commits

...

14 Commits

Author SHA1 Message Date
Lennart Poettering 278c13431b github: ask for arch+kernel in bug report form
Quite often we see kernel and arch specific issues, let's ask for the
version right-away when people file the issue.

Other tweaks.
2020-10-02 12:38:28 +02:00
Lennart Poettering c14ebe07a9
Merge pull request #17172 from keszybz/read-login-defs
Read /etc/login.defs
2020-10-02 11:01:30 +02:00
Zbigniew Jędrzejewski-Szmek 4b6f9b202e sysusers: emit warnings about login.defs overrides on first user or group creation
*** Running /home/zbyszek/src/systemd-work/test/test-sysusers/test-14.input (with login.defs symlinked)
login.defs specifies UID allocation range 401–555 that is different than the built-in defaults (201–998)
login.defs specifies GID allocation range 405–666 that is different than the built-in defaults (201–990)
2020-10-01 19:53:45 +02:00
Zbigniew Jędrzejewski-Szmek a3709e3709 tests: when creating temp dirs, include test name in path
This makes it easier to figure out which directory we want to look at
when tests fail, and also which test left behind a directory when it
shouldn't.
2020-10-01 19:53:45 +02:00
Zbigniew Jędrzejewski-Szmek aa25270cb2 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.
2020-10-01 19:53:45 +02:00
Zbigniew Jędrzejewski-Szmek 044df624aa test-sysusers: fix how paths are calculated
We were looking at ${f%.*}, i.e. the $f with any suffix starting with a dot removed.
This worked fine for paths like /some/path/test-11.input. It also worked
for paths like /some/path/inline (there were no dots, so we got $f back unscathed).
But in the ubuntu CI the package is built in a temporary directory like
/tmp/autopkgtest-lxc.nnnfqb26/downtmp/build.UfW/ (yes, it has a dot, even two.).
That still worked for the first case, but in the second case we truncated things
after the first dot, and we would try to get
/tmp/autopkgtest-lxc.nnnfqb26/downtmp/build and try to load
/tmp/autopkgtest-lxc.nnnfqb26/downtmp/build.expected-password, which obviously
didn't work as expected. To avoid this issue, do the suffix removal only when
we know that there really is a suffix.

A second minor issue was that we would try to copy $1.expected-*, and sometimes
$1 would be given, and sometimes not. Effectively we were relying on there
not being any files matching .expected-*. There weren't any such files, but let's
avoid this ugliness and always pass $1.
2020-10-01 17:52:51 +02:00
Zbigniew Jędrzejewski-Szmek 69a7c5fb1f test-sysusers: sort examples
This shouldn't affect the outcome, but makes outputs easier to compare.
2020-10-01 17:52:51 +02:00
Zbigniew Jędrzejewski-Szmek bd7e6aa73a test/TEST-21-SYSUSERS: turn into a unit test
All this test does is manipulate text files in a subdir specified with --testroot.
It can be a normal unittest without the overhead of creating a machine image.

As a bonus, also test the .standalone version.
2020-10-01 17:52:51 +02:00
Zbigniew Jędrzejewski-Szmek d338bfff4a basic/fileio: add chase_symlinks_and_fopen_unlocked() 2020-10-01 17:52:50 +02:00
Zbigniew Jędrzejewski-Szmek 196b596867 shared/uid-range: reduce scope of iterator variables 2020-10-01 17:52:50 +02:00
Zbigniew Jędrzejewski-Szmek fc1a5d1a70 Also parse the minimum uid/gid values
We don't (and shouldn't I think) look at them when determining the type of the
user, but they should be used during user/group allocation. (For example, an
admin may specify SYS_UID_MIN==200 to allow statically numbered users that are
shared with other systems in the range 1–199.)
2020-10-01 17:52:41 +02:00
Zbigniew Jędrzejewski-Szmek 53393c894d Look at /etc/login.defs for the system_max_[ug]id values
It makes little sense to make the boundary between systemd and user guids
configurable. Nevertheless, a completely fixed compile-time define is not
enough in two scenarios:
- the systemd_uid_max boundary has moved over time. The default used to be
  500 for a long time. Systems which are upgraded over time might have users
  in the wrong range, but changing existing systems is complicated and
  expensive (offline disks, backups, remote systems, read-only media, etc.)
- systems are used in a heterogenous enviornment, where some vendors pick
  one value and others another.
So let's make this boundary overridable using /etc/login.defs.

Fixes #3855, #10184.
2020-10-01 17:49:31 +02:00
Zbigniew Jędrzejewski-Szmek 28add648a8 coredump: use uid_is_system() when appropriate 2020-09-25 17:18:56 +02:00
Zbigniew Jędrzejewski-Szmek 98dcb8f4c7 Move {uid,gid}_is_*() from basic to shared
Those are functions that express policy, and nothing in basic/ uses
(or should use) them.
2020-09-25 17:18:56 +02:00
90 changed files with 629 additions and 236 deletions

View File

@ -5,7 +5,7 @@ about: A report of an error in a recent systemd version
--- ---
**systemd version the issue has been seen with** **systemd version the issue has been seen with**
> ... > …
<!-- **NOTE:** Do not submit bug reports about anything but the two most recently released (non-rc) systemd versions upstream! --> <!-- **NOTE:** Do not submit bug reports about anything but the two most recently released (non-rc) systemd versions upstream! -->
<!-- See https://github.com/systemd/systemd/releases for the list of most recent releases. --> <!-- See https://github.com/systemd/systemd/releases for the list of most recent releases. -->
@ -14,6 +14,12 @@ about: A report of an error in a recent systemd version
**Used distribution** **Used distribution**
> … > …
**Linux kernel version used** (`uname -a`)
> …
**CPU architecture issue was seen on**
> …
**Expected behaviour you didn't see** **Expected behaviour you didn't see**
> … > …
@ -21,3 +27,4 @@ about: A report of an error in a recent systemd version
> … > …
**Steps to reproduce the problem** **Steps to reproduce the problem**
> …

View File

@ -171,10 +171,11 @@ pick — given that 64K UIDs are assigned to each container according to this
allocation logic, the maximum UID used for this range is hence allocation logic, the maximum UID used for this range is hence
1878982656+65535=1879048191.) 1878982656+65535=1879048191.)
Note that systemd does not make any of these values runtime-configurable. All Systemd has compile-time default for these boundaries. Using those defaults is
these boundaries are chosen during build time. That said, the system UID/GID recommended. It will nevertheless query `/etc/login.defs` at runtime, when
boundary is traditionally configured in /etc/login.defs, though systemd won't compiled with `-Dcompat-mutable-uid-boundaries=true` and that file is present.
look there during runtime. Support for this is considered only a compatibility feature and should not be
used except when upgrading systems which were creating with different defaults.
## Considerations for container managers ## Considerations for container managers

View File

@ -299,6 +299,7 @@ substs.set('CERTIFICATEROOT', get_option('certif
substs.set('RANDOM_SEED', join_paths(randomseeddir, 'random-seed')) substs.set('RANDOM_SEED', join_paths(randomseeddir, 'random-seed'))
substs.set('SYSTEM_SYSVINIT_PATH', sysvinit_path) substs.set('SYSTEM_SYSVINIT_PATH', sysvinit_path)
substs.set('SYSTEM_SYSVRCND_PATH', sysvrcnd_path) substs.set('SYSTEM_SYSVRCND_PATH', sysvrcnd_path)
substs.set('SYSTEMD_TEST_DATA', join_paths(testsdir, 'testdata'))
substs.set('RC_LOCAL_PATH', get_option('rc-local')) substs.set('RC_LOCAL_PATH', get_option('rc-local'))
substs.set('MEMORY_ACCOUNTING_DEFAULT', memory_accounting_default ? 'yes' : 'no') substs.set('MEMORY_ACCOUNTING_DEFAULT', memory_accounting_default ? 'yes' : 'no')
substs.set('STATUS_UNIT_FORMAT_DEFAULT', status_unit_format_default) substs.set('STATUS_UNIT_FORMAT_DEFAULT', status_unit_format_default)
@ -694,35 +695,31 @@ if time_epoch == -1
endif endif
conf.set('TIME_EPOCH', time_epoch) conf.set('TIME_EPOCH', time_epoch)
system_uid_max = get_option('system-uid-max') foreach tuple : [['system-alloc-uid-min', 'SYS_UID_MIN', 1], # Also see login.defs(5).
if system_uid_max == -1 ['system-uid-max', 'SYS_UID_MAX', 999],
system_uid_max = run_command( ['system-alloc-gid-min', 'SYS_GID_MIN', 1],
awk, ['system-gid-max', 'SYS_GID_MAX', 999]]
'/^\s*SYS_UID_MAX\s+/ { uid=$2 } END { print uid }', v = get_option(tuple[0])
'/etc/login.defs').stdout().strip() if v == -1
if system_uid_max == '' v = run_command(
system_uid_max = 999 awk,
else '/^\s*@0@\s+/ { uid=$2 } END { print uid }'.format(tuple[1]),
system_uid_max = system_uid_max.to_int() '/etc/login.defs').stdout().strip()
if v == ''
v = tuple[2]
else
v = v.to_int()
endif
endif endif
conf.set(tuple[0].underscorify().to_upper(), v)
substs.set(tuple[0].underscorify().to_upper(), v)
endforeach
if conf.get('SYSTEM_ALLOC_UID_MIN') >= conf.get('SYSTEM_UID_MAX')
error('Invalid uid allocation range')
endif endif
conf.set('SYSTEM_UID_MAX', system_uid_max) if conf.get('SYSTEM_ALLOC_GID_MIN') >= conf.get('SYSTEM_GID_MAX')
substs.set('systemuidmax', system_uid_max) error('Invalid gid allocation range')
system_gid_max = get_option('system-gid-max')
if system_gid_max == -1
system_gid_max = run_command(
awk,
'/^\s*SYS_GID_MAX\s+/ { gid=$2 } END { print gid }',
'/etc/login.defs').stdout().strip()
if system_gid_max == ''
system_gid_max = 999
else
system_gid_max = system_gid_max.to_int()
endif
endif endif
conf.set('SYSTEM_GID_MAX', system_gid_max)
substs.set('systemgidmax', system_gid_max)
dynamic_uid_min = get_option('dynamic-uid-min') dynamic_uid_min = get_option('dynamic-uid-min')
dynamic_uid_max = get_option('dynamic-uid-max') dynamic_uid_max = get_option('dynamic-uid-max')
@ -1444,6 +1441,7 @@ foreach term : ['analyze',
'idn', 'idn',
'ima', 'ima',
'initrd', 'initrd',
'compat-mutable-uid-boundaries',
'ldconfig', 'ldconfig',
'localed', 'localed',
'logind', 'logind',
@ -1470,8 +1468,11 @@ foreach term : ['analyze',
have = get_option(term) have = get_option(term)
name = 'ENABLE_' + term.underscorify().to_upper() name = 'ENABLE_' + term.underscorify().to_upper()
conf.set10(name, have) conf.set10(name, have)
substs.set10(name, have)
endforeach endforeach
enable_sysusers = conf.get('ENABLE_SYSUSERS') == 1
foreach tuple : [['nss-mymachines', 'machined'], foreach tuple : [['nss-mymachines', 'machined'],
['nss-resolve', 'resolve']] ['nss-resolve', 'resolve']]
want = get_option(tuple[0]) want = get_option(tuple[0])
@ -2966,8 +2967,8 @@ public_programs += executable(
install_rpath : rootlibexecdir, install_rpath : rootlibexecdir,
install : true) install : true)
if conf.get('ENABLE_SYSUSERS') == 1 if enable_sysusers
public_programs += executable( exe = executable(
'systemd-sysusers', 'systemd-sysusers',
'src/sysusers/sysusers.c', 'src/sysusers/sysusers.c',
include_directories : includes, include_directories : includes,
@ -2975,9 +2976,17 @@ if conf.get('ENABLE_SYSUSERS') == 1
install_rpath : rootlibexecdir, install_rpath : rootlibexecdir,
install : true, install : true,
install_dir : rootbindir) install_dir : rootbindir)
public_programs += exe
if want_tests != 'false'
test('test-sysusers',
test_sysusers_sh,
# https://github.com/mesonbuild/meson/issues/2681
args : exe.full_path())
endif
if have_standalone_binaries if have_standalone_binaries
public_programs += executable( exe = executable(
'systemd-sysusers.standalone', 'systemd-sysusers.standalone',
'src/sysusers/sysusers.c', 'src/sysusers/sysusers.c',
include_directories : includes, include_directories : includes,
@ -2988,6 +2997,14 @@ if conf.get('ENABLE_SYSUSERS') == 1
libjournal_client], libjournal_client],
install : true, install : true,
install_dir : rootbindir) install_dir : rootbindir)
public_programs += exe
if want_tests != 'false'
test('test-sysusers.standalone',
test_sysusers_sh,
# https://github.com/mesonbuild/meson/issues/2681
args : exe.full_path())
endif
endif endif
endif endif
@ -3539,12 +3556,12 @@ status = [
get_option('debug-tty')), get_option('debug-tty')),
'TTY GID: @0@'.format(tty_gid), 'TTY GID: @0@'.format(tty_gid),
'users GID: @0@'.format(substs.get('USERS_GID')), 'users GID: @0@'.format(substs.get('USERS_GID')),
'maximum system UID: @0@'.format(system_uid_max), 'system UIDs: <=@0@ (alloc >=@1@)'.format(conf.get('SYSTEM_UID_MAX'),
'maximum system GID: @0@'.format(system_gid_max), conf.get('SYSTEM_ALLOC_UID_MIN')),
'minimum dynamic UID: @0@'.format(dynamic_uid_min), 'system GIDs: <=@0@ (alloc >=@1@)'.format(conf.get('SYSTEM_GID_MAX'),
'maximum dynamic UID: @0@'.format(dynamic_uid_max), conf.get('SYSTEM_ALLOC_GID_MIN')),
'minimum container UID base: @0@'.format(container_uid_base_min), 'dynamic UIDs: @0@@1@'.format(dynamic_uid_min, dynamic_uid_max),
'maximum container UID base: @0@'.format(container_uid_base_max), 'container UID bases: @0@@1@'.format(container_uid_base_min, container_uid_base_max),
'/dev/kvm access mode: @0@'.format(get_option('dev-kvm-mode')), '/dev/kvm access mode: @0@'.format(get_option('dev-kvm-mode')),
'render group access mode: @0@'.format(get_option('group-render-mode')), 'render group access mode: @0@'.format(get_option('group-render-mode')),
'certificate root directory: @0@'.format(get_option('certificate-root')), 'certificate root directory: @0@'.format(get_option('certificate-root')),
@ -3628,6 +3645,7 @@ foreach tuple : [
['libcurl'], ['libcurl'],
['idn'], ['idn'],
['initrd'], ['initrd'],
['compat-mutable-uid-boundaries'],
['libidn2'], ['libidn2'],
['libidn'], ['libidn'],
['libiptc'], ['libiptc'],

View File

@ -28,9 +28,9 @@ option('static-libsystemd', type : 'combo',
description : '''install a static library for libsystemd''') description : '''install a static library for libsystemd''')
option('static-libudev', type : 'combo', option('static-libudev', type : 'combo',
choices : ['false', 'true', 'pic', 'no-pic'], choices : ['false', 'true', 'pic', 'no-pic'],
description : '''install a static library for libudev''') description : 'install a static library for libudev')
option('standalone-binaries', type : 'boolean', value : 'false', option('standalone-binaries', type : 'boolean', value : 'false',
description : '''also build standalone versions of supported binaries''') description : 'also build standalone versions of supported binaries')
option('sysvinit-path', type : 'string', value : '/etc/init.d', option('sysvinit-path', type : 'string', value : '/etc/init.d',
description : 'the directory where the SysV init scripts are located') description : 'the directory where the SysV init scripts are located')
@ -40,8 +40,10 @@ option('telinit-path', type : 'string', value : '/lib/sysvinit/telinit',
description : 'path to telinit') description : 'path to telinit')
option('rc-local', type : 'string', option('rc-local', type : 'string',
value : '/etc/rc.local') value : '/etc/rc.local')
option('initrd', type: 'boolean', option('initrd', type : 'boolean',
description : 'install services for use when running systemd in initrd') description : 'install services for use when running systemd in initrd')
option('compat-mutable-uid-boundaries', type : 'boolean', value : 'false',
description : 'look at uid boundaries in /etc/login.defs for compatibility')
option('quotaon-path', type : 'string', description : 'path to quotaon') option('quotaon-path', type : 'string', description : 'path to quotaon')
option('quotacheck-path', type : 'string', description : 'path to quotacheck') option('quotacheck-path', type : 'string', description : 'path to quotacheck')
@ -192,6 +194,10 @@ option('status-unit-format-default', type : 'combo',
description : 'use unit name or description in messages by default') description : 'use unit name or description in messages by default')
option('time-epoch', type : 'integer', value : '-1', option('time-epoch', type : 'integer', value : '-1',
description : 'time epoch for time clients') description : 'time epoch for time clients')
option('system-alloc-uid-min', type : 'integer', value : '-1',
description : 'minimum system UID used when allocating')
option('system-alloc-gid-min', type : 'integer', value : '-1',
description : 'minimum system GID used when allocating')
option('system-uid-max', type : 'integer', value : '-1', option('system-uid-max', type : 'integer', value : '-1',
description : 'maximum system UID') description : 'maximum system UID')
option('system-gid-max', type : 'integer', value : '-1', option('system-gid-max', type : 'integer', value : '-1',

View File

@ -940,6 +940,42 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *root
return search_and_fopen_internal(path, mode, root, s, _f); return search_and_fopen_internal(path, mode, root, s, _f);
} }
int chase_symlinks_and_fopen_unlocked(
const char *path,
const char *root,
unsigned chase_flags,
const char *open_flags,
FILE **ret_file,
char **ret_path) {
_cleanup_close_ int fd = -1;
_cleanup_free_ char *final_path = NULL;
int mode_flags, r;
FILE *f;
assert(path);
assert(open_flags);
assert(ret_file);
mode_flags = mode_to_flags(open_flags);
if (mode_flags < 0)
return mode_flags;
fd = chase_symlinks_and_open(path, root, chase_flags, mode_flags, ret_path ? &final_path : NULL);
if (fd < 0)
return fd;
r = fdopen_unlocked(fd, open_flags, &f);
if (r < 0)
return r;
TAKE_FD(fd);
*ret_file = f;
if (ret_path)
*ret_path = TAKE_PTR(final_path);
return 0;
}
int fflush_and_check(FILE *f) { int fflush_and_check(FILE *f) {
assert(f); assert(f);

View File

@ -81,6 +81,14 @@ int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **r
int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f); int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f);
int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f); int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f);
int chase_symlinks_and_fopen_unlocked(
const char *path,
const char *root,
unsigned chase_flags,
const char *open_flags,
FILE **ret_file,
char **ret_path);
int fflush_and_check(FILE *f); int fflush_and_check(FILE *f);
int fflush_sync_and_check(FILE *f); int fflush_sync_and_check(FILE *f);

View File

@ -81,7 +81,7 @@ enum {
CHASE_PREFIX_ROOT = 1 << 0, /* The specified path will be prefixed by the specified root before beginning the iteration */ CHASE_PREFIX_ROOT = 1 << 0, /* The specified path will be prefixed by the specified root before beginning the iteration */
CHASE_NONEXISTENT = 1 << 1, /* It's OK if the path doesn't actually exist. */ CHASE_NONEXISTENT = 1 << 1, /* It's OK if the path doesn't actually exist. */
CHASE_NO_AUTOFS = 1 << 2, /* Return -EREMOTE if autofs mount point found */ CHASE_NO_AUTOFS = 1 << 2, /* Return -EREMOTE if autofs mount point found */
CHASE_SAFE = 1 << 3, /* Return EPERM if we ever traverse from unprivileged to privileged files or directories */ CHASE_SAFE = 1 << 3, /* Return -EPERM if we ever traverse from unprivileged to privileged files or directories */
CHASE_TRAIL_SLASH = 1 << 4, /* Any trailing slash will be preserved */ CHASE_TRAIL_SLASH = 1 << 4, /* Any trailing slash will be preserved */
CHASE_STEP = 1 << 5, /* Just execute a single step of the normalization */ CHASE_STEP = 1 << 5, /* Just execute a single step of the normalization */
CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most component. With ret_fd, when the path's CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most component. With ret_fd, when the path's

View File

@ -3,7 +3,7 @@
#include <grp.h> #include <grp.h>
#if ENABLE_GSHADOW #if ENABLE_GSHADOW
#include <gshadow.h> # include <gshadow.h>
#endif #endif
#include <pwd.h> #include <pwd.h>
#include <shadow.h> #include <shadow.h>
@ -61,30 +61,6 @@ int take_etc_passwd_lock(const char *root);
#define ETC_PASSWD_LOCK_PATH "/etc/.pwd.lock" #define ETC_PASSWD_LOCK_PATH "/etc/.pwd.lock"
static inline bool uid_is_system(uid_t uid) {
return uid <= SYSTEM_UID_MAX;
}
static inline bool gid_is_system(gid_t gid) {
return gid <= SYSTEM_GID_MAX;
}
static inline bool uid_is_dynamic(uid_t uid) {
return DYNAMIC_UID_MIN <= uid && uid <= DYNAMIC_UID_MAX;
}
static inline bool gid_is_dynamic(gid_t gid) {
return uid_is_dynamic((uid_t) gid);
}
static inline bool uid_is_container(uid_t uid) {
return CONTAINER_UID_BASE_MIN <= uid && uid <= CONTAINER_UID_BASE_MAX;
}
static inline bool gid_is_container(gid_t gid) {
return uid_is_container((uid_t) gid);
}
/* The following macros add 1 when converting things, since UID 0 is a valid UID, while the pointer /* The following macros add 1 when converting things, since UID 0 is a valid UID, while the pointer
* NULL is special */ * NULL is special */
#define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1)) #define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1))

View File

@ -19,6 +19,7 @@
#include "stdio-util.h" #include "stdio-util.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
#include "user-record.h"
#include "user-util.h" #include "user-util.h"
/* Takes a value generated randomly or by hashing and turns it into a UID in the right range */ /* Takes a value generated randomly or by hashing and turns it into a UID in the right range */

View File

@ -80,9 +80,9 @@ modulesloaddir=${modules_load_dir}
catalog_dir=/usr/lib/systemd/catalog catalog_dir=/usr/lib/systemd/catalog
catalogdir=${catalog_dir} catalogdir=${catalog_dir}
system_uid_max=@systemuidmax@ system_uid_max=@SYSTEM_UID_MAX@
systemuidmax=${system_uid_max} systemuidmax=${system_uid_max}
system_gid_max=@systemgidmax@ system_gid_max=@SYSTEM_GID_MAX@
systemgidmax=${system_gid_max} systemgidmax=${system_gid_max}
dynamic_uid_min=@dynamicuidmin@ dynamic_uid_min=@dynamicuidmin@

View File

@ -46,6 +46,7 @@
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
#include "tmpfile-util.h" #include "tmpfile-util.h"
#include "user-record.h"
#include "user-util.h" #include "user-util.h"
/* The maximum size up to which we process coredumps */ /* The maximum size up to which we process coredumps */
@ -682,7 +683,7 @@ static int change_uid_gid(const Context *context) {
if (r < 0) if (r < 0)
return r; return r;
if (uid <= SYSTEM_UID_MAX) { if (uid_is_system(uid)) {
const char *user = "systemd-coredump"; const char *user = "systemd-coredump";
r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0); r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0);

View File

@ -55,6 +55,7 @@
#include "string-table.h" #include "string-table.h"
#include "string-util.h" #include "string-util.h"
#include "syslog-util.h" #include "syslog-util.h"
#include "user-record.h"
#include "user-util.h" #include "user-util.h"
#define USER_JOURNALS_MAX 1024 #define USER_JOURNALS_MAX 1024

View File

@ -6,7 +6,7 @@ set -ex
repart=$1 repart=$1
test -x $repart test -x $repart
D=$(mktemp --directory) D=$(mktemp --tmpdir --directory "test-repart.XXXXXXXXXX")
trap "rm -rf '$D'" EXIT INT QUIT PIPE trap "rm -rf '$D'" EXIT INT QUIT PIPE
mkdir -p $D/definitions mkdir -p $D/definitions

View File

@ -43,6 +43,7 @@
#include "string-table.h" #include "string-table.h"
#include "string-util.h" #include "string-util.h"
#include "tomoyo-util.h" #include "tomoyo-util.h"
#include "user-record.h"
#include "user-util.h" #include "user-util.h"
#include "util.h" #include "util.h"
#include "virt.h" #include "virt.h"

View File

@ -18,13 +18,11 @@ static bool uid_range_intersect(UidRange *range, uid_t start, uid_t nr) {
} }
static void uid_range_coalesce(UidRange **p, unsigned *n) { static void uid_range_coalesce(UidRange **p, unsigned *n) {
unsigned i, j;
assert(p); assert(p);
assert(n); assert(n);
for (i = 0; i < *n; i++) { for (unsigned i = 0; i < *n; i++) {
for (j = i + 1; j < *n; j++) { for (unsigned j = i + 1; j < *n; j++) {
UidRange *x = (*p)+i, *y = (*p)+j; UidRange *x = (*p)+i, *y = (*p)+j;
if (uid_range_intersect(x, y->start, y->nr)) { if (uid_range_intersect(x, y->start, y->nr)) {
@ -59,7 +57,6 @@ static int uid_range_compare(const UidRange *a, const UidRange *b) {
int uid_range_add(UidRange **p, unsigned *n, uid_t start, uid_t nr) { int uid_range_add(UidRange **p, unsigned *n, uid_t start, uid_t nr) {
bool found = false; bool found = false;
UidRange *x; UidRange *x;
unsigned i;
assert(p); assert(p);
assert(n); assert(n);
@ -67,7 +64,7 @@ int uid_range_add(UidRange **p, unsigned *n, uid_t start, uid_t nr) {
if (nr <= 0) if (nr <= 0)
return 0; return 0;
for (i = 0; i < *n; i++) { for (unsigned i = 0; i < *n; i++) {
x = (*p) + i; x = (*p) + i;
if (uid_range_intersect(x, start, nr)) { if (uid_range_intersect(x, start, nr)) {
found = true; found = true;
@ -143,14 +140,13 @@ int uid_range_add_str(UidRange **p, unsigned *n, const char *s) {
int uid_range_next_lower(const UidRange *p, unsigned n, uid_t *uid) { int uid_range_next_lower(const UidRange *p, unsigned n, uid_t *uid) {
uid_t closest = UID_INVALID, candidate; uid_t closest = UID_INVALID, candidate;
unsigned i;
assert(p); assert(p);
assert(uid); assert(uid);
candidate = *uid - 1; candidate = *uid - 1;
for (i = 0; i < n; i++) { for (unsigned i = 0; i < n; i++) {
uid_t begin, end; uid_t begin, end;
begin = p[i].start; begin = p[i].start;
@ -173,12 +169,10 @@ int uid_range_next_lower(const UidRange *p, unsigned n, uid_t *uid) {
} }
bool uid_range_contains(const UidRange *p, unsigned n, uid_t uid) { bool uid_range_contains(const UidRange *p, unsigned n, uid_t uid) {
unsigned i;
assert(p); assert(p);
assert(uid); assert(uid);
for (i = 0; i < n; i++) for (unsigned i = 0; i < n; i++)
if (uid >= p[i].start && uid < p[i].start + p[i].nr) if (uid >= p[i].start && uid < p[i].start + p[i].nr)
return true; return true;

View File

@ -5,6 +5,8 @@
#include "cgroup-util.h" #include "cgroup-util.h"
#include "dns-domain.h" #include "dns-domain.h"
#include "env-util.h" #include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h" #include "fs-util.h"
#include "hexdecoct.h" #include "hexdecoct.h"
#include "hostname-util.h" #include "hostname-util.h"
@ -21,6 +23,122 @@
#define DEFAULT_RATELIMIT_BURST 30 #define DEFAULT_RATELIMIT_BURST 30
#define DEFAULT_RATELIMIT_INTERVAL_USEC (1*USEC_PER_MINUTE) #define DEFAULT_RATELIMIT_INTERVAL_USEC (1*USEC_PER_MINUTE)
#if ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES
static int parse_alloc_uid(const char *path, const char *name, const char *t, uid_t *ret_uid) {
uid_t uid;
int r;
r = parse_uid(t, &uid);
if (r < 0)
return log_debug_errno(r, "%s: failed to parse %s %s, ignoring: %m", path, name, t);
if (uid == 0)
uid = 1;
*ret_uid = uid;
return 0;
}
#endif
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 = chase_symlinks_and_fopen_unlocked(path, root, CHASE_PREFIX_ROOT, "re", &f, NULL);
if (r == -ENOENT)
goto assign;
if (r < 0)
return log_debug_errno(r, "Failed to open %s: %m", path);
for (;;) {
_cleanup_free_ char *line = NULL;
char *t;
r = read_line(f, LINE_MAX, &line);
if (r < 0)
return log_debug_errno(r, "Failed to read %s: %m", path);
if (r == 0)
break;
if ((t = first_word(line, "SYS_UID_MIN")))
(void) parse_alloc_uid(path, "SYS_UID_MIN", t, &defs.system_alloc_uid_min);
else if ((t = first_word(line, "SYS_UID_MAX")))
(void) parse_alloc_uid(path, "SYS_UID_MAX", t, &defs.system_uid_max);
else if ((t = first_word(line, "SYS_GID_MIN")))
(void) parse_alloc_uid(path, "SYS_GID_MIN", t, &defs.system_alloc_gid_min);
else if ((t = first_word(line, "SYS_GID_MAX")))
(void) parse_alloc_uid(path, "SYS_GID_MAX", t, &defs.system_gid_max);
}
assign:
if (defs.system_alloc_uid_min > defs.system_uid_max) {
log_debug("%s: SYS_UID_MIN > SYS_UID_MAX, resetting.", path);
defs.system_alloc_uid_min = MIN(defs.system_uid_max - 1, (uid_t) SYSTEM_ALLOC_UID_MIN);
/* Look at sys_uid_max to make sure sys_uid_min..sys_uid_max remains a valid range. */
}
if (defs.system_alloc_gid_min > defs.system_gid_max) {
log_debug("%s: SYS_GID_MIN > SYS_GID_MAX, resetting.", 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;
}
const UGIDAllocationRange *acquire_ugid_allocation_range(void) {
#if ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES
static thread_local UGIDAllocationRange defs = {
#else
static const UGIDAllocationRange defs = {
#endif
.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
/* This function will ignore failure to read the file, so it should only be called from places where
* we don't crucially depend on the answer. In other words, it's appropriate for journald, but
* probably not for sysusers. */
static thread_local bool initialized = false;
if (!initialized) {
(void) read_login_defs(&defs, NULL, NULL);
initialized = true;
}
#endif
return &defs;
}
bool uid_is_system(uid_t uid) {
const UGIDAllocationRange *defs;
assert_se(defs = acquire_ugid_allocation_range());
return uid <= defs->system_uid_max;
}
bool gid_is_system(gid_t gid) {
const UGIDAllocationRange *defs;
assert_se(defs = acquire_ugid_allocation_range());
return gid <= defs->system_gid_max;
}
UserRecord* user_record_new(void) { UserRecord* user_record_new(void) {
UserRecord *h; UserRecord *h;

View File

@ -17,6 +17,35 @@
/* The default disk size to use when nothing else is specified, relative to free disk space */ /* The default disk size to use when nothing else is specified, relative to free disk space */
#define USER_DISK_SIZE_DEFAULT_PERCENT 85 #define USER_DISK_SIZE_DEFAULT_PERCENT 85
bool uid_is_system(uid_t uid);
bool gid_is_system(gid_t gid);
static inline bool uid_is_dynamic(uid_t uid) {
return DYNAMIC_UID_MIN <= uid && uid <= DYNAMIC_UID_MAX;
}
static inline bool gid_is_dynamic(gid_t gid) {
return uid_is_dynamic((uid_t) gid);
}
static inline bool uid_is_container(uid_t uid) {
return CONTAINER_UID_BASE_MIN <= uid && uid <= CONTAINER_UID_BASE_MAX;
}
static inline bool gid_is_container(gid_t gid) {
return uid_is_container((uid_t) gid);
}
typedef struct UGIDAllocationRange {
uid_t system_alloc_uid_min;
uid_t system_uid_max;
gid_t system_alloc_gid_min;
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 { typedef enum UserDisposition {
USER_INTRINSIC, /* root and nobody */ USER_INTRINSIC, /* root and nobody */
USER_SYSTEM, /* statically allocated users for system services */ USER_SYSTEM, /* statically allocated users for system services */

View File

@ -26,6 +26,7 @@
#include "strv.h" #include "strv.h"
#include "tmpfile-util-label.h" #include "tmpfile-util-label.h"
#include "uid-range.h" #include "uid-range.h"
#include "user-record.h"
#include "user-util.h" #include "user-util.h"
#include "utf8.h" #include "utf8.h"
#include "util.h" #include "util.h"
@ -83,6 +84,9 @@ static uid_t search_uid = UID_INVALID;
static UidRange *uid_range = NULL; static UidRange *uid_range = NULL;
static unsigned n_uid_range = 0; static unsigned n_uid_range = 0;
static UGIDAllocationRange login_defs = {};
static bool login_defs_need_warning = false;
STATIC_DESTRUCTOR_REGISTER(groups, ordered_hashmap_freep); STATIC_DESTRUCTOR_REGISTER(groups, ordered_hashmap_freep);
STATIC_DESTRUCTOR_REGISTER(users, ordered_hashmap_freep); STATIC_DESTRUCTOR_REGISTER(users, ordered_hashmap_freep);
STATIC_DESTRUCTOR_REGISTER(members, ordered_hashmap_freep); STATIC_DESTRUCTOR_REGISTER(members, ordered_hashmap_freep);
@ -104,6 +108,26 @@ static int errno_is_not_exists(int code) {
return IN_SET(code, 0, ENOENT, ESRCH, EBADF, EPERM); return IN_SET(code, 0, ENOENT, ESRCH, EBADF, EPERM);
} }
static void maybe_emit_login_defs_warning(void) {
if (!login_defs_need_warning)
return;
if (login_defs.system_alloc_uid_min != SYSTEM_ALLOC_UID_MIN ||
login_defs.system_uid_max != SYSTEM_UID_MAX)
log_warning("login.defs specifies UID allocation range "UID_FMT""UID_FMT
" that is different than the built-in defaults ("UID_FMT""UID_FMT")",
login_defs.system_alloc_uid_min, login_defs.system_uid_max,
SYSTEM_ALLOC_UID_MIN, SYSTEM_UID_MAX);
if (login_defs.system_alloc_gid_min != SYSTEM_ALLOC_GID_MIN ||
login_defs.system_gid_max != SYSTEM_GID_MAX)
log_warning("login.defs specifies GID allocation range "GID_FMT""GID_FMT
" that is different than the built-in defaults ("GID_FMT""GID_FMT")",
login_defs.system_alloc_gid_min, login_defs.system_gid_max,
SYSTEM_ALLOC_GID_MIN, SYSTEM_GID_MAX);
login_defs_need_warning = false;
}
static int load_user_database(void) { static int load_user_database(void) {
_cleanup_fclose_ FILE *f = NULL; _cleanup_fclose_ FILE *f = NULL;
const char *passwd_path; const char *passwd_path;
@ -1001,6 +1025,8 @@ static int add_user(Item *i) {
/* And if that didn't work either, let's try to find a free one */ /* And if that didn't work either, let's try to find a free one */
if (!i->uid_set) { if (!i->uid_set) {
maybe_emit_login_defs_warning();
for (;;) { for (;;) {
r = uid_range_next_lower(uid_range, n_uid_range, &search_uid); r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
if (r < 0) if (r < 0)
@ -1167,6 +1193,8 @@ static int add_group(Item *i) {
/* And if that didn't work either, let's try to find a free one */ /* And if that didn't work either, let's try to find a free one */
if (!i->gid_set) { if (!i->gid_set) {
maybe_emit_login_defs_warning();
for (;;) { for (;;) {
/* We look for new GIDs in the UID pool! */ /* We look for new GIDs in the UID pool! */
r = uid_range_next_lower(uid_range, n_uid_range, &search_uid); r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
@ -1949,10 +1977,25 @@ static int run(int argc, char *argv[]) {
return log_error_errno(errno, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m"); return log_error_errno(errno, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m");
if (!uid_range) { if (!uid_range) {
/* Default to default range of 1..SYSTEM_UID_MAX */ /* Default to default range of SYSTEMD_UID_MIN..SYSTEM_UID_MAX. */
r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX); r = read_login_defs(&login_defs, NULL, arg_root);
if (r < 0) if (r < 0)
return log_oom(); return log_error_errno(r, "Failed to read %s%s: %m",
strempty(arg_root), "/etc/login.defs");
login_defs_need_warning = true;
/* 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 = login_defs.system_alloc_uid_min,
end = MIN3((uid_t) SYSTEM_UID_MAX, login_defs.system_uid_max, login_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(); r = add_implicit();

View File

@ -339,6 +339,10 @@ tests += [
[], [],
[]], []],
[['src/test/test-user-record.c'],
[],
[]],
[['src/test/test-user-util.c'], [['src/test/test-user-util.c'],
[], [],
[]], []],

View File

@ -31,6 +31,7 @@
#include "strv.h" #include "strv.h"
#include "tests.h" #include "tests.h"
#include "tomoyo-util.h" #include "tomoyo-util.h"
#include "user-record.h"
#include "user-util.h" #include "user-util.h"
#include "virt.h" #include "virt.h"

104
src/test/test-user-record.c Normal file
View File

@ -0,0 +1,104 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#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__);
const UGIDAllocationRange *defs;
assert_se(defs = acquire_ugid_allocation_range());
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);
}
static void test_uid_is_system(void) {
log_info("/* %s */", __func__);
uid_t uid = 0;
log_info("uid_is_system("UID_FMT") = %s", uid, yes_no(uid_is_system(uid)));
uid = 999;
log_info("uid_is_system("UID_FMT") = %s", uid, yes_no(uid_is_system(uid)));
uid = getuid();
log_info("uid_is_system("UID_FMT") = %s", uid, yes_no(uid_is_system(uid)));
}
static void test_gid_is_system(void) {
log_info("/* %s */", __func__);
gid_t gid = 0;
log_info("gid_is_system("GID_FMT") = %s", gid, yes_no(gid_is_system(gid)));
gid = 999;
log_info("gid_is_system("GID_FMT") = %s", gid, yes_no(gid_is_system(gid)));
gid = getgid();
log_info("gid_is_system("GID_FMT") = %s", gid, yes_no(gid_is_system(gid)));
}
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();
return 0;
}

View File

@ -2,8 +2,6 @@
in_files = ['basic.conf'] in_files = ['basic.conf']
enable_sysusers = conf.get('ENABLE_SYSUSERS') == 1
foreach file : in_files foreach file : in_files
gen = configure_file( gen = configure_file(
input : file + '.in', input : file + '.in',

View File

@ -1 +0,0 @@
../TEST-01-BASIC/Makefile

View File

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

View File

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

View File

@ -1 +0,0 @@
aaa:x:SYSTEM_UID_MAX:987::/:NOLOGIN

View File

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

View File

@ -1 +0,0 @@
u1:x:SYSTEM_UID_MAX:SYSTEM_UID_MAX::/:NOLOGIN

View File

@ -1 +0,0 @@
username:x:SYSTEM_UID_MAX:300::/:NOLOGIN

View File

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

View File

@ -1,128 +0,0 @@
#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="Sysuser-related tests"
IMAGE_NAME="sysusers"
. $TEST_BASE_DIR/test-functions
test_setup() {
mkdir -p $TESTDIR/etc/sysusers.d $TESTDIR/usr/lib/sysusers.d $TESTDIR/tmp
}
prepare_testdir() {
rm -f $TESTDIR/etc/*{passwd,group,shadow}
for i in $1.initial-{passwd,group,shadow}; do
test -f $i && cp $i $TESTDIR/etc/${i#*.initial-}
done
return 0
}
preprocess() {
in="$1"
# see meson.build how to extract this. gcc -E was used before to
# get this value from config.h, however the autopkgtest fails with
# it
[[ -e /etc/login.defs ]] && login_defs_file="/etc/login.defs" || login_defs_file="/usr/etc/login.defs"
SYSTEM_UID_MAX=$(awk 'BEGIN { uid=999 } /^\s*SYS_UID_MAX\s+/ { uid=$2 } END { print uid }' $login_defs_file)
SYSTEM_GID_MAX=$(awk 'BEGIN { gid=999 } /^\s*SYS_GID_MAX\s+/ { gid=$2 } END { print gid }' $login_defs_file)
# we can't rely on config.h to get the nologin path, as autopkgtest
# uses pre-compiled binaries, so extract it from the systemd-sysusers
# binary which we are about to execute
NOLOGIN=$(strings $(type -p systemd-sysusers) | grep nologin)
sed -e "s/SYSTEM_UID_MAX/${SYSTEM_UID_MAX}/g" \
-e "s/SYSTEM_GID_MAX/${SYSTEM_GID_MAX}/g" \
-e "s#NOLOGIN#${NOLOGIN}#g" "$in"
}
compare() {
if ! diff -u $TESTDIR/etc/passwd <(preprocess ${1%.*}.expected-passwd); then
echo "**** Unexpected output for $f"
exit 1
fi
if ! diff -u $TESTDIR/etc/group <(preprocess ${1%.*}.expected-group); then
echo "**** Unexpected output for $f $2"
exit 1
fi
}
test_run() {
# ensure our build of systemd-sysusers is run
PATH=${BUILD_DIR}:$PATH
rm -f $TESTDIR/etc/sysusers.d/* $TESTDIR/usr/lib/sysusers.d/*
# happy tests
for f in test-*.input; do
echo "*** Running $f"
prepare_testdir ${f%.input}
cp $f $TESTDIR/usr/lib/sysusers.d/test.conf
systemd-sysusers --root=$TESTDIR
compare $f ""
done
for f in test-*.input; do
echo "*** Running $f on stdin"
prepare_testdir ${f%.input}
touch $TESTDIR/etc/sysusers.d/test.conf
cat $f | systemd-sysusers --root=$TESTDIR -
compare $f "on stdin"
done
for f in test-*.input; do
echo "*** Running $f on stdin with --replace"
prepare_testdir ${f%.input}
touch $TESTDIR/etc/sysusers.d/test.conf
# this overrides test.conf which is masked on disk
cat $f | systemd-sysusers --root=$TESTDIR --replace=/etc/sysusers.d/test.conf -
# this should be ignored
cat test-1.input | systemd-sysusers --root=$TESTDIR --replace=/usr/lib/sysusers.d/test.conf -
compare $f "on stdin with --replace"
done
# test --inline
echo "*** Testing --inline"
prepare_testdir
# copy a random file to make sure it is ignored
cp $f $TESTDIR/etc/sysusers.d/confuse.conf
systemd-sysusers --root=$TESTDIR --inline \
"u u1 222 - - /bin/zsh" \
"g g1 111"
compare inline "(--inline)"
# test --replace
echo "*** Testing --inline with --replace"
prepare_testdir
# copy a random file to make sure it is ignored
cp $f $TESTDIR/etc/sysusers.d/confuse.conf
systemd-sysusers --root=$TESTDIR \
--inline \
--replace=/etc/sysusers.d/confuse.conf \
"u u1 222 - - /bin/zsh" \
"g g1 111"
compare inline "(--inline --replace=…)"
rm -f $TESTDIR/etc/sysusers.d/* $TESTDIR/usr/lib/sysusers.d/*
# tests for error conditions
for f in unhappy-*.input; do
echo "*** Running test $f"
prepare_testdir ${f%.input}
cp $f $TESTDIR/usr/lib/sysusers.d/test.conf
systemd-sysusers --root=$TESTDIR 2>&1 | tail -n1 > $TESTDIR/tmp/err
if ! diff -u $TESTDIR/tmp/err ${f%.*}.expected-err; then
echo "**** Unexpected error output for $f"
cat $TESTDIR/tmp/err
exit 1
fi
done
}
do_test "$@"

View File

@ -18,7 +18,7 @@ if [ ! -x "$SYSTEMD_HWDB" ]; then
exit 1 exit 1
fi fi
D=$(mktemp --directory) D=$(mktemp --tmpdir --directory "hwdb-test.XXXXXXXXXX")
trap "rm -rf '$D'" EXIT INT QUIT PIPE trap "rm -rf '$D'" EXIT INT QUIT PIPE
mkdir -p "$D/etc/udev" mkdir -p "$D/etc/udev"
ln -s "$ROOTDIR/hwdb.d" "$D/etc/udev/hwdb.d" ln -s "$ROOTDIR/hwdb.d" "$D/etc/udev/hwdb.d"

View File

@ -58,6 +58,21 @@ test_network_generator_conversion_sh = find_program('test-network-generator-conv
############################################################ ############################################################
test_sysusers_dir = join_paths(meson.current_source_dir(), 'test-sysusers')
test_sysusers_sh = configure_file(
input : 'test-sysusers.sh.in',
output : 'test-sysusers.sh',
configuration : substs)
if install_tests and conf.get('ENABLE_SYSUSERS') == 1
install_data(test_sysusers_sh,
install_dir : testsdir)
install_subdir('test-sysusers',
install_dir : testdata_dir)
endif
############################################################
rule_syntax_check_py = find_program('rule-syntax-check.py') rule_syntax_check_py = find_program('rule-syntax-check.py')
if want_tests != 'false' if want_tests != 'false'
test('rule-syntax-check', test('rule-syntax-check',

View File

@ -17,7 +17,7 @@ for f in "$src"/test-*.input; do
echo "*** Running $f" echo "*** Running $f"
( (
out=$(mktemp --directory) out=$(mktemp --tmpdir --directory "test-network-generator-conversion.XXXXXXXXXX")
trap "rm -rf '$out'" EXIT INT QUIT PIPE trap "rm -rf '$out'" EXIT INT QUIT PIPE
$generator --root "$out" -- $(cat $f) $generator --root "$out" -- $(cat $f)

160
test/test-sysusers.sh.in Executable file
View File

@ -0,0 +1,160 @@
#!/usr/bin/env bash
set -e
SYSUSERS="${1:-systemd-sysusers}"
[ -e "$(dirname $0)/../systemd-runtest.env" ] && . "$(dirname $0)/../systemd-runtest.env"
SYSTEMD_TEST_DATA=${SYSTEMD_TEST_DATA:-@SYSTEMD_TEST_DATA@}
SOURCE=$SYSTEMD_TEST_DATA/test-sysusers
TESTDIR=$(mktemp --tmpdir --directory "test-sysusers.XXXXXXXXXX")
trap "rm -rf '$TESTDIR'" EXIT INT QUIT PIPE
prepare_testdir() {
mkdir -p $TESTDIR/etc/sysusers.d/
mkdir -p $TESTDIR/usr/lib/sysusers.d/
rm -f $TESTDIR/etc/*{passwd,group,shadow}
for i in $1.initial-{passwd,group,shadow}; do
test -f $i && cp $i $TESTDIR/etc/${i#*.initial-}
done
return 0
}
[ @SYSTEM_UID_MAX@ -lt @SYSTEM_GID_MAX@ ] && system_guid_max=@SYSTEM_UID_MAX@ || system_guid_max=@SYSTEM_GID_MAX@
preprocess() {
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 $3); then
echo "**** Unexpected output for $f $2"
exit 1
fi
if ! diff -u $TESTDIR/etc/group <(preprocess $1.expected-group $3); then
echo "**** Unexpected output for $f $2"
exit 1
fi
}
rm -f $TESTDIR/etc/sysusers.d/* $TESTDIR/usr/lib/sysusers.d/*
# happy tests
for f in $(ls -1 $SOURCE/test-*.input | sort -V); do
echo "*** Running $f"
prepare_testdir ${f%.input}
cp $f $TESTDIR/usr/lib/sysusers.d/test.conf
$SYSUSERS --root=$TESTDIR
compare ${f%.*} ""
done
for f in $(ls -1 $SOURCE/test-*.input | sort -V); do
echo "*** Running $f on stdin"
prepare_testdir ${f%.input}
touch $TESTDIR/etc/sysusers.d/test.conf
cat $f | $SYSUSERS --root=$TESTDIR -
compare ${f%.*} "on stdin"
done
for f in $(ls -1 $SOURCE/test-*.input | sort -V); do
echo "*** Running $f on stdin with --replace"
prepare_testdir ${f%.input}
touch $TESTDIR/etc/sysusers.d/test.conf
# this overrides test.conf which is masked on disk
cat $f | $SYSUSERS --root=$TESTDIR --replace=/etc/sysusers.d/test.conf -
# this should be ignored
cat $SOURCE/test-1.input | $SYSUSERS --root=$TESTDIR --replace=/usr/lib/sysusers.d/test.conf -
compare ${f%.*} "on stdin with --replace"
done
# test --inline
echo "*** Testing --inline"
prepare_testdir $SOURCE/inline
# copy a random file to make sure it is ignored
cp $f $TESTDIR/etc/sysusers.d/confuse.conf
$SYSUSERS --root=$TESTDIR --inline \
"u u1 222 - - /bin/zsh" \
"g g1 111"
compare $SOURCE/inline "(--inline)"
# test --replace
echo "*** Testing --inline with --replace"
prepare_testdir $SOURCE/inline
# copy a random file to make sure it is ignored
cp $f $TESTDIR/etc/sysusers.d/confuse.conf
$SYSUSERS --root=$TESTDIR \
--inline \
--replace=/etc/sysusers.d/confuse.conf \
"u u1 222 - - /bin/zsh" \
"g g1 111"
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"
prepare_testdir ${f%.input}
cp $f $TESTDIR/usr/lib/sysusers.d/test.conf
$SYSUSERS --root=$TESTDIR 2>&1 | tail -n1 > $TESTDIR/err
if ! diff -u $TESTDIR/err ${f%.*}.expected-err; then
echo "**** Unexpected error output for $f"
cat $TESTDIR/err
exit 1
fi
done

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
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: u2:x:777:
u3:x:778: u3:x:778:
u4:x:779: 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 u2:x:777:777:some gecos:/random/dir:/bin/zsh
u3:x:778:778::/random/dir2:/bin/bash u3:x:778:778::/random/dir2:/bin/bash
u4:x:779:779::/:/bin/csh 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. # replacement of all fields up to the login shell.
# #
#Type Name ID GECOS homedir shell #Type Name ID GECOS homedir shell

View File

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

View File

@ -0,0 +1 @@
u1:x:SYSTEM_UGID_MAX:SYSTEM_UGID_MAX::/:NOLOGIN

View File

@ -0,0 +1 @@
username:x:SYSTEM_UGID_MAX:300::/:NOLOGIN

View File

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