chiark / gitweb /
json: fix a mem leak
[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(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 log_oom();
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         }
249         if (r < 0)
250                 return r;
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 -EINVAL;
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 -EINVAL;
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         return 0;
292 }
293
294 static int acl_entry_equal(acl_entry_t a, acl_entry_t b) {
295         acl_tag_t tag_a, tag_b;
296
297         if (acl_get_tag_type(a, &tag_a) < 0)
298                 return -errno;
299
300         if (acl_get_tag_type(b, &tag_b) < 0)
301                 return -errno;
302
303         if (tag_a != tag_b)
304                 return false;
305
306         switch (tag_a) {
307         case ACL_USER_OBJ:
308         case ACL_GROUP_OBJ:
309         case ACL_MASK:
310         case ACL_OTHER:
311                 /* can have only one of those */
312                 return true;
313         case ACL_USER: {
314                 _cleanup_(acl_free_uid_tpp) uid_t *uid_a = NULL, *uid_b = NULL;
315
316                 uid_a = acl_get_qualifier(a);
317                 if (!uid_a)
318                         return -errno;
319
320                 uid_b = acl_get_qualifier(b);
321                 if (!uid_b)
322                         return -errno;
323
324                 return *uid_a == *uid_b;
325         }
326         case ACL_GROUP: {
327                 _cleanup_(acl_free_gid_tpp) gid_t *gid_a = NULL, *gid_b = NULL;
328
329                 gid_a = acl_get_qualifier(a);
330                 if (!gid_a)
331                         return -errno;
332
333                 gid_b = acl_get_qualifier(b);
334                 if (!gid_b)
335                         return -errno;
336
337                 return *gid_a == *gid_b;
338         }
339         default:
340                 assert_not_reached("Unknown acl tag type");
341         }
342 }
343
344 static int find_acl_entry(acl_t acl, acl_entry_t entry, acl_entry_t *out) {
345         acl_entry_t i;
346         int r;
347
348         for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
349              r > 0;
350              r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
351
352                 r = acl_entry_equal(i, entry);
353                 if (r < 0)
354                         return r;
355                 if (r > 0) {
356                         *out = i;
357                         return 1;
358                 }
359         }
360         if (r < 0)
361                 return -errno;
362         return 0;
363 }
364
365 int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl) {
366         _cleanup_(acl_freep) acl_t old;
367         acl_entry_t i;
368         int r;
369
370         old = acl_get_file(path, type);
371         if (!old)
372                 return -errno;
373
374         for (r = acl_get_entry(new, ACL_FIRST_ENTRY, &i);
375              r > 0;
376              r = acl_get_entry(new, ACL_NEXT_ENTRY, &i)) {
377
378                 acl_entry_t j;
379
380                 r = find_acl_entry(old, i, &j);
381                 if (r < 0)
382                         return r;
383                 if (r == 0)
384                         if (acl_create_entry(&old, &j) < 0)
385                                 return -errno;
386
387                 if (acl_copy_entry(j, i) < 0)
388                         return -errno;
389         }
390         if (r < 0)
391                 return -errno;
392
393         *acl = old;
394         old = NULL;
395         return 0;
396 }