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