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