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