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