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