chiark / gitweb /
44076d04c063509246f7ac9137db1c4d721db835
[elogind.git] / src / shared / acl-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2011,2013 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <stdbool.h>
24
25 #include "acl-util.h"
26 #include "util.h"
27 #include "strv.h"
28
29 int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry) {
30         acl_entry_t i;
31         int r;
32
33         assert(acl);
34         assert(entry);
35
36         for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
37              r > 0;
38              r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
39
40                 acl_tag_t tag;
41                 uid_t *u;
42                 bool b;
43
44                 if (acl_get_tag_type(i, &tag) < 0)
45                         return -errno;
46
47                 if (tag != ACL_USER)
48                         continue;
49
50                 u = acl_get_qualifier(i);
51                 if (!u)
52                         return -errno;
53
54                 b = *u == uid;
55                 acl_free(u);
56
57                 if (b) {
58                         *entry = i;
59                         return 1;
60                 }
61         }
62         if (r < 0)
63                 return -errno;
64
65         return 0;
66 }
67
68 /// UNNEEDED by elogind
69 #if 0
70 int calc_acl_mask_if_needed(acl_t *acl_p) {
71         acl_entry_t i;
72         int r;
73
74         assert(acl_p);
75
76         for (r = acl_get_entry(*acl_p, ACL_FIRST_ENTRY, &i);
77              r > 0;
78              r = acl_get_entry(*acl_p, ACL_NEXT_ENTRY, &i)) {
79                 acl_tag_t tag;
80
81                 if (acl_get_tag_type(i, &tag) < 0)
82                         return -errno;
83
84                 if (tag == ACL_MASK)
85                         return 0;
86
87                 if (IN_SET(tag, ACL_USER, ACL_GROUP)) {
88                         if (acl_calc_mask(acl_p) < 0)
89                                 return -errno;
90
91                         return 1;
92                 }
93         }
94         if (r < 0)
95                 return -errno;
96
97         return 0;
98 }
99
100 int 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;
128
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;
161         return 0;
162 }
163
164 int acl_search_groups(const char *path, char ***ret_groups) {
165         _cleanup_strv_free_ char **g = NULL;
166         _cleanup_(acl_free) acl_t acl = NULL;
167         bool ret = false;
168         acl_entry_t entry;
169         int r;
170
171         assert(path);
172
173         acl = acl_get_file(path, ACL_TYPE_DEFAULT);
174         if (!acl)
175                 return -errno;
176
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;
181
182                 if (r < 0)
183                         return -errno;
184                 if (r == 0)
185                         break;
186
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;
196
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;
206
207                         name = gid_to_name(*gid);
208                         if (!name)
209                                 return -ENOMEM;
210
211                         r = strv_consume(&g, name);
212                         if (r < 0)
213                                 return r;
214                 }
215
216         next:
217                 r = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
218         }
219
220         if (ret_groups) {
221                 *ret_groups = g;
222                 g = NULL;
223         }
224
225         return ret;
226 }
227
228 int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask) {
229         _cleanup_free_ char **a = NULL, **d = NULL; /* strings are not be freed */
230         _cleanup_strv_free_ char **split;
231         char **entry;
232         int r = -EINVAL;
233         _cleanup_(acl_freep) acl_t a_acl = NULL, d_acl = NULL;
234
235         split = strv_split(text, ",");
236         if (!split)
237                 return -ENOMEM;
238
239         STRV_FOREACH(entry, split) {
240                 char *p;
241
242                 p = startswith(*entry, "default:");
243                 if (!p)
244                         p = startswith(*entry, "d:");
245
246                 if (p)
247                         r = strv_push(&d, p);
248                 else
249                         r = strv_push(&a, *entry);
250                 if (r < 0)
251                         return r;
252         }
253
254         if (!strv_isempty(a)) {
255                 _cleanup_free_ char *join;
256
257                 join = strv_join(a, ",");
258                 if (!join)
259                         return -ENOMEM;
260
261                 a_acl = acl_from_text(join);
262                 if (!a_acl)
263                         return -errno;
264
265                 if (want_mask) {
266                         r = calc_acl_mask_if_needed(&a_acl);
267                         if (r < 0)
268                                 return r;
269                 }
270         }
271
272         if (!strv_isempty(d)) {
273                 _cleanup_free_ char *join;
274
275                 join = strv_join(d, ",");
276                 if (!join)
277                         return -ENOMEM;
278
279                 d_acl = acl_from_text(join);
280                 if (!d_acl)
281                         return -errno;
282
283                 if (want_mask) {
284                         r = calc_acl_mask_if_needed(&d_acl);
285                         if (r < 0)
286                                 return r;
287                 }
288         }
289
290         *acl_access = a_acl;
291         *acl_default = d_acl;
292         a_acl = d_acl = NULL;
293
294         return 0;
295 }
296
297 static int acl_entry_equal(acl_entry_t a, acl_entry_t b) {
298         acl_tag_t tag_a, tag_b;
299
300         if (acl_get_tag_type(a, &tag_a) < 0)
301                 return -errno;
302
303         if (acl_get_tag_type(b, &tag_b) < 0)
304                 return -errno;
305
306         if (tag_a != tag_b)
307                 return false;
308
309         switch (tag_a) {
310         case ACL_USER_OBJ:
311         case ACL_GROUP_OBJ:
312         case ACL_MASK:
313         case ACL_OTHER:
314                 /* can have only one of those */
315                 return true;
316         case ACL_USER: {
317                 _cleanup_(acl_free_uid_tpp) uid_t *uid_a = NULL, *uid_b = NULL;
318
319                 uid_a = acl_get_qualifier(a);
320                 if (!uid_a)
321                         return -errno;
322
323                 uid_b = acl_get_qualifier(b);
324                 if (!uid_b)
325                         return -errno;
326
327                 return *uid_a == *uid_b;
328         }
329         case ACL_GROUP: {
330                 _cleanup_(acl_free_gid_tpp) gid_t *gid_a = NULL, *gid_b = NULL;
331
332                 gid_a = acl_get_qualifier(a);
333                 if (!gid_a)
334                         return -errno;
335
336                 gid_b = acl_get_qualifier(b);
337                 if (!gid_b)
338                         return -errno;
339
340                 return *gid_a == *gid_b;
341         }
342         default:
343                 assert_not_reached("Unknown acl tag type");
344         }
345 }
346
347 static int find_acl_entry(acl_t acl, acl_entry_t entry, acl_entry_t *out) {
348         acl_entry_t i;
349         int r;
350
351         for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
352              r > 0;
353              r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
354
355                 r = acl_entry_equal(i, entry);
356                 if (r < 0)
357                         return r;
358                 if (r > 0) {
359                         *out = i;
360                         return 1;
361                 }
362         }
363         if (r < 0)
364                 return -errno;
365         return 0;
366 }
367
368 int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl) {
369         _cleanup_(acl_freep) acl_t old;
370         acl_entry_t i;
371         int r;
372
373         old = acl_get_file(path, type);
374         if (!old)
375                 return -errno;
376
377         for (r = acl_get_entry(new, ACL_FIRST_ENTRY, &i);
378              r > 0;
379              r = acl_get_entry(new, ACL_NEXT_ENTRY, &i)) {
380
381                 acl_entry_t j;
382
383                 r = find_acl_entry(old, i, &j);
384                 if (r < 0)
385                         return r;
386                 if (r == 0)
387                         if (acl_create_entry(&old, &j) < 0)
388                                 return -errno;
389
390                 if (acl_copy_entry(j, i) < 0)
391                         return -errno;
392         }
393         if (r < 0)
394                 return -errno;
395
396         *acl = old;
397         old = NULL;
398         return 0;
399 }
400 #endif // 0