]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/acl-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
9 #include "alloc-util.h"
10 #include "string-util.h"
12 #include "user-util.h"
15 int acl_find_uid(acl_t acl
, uid_t uid
, acl_entry_t
*ret_entry
) {
20 assert(uid_is_valid(uid
));
23 for (r
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &i
);
25 r
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &i
)) {
31 if (acl_get_tag_type(i
, &tag
) < 0)
37 u
= acl_get_qualifier(i
);
56 int calc_acl_mask_if_needed(acl_t
*acl_p
) {
63 for (r
= acl_get_entry(*acl_p
, ACL_FIRST_ENTRY
, &i
);
65 r
= acl_get_entry(*acl_p
, ACL_NEXT_ENTRY
, &i
)) {
68 if (acl_get_tag_type(i
, &tag
) < 0)
74 if (IN_SET(tag
, ACL_USER
, ACL_GROUP
))
80 if (need
&& acl_calc_mask(acl_p
) < 0)
86 int add_base_acls_if_needed(acl_t
*acl_p
, const char *path
) {
89 bool have_user_obj
= false, have_group_obj
= false, have_other
= false;
91 _cleanup_(acl_freep
) acl_t basic
= NULL
;
95 for (r
= acl_get_entry(*acl_p
, ACL_FIRST_ENTRY
, &i
);
97 r
= acl_get_entry(*acl_p
, ACL_NEXT_ENTRY
, &i
)) {
100 if (acl_get_tag_type(i
, &tag
) < 0)
103 if (tag
== ACL_USER_OBJ
)
104 have_user_obj
= true;
105 else if (tag
== ACL_GROUP_OBJ
)
106 have_group_obj
= true;
107 else if (tag
== ACL_OTHER
)
109 if (have_user_obj
&& have_group_obj
&& have_other
)
119 basic
= acl_from_mode(st
.st_mode
);
123 for (r
= acl_get_entry(basic
, ACL_FIRST_ENTRY
, &i
);
125 r
= acl_get_entry(basic
, ACL_NEXT_ENTRY
, &i
)) {
129 if (acl_get_tag_type(i
, &tag
) < 0)
132 if ((tag
== ACL_USER_OBJ
&& have_user_obj
) ||
133 (tag
== ACL_GROUP_OBJ
&& have_group_obj
) ||
134 (tag
== ACL_OTHER
&& have_other
))
137 r
= acl_create_entry(acl_p
, &dst
);
141 r
= acl_copy_entry(dst
, i
);
150 int acl_search_groups(const char *path
, char ***ret_groups
) {
151 _cleanup_strv_free_
char **g
= NULL
;
152 _cleanup_(acl_freep
) acl_t acl
= NULL
;
159 acl
= acl_get_file(path
, ACL_TYPE_DEFAULT
);
163 r
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
);
165 _cleanup_(acl_free_gid_tpp
) gid_t
*gid
= NULL
;
173 if (acl_get_tag_type(entry
, &tag
) < 0)
176 if (tag
!= ACL_GROUP
)
179 gid
= acl_get_qualifier(entry
);
183 if (in_gid(*gid
) > 0) {
193 name
= gid_to_name(*gid
);
197 r
= strv_consume(&g
, name
);
203 r
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
);
207 *ret_groups
= TAKE_PTR(g
);
212 int parse_acl(const char *text
, acl_t
*acl_access
, acl_t
*acl_default
, bool want_mask
) {
213 _cleanup_free_
char **a
= NULL
, **d
= NULL
; /* strings are not freed */
214 _cleanup_strv_free_
char **split
= NULL
;
216 _cleanup_(acl_freep
) acl_t a_acl
= NULL
, d_acl
= NULL
;
218 split
= strv_split(text
, ",");
222 STRV_FOREACH(entry
, split
) {
225 p
= STARTSWITH_SET(*entry
, "default:", "d:");
227 r
= strv_push(&d
, p
);
229 r
= strv_push(&a
, *entry
);
234 if (!strv_isempty(a
)) {
235 _cleanup_free_
char *join
= NULL
;
237 join
= strv_join(a
, ",");
241 a_acl
= acl_from_text(join
);
246 r
= calc_acl_mask_if_needed(&a_acl
);
252 if (!strv_isempty(d
)) {
253 _cleanup_free_
char *join
= NULL
;
255 join
= strv_join(d
, ",");
259 d_acl
= acl_from_text(join
);
264 r
= calc_acl_mask_if_needed(&d_acl
);
270 *acl_access
= TAKE_PTR(a_acl
);
271 *acl_default
= TAKE_PTR(d_acl
);
276 static int acl_entry_equal(acl_entry_t a
, acl_entry_t b
) {
277 acl_tag_t tag_a
, tag_b
;
279 if (acl_get_tag_type(a
, &tag_a
) < 0)
282 if (acl_get_tag_type(b
, &tag_b
) < 0)
293 /* can have only one of those */
296 _cleanup_(acl_free_uid_tpp
) uid_t
*uid_a
= NULL
, *uid_b
= NULL
;
298 uid_a
= acl_get_qualifier(a
);
302 uid_b
= acl_get_qualifier(b
);
306 return *uid_a
== *uid_b
;
309 _cleanup_(acl_free_gid_tpp
) gid_t
*gid_a
= NULL
, *gid_b
= NULL
;
311 gid_a
= acl_get_qualifier(a
);
315 gid_b
= acl_get_qualifier(b
);
319 return *gid_a
== *gid_b
;
322 assert_not_reached();
326 static int find_acl_entry(acl_t acl
, acl_entry_t entry
, acl_entry_t
*out
) {
330 for (r
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &i
);
332 r
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &i
)) {
334 r
= acl_entry_equal(i
, entry
);
347 int acls_for_file(const char *path
, acl_type_t type
, acl_t
new, acl_t
*acl
) {
348 _cleanup_(acl_freep
) acl_t old
;
352 old
= acl_get_file(path
, type
);
356 for (r
= acl_get_entry(new, ACL_FIRST_ENTRY
, &i
);
358 r
= acl_get_entry(new, ACL_NEXT_ENTRY
, &i
)) {
362 r
= find_acl_entry(old
, i
, &j
);
366 if (acl_create_entry(&old
, &j
) < 0)
369 if (acl_copy_entry(j
, i
) < 0)
375 *acl
= TAKE_PTR(old
);
380 /* POSIX says that ACL_{READ,WRITE,EXECUTE} don't have to be bitmasks. But that is a natural thing to do and
381 * all extant implementations do it. Let's make sure that we fail verbosely in the (imho unlikely) scenario
382 * that we get a new implementation that does not satisfy this. */
383 assert_cc(!(ACL_READ
& ACL_WRITE
));
384 assert_cc(!(ACL_WRITE
& ACL_EXECUTE
));
385 assert_cc(!(ACL_EXECUTE
& ACL_READ
));
386 assert_cc((unsigned) ACL_READ
== ACL_READ
);
387 assert_cc((unsigned) ACL_WRITE
== ACL_WRITE
);
388 assert_cc((unsigned) ACL_EXECUTE
== ACL_EXECUTE
);
390 int fd_add_uid_acl_permission(
395 _cleanup_(acl_freep
) acl_t acl
= NULL
;
396 acl_permset_t permset
;
400 /* Adds an ACL entry for the specified file to allow the indicated access to the specified
401 * user. Operates purely incrementally. */
404 assert(uid_is_valid(uid
));
406 acl
= acl_get_fd(fd
);
410 r
= acl_find_uid(acl
, uid
, &entry
);
412 if (acl_create_entry(&acl
, &entry
) < 0 ||
413 acl_set_tag_type(entry
, ACL_USER
) < 0 ||
414 acl_set_qualifier(entry
, &uid
) < 0)
418 if (acl_get_permset(entry
, &permset
) < 0)
421 if ((mask
& ACL_READ
) && acl_add_perm(permset
, ACL_READ
) < 0)
423 if ((mask
& ACL_WRITE
) && acl_add_perm(permset
, ACL_WRITE
) < 0)
425 if ((mask
& ACL_EXECUTE
) && acl_add_perm(permset
, ACL_EXECUTE
) < 0)
428 r
= calc_acl_mask_if_needed(&acl
);
432 if (acl_set_fd(fd
, acl
) < 0)