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
'idn',
'ima',
'initrd',
+ 'compat-mutable-uid-boundaries',
'ldconfig',
'localed',
'logind',
['libcurl'],
['idn'],
['initrd'],
+ ['compat-mutable-uid-boundaries'],
['libidn2'],
['libidn'],
['libiptc'],
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')
description : 'path to telinit')
option('rc-local', type : 'string',
value : '/etc/rc.local')
-option('initrd', type: 'boolean',
+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')
#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"
#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;
+}
+
+static int read_login_defs(UGIDAllocationRange *ret_defs, const char *path) {
+ _cleanup_fclose_ FILE *f = NULL;
+ UGIDAllocationRange defs = {
+ .system_uid_max = SYSTEM_UID_MAX,
+ .system_gid_max = SYSTEM_GID_MAX,
+ };
+ int r;
+
+ if (!path)
+ path = "/etc/login.defs";
+
+ r = fopen_unlocked(path, "re", &f);
+ 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_MAX")))
+ (void) parse_alloc_uid(path, "SYS_UID_MAX", t, &defs.system_uid_max);
+ else if ((t = first_word(line, "SYS_GID_MAX")))
+ (void) parse_alloc_uid(path, "SYS_GID_MAX", t, &defs.system_gid_max);
+ }
+
+ assign:
+ *ret_defs = defs;
+ return 0;
+}
+#endif
+
+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_uid_max = SYSTEM_UID_MAX,
+ .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);
+ 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;
/* The default disk size to use when nothing else is specified, relative to free disk space */
#define USER_DISK_SIZE_DEFAULT_PERCENT 85
-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;
-}
+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;
return uid_is_container((uid_t) gid);
}
+typedef struct UGIDAllocationRange {
+ uid_t system_uid_max;
+ gid_t system_gid_max;
+} UGIDAllocationRange;
+
+const UGIDAllocationRange *acquire_ugid_allocation_range(void);
+
typedef enum UserDisposition {
USER_INTRINSIC, /* root and nobody */
USER_SYSTEM, /* statically allocated users for system services */
[],
[]],
+ [['src/test/test-user-record.c'],
+ [],
+ []],
+
[['src/test/test-user-util.c'],
[],
[]],
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "format-util.h"
+#include "tests.h"
+#include "user-record.h"
+
+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_uid_max="UID_FMT, defs->system_uid_max);
+ 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_acquire_ugid_allocation_range();
+ test_uid_is_system();
+ test_gid_is_system();
+
+ return 0;
+}