]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/acl-util.c
resolved: TCP fast open connections
[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
f4b47811
LP
8#include <errno.h>
9#include <stdbool.h>
10
b1d4f8e1 11#include "acl-util.h"
cf0fbc49 12#include "alloc-util.h"
07630cea 13#include "string-util.h"
478c8269 14#include "strv.h"
b1d4f8e1 15#include "user-util.h"
07630cea 16#include "util.h"
f4b47811
LP
17
18int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry) {
19 acl_entry_t i;
dd4105b0 20 int r;
f4b47811
LP
21
22 assert(acl);
23 assert(entry);
24
dd4105b0
ZJS
25 for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
26 r > 0;
27 r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
f4b47811
LP
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 }
dd4105b0 51 if (r < 0)
f4b47811
LP
52 return -errno;
53
54 return 0;
55}
478c8269 56
23ad4dd8
JAS
57int calc_acl_mask_if_needed(acl_t *acl_p) {
58 acl_entry_t i;
dd4105b0 59 int r;
6debb398 60 bool need = false;
23ad4dd8
JAS
61
62 assert(acl_p);
63
dd4105b0
ZJS
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)) {
23ad4dd8
JAS
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;
e346512c 74
6debb398
ZJS
75 if (IN_SET(tag, ACL_USER, ACL_GROUP))
76 need = true;
23ad4dd8 77 }
dd4105b0 78 if (r < 0)
23ad4dd8
JAS
79 return -errno;
80
6debb398
ZJS
81 if (need && acl_calc_mask(acl_p) < 0)
82 return -errno;
83
84 return need;
dd4105b0
ZJS
85}
86
87int 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;
23ad4dd8 115
dd4105b0
ZJS
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;
23ad4dd8
JAS
148 return 0;
149}
150
e346512c
LP
151int acl_search_groups(const char *path, char ***ret_groups) {
152 _cleanup_strv_free_ char **g = NULL;
29d87223 153 _cleanup_(acl_freep) acl_t acl = NULL;
e346512c
LP
154 bool ret = false;
155 acl_entry_t entry;
156 int r;
478c8269
ZJS
157
158 assert(path);
478c8269
ZJS
159
160 acl = acl_get_file(path, ACL_TYPE_DEFAULT);
e346512c
LP
161 if (!acl)
162 return -errno;
478c8269 163
e346512c
LP
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;
478c8269 168
e346512c
LP
169 if (r < 0)
170 return -errno;
171 if (r == 0)
172 break;
478c8269 173
e346512c
LP
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;
478c8269 183
e346512c
LP
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;
478c8269
ZJS
193
194 name = gid_to_name(*gid);
e346512c
LP
195 if (!name)
196 return -ENOMEM;
197
198 r = strv_consume(&g, name);
199 if (r < 0)
200 return r;
478c8269
ZJS
201 }
202
e346512c
LP
203 next:
204 r = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
478c8269
ZJS
205 }
206
1cc6c93a
YW
207 if (ret_groups)
208 *ret_groups = TAKE_PTR(g);
e346512c
LP
209
210 return ret;
478c8269 211}
f8eeeaf9 212
e738c945 213int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask) {
848f0178 214 _cleanup_free_ char **a = NULL, **d = NULL; /* strings are not freed */
f8eeeaf9
ZJS
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)
e738c945 222 return -ENOMEM;
f8eeeaf9
ZJS
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);
e738c945
LP
235 if (r < 0)
236 return r;
f8eeeaf9 237 }
f8eeeaf9
ZJS
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)
e738c945 248 return -errno;
f8eeeaf9 249
50d9e46d
ZJS
250 if (want_mask) {
251 r = calc_acl_mask_if_needed(&a_acl);
252 if (r < 0)
253 return r;
254 }
f8eeeaf9
ZJS
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)
e738c945 266 return -errno;
f8eeeaf9 267
50d9e46d
ZJS
268 if (want_mask) {
269 r = calc_acl_mask_if_needed(&d_acl);
270 if (r < 0)
271 return r;
272 }
f8eeeaf9
ZJS
273 }
274
1cc6c93a
YW
275 *acl_access = TAKE_PTR(a_acl);
276 *acl_default = TAKE_PTR(d_acl);
e738c945 277
f8eeeaf9
ZJS
278 return 0;
279}
50d9e46d 280
1c73f3bc
ZJS
281static 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: {
76dcbc49 301 _cleanup_(acl_free_uid_tpp) uid_t *uid_a = NULL, *uid_b = NULL;
1c73f3bc
ZJS
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: {
76dcbc49 314 _cleanup_(acl_free_gid_tpp) gid_t *gid_a = NULL, *gid_b = NULL;
1c73f3bc
ZJS
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
331static 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
50d9e46d
ZJS
352int 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;
dd4105b0 355 int r;
50d9e46d
ZJS
356
357 old = acl_get_file(path, type);
358 if (!old)
359 return -errno;
360
dd4105b0
ZJS
361 for (r = acl_get_entry(new, ACL_FIRST_ENTRY, &i);
362 r > 0;
363 r = acl_get_entry(new, ACL_NEXT_ENTRY, &i)) {
50d9e46d
ZJS
364
365 acl_entry_t j;
366
1c73f3bc
ZJS
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;
50d9e46d
ZJS
373
374 if (acl_copy_entry(j, i) < 0)
375 return -errno;
376 }
50d9e46d 377 if (r < 0)
dd4105b0 378 return -errno;
50d9e46d 379
1cc6c93a
YW
380 *acl = TAKE_PTR(old);
381
50d9e46d
ZJS
382 return 0;
383}
5c3bde3f
ZJS
384
385int 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}