chiark / gitweb /
Fix service file to match installed elogind binary location
[elogind.git] / src / shared / acl-util.c
1 /***
2   This file is part of systemd.
3
4   Copyright 2011,2013 Lennart Poettering
5
6   systemd is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as published by
8   the Free Software Foundation; either version 2.1 of the License, or
9   (at your option) any later version.
10
11   systemd is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public License
17   along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <errno.h>
21 #include <stdbool.h>
22
23 #include "acl-util.h"
24 #include "alloc-util.h"
25 #include "string-util.h"
26 #include "strv.h"
27 #include "user-util.h"
28 #include "util.h"
29
30 int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry) {
31         acl_entry_t i;
32         int r;
33
34         assert(acl);
35         assert(entry);
36
37         for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
38              r > 0;
39              r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
40
41                 acl_tag_t tag;
42                 uid_t *u;
43                 bool b;
44
45                 if (acl_get_tag_type(i, &tag) < 0)
46                         return -errno;
47
48                 if (tag != ACL_USER)
49                         continue;
50
51                 u = acl_get_qualifier(i);
52                 if (!u)
53                         return -errno;
54
55                 b = *u == uid;
56                 acl_free(u);
57
58                 if (b) {
59                         *entry = i;
60                         return 1;
61                 }
62         }
63         if (r < 0)
64                 return -errno;
65
66         return 0;
67 }
68
69 #if 0 /// UNNEEDED by elogind
70 int calc_acl_mask_if_needed(acl_t *acl_p) {
71         acl_entry_t i;
72         int r;
73         bool need = false;
74
75         assert(acl_p);
76
77         for (r = acl_get_entry(*acl_p, ACL_FIRST_ENTRY, &i);
78              r > 0;
79              r = acl_get_entry(*acl_p, ACL_NEXT_ENTRY, &i)) {
80                 acl_tag_t tag;
81
82                 if (acl_get_tag_type(i, &tag) < 0)
83                         return -errno;
84
85                 if (tag == ACL_MASK)
86                         return 0;
87
88                 if (IN_SET(tag, ACL_USER, ACL_GROUP))
89                         need = true;
90         }
91         if (r < 0)
92                 return -errno;
93
94         if (need && acl_calc_mask(acl_p) < 0)
95                 return -errno;
96
97         return need;
98 }
99
100 int add_base_acls_if_needed(acl_t *acl_p, const char *path) {
101         acl_entry_t i;
102         int r;
103         bool have_user_obj = false, have_group_obj = false, have_other = false;
104         struct stat st;
105         _cleanup_(acl_freep) acl_t basic = NULL;
106
107         assert(acl_p);
108
109         for (r = acl_get_entry(*acl_p, ACL_FIRST_ENTRY, &i);
110              r > 0;
111              r = acl_get_entry(*acl_p, ACL_NEXT_ENTRY, &i)) {
112                 acl_tag_t tag;
113
114                 if (acl_get_tag_type(i, &tag) < 0)
115                         return -errno;
116
117                 if (tag == ACL_USER_OBJ)
118                         have_user_obj = true;
119                 else if (tag == ACL_GROUP_OBJ)
120                         have_group_obj = true;
121                 else if (tag == ACL_OTHER)
122                         have_other = true;
123                 if (have_user_obj && have_group_obj && have_other)
124                         return 0;
125         }
126         if (r < 0)
127                 return -errno;
128
129         r = stat(path, &st);
130         if (r < 0)
131                 return -errno;
132
133         basic = acl_from_mode(st.st_mode);
134         if (!basic)
135                 return -errno;
136
137         for (r = acl_get_entry(basic, ACL_FIRST_ENTRY, &i);
138              r > 0;
139              r = acl_get_entry(basic, ACL_NEXT_ENTRY, &i)) {
140                 acl_tag_t tag;
141                 acl_entry_t dst;
142
143                 if (acl_get_tag_type(i, &tag) < 0)
144                         return -errno;
145
146                 if ((tag == ACL_USER_OBJ && have_user_obj) ||
147                     (tag == ACL_GROUP_OBJ && have_group_obj) ||
148                     (tag == ACL_OTHER && have_other))
149                         continue;
150
151                 r = acl_create_entry(acl_p, &dst);
152                 if (r < 0)
153                         return -errno;
154
155                 r = acl_copy_entry(dst, i);
156                 if (r < 0)
157                         return -errno;
158         }
159         if (r < 0)
160                 return -errno;
161         return 0;
162 }
163
164 int acl_search_groups(const char *path, char ***ret_groups) {
165         _cleanup_strv_free_ char **g = NULL;
166         _cleanup_(acl_freep) acl_t acl = NULL;
167         bool ret = false;
168         acl_entry_t entry;
169         int r;
170
171         assert(path);
172
173         acl = acl_get_file(path, ACL_TYPE_DEFAULT);
174         if (!acl)
175                 return -errno;
176
177         r = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
178         for (;;) {
179                 _cleanup_(acl_free_gid_tpp) gid_t *gid = NULL;
180                 acl_tag_t tag;
181
182                 if (r < 0)
183                         return -errno;
184                 if (r == 0)
185                         break;
186
187                 if (acl_get_tag_type(entry, &tag) < 0)
188                         return -errno;
189
190                 if (tag != ACL_GROUP)
191                         goto next;
192
193                 gid = acl_get_qualifier(entry);
194                 if (!gid)
195                         return -errno;
196
197                 if (in_gid(*gid) > 0) {
198                         if (!ret_groups)
199                                 return true;
200
201                         ret = true;
202                 }
203
204                 if (ret_groups) {
205                         char *name;
206
207                         name = gid_to_name(*gid);
208                         if (!name)
209                                 return -ENOMEM;
210
211                         r = strv_consume(&g, name);
212                         if (r < 0)
213                                 return r;
214                 }
215
216         next:
217                 r = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
218         }
219
220         if (ret_groups) {
221                 *ret_groups = g;
222                 g = NULL;
223         }
224
225         return ret;
226 }
227
228 int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask) {
229         _cleanup_free_ char **a = NULL, **d = NULL; /* strings are not be freed */
230         _cleanup_strv_free_ char **split;
231         char **entry;
232         int r = -EINVAL;
233         _cleanup_(acl_freep) acl_t a_acl = NULL, d_acl = NULL;
234
235         split = strv_split(text, ",");
236         if (!split)
237                 return -ENOMEM;
238
239         STRV_FOREACH(entry, split) {
240                 char *p;
241
242                 p = startswith(*entry, "default:");
243                 if (!p)
244                         p = startswith(*entry, "d:");
245
246                 if (p)
247                         r = strv_push(&d, p);
248                 else
249                         r = strv_push(&a, *entry);
250                 if (r < 0)
251                         return r;
252         }
253
254         if (!strv_isempty(a)) {
255                 _cleanup_free_ char *join;
256
257                 join = strv_join(a, ",");
258                 if (!join)
259                         return -ENOMEM;
260
261                 a_acl = acl_from_text(join);
262                 if (!a_acl)
263                         return -errno;
264
265                 if (want_mask) {
266                         r = calc_acl_mask_if_needed(&a_acl);
267                         if (r < 0)
268                                 return r;
269                 }
270         }
271
272         if (!strv_isempty(d)) {
273                 _cleanup_free_ char *join;
274
275                 join = strv_join(d, ",");
276                 if (!join)
277                         return -ENOMEM;
278
279                 d_acl = acl_from_text(join);
280                 if (!d_acl)
281                         return -errno;
282
283                 if (want_mask) {
284                         r = calc_acl_mask_if_needed(&d_acl);
285                         if (r < 0)
286                                 return r;
287                 }
288         }
289
290         *acl_access = a_acl;
291         *acl_default = d_acl;
292         a_acl = d_acl = NULL;
293
294         return 0;
295 }
296
297 static int acl_entry_equal(acl_entry_t a, acl_entry_t b) {
298         acl_tag_t tag_a, tag_b;
299
300         if (acl_get_tag_type(a, &tag_a) < 0)
301                 return -errno;
302
303         if (acl_get_tag_type(b, &tag_b) < 0)
304                 return -errno;
305
306         if (tag_a != tag_b)
307                 return false;
308
309         switch (tag_a) {
310         case ACL_USER_OBJ:
311         case ACL_GROUP_OBJ:
312         case ACL_MASK:
313         case ACL_OTHER:
314                 /* can have only one of those */
315                 return true;
316         case ACL_USER: {
317                 _cleanup_(acl_free_uid_tpp) uid_t *uid_a = NULL, *uid_b = NULL;
318
319                 uid_a = acl_get_qualifier(a);
320                 if (!uid_a)
321                         return -errno;
322
323                 uid_b = acl_get_qualifier(b);
324                 if (!uid_b)
325                         return -errno;
326
327                 return *uid_a == *uid_b;
328         }
329         case ACL_GROUP: {
330                 _cleanup_(acl_free_gid_tpp) gid_t *gid_a = NULL, *gid_b = NULL;
331
332                 gid_a = acl_get_qualifier(a);
333                 if (!gid_a)
334                         return -errno;
335
336                 gid_b = acl_get_qualifier(b);
337                 if (!gid_b)
338                         return -errno;
339
340                 return *gid_a == *gid_b;
341         }
342         default:
343                 assert_not_reached("Unknown acl tag type");
344         }
345 }
346
347 static int find_acl_entry(acl_t acl, acl_entry_t entry, acl_entry_t *out) {
348         acl_entry_t i;
349         int r;
350
351         for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
352              r > 0;
353              r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
354
355                 r = acl_entry_equal(i, entry);
356                 if (r < 0)
357                         return r;
358                 if (r > 0) {
359                         *out = i;
360                         return 1;
361                 }
362         }
363         if (r < 0)
364                 return -errno;
365         return 0;
366 }
367
368 int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl) {
369         _cleanup_(acl_freep) acl_t old;
370         acl_entry_t i;
371         int r;
372
373         old = acl_get_file(path, type);
374         if (!old)
375                 return -errno;
376
377         for (r = acl_get_entry(new, ACL_FIRST_ENTRY, &i);
378              r > 0;
379              r = acl_get_entry(new, ACL_NEXT_ENTRY, &i)) {
380
381                 acl_entry_t j;
382
383                 r = find_acl_entry(old, i, &j);
384                 if (r < 0)
385                         return r;
386                 if (r == 0)
387                         if (acl_create_entry(&old, &j) < 0)
388                                 return -errno;
389
390                 if (acl_copy_entry(j, i) < 0)
391                         return -errno;
392         }
393         if (r < 0)
394                 return -errno;
395
396         *acl = old;
397         old = NULL;
398         return 0;
399 }
400
401 int add_acls_for_user(int fd, uid_t uid) {
402         _cleanup_(acl_freep) acl_t acl = NULL;
403         acl_entry_t entry;
404         acl_permset_t permset;
405         int r;
406
407         acl = acl_get_fd(fd);
408         if (!acl)
409                 return -errno;
410
411         r = acl_find_uid(acl, uid, &entry);
412         if (r <= 0) {
413                 if (acl_create_entry(&acl, &entry) < 0 ||
414                     acl_set_tag_type(entry, ACL_USER) < 0 ||
415                     acl_set_qualifier(entry, &uid) < 0)
416                         return -errno;
417         }
418
419         /* We do not recalculate the mask unconditionally here,
420          * so that the fchmod() mask above stays intact. */
421         if (acl_get_permset(entry, &permset) < 0 ||
422             acl_add_perm(permset, ACL_READ) < 0)
423                 return -errno;
424
425         r = calc_acl_mask_if_needed(&acl);
426         if (r < 0)
427                 return r;
428
429         return acl_set_fd(fd, acl);
430 }
431 #endif // 0