Compare commits
14 Commits
a53d1b17e3
...
278c13431b
Author | SHA1 | Date |
---|---|---|
Lennart Poettering | 278c13431b | |
Lennart Poettering | c14ebe07a9 | |
Zbigniew Jędrzejewski-Szmek | 4b6f9b202e | |
Zbigniew Jędrzejewski-Szmek | a3709e3709 | |
Zbigniew Jędrzejewski-Szmek | aa25270cb2 | |
Zbigniew Jędrzejewski-Szmek | 044df624aa | |
Zbigniew Jędrzejewski-Szmek | 69a7c5fb1f | |
Zbigniew Jędrzejewski-Szmek | bd7e6aa73a | |
Zbigniew Jędrzejewski-Szmek | d338bfff4a | |
Zbigniew Jędrzejewski-Szmek | 196b596867 | |
Zbigniew Jędrzejewski-Szmek | fc1a5d1a70 | |
Zbigniew Jędrzejewski-Szmek | 53393c894d | |
Zbigniew Jędrzejewski-Szmek | 28add648a8 | |
Zbigniew Jędrzejewski-Szmek | 98dcb8f4c7 |
|
@ -5,7 +5,7 @@ about: A report of an error in a recent systemd version
|
|||
---
|
||||
|
||||
**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! -->
|
||||
<!-- 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**
|
||||
> …
|
||||
|
||||
**Linux kernel version used** (`uname -a`)
|
||||
> …
|
||||
|
||||
**CPU architecture issue was seen on**
|
||||
> …
|
||||
|
||||
**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**
|
||||
> …
|
||||
|
|
|
@ -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
|
||||
1878982656+65535=1879048191.)
|
||||
|
||||
Note that systemd does not make any of these values runtime-configurable. All
|
||||
these boundaries are chosen during build time. That said, the system UID/GID
|
||||
boundary is traditionally configured in /etc/login.defs, though systemd won't
|
||||
look there during runtime.
|
||||
Systemd has compile-time default for these boundaries. Using those defaults is
|
||||
recommended. It will nevertheless query `/etc/login.defs` at runtime, when
|
||||
compiled with `-Dcompat-mutable-uid-boundaries=true` and that file is present.
|
||||
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
|
||||
|
||||
|
|
80
meson.build
80
meson.build
|
@ -299,6 +299,7 @@ substs.set('CERTIFICATEROOT', get_option('certif
|
|||
substs.set('RANDOM_SEED', join_paths(randomseeddir, 'random-seed'))
|
||||
substs.set('SYSTEM_SYSVINIT_PATH', sysvinit_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('MEMORY_ACCOUNTING_DEFAULT', memory_accounting_default ? 'yes' : 'no')
|
||||
substs.set('STATUS_UNIT_FORMAT_DEFAULT', status_unit_format_default)
|
||||
|
@ -694,35 +695,31 @@ if time_epoch == -1
|
|||
endif
|
||||
conf.set('TIME_EPOCH', time_epoch)
|
||||
|
||||
system_uid_max = get_option('system-uid-max')
|
||||
if system_uid_max == -1
|
||||
system_uid_max = run_command(
|
||||
foreach tuple : [['system-alloc-uid-min', 'SYS_UID_MIN', 1], # Also see login.defs(5).
|
||||
['system-uid-max', 'SYS_UID_MAX', 999],
|
||||
['system-alloc-gid-min', 'SYS_GID_MIN', 1],
|
||||
['system-gid-max', 'SYS_GID_MAX', 999]]
|
||||
v = get_option(tuple[0])
|
||||
if v == -1
|
||||
v = run_command(
|
||||
awk,
|
||||
'/^\s*SYS_UID_MAX\s+/ { uid=$2 } END { print uid }',
|
||||
'/^\s*@0@\s+/ { uid=$2 } END { print uid }'.format(tuple[1]),
|
||||
'/etc/login.defs').stdout().strip()
|
||||
if system_uid_max == ''
|
||||
system_uid_max = 999
|
||||
if v == ''
|
||||
v = tuple[2]
|
||||
else
|
||||
system_uid_max = system_uid_max.to_int()
|
||||
v = v.to_int()
|
||||
endif
|
||||
endif
|
||||
conf.set('SYSTEM_UID_MAX', system_uid_max)
|
||||
substs.set('systemuidmax', system_uid_max)
|
||||
|
||||
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()
|
||||
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
|
||||
if conf.get('SYSTEM_ALLOC_GID_MIN') >= conf.get('SYSTEM_GID_MAX')
|
||||
error('Invalid gid allocation range')
|
||||
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_max = get_option('dynamic-uid-max')
|
||||
|
@ -1444,6 +1441,7 @@ foreach term : ['analyze',
|
|||
'idn',
|
||||
'ima',
|
||||
'initrd',
|
||||
'compat-mutable-uid-boundaries',
|
||||
'ldconfig',
|
||||
'localed',
|
||||
'logind',
|
||||
|
@ -1470,8 +1468,11 @@ 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
|
||||
|
||||
foreach tuple : [['nss-mymachines', 'machined'],
|
||||
['nss-resolve', 'resolve']]
|
||||
want = get_option(tuple[0])
|
||||
|
@ -2966,8 +2967,8 @@ public_programs += executable(
|
|||
install_rpath : rootlibexecdir,
|
||||
install : true)
|
||||
|
||||
if conf.get('ENABLE_SYSUSERS') == 1
|
||||
public_programs += executable(
|
||||
if enable_sysusers
|
||||
exe = executable(
|
||||
'systemd-sysusers',
|
||||
'src/sysusers/sysusers.c',
|
||||
include_directories : includes,
|
||||
|
@ -2975,9 +2976,17 @@ if conf.get('ENABLE_SYSUSERS') == 1
|
|||
install_rpath : rootlibexecdir,
|
||||
install : true,
|
||||
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
|
||||
public_programs += executable(
|
||||
exe = executable(
|
||||
'systemd-sysusers.standalone',
|
||||
'src/sysusers/sysusers.c',
|
||||
include_directories : includes,
|
||||
|
@ -2988,6 +2997,14 @@ if conf.get('ENABLE_SYSUSERS') == 1
|
|||
libjournal_client],
|
||||
install : true,
|
||||
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
|
||||
|
||||
|
@ -3539,12 +3556,12 @@ status = [
|
|||
get_option('debug-tty')),
|
||||
'TTY GID: @0@'.format(tty_gid),
|
||||
'users GID: @0@'.format(substs.get('USERS_GID')),
|
||||
'maximum system UID: @0@'.format(system_uid_max),
|
||||
'maximum system GID: @0@'.format(system_gid_max),
|
||||
'minimum dynamic UID: @0@'.format(dynamic_uid_min),
|
||||
'maximum dynamic UID: @0@'.format(dynamic_uid_max),
|
||||
'minimum container UID base: @0@'.format(container_uid_base_min),
|
||||
'maximum container UID base: @0@'.format(container_uid_base_max),
|
||||
'system UIDs: <=@0@ (alloc >=@1@)'.format(conf.get('SYSTEM_UID_MAX'),
|
||||
conf.get('SYSTEM_ALLOC_UID_MIN')),
|
||||
'system GIDs: <=@0@ (alloc >=@1@)'.format(conf.get('SYSTEM_GID_MAX'),
|
||||
conf.get('SYSTEM_ALLOC_GID_MIN')),
|
||||
'dynamic UIDs: @0@–@1@'.format(dynamic_uid_min, dynamic_uid_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')),
|
||||
'render group access mode: @0@'.format(get_option('group-render-mode')),
|
||||
'certificate root directory: @0@'.format(get_option('certificate-root')),
|
||||
|
@ -3628,6 +3645,7 @@ foreach tuple : [
|
|||
['libcurl'],
|
||||
['idn'],
|
||||
['initrd'],
|
||||
['compat-mutable-uid-boundaries'],
|
||||
['libidn2'],
|
||||
['libidn'],
|
||||
['libiptc'],
|
||||
|
|
|
@ -28,9 +28,9 @@ option('static-libsystemd', type : 'combo',
|
|||
description : '''install a static library for libsystemd''')
|
||||
option('static-libudev', type : 'combo',
|
||||
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',
|
||||
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',
|
||||
description : 'the directory where the SysV init scripts are located')
|
||||
|
@ -42,6 +42,8 @@ option('rc-local', type : 'string',
|
|||
value : '/etc/rc.local')
|
||||
option('initrd', type : 'boolean',
|
||||
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('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')
|
||||
option('time-epoch', type : 'integer', value : '-1',
|
||||
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',
|
||||
description : 'maximum system UID')
|
||||
option('system-gid-max', type : 'integer', value : '-1',
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
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) {
|
||||
assert(f);
|
||||
|
||||
|
|
|
@ -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_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_sync_and_check(FILE *f);
|
||||
|
||||
|
|
|
@ -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_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_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_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
|
||||
|
|
|
@ -61,30 +61,6 @@ int take_etc_passwd_lock(const char *root);
|
|||
|
||||
#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
|
||||
* NULL is special */
|
||||
#define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1))
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "stdio-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "user-record.h"
|
||||
#include "user-util.h"
|
||||
|
||||
/* Takes a value generated randomly or by hashing and turns it into a UID in the right range */
|
||||
|
|
|
@ -80,9 +80,9 @@ modulesloaddir=${modules_load_dir}
|
|||
catalog_dir=/usr/lib/systemd/catalog
|
||||
catalogdir=${catalog_dir}
|
||||
|
||||
system_uid_max=@systemuidmax@
|
||||
system_uid_max=@SYSTEM_UID_MAX@
|
||||
systemuidmax=${system_uid_max}
|
||||
system_gid_max=@systemgidmax@
|
||||
system_gid_max=@SYSTEM_GID_MAX@
|
||||
systemgidmax=${system_gid_max}
|
||||
|
||||
dynamic_uid_min=@dynamicuidmin@
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "user-record.h"
|
||||
#include "user-util.h"
|
||||
|
||||
/* The maximum size up to which we process coredumps */
|
||||
|
@ -682,7 +683,7 @@ static int change_uid_gid(const Context *context) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (uid <= SYSTEM_UID_MAX) {
|
||||
if (uid_is_system(uid)) {
|
||||
const char *user = "systemd-coredump";
|
||||
|
||||
r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0);
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "syslog-util.h"
|
||||
#include "user-record.h"
|
||||
#include "user-util.h"
|
||||
|
||||
#define USER_JOURNALS_MAX 1024
|
||||
|
|
|
@ -6,7 +6,7 @@ set -ex
|
|||
repart=$1
|
||||
test -x $repart
|
||||
|
||||
D=$(mktemp --directory)
|
||||
D=$(mktemp --tmpdir --directory "test-repart.XXXXXXXXXX")
|
||||
trap "rm -rf '$D'" EXIT INT QUIT PIPE
|
||||
mkdir -p $D/definitions
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "tomoyo-util.h"
|
||||
#include "user-record.h"
|
||||
#include "user-util.h"
|
||||
#include "util.h"
|
||||
#include "virt.h"
|
||||
|
|
|
@ -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) {
|
||||
unsigned i, j;
|
||||
|
||||
assert(p);
|
||||
assert(n);
|
||||
|
||||
for (i = 0; i < *n; i++) {
|
||||
for (j = i + 1; j < *n; j++) {
|
||||
for (unsigned i = 0; i < *n; i++) {
|
||||
for (unsigned j = i + 1; j < *n; j++) {
|
||||
UidRange *x = (*p)+i, *y = (*p)+j;
|
||||
|
||||
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) {
|
||||
bool found = false;
|
||||
UidRange *x;
|
||||
unsigned i;
|
||||
|
||||
assert(p);
|
||||
assert(n);
|
||||
|
@ -67,7 +64,7 @@ int uid_range_add(UidRange **p, unsigned *n, uid_t start, uid_t nr) {
|
|||
if (nr <= 0)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < *n; i++) {
|
||||
for (unsigned i = 0; i < *n; i++) {
|
||||
x = (*p) + i;
|
||||
if (uid_range_intersect(x, start, nr)) {
|
||||
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) {
|
||||
uid_t closest = UID_INVALID, candidate;
|
||||
unsigned i;
|
||||
|
||||
assert(p);
|
||||
assert(uid);
|
||||
|
||||
candidate = *uid - 1;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
uid_t begin, end;
|
||||
|
||||
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) {
|
||||
unsigned i;
|
||||
|
||||
assert(p);
|
||||
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)
|
||||
return true;
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include "cgroup-util.h"
|
||||
#include "dns-domain.h"
|
||||
#include "env-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "hostname-util.h"
|
||||
|
@ -21,6 +23,122 @@
|
|||
#define DEFAULT_RATELIMIT_BURST 30
|
||||
#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 *h;
|
||||
|
||||
|
|
|
@ -17,6 +17,35 @@
|
|||
/* The default disk size to use when nothing else is specified, relative to free disk space */
|
||||
#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 {
|
||||
USER_INTRINSIC, /* root and nobody */
|
||||
USER_SYSTEM, /* statically allocated users for system services */
|
||||
|
|
|
@ -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"
|
||||
|
@ -83,6 +84,9 @@ static uid_t search_uid = UID_INVALID;
|
|||
static UidRange *uid_range = NULL;
|
||||
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(users, 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);
|
||||
}
|
||||
|
||||
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) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
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 */
|
||||
if (!i->uid_set) {
|
||||
maybe_emit_login_defs_warning();
|
||||
|
||||
for (;;) {
|
||||
r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
|
||||
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 */
|
||||
if (!i->gid_set) {
|
||||
maybe_emit_login_defs_warning();
|
||||
|
||||
for (;;) {
|
||||
/* We look for new GIDs in the UID pool! */
|
||||
r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
|
||||
|
@ -1949,11 +1977,26 @@ 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. */
|
||||
r = read_login_defs(&login_defs, NULL, arg_root);
|
||||
if (r < 0)
|
||||
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();
|
||||
if (r < 0)
|
||||
|
|
|
@ -339,6 +339,10 @@ tests += [
|
|||
[],
|
||||
[]],
|
||||
|
||||
[['src/test/test-user-record.c'],
|
||||
[],
|
||||
[]],
|
||||
|
||||
[['src/test/test-user-util.c'],
|
||||
[],
|
||||
[]],
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "strv.h"
|
||||
#include "tests.h"
|
||||
#include "tomoyo-util.h"
|
||||
#include "user-record.h"
|
||||
#include "user-util.h"
|
||||
#include "virt.h"
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
in_files = ['basic.conf']
|
||||
|
||||
enable_sysusers = conf.get('ENABLE_SYSUSERS') == 1
|
||||
|
||||
foreach file : in_files
|
||||
gen = configure_file(
|
||||
input : file + '.in',
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../TEST-01-BASIC/Makefile
|
|
@ -1,2 +0,0 @@
|
|||
u1:x:300:u2
|
||||
u2:x:SYSTEM_UID_MAX:
|
|
@ -1,2 +0,0 @@
|
|||
u1:x:300:300::/:NOLOGIN
|
||||
u2:x:SYSTEM_UID_MAX:SYSTEM_UID_MAX::/:NOLOGIN
|
|
@ -1 +0,0 @@
|
|||
aaa:x:SYSTEM_UID_MAX:987::/:NOLOGIN
|
|
@ -1,2 +0,0 @@
|
|||
g1:x:111:
|
||||
u1:x:SYSTEM_UID_MAX:
|
|
@ -1 +0,0 @@
|
|||
u1:x:SYSTEM_UID_MAX:SYSTEM_UID_MAX::/:NOLOGIN
|
|
@ -1 +0,0 @@
|
|||
username:x:SYSTEM_UID_MAX:300::/:NOLOGIN
|
|
@ -1,2 +0,0 @@
|
|||
user1:x:300:300::/:NOLOGIN
|
||||
user2:x:SYSTEM_UID_MAX:300::/:NOLOGIN
|
|
@ -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 "$@"
|
|
@ -18,7 +18,7 @@ if [ ! -x "$SYSTEMD_HWDB" ]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
D=$(mktemp --directory)
|
||||
D=$(mktemp --tmpdir --directory "hwdb-test.XXXXXXXXXX")
|
||||
trap "rm -rf '$D'" EXIT INT QUIT PIPE
|
||||
mkdir -p "$D/etc/udev"
|
||||
ln -s "$ROOTDIR/hwdb.d" "$D/etc/udev/hwdb.d"
|
||||
|
|
|
@ -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')
|
||||
if want_tests != 'false'
|
||||
test('rule-syntax-check',
|
||||
|
|
|
@ -17,7 +17,7 @@ for f in "$src"/test-*.input; do
|
|||
echo "*** Running $f"
|
||||
|
||||
(
|
||||
out=$(mktemp --directory)
|
||||
out=$(mktemp --tmpdir --directory "test-network-generator-conversion.XXXXXXXXXX")
|
||||
trap "rm -rf '$out'" EXIT INT QUIT PIPE
|
||||
|
||||
$generator --root "$out" -- $(cat $f)
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
u1:x:300:u2
|
||||
u2:x:SYSTEM_UGID_MAX:
|
|
@ -0,0 +1,2 @@
|
|||
u1:x:300:300::/: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
|
|
@ -0,0 +1 @@
|
|||
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
|
|
@ -0,0 +1,2 @@
|
|||
g1:x:111:
|
||||
u1:x:SYSTEM_UGID_MAX:
|
|
@ -0,0 +1 @@
|
|||
u1:x:SYSTEM_UGID_MAX:SYSTEM_UGID_MAX::/:NOLOGIN
|
|
@ -0,0 +1 @@
|
|||
username:x:SYSTEM_UGID_MAX:300::/:NOLOGIN
|
|
@ -0,0 +1,2 @@
|
|||
user1:x:300:300::/:NOLOGIN
|
||||
user2:x:SYSTEM_UGID_MAX:300::/:NOLOGIN
|
Loading…
Reference in New Issue