]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/acl-util.c
tree-wide: use TAKE_PTR() and TAKE_FD() macros
[thirdparty/systemd.git] / src / shared / acl-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
f4b47811
LP
2/***
3 This file is part of systemd.
4
478c8269 5 Copyright 2011,2013 Lennart Poettering
f4b47811
LP
6
7 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
f4b47811
LP
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
5430f7f2 15 Lesser General Public License for more details.
f4b47811 16
5430f7f2 17 You should have received a copy of the GNU Lesser General Public License
f4b47811
LP
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
f4b47811
LP
21#include <errno.h>
22#include <stdbool.h>
23
b1d4f8e1 24#include "acl-util.h"
cf0fbc49 25#include "alloc-util.h"
07630cea 26#include "string-util.h"
478c8269 27#include "strv.h"
b1d4f8e1 28#include "user-util.h"
07630cea 29#include "util.h"
f4b47811
LP
30
31int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry) {
32 acl_entry_t i;
dd4105b0 33 int r;
f4b47811
LP
34
35 assert(acl);
36 assert(entry);
37
dd4105b0
ZJS
38 for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
39 r > 0;
40 r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
f4b47811
LP
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 }
dd4105b0 64 if (r < 0)
f4b47811
LP
65 return -errno;
66
67 return 0;
68}
478c8269 69
23ad4dd8
JAS
70int calc_acl_mask_if_needed(acl_t *acl_p) {
71 acl_entry_t i;
dd4105b0 72 int r;
6debb398 73 bool need = false;
23ad4dd8
JAS
74
75 assert(acl_p);
76
dd4105b0
ZJS
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)) {
23ad4dd8
JAS
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;
e346512c 87
6debb398
ZJS
88 if (IN_SET(tag, ACL_USER, ACL_GROUP))
89 need = true;
23ad4dd8 90 }
dd4105b0 91 if (r < 0)
23ad4dd8
JAS
92 return -errno;
93
6debb398
ZJS
94 if (need && acl_calc_mask(acl_p) < 0)
95 return -errno;
96
97 return need;
dd4105b0
ZJS
98}
99
100int 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;
23ad4dd8 128
dd4105b0
ZJS
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;
23ad4dd8
JAS
161 return 0;
162}
163
e346512c
LP
164int acl_search_groups(const char *path, char ***ret_groups) {
165 _cleanup_strv_free_ char **g = NULL;
29d87223 166 _cleanup_(acl_freep) acl_t acl = NULL;
e346512c
LP
167 bool ret = false;
168 acl_entry_t entry;
169 int r;
478c8269
ZJS
170
171 assert(path);
478c8269
ZJS
172
173 acl = acl_get_file(path, ACL_TYPE_DEFAULT);
e346512c
LP
174 if (!acl)
175 return -errno;
478c8269 176
e346512c
LP
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;
478c8269 181
e346512c
LP
182 if (r < 0)
183 return -errno;
184 if (r == 0)
185 break;
478c8269 186
e346512c
LP
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;
478c8269 196
e346512c
LP
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;
478c8269
ZJS
206
207 name = gid_to_name(*gid);
e346512c
LP
208 if (!name)
209 return -ENOMEM;
210
211 r = strv_consume(&g, name);
212 if (r < 0)
213 return r;
478c8269
ZJS
214 }
215
e346512c
LP
216 next:
217 r = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
478c8269
ZJS
218 }
219
1cc6c93a
YW
220 if (ret_groups)
221 *ret_groups = TAKE_PTR(g);
e346512c
LP
222
223 return ret;
478c8269 224}
f8eeeaf9 225
e738c945 226int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask) {
848f0178 227 _cleanup_free_ char **a = NULL, **d = NULL; /* strings are not freed */
f8eeeaf9
ZJS
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)
e738c945 235 return -ENOMEM;
f8eeeaf9
ZJS
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);
e738c945
LP
248 if (r < 0)
249 return r;
f8eeeaf9 250 }
f8eeeaf9
ZJS
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)
e738c945 261 return -errno;
f8eeeaf9 262
50d9e46d
ZJS
263 if (want_mask) {
264 r = calc_acl_mask_if_needed(&a_acl);
265 if (r < 0)
266 return r;
267 }
f8eeeaf9
ZJS
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)
e738c945 279 return -errno;
f8eeeaf9 280
50d9e46d
ZJS
281 if (want_mask) {
282 r = calc_acl_mask_if_needed(&d_acl);
283 if (r < 0)
284 return r;
285 }
f8eeeaf9
ZJS
286 }
287
1cc6c93a
YW
288 *acl_access = TAKE_PTR(a_acl);
289 *acl_default = TAKE_PTR(d_acl);
e738c945 290
f8eeeaf9
ZJS
291 return 0;
292}
50d9e46d 293
1c73f3bc
ZJS
294static 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: {
76dcbc49 314 _cleanup_(acl_free_uid_tpp) uid_t *uid_a = NULL, *uid_b = NULL;
1c73f3bc
ZJS
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: {
76dcbc49 327 _cleanup_(acl_free_gid_tpp) gid_t *gid_a = NULL, *gid_b = NULL;
1c73f3bc
ZJS
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
344static 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
50d9e46d
ZJS
365int 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;
dd4105b0 368 int r;
50d9e46d
ZJS
369
370 old = acl_get_file(path, type);
371 if (!old)
372 return -errno;
373
dd4105b0
ZJS
374 for (r = acl_get_entry(new, ACL_FIRST_ENTRY, &i);
375 r > 0;
376 r = acl_get_entry(new, ACL_NEXT_ENTRY, &i)) {
50d9e46d
ZJS
377
378 acl_entry_t j;
379
1c73f3bc
ZJS
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;
50d9e46d
ZJS
386
387 if (acl_copy_entry(j, i) < 0)
388 return -errno;
389 }
50d9e46d 390 if (r < 0)
dd4105b0 391 return -errno;
50d9e46d 392
1cc6c93a
YW
393 *acl = TAKE_PTR(old);
394
50d9e46d
ZJS
395 return 0;
396}
5c3bde3f
ZJS
397
398int 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}