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