]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/acl-util.c
tree-wide: drop 'This file is part of systemd' blurb
[thirdparty/systemd.git] / src / shared / acl-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 Copyright 2011,2013 Lennart Poettering
4 ***/
5
6 #include <errno.h>
7 #include <stdbool.h>
8
9 #include "acl-util.h"
10 #include "alloc-util.h"
11 #include "string-util.h"
12 #include "strv.h"
13 #include "user-util.h"
14 #include "util.h"
15
16 int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry) {
17 acl_entry_t i;
18 int r;
19
20 assert(acl);
21 assert(entry);
22
23 for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
24 r > 0;
25 r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
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) {
45 *entry = i;
46 return 1;
47 }
48 }
49 if (r < 0)
50 return -errno;
51
52 return 0;
53 }
54
55 int calc_acl_mask_if_needed(acl_t *acl_p) {
56 acl_entry_t i;
57 int r;
58 bool need = false;
59
60 assert(acl_p);
61
62 for (r = acl_get_entry(*acl_p, ACL_FIRST_ENTRY, &i);
63 r > 0;
64 r = acl_get_entry(*acl_p, ACL_NEXT_ENTRY, &i)) {
65 acl_tag_t tag;
66
67 if (acl_get_tag_type(i, &tag) < 0)
68 return -errno;
69
70 if (tag == ACL_MASK)
71 return 0;
72
73 if (IN_SET(tag, ACL_USER, ACL_GROUP))
74 need = true;
75 }
76 if (r < 0)
77 return -errno;
78
79 if (need && acl_calc_mask(acl_p) < 0)
80 return -errno;
81
82 return need;
83 }
84
85 int add_base_acls_if_needed(acl_t *acl_p, const char *path) {
86 acl_entry_t i;
87 int r;
88 bool have_user_obj = false, have_group_obj = false, have_other = false;
89 struct stat st;
90 _cleanup_(acl_freep) acl_t basic = NULL;
91
92 assert(acl_p);
93
94 for (r = acl_get_entry(*acl_p, ACL_FIRST_ENTRY, &i);
95 r > 0;
96 r = acl_get_entry(*acl_p, ACL_NEXT_ENTRY, &i)) {
97 acl_tag_t tag;
98
99 if (acl_get_tag_type(i, &tag) < 0)
100 return -errno;
101
102 if (tag == ACL_USER_OBJ)
103 have_user_obj = true;
104 else if (tag == ACL_GROUP_OBJ)
105 have_group_obj = true;
106 else if (tag == ACL_OTHER)
107 have_other = true;
108 if (have_user_obj && have_group_obj && have_other)
109 return 0;
110 }
111 if (r < 0)
112 return -errno;
113
114 r = stat(path, &st);
115 if (r < 0)
116 return -errno;
117
118 basic = acl_from_mode(st.st_mode);
119 if (!basic)
120 return -errno;
121
122 for (r = acl_get_entry(basic, ACL_FIRST_ENTRY, &i);
123 r > 0;
124 r = acl_get_entry(basic, ACL_NEXT_ENTRY, &i)) {
125 acl_tag_t tag;
126 acl_entry_t dst;
127
128 if (acl_get_tag_type(i, &tag) < 0)
129 return -errno;
130
131 if ((tag == ACL_USER_OBJ && have_user_obj) ||
132 (tag == ACL_GROUP_OBJ && have_group_obj) ||
133 (tag == ACL_OTHER && have_other))
134 continue;
135
136 r = acl_create_entry(acl_p, &dst);
137 if (r < 0)
138 return -errno;
139
140 r = acl_copy_entry(dst, i);
141 if (r < 0)
142 return -errno;
143 }
144 if (r < 0)
145 return -errno;
146 return 0;
147 }
148
149 int acl_search_groups(const char *path, char ***ret_groups) {
150 _cleanup_strv_free_ char **g = NULL;
151 _cleanup_(acl_freep) acl_t acl = NULL;
152 bool ret = false;
153 acl_entry_t entry;
154 int r;
155
156 assert(path);
157
158 acl = acl_get_file(path, ACL_TYPE_DEFAULT);
159 if (!acl)
160 return -errno;
161
162 r = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
163 for (;;) {
164 _cleanup_(acl_free_gid_tpp) gid_t *gid = NULL;
165 acl_tag_t tag;
166
167 if (r < 0)
168 return -errno;
169 if (r == 0)
170 break;
171
172 if (acl_get_tag_type(entry, &tag) < 0)
173 return -errno;
174
175 if (tag != ACL_GROUP)
176 goto next;
177
178 gid = acl_get_qualifier(entry);
179 if (!gid)
180 return -errno;
181
182 if (in_gid(*gid) > 0) {
183 if (!ret_groups)
184 return true;
185
186 ret = true;
187 }
188
189 if (ret_groups) {
190 char *name;
191
192 name = gid_to_name(*gid);
193 if (!name)
194 return -ENOMEM;
195
196 r = strv_consume(&g, name);
197 if (r < 0)
198 return r;
199 }
200
201 next:
202 r = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
203 }
204
205 if (ret_groups)
206 *ret_groups = TAKE_PTR(g);
207
208 return ret;
209 }
210
211 int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask) {
212 _cleanup_free_ char **a = NULL, **d = NULL; /* strings are not freed */
213 _cleanup_strv_free_ char **split;
214 char **entry;
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)
220 return -ENOMEM;
221
222 STRV_FOREACH(entry, split) {
223 char *p;
224
225 p = startswith(*entry, "default:");
226 if (!p)
227 p = startswith(*entry, "d:");
228
229 if (p)
230 r = strv_push(&d, p);
231 else
232 r = strv_push(&a, *entry);
233 if (r < 0)
234 return r;
235 }
236
237 if (!strv_isempty(a)) {
238 _cleanup_free_ char *join;
239
240 join = strv_join(a, ",");
241 if (!join)
242 return -ENOMEM;
243
244 a_acl = acl_from_text(join);
245 if (!a_acl)
246 return -errno;
247
248 if (want_mask) {
249 r = calc_acl_mask_if_needed(&a_acl);
250 if (r < 0)
251 return r;
252 }
253 }
254
255 if (!strv_isempty(d)) {
256 _cleanup_free_ char *join;
257
258 join = strv_join(d, ",");
259 if (!join)
260 return -ENOMEM;
261
262 d_acl = acl_from_text(join);
263 if (!d_acl)
264 return -errno;
265
266 if (want_mask) {
267 r = calc_acl_mask_if_needed(&d_acl);
268 if (r < 0)
269 return r;
270 }
271 }
272
273 *acl_access = TAKE_PTR(a_acl);
274 *acl_default = TAKE_PTR(d_acl);
275
276 return 0;
277 }
278
279 static int acl_entry_equal(acl_entry_t a, acl_entry_t b) {
280 acl_tag_t tag_a, tag_b;
281
282 if (acl_get_tag_type(a, &tag_a) < 0)
283 return -errno;
284
285 if (acl_get_tag_type(b, &tag_b) < 0)
286 return -errno;
287
288 if (tag_a != tag_b)
289 return false;
290
291 switch (tag_a) {
292 case ACL_USER_OBJ:
293 case ACL_GROUP_OBJ:
294 case ACL_MASK:
295 case ACL_OTHER:
296 /* can have only one of those */
297 return true;
298 case ACL_USER: {
299 _cleanup_(acl_free_uid_tpp) uid_t *uid_a = NULL, *uid_b = NULL;
300
301 uid_a = acl_get_qualifier(a);
302 if (!uid_a)
303 return -errno;
304
305 uid_b = acl_get_qualifier(b);
306 if (!uid_b)
307 return -errno;
308
309 return *uid_a == *uid_b;
310 }
311 case ACL_GROUP: {
312 _cleanup_(acl_free_gid_tpp) gid_t *gid_a = NULL, *gid_b = NULL;
313
314 gid_a = acl_get_qualifier(a);
315 if (!gid_a)
316 return -errno;
317
318 gid_b = acl_get_qualifier(b);
319 if (!gid_b)
320 return -errno;
321
322 return *gid_a == *gid_b;
323 }
324 default:
325 assert_not_reached("Unknown acl tag type");
326 }
327 }
328
329 static int find_acl_entry(acl_t acl, acl_entry_t entry, acl_entry_t *out) {
330 acl_entry_t i;
331 int r;
332
333 for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
334 r > 0;
335 r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
336
337 r = acl_entry_equal(i, entry);
338 if (r < 0)
339 return r;
340 if (r > 0) {
341 *out = i;
342 return 1;
343 }
344 }
345 if (r < 0)
346 return -errno;
347 return 0;
348 }
349
350 int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl) {
351 _cleanup_(acl_freep) acl_t old;
352 acl_entry_t i;
353 int r;
354
355 old = acl_get_file(path, type);
356 if (!old)
357 return -errno;
358
359 for (r = acl_get_entry(new, ACL_FIRST_ENTRY, &i);
360 r > 0;
361 r = acl_get_entry(new, ACL_NEXT_ENTRY, &i)) {
362
363 acl_entry_t j;
364
365 r = find_acl_entry(old, i, &j);
366 if (r < 0)
367 return r;
368 if (r == 0)
369 if (acl_create_entry(&old, &j) < 0)
370 return -errno;
371
372 if (acl_copy_entry(j, i) < 0)
373 return -errno;
374 }
375 if (r < 0)
376 return -errno;
377
378 *acl = TAKE_PTR(old);
379
380 return 0;
381 }
382
383 int add_acls_for_user(int fd, uid_t uid) {
384 _cleanup_(acl_freep) acl_t acl = NULL;
385 acl_entry_t entry;
386 acl_permset_t permset;
387 int r;
388
389 acl = acl_get_fd(fd);
390 if (!acl)
391 return -errno;
392
393 r = acl_find_uid(acl, uid, &entry);
394 if (r <= 0) {
395 if (acl_create_entry(&acl, &entry) < 0 ||
396 acl_set_tag_type(entry, ACL_USER) < 0 ||
397 acl_set_qualifier(entry, &uid) < 0)
398 return -errno;
399 }
400
401 /* We do not recalculate the mask unconditionally here,
402 * so that the fchmod() mask above stays intact. */
403 if (acl_get_permset(entry, &permset) < 0 ||
404 acl_add_perm(permset, ACL_READ) < 0)
405 return -errno;
406
407 r = calc_acl_mask_if_needed(&acl);
408 if (r < 0)
409 return r;
410
411 return acl_set_fd(fd, acl);
412 }