chiark / gitweb /
tmpfiles: implement augmenting of existing ACLs
[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 <assert.h>
23 #include <errno.h>
24 #include <stdbool.h>
25
26 #include "acl-util.h"
27 #include "util.h"
28 #include "strv.h"
29
30 int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry) {
31         acl_entry_t i;
32         int found;
33
34         assert(acl);
35         assert(entry);
36
37         for (found = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
38              found > 0;
39              found = 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
64         if (found < 0)
65                 return -errno;
66
67         return 0;
68 }
69
70 int calc_acl_mask_if_needed(acl_t *acl_p) {
71         acl_entry_t i;
72         int found;
73
74         assert(acl_p);
75
76         for (found = acl_get_entry(*acl_p, ACL_FIRST_ENTRY, &i);
77              found > 0;
78              found = acl_get_entry(*acl_p, ACL_NEXT_ENTRY, &i)) {
79
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
89         if (found < 0)
90                 return -errno;
91
92         if (acl_calc_mask(acl_p) < 0)
93                 return -errno;
94
95         return 0;
96 }
97
98 int search_acl_groups(char*** dst, const char* path, bool* belong) {
99         acl_t acl;
100
101         assert(path);
102         assert(belong);
103
104         acl = acl_get_file(path, ACL_TYPE_DEFAULT);
105         if (acl) {
106                 acl_entry_t entry;
107                 int r;
108
109                 r = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
110                 while (r > 0) {
111                         acl_tag_t tag;
112                         gid_t *gid;
113                         char *name;
114
115                         r = acl_get_tag_type(entry, &tag);
116                         if (r < 0)
117                                 break;
118
119                         if (tag != ACL_GROUP)
120                                 goto next;
121
122                         gid = acl_get_qualifier(entry);
123                         if (!gid)
124                                 break;
125
126                         if (in_gid(*gid) > 0) {
127                                 *belong = true;
128                                 break;
129                         }
130
131                         name = gid_to_name(*gid);
132                         if (!name) {
133                                 acl_free(acl);
134                                 return log_oom();
135                         }
136
137                         r = strv_consume(dst, name);
138                         if (r < 0) {
139                                 acl_free(acl);
140                                 return log_oom();
141                         }
142
143                 next:
144                         r = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
145                 }
146
147                 acl_free(acl);
148         }
149
150         return 0;
151 }
152
153 int parse_acl(char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask) {
154         _cleanup_free_ char **a = NULL, **d = NULL; /* strings are not be freed */
155         _cleanup_strv_free_ char **split;
156         char **entry;
157         int r = -EINVAL;
158         _cleanup_(acl_freep) acl_t a_acl = NULL, d_acl = NULL;
159
160         split = strv_split(text, ",");
161         if (!split)
162                 return log_oom();
163
164         STRV_FOREACH(entry, split) {
165                 char *p;
166
167                 p = startswith(*entry, "default:");
168                 if (!p)
169                         p = startswith(*entry, "d:");
170
171                 if (p)
172                         r = strv_push(&d, p);
173                 else
174                         r = strv_push(&a, *entry);
175         }
176         if (r < 0)
177                 return r;
178
179         if (!strv_isempty(a)) {
180                 _cleanup_free_ char *join;
181
182                 join = strv_join(a, ",");
183                 if (!join)
184                         return -ENOMEM;
185
186                 a_acl = acl_from_text(join);
187                 if (!a_acl)
188                         return -EINVAL;
189
190                 if (want_mask) {
191                         r = calc_acl_mask_if_needed(&a_acl);
192                         if (r < 0)
193                                 return r;
194                 }
195         }
196
197         if (!strv_isempty(d)) {
198                 _cleanup_free_ char *join;
199
200                 join = strv_join(d, ",");
201                 if (!join)
202                         return -ENOMEM;
203
204                 d_acl = acl_from_text(join);
205                 if (!d_acl)
206                         return -EINVAL;
207
208                 if (want_mask) {
209                         r = calc_acl_mask_if_needed(&d_acl);
210                         if (r < 0)
211                                 return r;
212                 }
213         }
214
215         *acl_access = a_acl;
216         *acl_default = d_acl;
217         a_acl = d_acl = NULL;
218         return 0;
219 }
220
221 int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl) {
222         _cleanup_(acl_freep) acl_t old;
223         acl_entry_t i;
224         int found, r;
225
226         old = acl_get_file(path, type);
227         if (!old)
228                 return -errno;
229
230         for (found = acl_get_entry(new, ACL_FIRST_ENTRY, &i);
231              found > 0;
232              found = acl_get_entry(new, ACL_NEXT_ENTRY, &i)) {
233
234                 acl_entry_t j;
235
236                 if (acl_create_entry(&old, &j) < 0)
237                         return -errno;
238
239                 if (acl_copy_entry(j, i) < 0)
240                         return -errno;
241         }
242
243         r = calc_acl_mask_if_needed(&old);
244         if (r < 0)
245                 return r;
246
247         *acl = old;
248         old = NULL;
249         return 0;
250 }