1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "alloc-util.h"
7 #include "errno-util.h"
8 #include "extract-word.h"
9 #include "string-util.h"
11 #include "user-util.h"
15 static 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
;
96 for (r
= acl_get_entry(*acl_p
, ACL_FIRST_ENTRY
, &i
);
98 r
= acl_get_entry(*acl_p
, ACL_NEXT_ENTRY
, &i
)) {
101 if (acl_get_tag_type(i
, &tag
) < 0)
104 if (tag
== ACL_USER_OBJ
)
105 have_user_obj
= true;
106 else if (tag
== ACL_GROUP_OBJ
)
107 have_group_obj
= true;
108 else if (tag
== ACL_OTHER
)
110 if (have_user_obj
&& have_group_obj
&& have_other
)
120 basic
= acl_from_mode(st
.st_mode
);
124 for (r
= acl_get_entry(basic
, ACL_FIRST_ENTRY
, &i
);
126 r
= acl_get_entry(basic
, ACL_NEXT_ENTRY
, &i
)) {
130 if (acl_get_tag_type(i
, &tag
) < 0)
133 if ((tag
== ACL_USER_OBJ
&& have_user_obj
) ||
134 (tag
== ACL_GROUP_OBJ
&& have_group_obj
) ||
135 (tag
== ACL_OTHER
&& have_other
))
138 r
= acl_create_entry(acl_p
, &dst
);
142 r
= acl_copy_entry(dst
, i
);
151 int acl_search_groups(const char *path
, char ***ret_groups
) {
152 _cleanup_strv_free_
char **g
= NULL
;
153 _cleanup_(acl_freep
) acl_t acl
= NULL
;
160 acl
= acl_get_file(path
, ACL_TYPE_DEFAULT
);
164 r
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
);
166 _cleanup_(acl_free_gid_tpp
) gid_t
*gid
= NULL
;
174 if (acl_get_tag_type(entry
, &tag
) < 0)
177 if (tag
!= ACL_GROUP
)
180 gid
= acl_get_qualifier(entry
);
184 if (in_gid(*gid
) > 0) {
194 name
= gid_to_name(*gid
);
198 r
= strv_consume(&g
, name
);
204 r
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
);
208 *ret_groups
= TAKE_PTR(g
);
215 acl_t
*ret_acl_access
,
216 acl_t
*ret_acl_access_exec
, /* extra rules to apply to inodes subject to uppercase X handling */
217 acl_t
*ret_acl_default
,
220 _cleanup_strv_free_
char **a
= NULL
, **e
= NULL
, **d
= NULL
, **split
= NULL
;
221 _cleanup_(acl_freep
) acl_t a_acl
= NULL
, e_acl
= NULL
, d_acl
= NULL
;
225 assert(ret_acl_access
);
226 assert(ret_acl_access_exec
);
227 assert(ret_acl_default
);
229 split
= strv_split(text
, ",");
233 STRV_FOREACH(entry
, split
) {
234 _cleanup_strv_free_
char **entry_split
= NULL
;
235 _cleanup_free_
char *entry_join
= NULL
;
238 n
= strv_split_full(&entry_split
, *entry
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
|EXTRACT_RETAIN_ESCAPE
);
245 string_replace_char(entry_split
[n
-1], 'X', 'x');
248 if (!STR_IN_SET(entry_split
[0], "default", "d"))
251 entry_join
= strv_join(entry_split
+ 1, ":");
255 r
= strv_consume(&d
, TAKE_PTR(entry_join
));
256 } else { /* n == 3 */
257 entry_join
= strv_join(entry_split
, ":");
261 if (!streq(*entry
, entry_join
))
262 r
= strv_consume(&e
, TAKE_PTR(entry_join
));
264 r
= strv_consume(&a
, TAKE_PTR(entry_join
));
270 if (!strv_isempty(a
)) {
271 _cleanup_free_
char *join
= NULL
;
273 join
= strv_join(a
, ",");
277 a_acl
= acl_from_text(join
);
282 r
= calc_acl_mask_if_needed(&a_acl
);
288 if (!strv_isempty(e
)) {
289 _cleanup_free_
char *join
= NULL
;
291 join
= strv_join(e
, ",");
295 e_acl
= acl_from_text(join
);
299 /* The mask must be calculated after deciding whether the execute bit should be set. */
302 if (!strv_isempty(d
)) {
303 _cleanup_free_
char *join
= NULL
;
305 join
= strv_join(d
, ",");
309 d_acl
= acl_from_text(join
);
314 r
= calc_acl_mask_if_needed(&d_acl
);
320 *ret_acl_access
= TAKE_PTR(a_acl
);
321 *ret_acl_access_exec
= TAKE_PTR(e_acl
);
322 *ret_acl_default
= TAKE_PTR(d_acl
);
327 static int acl_entry_equal(acl_entry_t a
, acl_entry_t b
) {
328 acl_tag_t tag_a
, tag_b
;
330 if (acl_get_tag_type(a
, &tag_a
) < 0)
333 if (acl_get_tag_type(b
, &tag_b
) < 0)
344 /* can have only one of those */
347 _cleanup_(acl_free_uid_tpp
) uid_t
*uid_a
= NULL
, *uid_b
= NULL
;
349 uid_a
= acl_get_qualifier(a
);
353 uid_b
= acl_get_qualifier(b
);
357 return *uid_a
== *uid_b
;
360 _cleanup_(acl_free_gid_tpp
) gid_t
*gid_a
= NULL
, *gid_b
= NULL
;
362 gid_a
= acl_get_qualifier(a
);
366 gid_b
= acl_get_qualifier(b
);
370 return *gid_a
== *gid_b
;
373 assert_not_reached();
377 static int find_acl_entry(acl_t acl
, acl_entry_t entry
, acl_entry_t
*ret
) {
381 for (r
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &i
);
383 r
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &i
)) {
385 r
= acl_entry_equal(i
, entry
);
400 int acls_for_file(const char *path
, acl_type_t type
, acl_t acl
, acl_t
*ret
) {
401 _cleanup_(acl_freep
) acl_t applied
= NULL
;
407 applied
= acl_get_file(path
, type
);
411 for (r
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &i
);
413 r
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &i
)) {
417 r
= find_acl_entry(applied
, i
, &j
);
419 if (acl_create_entry(&applied
, &j
) < 0)
424 if (acl_copy_entry(j
, i
) < 0)
431 *ret
= TAKE_PTR(applied
);
436 /* POSIX says that ACL_{READ,WRITE,EXECUTE} don't have to be bitmasks. But that is a natural thing to do and
437 * all extant implementations do it. Let's make sure that we fail verbosely in the (imho unlikely) scenario
438 * that we get a new implementation that does not satisfy this. */
439 assert_cc(!(ACL_READ
& ACL_WRITE
));
440 assert_cc(!(ACL_WRITE
& ACL_EXECUTE
));
441 assert_cc(!(ACL_EXECUTE
& ACL_READ
));
442 assert_cc((unsigned) ACL_READ
== ACL_READ
);
443 assert_cc((unsigned) ACL_WRITE
== ACL_WRITE
);
444 assert_cc((unsigned) ACL_EXECUTE
== ACL_EXECUTE
);
446 int fd_add_uid_acl_permission(
451 _cleanup_(acl_freep
) acl_t acl
= NULL
;
452 acl_permset_t permset
;
456 /* Adds an ACL entry for the specified file to allow the indicated access to the specified
457 * user. Operates purely incrementally. */
460 assert(uid_is_valid(uid
));
462 acl
= acl_get_fd(fd
);
466 r
= acl_find_uid(acl
, uid
, &entry
);
468 if (acl_create_entry(&acl
, &entry
) < 0 ||
469 acl_set_tag_type(entry
, ACL_USER
) < 0 ||
470 acl_set_qualifier(entry
, &uid
) < 0)
474 if (acl_get_permset(entry
, &permset
) < 0)
477 if ((mask
& ACL_READ
) && acl_add_perm(permset
, ACL_READ
) < 0)
479 if ((mask
& ACL_WRITE
) && acl_add_perm(permset
, ACL_WRITE
) < 0)
481 if ((mask
& ACL_EXECUTE
) && acl_add_perm(permset
, ACL_EXECUTE
) < 0)
484 r
= calc_acl_mask_if_needed(&acl
);
488 if (acl_set_fd(fd
, acl
) < 0)
494 int fd_acl_make_read_only(int fd
) {
495 _cleanup_(acl_freep
) acl_t acl
= NULL
;
496 bool changed
= false;
502 /* Safely drops all W bits from all relevant ACL entries of the file, without changing entries which
503 * are masked by the ACL mask */
505 acl
= acl_get_fd(fd
);
508 if (!ERRNO_IS_NOT_SUPPORTED(errno
))
511 /* No ACLs? Then just update the regular mode_t */
512 return fd_acl_make_read_only_fallback(fd
);
515 for (r
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &i
);
517 r
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &i
)) {
518 acl_permset_t permset
;
522 if (acl_get_tag_type(i
, &tag
) < 0)
525 /* These three control the x bits overall (as ACL_MASK affects all remaining tags) */
526 if (!IN_SET(tag
, ACL_USER_OBJ
, ACL_MASK
, ACL_OTHER
))
529 if (acl_get_permset(i
, &permset
) < 0)
532 b
= acl_get_perm(permset
, ACL_WRITE
);
537 if (acl_delete_perm(permset
, ACL_WRITE
) < 0)
549 if (acl_set_fd(fd
, acl
) < 0) {
550 if (!ERRNO_IS_NOT_SUPPORTED(errno
))
553 return fd_acl_make_read_only_fallback(fd
);
559 int fd_acl_make_writable(int fd
) {
560 _cleanup_(acl_freep
) acl_t acl
= NULL
;
564 /* Safely adds the writable bit to the owner's ACL entry of this inode. (And only the owner's! – This
565 * not the obvious inverse of fd_acl_make_read_only() hence!) */
567 acl
= acl_get_fd(fd
);
569 if (!ERRNO_IS_NOT_SUPPORTED(errno
))
572 /* No ACLs? Then just update the regular mode_t */
573 return fd_acl_make_writable_fallback(fd
);
576 for (r
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &i
);
578 r
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &i
)) {
579 acl_permset_t permset
;
583 if (acl_get_tag_type(i
, &tag
) < 0)
586 if (tag
!= ACL_USER_OBJ
)
589 if (acl_get_permset(i
, &permset
) < 0)
592 b
= acl_get_perm(permset
, ACL_WRITE
);
597 return 0; /* Already set? Then there's nothing to do. */
599 if (acl_add_perm(permset
, ACL_WRITE
) < 0)
607 if (acl_set_fd(fd
, acl
) < 0) {
608 if (!ERRNO_IS_NOT_SUPPORTED(errno
))
611 return fd_acl_make_writable_fallback(fd
);
618 int fd_acl_make_read_only_fallback(int fd
) {
623 if (fstat(fd
, &st
) < 0)
626 if ((st
.st_mode
& 0222) == 0)
629 if (fchmod(fd
, st
.st_mode
& 0555) < 0)
635 int fd_acl_make_writable_fallback(int fd
) {
640 if (fstat(fd
, &st
) < 0)
643 if ((st
.st_mode
& 0200) != 0) /* already set */
646 if (fchmod(fd
, (st
.st_mode
& 07777) | 0200) < 0)