]>
Commit | Line | Data |
---|---|---|
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 | 17 | |
8dcc66ce YW |
18 | UidRange *uid_range_free(UidRange *range) { |
19 | if (!range) | |
20 | return NULL; | |
21 | ||
22 | free(range->entries); | |
23 | return mfree(range); | |
24 | } | |
25 | ||
26 | static bool uid_range_entry_intersect(const UidRangeEntry *a, const UidRangeEntry *b) { | |
6cc6cd92 YW |
27 | assert(a); |
28 | assert(b); | |
8530dc44 | 29 | |
6cc6cd92 | 30 | return a->start <= b->start + b->nr && a->start + a->nr >= b->start; |
8530dc44 LP |
31 | } |
32 | ||
8dcc66ce | 33 | static int uid_range_entry_compare(const UidRangeEntry *a, const UidRangeEntry *b) { |
34eeba47 YW |
34 | int r; |
35 | ||
36 | assert(a); | |
37 | assert(b); | |
38 | ||
39 | r = CMP(a->start, b->start); | |
40 | if (r != 0) | |
41 | return r; | |
42 | ||
43 | return CMP(a->nr, b->nr); | |
44 | } | |
45 | ||
8dcc66ce YW |
46 | static void uid_range_coalesce(UidRange *range) { |
47 | assert(range); | |
8530dc44 | 48 | |
8dcc66ce | 49 | if (range->n_entries <= 0) |
34eeba47 YW |
50 | return; |
51 | ||
8dcc66ce | 52 | typesafe_qsort(range->entries, range->n_entries, uid_range_entry_compare); |
34eeba47 | 53 | |
8dcc66ce YW |
54 | for (size_t i = 0; i < range->n_entries; i++) { |
55 | UidRangeEntry *x = range->entries + i; | |
b5739767 | 56 | |
8dcc66ce YW |
57 | for (size_t j = i + 1; j < range->n_entries; j++) { |
58 | UidRangeEntry *y = range->entries + j; | |
b5739767 | 59 | uid_t begin, end; |
8530dc44 | 60 | |
8dcc66ce | 61 | if (!uid_range_entry_intersect(x, y)) |
b5739767 | 62 | break; |
8530dc44 | 63 | |
b5739767 YW |
64 | begin = MIN(x->start, y->start); |
65 | end = MAX(x->start + x->nr, y->start + y->nr); | |
8530dc44 | 66 | |
b5739767 YW |
67 | x->start = begin; |
68 | x->nr = end - begin; | |
8530dc44 | 69 | |
8dcc66ce YW |
70 | if (range->n_entries > j + 1) |
71 | memmove(y, y + 1, sizeof(UidRangeEntry) * (range->n_entries - j - 1)); | |
8530dc44 | 72 | |
8dcc66ce | 73 | range->n_entries--; |
b5739767 | 74 | j--; |
8530dc44 LP |
75 | } |
76 | } | |
8530dc44 LP |
77 | } |
78 | ||
8dcc66ce YW |
79 | int uid_range_add_internal(UidRange **range, uid_t start, uid_t nr, bool coalesce) { |
80 | _cleanup_(uid_range_freep) UidRange *range_new = NULL; | |
81 | UidRange *p; | |
82 | ||
83 | assert(range); | |
8530dc44 LP |
84 | |
85 | if (nr <= 0) | |
86 | return 0; | |
87 | ||
0a5c6a57 LP |
88 | if (start > UINT32_MAX - nr) /* overflow check */ |
89 | return -ERANGE; | |
90 | ||
8dcc66ce YW |
91 | if (*range) |
92 | p = *range; | |
93 | else { | |
94 | range_new = new0(UidRange, 1); | |
95 | if (!range_new) | |
96 | return -ENOMEM; | |
97 | ||
98 | p = range_new; | |
99 | } | |
100 | ||
101 | if (!GREEDY_REALLOC(p->entries, p->n_entries + 1)) | |
f6c13f9f | 102 | return -ENOMEM; |
8530dc44 | 103 | |
8dcc66ce | 104 | p->entries[p->n_entries++] = (UidRangeEntry) { |
f6c13f9f YW |
105 | .start = start, |
106 | .nr = nr, | |
107 | }; | |
8530dc44 | 108 | |
f6c13f9f | 109 | if (coalesce) |
8dcc66ce YW |
110 | uid_range_coalesce(p); |
111 | ||
112 | TAKE_PTR(range_new); | |
113 | *range = p; | |
8530dc44 | 114 | |
8dcc66ce | 115 | return 0; |
8530dc44 LP |
116 | } |
117 | ||
8dcc66ce | 118 | int uid_range_add_str(UidRange **range, const char *s) { |
7cf337c5 | 119 | uid_t start, end; |
8530dc44 LP |
120 | int r; |
121 | ||
8dcc66ce | 122 | assert(range); |
8530dc44 LP |
123 | assert(s); |
124 | ||
7cf337c5 YW |
125 | r = parse_uid_range(s, &start, &end); |
126 | if (r < 0) | |
127 | return r; | |
8530dc44 | 128 | |
8dcc66ce | 129 | return uid_range_add_internal(range, start, end - start + 1, /* coalesce = */ true); |
8530dc44 LP |
130 | } |
131 | ||
8dcc66ce | 132 | int uid_range_next_lower(const UidRange *range, uid_t *uid) { |
fed1e721 | 133 | uid_t closest = UID_INVALID, candidate; |
8530dc44 | 134 | |
8dcc66ce | 135 | assert(range); |
8530dc44 LP |
136 | assert(uid); |
137 | ||
0a5c6a57 LP |
138 | if (*uid == 0) |
139 | return -EBUSY; | |
140 | ||
8530dc44 LP |
141 | candidate = *uid - 1; |
142 | ||
8dcc66ce | 143 | for (size_t i = 0; i < range->n_entries; i++) { |
8530dc44 LP |
144 | uid_t begin, end; |
145 | ||
8dcc66ce YW |
146 | begin = range->entries[i].start; |
147 | end = range->entries[i].start + range->entries[i].nr - 1; | |
8530dc44 LP |
148 | |
149 | if (candidate >= begin && candidate <= end) { | |
150 | *uid = candidate; | |
151 | return 1; | |
152 | } | |
153 | ||
154 | if (end < candidate) | |
155 | closest = end; | |
156 | } | |
157 | ||
fed1e721 | 158 | if (closest == UID_INVALID) |
8530dc44 LP |
159 | return -EBUSY; |
160 | ||
161 | *uid = closest; | |
162 | return 1; | |
163 | } | |
164 | ||
8dcc66ce | 165 | bool uid_range_covers(const UidRange *range, uid_t start, uid_t nr) { |
55656049 LP |
166 | if (nr == 0) /* empty range? always covered... */ |
167 | return true; | |
168 | ||
169 | if (start > UINT32_MAX - nr) /* range overflows? definitely not covered... */ | |
170 | return false; | |
8530dc44 | 171 | |
8dcc66ce YW |
172 | if (!range) |
173 | return false; | |
174 | ||
175 | for (size_t i = 0; i < range->n_entries; i++) | |
176 | if (start >= range->entries[i].start && | |
177 | start + nr <= range->entries[i].start + range->entries[i].nr) | |
8530dc44 LP |
178 | return true; |
179 | ||
180 | return false; | |
181 | } | |
5674aa7a | 182 | |
7312c422 MY |
183 | int uid_map_read_one(FILE *f, uid_t *ret_base, uid_t *ret_shift, uid_t *ret_range) { |
184 | uid_t uid_base, uid_shift, uid_range; | |
185 | int r; | |
186 | ||
187 | assert(f); | |
188 | assert(ret_base); | |
189 | assert(ret_shift); | |
190 | assert(ret_range); | |
191 | ||
192 | errno = 0; | |
193 | r = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range); | |
194 | if (r == EOF) | |
195 | return errno_or_else(ENOMSG); | |
196 | assert(r >= 0); | |
197 | if (r != 3) | |
198 | return -EBADMSG; | |
199 | ||
200 | *ret_base = uid_base; | |
201 | *ret_shift = uid_shift; | |
202 | *ret_range = uid_range; | |
203 | ||
204 | return 0; | |
205 | } | |
206 | ||
8dcc66ce YW |
207 | int uid_range_load_userns(UidRange **ret, const char *path) { |
208 | _cleanup_(uid_range_freep) UidRange *range = NULL; | |
5674aa7a LP |
209 | _cleanup_fclose_ FILE *f = NULL; |
210 | int r; | |
211 | ||
212 | /* If 'path' is NULL loads the UID range of the userns namespace we run. Otherwise load the data from | |
213 | * the specified file (which can be either uid_map or gid_map, in case caller needs to deal with GID | |
214 | * maps). | |
215 | * | |
216 | * To simplify things this will modify the passed array in case of later failure. */ | |
217 | ||
8dcc66ce | 218 | assert(ret); |
f6c13f9f | 219 | |
5674aa7a LP |
220 | if (!path) |
221 | path = "/proc/self/uid_map"; | |
222 | ||
223 | f = fopen(path, "re"); | |
224 | if (!f) { | |
225 | r = -errno; | |
226 | ||
cdba12b3 LP |
227 | if (r == -ENOENT && path_startswith(path, "/proc/")) |
228 | return proc_mounted() > 0 ? -EOPNOTSUPP : -ENOSYS; | |
5674aa7a LP |
229 | |
230 | return r; | |
231 | } | |
232 | ||
8dcc66ce YW |
233 | range = new0(UidRange, 1); |
234 | if (!range) | |
235 | return -ENOMEM; | |
236 | ||
5674aa7a LP |
237 | for (;;) { |
238 | uid_t uid_base, uid_shift, uid_range; | |
5674aa7a | 239 | |
7312c422 MY |
240 | r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range); |
241 | if (r == -ENOMSG) | |
f6c13f9f | 242 | break; |
7312c422 MY |
243 | if (r < 0) |
244 | return r; | |
5674aa7a | 245 | |
8dcc66ce | 246 | r = uid_range_add_internal(&range, uid_base, uid_range, /* coalesce = */ false); |
5674aa7a LP |
247 | if (r < 0) |
248 | return r; | |
249 | } | |
f6c13f9f | 250 | |
8dcc66ce | 251 | uid_range_coalesce(range); |
f6c13f9f | 252 | |
8dcc66ce | 253 | *ret = TAKE_PTR(range); |
f6c13f9f | 254 | return 0; |
5674aa7a | 255 | } |