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