1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <stdlib.h>
5 #include <string.h>
6
7 #include "alloc-util.h"
8 #include "errno-util.h"
9 #include "fd-util.h"
10 #include "format-util.h"
11 #include "macro.h"
12 #include "path-util.h"
13 #include "sort-util.h"
14 #include "stat-util.h"
15 #include "uid-range.h"
16 #include "user-util.h"
17
uid_range_intersect(UidRange * range,uid_t start,uid_t nr)18 static bool uid_range_intersect(UidRange *range, uid_t start, uid_t nr) {
19 assert(range);
20
21 return range->start <= start + nr &&
22 range->start + range->nr >= start;
23 }
24
uid_range_coalesce(UidRange ** p,size_t * n)25 static void uid_range_coalesce(UidRange **p, size_t *n) {
26 assert(p);
27 assert(n);
28
29 for (size_t i = 0; i < *n; i++) {
30 for (size_t j = i + 1; j < *n; j++) {
31 UidRange *x = (*p)+i, *y = (*p)+j;
32
33 if (uid_range_intersect(x, y->start, y->nr)) {
34 uid_t begin, end;
35
36 begin = MIN(x->start, y->start);
37 end = MAX(x->start + x->nr, y->start + y->nr);
38
39 x->start = begin;
40 x->nr = end - begin;
41
42 if (*n > j+1)
43 memmove(y, y+1, sizeof(UidRange) * (*n - j -1));
44
45 (*n)--;
46 j--;
47 }
48 }
49 }
50 }
51
uid_range_compare(const UidRange * a,const UidRange * b)52 static int uid_range_compare(const UidRange *a, const UidRange *b) {
53 int r;
54
55 r = CMP(a->start, b->start);
56 if (r != 0)
57 return r;
58
59 return CMP(a->nr, b->nr);
60 }
61
uid_range_add(UidRange ** p,size_t * n,uid_t start,uid_t nr)62 int uid_range_add(UidRange **p, size_t *n, uid_t start, uid_t nr) {
63 bool found = false;
64 UidRange *x;
65
66 assert(p);
67 assert(n);
68
69 if (nr <= 0)
70 return 0;
71
72 if (start > UINT32_MAX - nr) /* overflow check */
73 return -ERANGE;
74
75 for (size_t i = 0; i < *n; i++) {
76 x = (*p) + i;
77 if (uid_range_intersect(x, start, nr)) {
78 found = true;
79 break;
80 }
81 }
82
83 if (found) {
84 uid_t begin, end;
85
86 begin = MIN(x->start, start);
87 end = MAX(x->start + x->nr, start + nr);
88
89 x->start = begin;
90 x->nr = end - begin;
91 } else {
92 UidRange *t;
93
94 t = reallocarray(*p, *n + 1, sizeof(UidRange));
95 if (!t)
96 return -ENOMEM;
97
98 *p = t;
99 x = t + ((*n) ++);
100
101 x->start = start;
102 x->nr = nr;
103 }
104
105 typesafe_qsort(*p, *n, uid_range_compare);
106 uid_range_coalesce(p, n);
107
108 return *n;
109 }
110
uid_range_add_str(UidRange ** p,size_t * n,const char * s)111 int uid_range_add_str(UidRange **p, size_t *n, const char *s) {
112 uid_t start, nr;
113 const char *t;
114 int r;
115
116 assert(p);
117 assert(n);
118 assert(s);
119
120 t = strchr(s, '-');
121 if (t) {
122 char *b;
123 uid_t end;
124
125 b = strndupa_safe(s, t - s);
126 r = parse_uid(b, &start);
127 if (r < 0)
128 return r;
129
130 r = parse_uid(t+1, &end);
131 if (r < 0)
132 return r;
133
134 if (end < start)
135 return -EINVAL;
136
137 nr = end - start + 1;
138 } else {
139 r = parse_uid(s, &start);
140 if (r < 0)
141 return r;
142
143 nr = 1;
144 }
145
146 return uid_range_add(p, n, start, nr);
147 }
148
uid_range_next_lower(const UidRange * p,size_t n,uid_t * uid)149 int uid_range_next_lower(const UidRange *p, size_t n, uid_t *uid) {
150 uid_t closest = UID_INVALID, candidate;
151
152 assert(p);
153 assert(uid);
154
155 if (*uid == 0)
156 return -EBUSY;
157
158 candidate = *uid - 1;
159
160 for (size_t i = 0; i < n; i++) {
161 uid_t begin, end;
162
163 begin = p[i].start;
164 end = p[i].start + p[i].nr - 1;
165
166 if (candidate >= begin && candidate <= end) {
167 *uid = candidate;
168 return 1;
169 }
170
171 if (end < candidate)
172 closest = end;
173 }
174
175 if (closest == UID_INVALID)
176 return -EBUSY;
177
178 *uid = closest;
179 return 1;
180 }
181
uid_range_covers(const UidRange * p,size_t n,uid_t start,uid_t nr)182 bool uid_range_covers(const UidRange *p, size_t n, uid_t start, uid_t nr) {
183 assert(p || n == 0);
184
185 if (nr == 0) /* empty range? always covered... */
186 return true;
187
188 if (start > UINT32_MAX - nr) /* range overflows? definitely not covered... */
189 return false;
190
191 for (size_t i = 0; i < n; i++)
192 if (start >= p[i].start && start + nr <= p[i].start + p[i].nr)
193 return true;
194
195 return false;
196 }
197
uid_range_load_userns(UidRange ** p,size_t * n,const char * path)198 int uid_range_load_userns(UidRange **p, size_t *n, const char *path) {
199 _cleanup_fclose_ FILE *f = NULL;
200 int r;
201
202 /* If 'path' is NULL loads the UID range of the userns namespace we run. Otherwise load the data from
203 * the specified file (which can be either uid_map or gid_map, in case caller needs to deal with GID
204 * maps).
205 *
206 * To simplify things this will modify the passed array in case of later failure. */
207
208 if (!path)
209 path = "/proc/self/uid_map";
210
211 f = fopen(path, "re");
212 if (!f) {
213 r = -errno;
214
215 if (r == -ENOENT && path_startswith(path, "/proc/") && proc_mounted() > 0)
216 return -EOPNOTSUPP;
217
218 return r;
219 }
220
221 for (;;) {
222 uid_t uid_base, uid_shift, uid_range;
223 int k;
224
225 errno = 0;
226 k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
227 if (k == EOF) {
228 if (ferror(f))
229 return errno_or_else(EIO);
230
231 return 0;
232 }
233 if (k != 3)
234 return -EBADMSG;
235
236 r = uid_range_add(p, n, uid_base, uid_range);
237 if (r < 0)
238 return r;
239 }
240 }
241