Merge pull request #17172 from keszybz/read-login-defs

Read /etc/login.defs
This commit is contained in:
Lennart Poettering 2020-10-02 11:01:30 +02:00 committed by GitHub
commit c14ebe07a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
89 changed files with 621 additions and 235 deletions

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