1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "chase-symlinks.h"
4 #include "fd-util.h"
5 #include "fileio.h"
6 #include "string-util.h"
7 #include "uid-alloc-range.h"
8 #include "user-util.h"
9
10 static const UGIDAllocationRange default_ugid_allocation_range = {
11 .system_alloc_uid_min = SYSTEM_ALLOC_UID_MIN,
12 .system_uid_max = SYSTEM_UID_MAX,
13 .system_alloc_gid_min = SYSTEM_ALLOC_GID_MIN,
14 .system_gid_max = SYSTEM_GID_MAX,
15 };
16
17 #if ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES
parse_alloc_uid(const char * path,const char * name,const char * t,uid_t * ret_uid)18 static int parse_alloc_uid(const char *path, const char *name, const char *t, uid_t *ret_uid) {
19 uid_t uid;
20 int r;
21
22 r = parse_uid(t, &uid);
23 if (r < 0)
24 return log_debug_errno(r, "%s: failed to parse %s %s, ignoring: %m", path, name, t);
25 if (uid == 0)
26 uid = 1;
27
28 *ret_uid = uid;
29 return 0;
30 }
31 #endif
32
read_login_defs(UGIDAllocationRange * ret_defs,const char * path,const char * root)33 int read_login_defs(UGIDAllocationRange *ret_defs, const char *path, const char *root) {
34 #if ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES
35 _cleanup_fclose_ FILE *f = NULL;
36 UGIDAllocationRange defs;
37 int r;
38
39 if (!path)
40 path = "/etc/login.defs";
41
42 r = chase_symlinks_and_fopen_unlocked(path, root, CHASE_PREFIX_ROOT, "re", NULL, &f);
43 if (r == -ENOENT)
44 goto defaults;
45 if (r < 0)
46 return log_debug_errno(r, "Failed to open %s: %m", path);
47
48 defs = default_ugid_allocation_range;
49
50 for (;;) {
51 _cleanup_free_ char *line = NULL;
52 char *t;
53
54 r = read_line(f, LINE_MAX, &line);
55 if (r < 0)
56 return log_debug_errno(r, "Failed to read %s: %m", path);
57 if (r == 0)
58 break;
59
60 if ((t = first_word(line, "SYS_UID_MIN")))
61 (void) parse_alloc_uid(path, "SYS_UID_MIN", t, &defs.system_alloc_uid_min);
62 else if ((t = first_word(line, "SYS_UID_MAX")))
63 (void) parse_alloc_uid(path, "SYS_UID_MAX", t, &defs.system_uid_max);
64 else if ((t = first_word(line, "SYS_GID_MIN")))
65 (void) parse_alloc_uid(path, "SYS_GID_MIN", t, &defs.system_alloc_gid_min);
66 else if ((t = first_word(line, "SYS_GID_MAX")))
67 (void) parse_alloc_uid(path, "SYS_GID_MAX", t, &defs.system_gid_max);
68 }
69
70 if (defs.system_alloc_uid_min > defs.system_uid_max) {
71 log_debug("%s: SYS_UID_MIN > SYS_UID_MAX, resetting.", path);
72 defs.system_alloc_uid_min = MIN(defs.system_uid_max - 1, (uid_t) SYSTEM_ALLOC_UID_MIN);
73 /* Look at sys_uid_max to make sure sys_uid_min..sys_uid_max remains a valid range. */
74 }
75 if (defs.system_alloc_gid_min > defs.system_gid_max) {
76 log_debug("%s: SYS_GID_MIN > SYS_GID_MAX, resetting.", path);
77 defs.system_alloc_gid_min = MIN(defs.system_gid_max - 1, (gid_t) SYSTEM_ALLOC_GID_MIN);
78 /* Look at sys_gid_max to make sure sys_gid_min..sys_gid_max remains a valid range. */
79 }
80
81 *ret_defs = defs;
82 return 1;
83 defaults:
84 #endif
85 *ret_defs = default_ugid_allocation_range;
86 return 0;
87 }
88
acquire_ugid_allocation_range(void)89 const UGIDAllocationRange *acquire_ugid_allocation_range(void) {
90 #if ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES
91 static thread_local UGIDAllocationRange defs;
92 static thread_local int initialized = 0; /* == 0 → not initialized yet
93 * < 0 → failure
94 * > 0 → success */
95
96 /* This function will ignore failure to read the file, so it should only be called from places where
97 * we don't crucially depend on the answer. In other words, it's appropriate for journald, but
98 * probably not for sysusers. */
99
100 if (initialized == 0)
101 initialized = read_login_defs(&defs, NULL, NULL) < 0 ? -1 : 1;
102 if (initialized < 0)
103 return &default_ugid_allocation_range;
104
105 return &defs;
106
107 #endif
108 return &default_ugid_allocation_range;
109 }
110
uid_is_system(uid_t uid)111 bool uid_is_system(uid_t uid) {
112 const UGIDAllocationRange *defs;
113 assert_se(defs = acquire_ugid_allocation_range());
114
115 return uid <= defs->system_uid_max;
116 }
117
gid_is_system(gid_t gid)118 bool gid_is_system(gid_t gid) {
119 const UGIDAllocationRange *defs;
120 assert_se(defs = acquire_ugid_allocation_range());
121
122 return gid <= defs->system_gid_max;
123 }
124