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