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