1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
9 #include "alloc-util.h"
10 #include "errno-util.h"
11 #include "string-util.h"
13 #include "user-util.h"
17 int acl_find_uid(acl_t acl
, uid_t uid
, acl_entry_t
*ret_entry
) {
22 assert(uid_is_valid(uid
));
25 for (r
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &i
);
27 r
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &i
)) {
33 if (acl_get_tag_type(i
, &tag
) < 0)
39 u
= acl_get_qualifier(i
);
58 int calc_acl_mask_if_needed(acl_t
*acl_p
) {
65 for (r
= acl_get_entry(*acl_p
, ACL_FIRST_ENTRY
, &i
);
67 r
= acl_get_entry(*acl_p
, ACL_NEXT_ENTRY
, &i
)) {
70 if (acl_get_tag_type(i
, &tag
) < 0)
76 if (IN_SET(tag
, ACL_USER
, ACL_GROUP
))
82 if (need
&& acl_calc_mask(acl_p
) < 0)
88 int add_base_acls_if_needed(acl_t
*acl_p
, const char *path
) {
91 bool have_user_obj
= false, have_group_obj
= false, have_other
= false;
93 _cleanup_(acl_freep
) acl_t basic
= NULL
;
98 for (r
= acl_get_entry(*acl_p
, ACL_FIRST_ENTRY
, &i
);
100 r
= acl_get_entry(*acl_p
, ACL_NEXT_ENTRY
, &i
)) {
103 if (acl_get_tag_type(i
, &tag
) < 0)
106 if (tag
== ACL_USER_OBJ
)
107 have_user_obj
= true;
108 else if (tag
== ACL_GROUP_OBJ
)
109 have_group_obj
= true;
110 else if (tag
== ACL_OTHER
)
112 if (have_user_obj
&& have_group_obj
&& have_other
)
122 basic
= acl_from_mode(st
.st_mode
);
126 for (r
= acl_get_entry(basic
, ACL_FIRST_ENTRY
, &i
);
128 r
= acl_get_entry(basic
, ACL_NEXT_ENTRY
, &i
)) {
132 if (acl_get_tag_type(i
, &tag
) < 0)
135 if ((tag
== ACL_USER_OBJ
&& have_user_obj
) ||
136 (tag
== ACL_GROUP_OBJ
&& have_group_obj
) ||
137 (tag
== ACL_OTHER
&& have_other
))
140 r
= acl_create_entry(acl_p
, &dst
);
144 r
= acl_copy_entry(dst
, i
);
153 int acl_search_groups(const char *path
, char ***ret_groups
) {
154 _cleanup_strv_free_
char **g
= NULL
;
155 _cleanup_(acl_freep
) acl_t acl
= NULL
;
162 acl
= acl_get_file(path
, ACL_TYPE_DEFAULT
);
166 r
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
);
168 _cleanup_(acl_free_gid_tpp
) gid_t
*gid
= NULL
;
176 if (acl_get_tag_type(entry
, &tag
) < 0)
179 if (tag
!= ACL_GROUP
)
182 gid
= acl_get_qualifier(entry
);
186 if (in_gid(*gid
) > 0) {
196 name
= gid_to_name(*gid
);
200 r
= strv_consume(&g
, name
);
206 r
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
);
210 *ret_groups
= TAKE_PTR(g
);
217 acl_t
*ret_acl_access
,
218 acl_t
*ret_acl_access_exec
, /* extra rules to apply to inodes subject to uppercase X handling */
219 acl_t
*ret_acl_default
,
222 _cleanup_strv_free_
char **a
= NULL
, **e
= NULL
, **d
= NULL
, **split
= NULL
;
223 _cleanup_(acl_freep
) acl_t a_acl
= NULL
, e_acl
= NULL
, d_acl
= NULL
;
227 assert(ret_acl_access
);
228 assert(ret_acl_access_exec
);
229 assert(ret_acl_default
);
231 split
= strv_split(text
, ",");
235 STRV_FOREACH(entry
, split
) {
236 _cleanup_strv_free_
char **entry_split
= NULL
;
237 _cleanup_free_
char *entry_join
= NULL
;
240 n
= strv_split_full(&entry_split
, *entry
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
|EXTRACT_RETAIN_ESCAPE
);
247 string_replace_char(entry_split
[n
-1], 'X', 'x');
250 if (!STR_IN_SET(entry_split
[0], "default", "d"))
253 entry_join
= strv_join(entry_split
+ 1, ":");
257 r
= strv_consume(&d
, TAKE_PTR(entry_join
));
258 } else { /* n == 3 */
259 entry_join
= strv_join(entry_split
, ":");
263 if (!streq(*entry
, entry_join
))
264 r
= strv_consume(&e
, TAKE_PTR(entry_join
));
266 r
= strv_consume(&a
, TAKE_PTR(entry_join
));
272 if (!strv_isempty(a
)) {
273 _cleanup_free_
char *join
= NULL
;
275 join
= strv_join(a
, ",");
279 a_acl
= acl_from_text(join
);
284 r
= calc_acl_mask_if_needed(&a_acl
);
290 if (!strv_isempty(e
)) {
291 _cleanup_free_
char *join
= NULL
;
293 join
= strv_join(e
, ",");
297 e_acl
= acl_from_text(join
);
301 /* The mask must be calculated after deciding whether the execute bit should be set. */
304 if (!strv_isempty(d
)) {
305 _cleanup_free_
char *join
= NULL
;
307 join
= strv_join(d
, ",");
311 d_acl
= acl_from_text(join
);
316 r
= calc_acl_mask_if_needed(&d_acl
);
322 *ret_acl_access
= TAKE_PTR(a_acl
);
323 *ret_acl_access_exec
= TAKE_PTR(e_acl
);
324 *ret_acl_default
= TAKE_PTR(d_acl
);
329 static int acl_entry_equal(acl_entry_t a
, acl_entry_t b
) {
330 acl_tag_t tag_a
, tag_b
;
332 if (acl_get_tag_type(a
, &tag_a
) < 0)
335 if (acl_get_tag_type(b
, &tag_b
) < 0)
346 /* can have only one of those */
349 _cleanup_(acl_free_uid_tpp
) uid_t
*uid_a
= NULL
, *uid_b
= NULL
;
351 uid_a
= acl_get_qualifier(a
);
355 uid_b
= acl_get_qualifier(b
);
359 return *uid_a
== *uid_b
;
362 _cleanup_(acl_free_gid_tpp
) gid_t
*gid_a
= NULL
, *gid_b
= NULL
;
364 gid_a
= acl_get_qualifier(a
);
368 gid_b
= acl_get_qualifier(b
);
372 return *gid_a
== *gid_b
;
375 assert_not_reached();
379 static int find_acl_entry(acl_t acl
, acl_entry_t entry
, acl_entry_t
*ret
) {
383 for (r
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &i
);
385 r
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &i
)) {
387 r
= acl_entry_equal(i
, entry
);
402 int acls_for_file(const char *path
, acl_type_t type
, acl_t acl
, acl_t
*ret
) {
403 _cleanup_(acl_freep
) acl_t applied
= NULL
;
409 applied
= acl_get_file(path
, type
);
413 for (r
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &i
);
415 r
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &i
)) {
419 r
= find_acl_entry(applied
, i
, &j
);
421 if (acl_create_entry(&applied
, &j
) < 0)
426 if (acl_copy_entry(j
, i
) < 0)
433 *ret
= TAKE_PTR(applied
);
438 /* POSIX says that ACL_{READ,WRITE,EXECUTE} don't have to be bitmasks. But that is a natural thing to do and
439 * all extant implementations do it. Let's make sure that we fail verbosely in the (imho unlikely) scenario
440 * that we get a new implementation that does not satisfy this. */
441 assert_cc(!(ACL_READ
& ACL_WRITE
));
442 assert_cc(!(ACL_WRITE
& ACL_EXECUTE
));
443 assert_cc(!(ACL_EXECUTE
& ACL_READ
));
444 assert_cc((unsigned) ACL_READ
== ACL_READ
);
445 assert_cc((unsigned) ACL_WRITE
== ACL_WRITE
);
446 assert_cc((unsigned) ACL_EXECUTE
== ACL_EXECUTE
);
448 int fd_add_uid_acl_permission(
453 _cleanup_(acl_freep
) acl_t acl
= NULL
;
454 acl_permset_t permset
;
458 /* Adds an ACL entry for the specified file to allow the indicated access to the specified
459 * user. Operates purely incrementally. */
462 assert(uid_is_valid(uid
));
464 acl
= acl_get_fd(fd
);
468 r
= acl_find_uid(acl
, uid
, &entry
);
470 if (acl_create_entry(&acl
, &entry
) < 0 ||
471 acl_set_tag_type(entry
, ACL_USER
) < 0 ||
472 acl_set_qualifier(entry
, &uid
) < 0)
476 if (acl_get_permset(entry
, &permset
) < 0)
479 if ((mask
& ACL_READ
) && acl_add_perm(permset
, ACL_READ
) < 0)
481 if ((mask
& ACL_WRITE
) && acl_add_perm(permset
, ACL_WRITE
) < 0)
483 if ((mask
& ACL_EXECUTE
) && acl_add_perm(permset
, ACL_EXECUTE
) < 0)
486 r
= calc_acl_mask_if_needed(&acl
);
490 if (acl_set_fd(fd
, acl
) < 0)
496 int fd_acl_make_read_only(int fd
) {
497 _cleanup_(acl_freep
) acl_t acl
= NULL
;
498 bool changed
= false;
504 /* Safely drops all W bits from all relevant ACL entries of the file, without changing entries which
505 * are masked by the ACL mask */
507 acl
= acl_get_fd(fd
);
510 if (!ERRNO_IS_NOT_SUPPORTED(errno
))
513 /* No ACLs? Then just update the regular mode_t */
514 return fd_acl_make_read_only_fallback(fd
);
517 for (r
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &i
);
519 r
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &i
)) {
520 acl_permset_t permset
;
524 if (acl_get_tag_type(i
, &tag
) < 0)
527 /* These three control the x bits overall (as ACL_MASK affects all remaining tags) */
528 if (!IN_SET(tag
, ACL_USER_OBJ
, ACL_MASK
, ACL_OTHER
))
531 if (acl_get_permset(i
, &permset
) < 0)
534 b
= acl_get_perm(permset
, ACL_WRITE
);
539 if (acl_delete_perm(permset
, ACL_WRITE
) < 0)
551 if (acl_set_fd(fd
, acl
) < 0) {
552 if (!ERRNO_IS_NOT_SUPPORTED(errno
))
555 return fd_acl_make_read_only_fallback(fd
);
561 int fd_acl_make_writable(int fd
) {
562 _cleanup_(acl_freep
) acl_t acl
= NULL
;
566 /* Safely adds the writable bit to the owner's ACL entry of this inode. (And only the owner's! – This
567 * not the obvious inverse of fd_acl_make_read_only() hence!) */
569 acl
= acl_get_fd(fd
);
571 if (!ERRNO_IS_NOT_SUPPORTED(errno
))
574 /* No ACLs? Then just update the regular mode_t */
575 return fd_acl_make_writable_fallback(fd
);
578 for (r
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &i
);
580 r
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &i
)) {
581 acl_permset_t permset
;
585 if (acl_get_tag_type(i
, &tag
) < 0)
588 if (tag
!= ACL_USER_OBJ
)
591 if (acl_get_permset(i
, &permset
) < 0)
594 b
= acl_get_perm(permset
, ACL_WRITE
);
599 return 0; /* Already set? Then there's nothing to do. */
601 if (acl_add_perm(permset
, ACL_WRITE
) < 0)
609 if (acl_set_fd(fd
, acl
) < 0) {
610 if (!ERRNO_IS_NOT_SUPPORTED(errno
))
613 return fd_acl_make_writable_fallback(fd
);
620 int fd_acl_make_read_only_fallback(int fd
) {
625 if (fstat(fd
, &st
) < 0)
628 if ((st
.st_mode
& 0222) == 0)
631 if (fchmod(fd
, st
.st_mode
& 0555) < 0)
637 int fd_acl_make_writable_fallback(int fd
) {
642 if (fstat(fd
, &st
) < 0)
645 if ((st
.st_mode
& 0200) != 0) /* already set */
648 if (fchmod(fd
, (st
.st_mode
& 07777) | 0200) < 0)