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