]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/acl-util.c
Merge pull request #1957 from zonque/qemu-test
[thirdparty/systemd.git] / src / shared / acl-util.c
CommitLineData
f4b47811
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
478c8269 6 Copyright 2011,2013 Lennart Poettering
f4b47811
LP
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
f4b47811
LP
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
5430f7f2 16 Lesser General Public License for more details.
f4b47811 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
f4b47811
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
f4b47811
LP
22#include <errno.h>
23#include <stdbool.h>
24
b1d4f8e1 25#include "acl-util.h"
cf0fbc49 26#include "alloc-util.h"
07630cea 27#include "string-util.h"
478c8269 28#include "strv.h"
b1d4f8e1 29#include "user-util.h"
07630cea 30#include "util.h"
f4b47811
LP
31
32int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry) {
33 acl_entry_t i;
dd4105b0 34 int r;
f4b47811
LP
35
36 assert(acl);
37 assert(entry);
38
dd4105b0
ZJS
39 for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
40 r > 0;
41 r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
f4b47811
LP
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 }
dd4105b0 65 if (r < 0)
f4b47811
LP
66 return -errno;
67
68 return 0;
69}
478c8269 70
23ad4dd8
JAS
71int calc_acl_mask_if_needed(acl_t *acl_p) {
72 acl_entry_t i;
dd4105b0 73 int r;
23ad4dd8
JAS
74
75 assert(acl_p);
76
dd4105b0
ZJS
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)) {
23ad4dd8
JAS
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;
e346512c
LP
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 }
23ad4dd8 94 }
dd4105b0 95 if (r < 0)
23ad4dd8
JAS
96 return -errno;
97
e346512c 98 return 0;
dd4105b0
ZJS
99}
100
101int 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;
23ad4dd8 129
dd4105b0
ZJS
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;
23ad4dd8
JAS
162 return 0;
163}
164
e346512c
LP
165int 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;
478c8269
ZJS
171
172 assert(path);
478c8269
ZJS
173
174 acl = acl_get_file(path, ACL_TYPE_DEFAULT);
e346512c
LP
175 if (!acl)
176 return -errno;
478c8269 177
e346512c
LP
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;
478c8269 182
e346512c
LP
183 if (r < 0)
184 return -errno;
185 if (r == 0)
186 break;
478c8269 187
e346512c
LP
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;
478c8269 197
e346512c
LP
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;
478c8269
ZJS
207
208 name = gid_to_name(*gid);
e346512c
LP
209 if (!name)
210 return -ENOMEM;
211
212 r = strv_consume(&g, name);
213 if (r < 0)
214 return r;
478c8269
ZJS
215 }
216
e346512c
LP
217 next:
218 r = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
478c8269
ZJS
219 }
220
e346512c
LP
221 if (ret_groups) {
222 *ret_groups = g;
223 g = NULL;
224 }
225
226 return ret;
478c8269 227}
f8eeeaf9 228
e738c945 229int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask) {
f8eeeaf9
ZJS
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)
e738c945 238 return -ENOMEM;
f8eeeaf9
ZJS
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);
e738c945
LP
251 if (r < 0)
252 return r;
f8eeeaf9 253 }
f8eeeaf9
ZJS
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)
e738c945 264 return -errno;
f8eeeaf9 265
50d9e46d
ZJS
266 if (want_mask) {
267 r = calc_acl_mask_if_needed(&a_acl);
268 if (r < 0)
269 return r;
270 }
f8eeeaf9
ZJS
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)
e738c945 282 return -errno;
f8eeeaf9 283
50d9e46d
ZJS
284 if (want_mask) {
285 r = calc_acl_mask_if_needed(&d_acl);
286 if (r < 0)
287 return r;
288 }
f8eeeaf9
ZJS
289 }
290
291 *acl_access = a_acl;
292 *acl_default = d_acl;
293 a_acl = d_acl = NULL;
e738c945 294
f8eeeaf9
ZJS
295 return 0;
296}
50d9e46d 297
1c73f3bc
ZJS
298static 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: {
76dcbc49 318 _cleanup_(acl_free_uid_tpp) uid_t *uid_a = NULL, *uid_b = NULL;
1c73f3bc
ZJS
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: {
76dcbc49 331 _cleanup_(acl_free_gid_tpp) gid_t *gid_a = NULL, *gid_b = NULL;
1c73f3bc
ZJS
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
348static 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
50d9e46d
ZJS
369int 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;
dd4105b0 372 int r;
50d9e46d
ZJS
373
374 old = acl_get_file(path, type);
375 if (!old)
376 return -errno;
377
dd4105b0
ZJS
378 for (r = acl_get_entry(new, ACL_FIRST_ENTRY, &i);
379 r > 0;
380 r = acl_get_entry(new, ACL_NEXT_ENTRY, &i)) {
50d9e46d
ZJS
381
382 acl_entry_t j;
383
1c73f3bc
ZJS
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;
50d9e46d
ZJS
390
391 if (acl_copy_entry(j, i) < 0)
392 return -errno;
393 }
50d9e46d 394 if (r < 0)
dd4105b0 395 return -errno;
50d9e46d
ZJS
396
397 *acl = old;
398 old = NULL;
399 return 0;
400}