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