]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
f4b47811 | 2 | |
f4b47811 LP |
3 | #include <errno.h> |
4 | #include <stdbool.h> | |
ca78ad1d ZJS |
5 | #include <sys/stat.h> |
6 | #include <sys/types.h> | |
f4b47811 | 7 | |
b1d4f8e1 | 8 | #include "acl-util.h" |
cf0fbc49 | 9 | #include "alloc-util.h" |
07630cea | 10 | #include "string-util.h" |
478c8269 | 11 | #include "strv.h" |
b1d4f8e1 | 12 | #include "user-util.h" |
07630cea | 13 | #include "util.h" |
f4b47811 | 14 | |
71281a76 | 15 | int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *ret_entry) { |
f4b47811 | 16 | acl_entry_t i; |
dd4105b0 | 17 | int r; |
f4b47811 LP |
18 | |
19 | assert(acl); | |
71281a76 LP |
20 | assert(uid_is_valid(uid)); |
21 | assert(ret_entry); | |
f4b47811 | 22 | |
dd4105b0 ZJS |
23 | for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); |
24 | r > 0; | |
25 | r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { | |
f4b47811 LP |
26 | |
27 | acl_tag_t tag; | |
28 | uid_t *u; | |
29 | bool b; | |
30 | ||
31 | if (acl_get_tag_type(i, &tag) < 0) | |
32 | return -errno; | |
33 | ||
34 | if (tag != ACL_USER) | |
35 | continue; | |
36 | ||
37 | u = acl_get_qualifier(i); | |
38 | if (!u) | |
39 | return -errno; | |
40 | ||
41 | b = *u == uid; | |
42 | acl_free(u); | |
43 | ||
44 | if (b) { | |
71281a76 | 45 | *ret_entry = i; |
f4b47811 LP |
46 | return 1; |
47 | } | |
48 | } | |
dd4105b0 | 49 | if (r < 0) |
f4b47811 LP |
50 | return -errno; |
51 | ||
71281a76 | 52 | *ret_entry = NULL; |
f4b47811 LP |
53 | return 0; |
54 | } | |
478c8269 | 55 | |
23ad4dd8 JAS |
56 | int calc_acl_mask_if_needed(acl_t *acl_p) { |
57 | acl_entry_t i; | |
dd4105b0 | 58 | int r; |
6debb398 | 59 | bool need = false; |
23ad4dd8 JAS |
60 | |
61 | assert(acl_p); | |
62 | ||
dd4105b0 ZJS |
63 | for (r = acl_get_entry(*acl_p, ACL_FIRST_ENTRY, &i); |
64 | r > 0; | |
65 | r = acl_get_entry(*acl_p, ACL_NEXT_ENTRY, &i)) { | |
23ad4dd8 JAS |
66 | acl_tag_t tag; |
67 | ||
68 | if (acl_get_tag_type(i, &tag) < 0) | |
69 | return -errno; | |
70 | ||
71 | if (tag == ACL_MASK) | |
72 | return 0; | |
e346512c | 73 | |
6debb398 ZJS |
74 | if (IN_SET(tag, ACL_USER, ACL_GROUP)) |
75 | need = true; | |
23ad4dd8 | 76 | } |
dd4105b0 | 77 | if (r < 0) |
23ad4dd8 JAS |
78 | return -errno; |
79 | ||
6debb398 ZJS |
80 | if (need && acl_calc_mask(acl_p) < 0) |
81 | return -errno; | |
82 | ||
83 | return need; | |
dd4105b0 ZJS |
84 | } |
85 | ||
86 | int add_base_acls_if_needed(acl_t *acl_p, const char *path) { | |
87 | acl_entry_t i; | |
88 | int r; | |
89 | bool have_user_obj = false, have_group_obj = false, have_other = false; | |
90 | struct stat st; | |
91 | _cleanup_(acl_freep) acl_t basic = NULL; | |
92 | ||
93 | assert(acl_p); | |
94 | ||
95 | for (r = acl_get_entry(*acl_p, ACL_FIRST_ENTRY, &i); | |
96 | r > 0; | |
97 | r = acl_get_entry(*acl_p, ACL_NEXT_ENTRY, &i)) { | |
98 | acl_tag_t tag; | |
99 | ||
100 | if (acl_get_tag_type(i, &tag) < 0) | |
101 | return -errno; | |
102 | ||
103 | if (tag == ACL_USER_OBJ) | |
104 | have_user_obj = true; | |
105 | else if (tag == ACL_GROUP_OBJ) | |
106 | have_group_obj = true; | |
107 | else if (tag == ACL_OTHER) | |
108 | have_other = true; | |
109 | if (have_user_obj && have_group_obj && have_other) | |
110 | return 0; | |
111 | } | |
112 | if (r < 0) | |
113 | return -errno; | |
23ad4dd8 | 114 | |
dd4105b0 ZJS |
115 | r = stat(path, &st); |
116 | if (r < 0) | |
117 | return -errno; | |
118 | ||
119 | basic = acl_from_mode(st.st_mode); | |
120 | if (!basic) | |
121 | return -errno; | |
122 | ||
123 | for (r = acl_get_entry(basic, ACL_FIRST_ENTRY, &i); | |
124 | r > 0; | |
125 | r = acl_get_entry(basic, ACL_NEXT_ENTRY, &i)) { | |
126 | acl_tag_t tag; | |
127 | acl_entry_t dst; | |
128 | ||
129 | if (acl_get_tag_type(i, &tag) < 0) | |
130 | return -errno; | |
131 | ||
132 | if ((tag == ACL_USER_OBJ && have_user_obj) || | |
133 | (tag == ACL_GROUP_OBJ && have_group_obj) || | |
134 | (tag == ACL_OTHER && have_other)) | |
135 | continue; | |
136 | ||
137 | r = acl_create_entry(acl_p, &dst); | |
138 | if (r < 0) | |
139 | return -errno; | |
140 | ||
141 | r = acl_copy_entry(dst, i); | |
142 | if (r < 0) | |
143 | return -errno; | |
144 | } | |
145 | if (r < 0) | |
146 | return -errno; | |
23ad4dd8 JAS |
147 | return 0; |
148 | } | |
149 | ||
e346512c LP |
150 | int acl_search_groups(const char *path, char ***ret_groups) { |
151 | _cleanup_strv_free_ char **g = NULL; | |
29d87223 | 152 | _cleanup_(acl_freep) acl_t acl = NULL; |
e346512c LP |
153 | bool ret = false; |
154 | acl_entry_t entry; | |
155 | int r; | |
478c8269 ZJS |
156 | |
157 | assert(path); | |
478c8269 ZJS |
158 | |
159 | acl = acl_get_file(path, ACL_TYPE_DEFAULT); | |
e346512c LP |
160 | if (!acl) |
161 | return -errno; | |
478c8269 | 162 | |
e346512c LP |
163 | r = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); |
164 | for (;;) { | |
165 | _cleanup_(acl_free_gid_tpp) gid_t *gid = NULL; | |
166 | acl_tag_t tag; | |
478c8269 | 167 | |
e346512c LP |
168 | if (r < 0) |
169 | return -errno; | |
170 | if (r == 0) | |
171 | break; | |
478c8269 | 172 | |
e346512c LP |
173 | if (acl_get_tag_type(entry, &tag) < 0) |
174 | return -errno; | |
175 | ||
176 | if (tag != ACL_GROUP) | |
177 | goto next; | |
178 | ||
179 | gid = acl_get_qualifier(entry); | |
180 | if (!gid) | |
181 | return -errno; | |
478c8269 | 182 | |
e346512c LP |
183 | if (in_gid(*gid) > 0) { |
184 | if (!ret_groups) | |
185 | return true; | |
186 | ||
187 | ret = true; | |
188 | } | |
189 | ||
190 | if (ret_groups) { | |
191 | char *name; | |
478c8269 ZJS |
192 | |
193 | name = gid_to_name(*gid); | |
e346512c LP |
194 | if (!name) |
195 | return -ENOMEM; | |
196 | ||
197 | r = strv_consume(&g, name); | |
198 | if (r < 0) | |
199 | return r; | |
478c8269 ZJS |
200 | } |
201 | ||
e346512c LP |
202 | next: |
203 | r = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry); | |
478c8269 ZJS |
204 | } |
205 | ||
1cc6c93a YW |
206 | if (ret_groups) |
207 | *ret_groups = TAKE_PTR(g); | |
e346512c LP |
208 | |
209 | return ret; | |
478c8269 | 210 | } |
f8eeeaf9 | 211 | |
e738c945 | 212 | int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask) { |
848f0178 | 213 | _cleanup_free_ char **a = NULL, **d = NULL; /* strings are not freed */ |
c2b2df60 | 214 | _cleanup_strv_free_ char **split = NULL; |
f8eeeaf9 ZJS |
215 | int r = -EINVAL; |
216 | _cleanup_(acl_freep) acl_t a_acl = NULL, d_acl = NULL; | |
217 | ||
218 | split = strv_split(text, ","); | |
219 | if (!split) | |
e738c945 | 220 | return -ENOMEM; |
f8eeeaf9 ZJS |
221 | |
222 | STRV_FOREACH(entry, split) { | |
223 | char *p; | |
224 | ||
49fe5c09 | 225 | p = STARTSWITH_SET(*entry, "default:", "d:"); |
f2ea9cc7 Y |
226 | if (p) |
227 | r = strv_push(&d, p); | |
228 | else | |
229 | r = strv_push(&a, *entry); | |
e738c945 LP |
230 | if (r < 0) |
231 | return r; | |
f8eeeaf9 | 232 | } |
f8eeeaf9 ZJS |
233 | |
234 | if (!strv_isempty(a)) { | |
c2b2df60 | 235 | _cleanup_free_ char *join = NULL; |
f8eeeaf9 ZJS |
236 | |
237 | join = strv_join(a, ","); | |
238 | if (!join) | |
239 | return -ENOMEM; | |
240 | ||
241 | a_acl = acl_from_text(join); | |
242 | if (!a_acl) | |
e738c945 | 243 | return -errno; |
f8eeeaf9 | 244 | |
50d9e46d ZJS |
245 | if (want_mask) { |
246 | r = calc_acl_mask_if_needed(&a_acl); | |
247 | if (r < 0) | |
248 | return r; | |
249 | } | |
f8eeeaf9 ZJS |
250 | } |
251 | ||
252 | if (!strv_isempty(d)) { | |
c2b2df60 | 253 | _cleanup_free_ char *join = NULL; |
f8eeeaf9 ZJS |
254 | |
255 | join = strv_join(d, ","); | |
256 | if (!join) | |
257 | return -ENOMEM; | |
258 | ||
259 | d_acl = acl_from_text(join); | |
260 | if (!d_acl) | |
e738c945 | 261 | return -errno; |
f8eeeaf9 | 262 | |
50d9e46d ZJS |
263 | if (want_mask) { |
264 | r = calc_acl_mask_if_needed(&d_acl); | |
265 | if (r < 0) | |
266 | return r; | |
267 | } | |
f8eeeaf9 ZJS |
268 | } |
269 | ||
1cc6c93a YW |
270 | *acl_access = TAKE_PTR(a_acl); |
271 | *acl_default = TAKE_PTR(d_acl); | |
e738c945 | 272 | |
f8eeeaf9 ZJS |
273 | return 0; |
274 | } | |
50d9e46d | 275 | |
1c73f3bc ZJS |
276 | static int acl_entry_equal(acl_entry_t a, acl_entry_t b) { |
277 | acl_tag_t tag_a, tag_b; | |
278 | ||
279 | if (acl_get_tag_type(a, &tag_a) < 0) | |
280 | return -errno; | |
281 | ||
282 | if (acl_get_tag_type(b, &tag_b) < 0) | |
283 | return -errno; | |
284 | ||
285 | if (tag_a != tag_b) | |
286 | return false; | |
287 | ||
288 | switch (tag_a) { | |
289 | case ACL_USER_OBJ: | |
290 | case ACL_GROUP_OBJ: | |
291 | case ACL_MASK: | |
292 | case ACL_OTHER: | |
293 | /* can have only one of those */ | |
294 | return true; | |
295 | case ACL_USER: { | |
76dcbc49 | 296 | _cleanup_(acl_free_uid_tpp) uid_t *uid_a = NULL, *uid_b = NULL; |
1c73f3bc ZJS |
297 | |
298 | uid_a = acl_get_qualifier(a); | |
299 | if (!uid_a) | |
300 | return -errno; | |
301 | ||
302 | uid_b = acl_get_qualifier(b); | |
303 | if (!uid_b) | |
304 | return -errno; | |
305 | ||
306 | return *uid_a == *uid_b; | |
307 | } | |
308 | case ACL_GROUP: { | |
76dcbc49 | 309 | _cleanup_(acl_free_gid_tpp) gid_t *gid_a = NULL, *gid_b = NULL; |
1c73f3bc ZJS |
310 | |
311 | gid_a = acl_get_qualifier(a); | |
312 | if (!gid_a) | |
313 | return -errno; | |
314 | ||
315 | gid_b = acl_get_qualifier(b); | |
316 | if (!gid_b) | |
317 | return -errno; | |
318 | ||
319 | return *gid_a == *gid_b; | |
320 | } | |
321 | default: | |
04499a70 | 322 | assert_not_reached(); |
1c73f3bc ZJS |
323 | } |
324 | } | |
325 | ||
326 | static int find_acl_entry(acl_t acl, acl_entry_t entry, acl_entry_t *out) { | |
327 | acl_entry_t i; | |
328 | int r; | |
329 | ||
330 | for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); | |
331 | r > 0; | |
332 | r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { | |
333 | ||
334 | r = acl_entry_equal(i, entry); | |
335 | if (r < 0) | |
336 | return r; | |
337 | if (r > 0) { | |
338 | *out = i; | |
339 | return 1; | |
340 | } | |
341 | } | |
342 | if (r < 0) | |
343 | return -errno; | |
344 | return 0; | |
345 | } | |
346 | ||
50d9e46d ZJS |
347 | int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl) { |
348 | _cleanup_(acl_freep) acl_t old; | |
349 | acl_entry_t i; | |
dd4105b0 | 350 | int r; |
50d9e46d ZJS |
351 | |
352 | old = acl_get_file(path, type); | |
353 | if (!old) | |
354 | return -errno; | |
355 | ||
dd4105b0 ZJS |
356 | for (r = acl_get_entry(new, ACL_FIRST_ENTRY, &i); |
357 | r > 0; | |
358 | r = acl_get_entry(new, ACL_NEXT_ENTRY, &i)) { | |
50d9e46d ZJS |
359 | |
360 | acl_entry_t j; | |
361 | ||
1c73f3bc ZJS |
362 | r = find_acl_entry(old, i, &j); |
363 | if (r < 0) | |
364 | return r; | |
365 | if (r == 0) | |
366 | if (acl_create_entry(&old, &j) < 0) | |
367 | return -errno; | |
50d9e46d ZJS |
368 | |
369 | if (acl_copy_entry(j, i) < 0) | |
370 | return -errno; | |
371 | } | |
50d9e46d | 372 | if (r < 0) |
dd4105b0 | 373 | return -errno; |
50d9e46d | 374 | |
1cc6c93a YW |
375 | *acl = TAKE_PTR(old); |
376 | ||
50d9e46d ZJS |
377 | return 0; |
378 | } | |
5c3bde3f | 379 | |
567aeb58 ZJS |
380 | /* POSIX says that ACL_{READ,WRITE,EXECUTE} don't have to be bitmasks. But that is a natural thing to do and |
381 | * all extant implementations do it. Let's make sure that we fail verbosely in the (imho unlikely) scenario | |
382 | * that we get a new implementation that does not satisfy this. */ | |
383 | assert_cc(!(ACL_READ & ACL_WRITE)); | |
384 | assert_cc(!(ACL_WRITE & ACL_EXECUTE)); | |
385 | assert_cc(!(ACL_EXECUTE & ACL_READ)); | |
386 | assert_cc((unsigned) ACL_READ == ACL_READ); | |
387 | assert_cc((unsigned) ACL_WRITE == ACL_WRITE); | |
388 | assert_cc((unsigned) ACL_EXECUTE == ACL_EXECUTE); | |
389 | ||
9db59d92 LP |
390 | int fd_add_uid_acl_permission( |
391 | int fd, | |
392 | uid_t uid, | |
567aeb58 | 393 | unsigned mask) { |
9db59d92 | 394 | |
5c3bde3f | 395 | _cleanup_(acl_freep) acl_t acl = NULL; |
5c3bde3f | 396 | acl_permset_t permset; |
2ea6247e | 397 | acl_entry_t entry; |
5c3bde3f ZJS |
398 | int r; |
399 | ||
9db59d92 LP |
400 | /* Adds an ACL entry for the specified file to allow the indicated access to the specified |
401 | * user. Operates purely incrementally. */ | |
402 | ||
2ea6247e LP |
403 | assert(fd >= 0); |
404 | assert(uid_is_valid(uid)); | |
405 | ||
5c3bde3f ZJS |
406 | acl = acl_get_fd(fd); |
407 | if (!acl) | |
408 | return -errno; | |
409 | ||
410 | r = acl_find_uid(acl, uid, &entry); | |
411 | if (r <= 0) { | |
412 | if (acl_create_entry(&acl, &entry) < 0 || | |
413 | acl_set_tag_type(entry, ACL_USER) < 0 || | |
414 | acl_set_qualifier(entry, &uid) < 0) | |
415 | return -errno; | |
416 | } | |
417 | ||
9db59d92 LP |
418 | if (acl_get_permset(entry, &permset) < 0) |
419 | return -errno; | |
420 | ||
567aeb58 | 421 | if ((mask & ACL_READ) && acl_add_perm(permset, ACL_READ) < 0) |
9db59d92 | 422 | return -errno; |
567aeb58 | 423 | if ((mask & ACL_WRITE) && acl_add_perm(permset, ACL_WRITE) < 0) |
9db59d92 | 424 | return -errno; |
567aeb58 | 425 | if ((mask & ACL_EXECUTE) && acl_add_perm(permset, ACL_EXECUTE) < 0) |
5c3bde3f ZJS |
426 | return -errno; |
427 | ||
428 | r = calc_acl_mask_if_needed(&acl); | |
429 | if (r < 0) | |
430 | return r; | |
431 | ||
2ea6247e LP |
432 | if (acl_set_fd(fd, acl) < 0) |
433 | return -errno; | |
434 | ||
435 | return 0; | |
5c3bde3f | 436 | } |