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