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