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