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