Skip to content

Commit a259797

Browse files
poetteringbluca
authored andcommitted
execute: fix credential dir handling for fs which support ACLs
When the credential dir is backed by an fs that supports ACLs we must be more careful with adjusting the 'x' bit of the directory, as any chmod() call on the dir will reset the mask entry of the ACL entirely which we don't want. Hence, do a manual set of ACL changes, that only add/drop the 'x' bit but otherwise leave the ACL as it is. This matters if we use tmpfs rather than ramfs to store credentials. (cherry picked from commit f76ce81) (cherry picked from commit ee3ed28) (cherry picked from commit ef943b2)
1 parent 6c9cb50 commit a259797

File tree

5 files changed

+243
-3
lines changed

5 files changed

+243
-3
lines changed

src/core/execute.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2872,6 +2872,10 @@ static int acquire_credentials(
28722872
if (dfd < 0)
28732873
return -errno;
28742874

2875+
r = fd_acl_make_writable(dfd); /* Add the "w" bit, if we are reusing an already set up credentials dir where it was unset */
2876+
if (r < 0)
2877+
return r;
2878+
28752879
/* First, load credentials off disk (or acquire via AF_UNIX socket) */
28762880
HASHMAP_FOREACH(lc, context->load_credentials) {
28772881
_cleanup_close_ int sub_fd = -1;
@@ -2964,8 +2968,9 @@ static int acquire_credentials(
29642968
left -= add;
29652969
}
29662970

2967-
if (fchmod(dfd, 0500) < 0) /* Now take away the "w" bit */
2968-
return -errno;
2971+
r = fd_acl_make_read_only(dfd); /* Now take away the "w" bit */
2972+
if (r < 0)
2973+
return r;
29692974

29702975
/* After we created all keys with the right perms, also make sure the credential store as a whole is
29712976
* accessible */

src/shared/acl-util.c

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@
77

88
#include "acl-util.h"
99
#include "alloc-util.h"
10+
#include "errno-util.h"
1011
#include "string-util.h"
1112
#include "strv.h"
1213
#include "user-util.h"
1314
#include "util.h"
1415

16+
#if HAVE_ACL
17+
1518
int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *ret_entry) {
1619
acl_entry_t i;
1720
int r;
@@ -434,3 +437,161 @@ int fd_add_uid_acl_permission(
434437

435438
return 0;
436439
}
440+
441+
int fd_acl_make_read_only(int fd) {
442+
_cleanup_(acl_freep) acl_t acl = NULL;
443+
bool changed = false;
444+
acl_entry_t i;
445+
int r;
446+
447+
assert(fd >= 0);
448+
449+
/* Safely drops all W bits from all relevant ACL entries of the file, without changing entries which
450+
* are masked by the ACL mask */
451+
452+
acl = acl_get_fd(fd);
453+
if (!acl) {
454+
455+
if (!ERRNO_IS_NOT_SUPPORTED(errno))
456+
return -errno;
457+
458+
/* No ACLs? Then just update the regular mode_t */
459+
return fd_acl_make_read_only_fallback(fd);
460+
}
461+
462+
for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
463+
r > 0;
464+
r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
465+
acl_permset_t permset;
466+
acl_tag_t tag;
467+
int b;
468+
469+
if (acl_get_tag_type(i, &tag) < 0)
470+
return -errno;
471+
472+
/* These three control the x bits overall (as ACL_MASK affects all remaining tags) */
473+
if (!IN_SET(tag, ACL_USER_OBJ, ACL_MASK, ACL_OTHER))
474+
continue;
475+
476+
if (acl_get_permset(i, &permset) < 0)
477+
return -errno;
478+
479+
b = acl_get_perm(permset, ACL_WRITE);
480+
if (b < 0)
481+
return -errno;
482+
483+
if (b) {
484+
if (acl_delete_perm(permset, ACL_WRITE) < 0)
485+
return -errno;
486+
487+
changed = true;
488+
}
489+
}
490+
if (r < 0)
491+
return -errno;
492+
493+
if (!changed)
494+
return 0;
495+
496+
if (acl_set_fd(fd, acl) < 0) {
497+
if (!ERRNO_IS_NOT_SUPPORTED(errno))
498+
return -errno;
499+
500+
return fd_acl_make_read_only_fallback(fd);
501+
}
502+
503+
return 1;
504+
}
505+
506+
int fd_acl_make_writable(int fd) {
507+
_cleanup_(acl_freep) acl_t acl = NULL;
508+
acl_entry_t i;
509+
int r;
510+
511+
/* Safely adds the writable bit to the owner's ACL entry of this inode. (And only the owner's! – This
512+
* not the obvious inverse of fd_acl_make_read_only() hence!) */
513+
514+
acl = acl_get_fd(fd);
515+
if (!acl) {
516+
if (!ERRNO_IS_NOT_SUPPORTED(errno))
517+
return -errno;
518+
519+
/* No ACLs? Then just update the regular mode_t */
520+
return fd_acl_make_writable_fallback(fd);
521+
}
522+
523+
for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
524+
r > 0;
525+
r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
526+
acl_permset_t permset;
527+
acl_tag_t tag;
528+
int b;
529+
530+
if (acl_get_tag_type(i, &tag) < 0)
531+
return -errno;
532+
533+
if (tag != ACL_USER_OBJ)
534+
continue;
535+
536+
if (acl_get_permset(i, &permset) < 0)
537+
return -errno;
538+
539+
b = acl_get_perm(permset, ACL_WRITE);
540+
if (b < 0)
541+
return -errno;
542+
543+
if (b)
544+
return 0; /* Already set? Then there's nothing to do. */
545+
546+
if (acl_add_perm(permset, ACL_WRITE) < 0)
547+
return -errno;
548+
549+
break;
550+
}
551+
if (r < 0)
552+
return -errno;
553+
554+
if (acl_set_fd(fd, acl) < 0) {
555+
if (!ERRNO_IS_NOT_SUPPORTED(errno))
556+
return -errno;
557+
558+
return fd_acl_make_writable_fallback(fd);
559+
}
560+
561+
return 1;
562+
}
563+
#endif
564+
565+
int fd_acl_make_read_only_fallback(int fd) {
566+
struct stat st;
567+
568+
assert(fd >= 0);
569+
570+
if (fstat(fd, &st) < 0)
571+
return -errno;
572+
573+
if ((st.st_mode & 0222) == 0)
574+
return 0;
575+
576+
if (fchmod(fd, st.st_mode & 0555) < 0)
577+
return -errno;
578+
579+
return 1;
580+
}
581+
582+
int fd_acl_make_writable_fallback(int fd) {
583+
struct stat st;
584+
585+
assert(fd >= 0);
586+
587+
if (fstat(fd, &st) < 0)
588+
return -errno;
589+
590+
if ((st.st_mode & 0200) != 0) /* already set */
591+
return 0;
592+
593+
if (fchmod(fd, (st.st_mode & 07777) | 0200) < 0)
594+
return -errno;
595+
596+
return 1;
597+
}

src/shared/acl-util.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
#include <errno.h>
55
#include <unistd.h>
66

7+
int fd_acl_make_read_only_fallback(int fd);
8+
int fd_acl_make_writable_fallback(int fd);
9+
710
#if HAVE_ACL
811
#include <acl/libacl.h>
912
#include <stdbool.h>
@@ -19,6 +22,9 @@ int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want
1922
int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl);
2023
int fd_add_uid_acl_permission(int fd, uid_t uid, unsigned mask);
2124

25+
int fd_acl_make_read_only(int fd);
26+
int fd_acl_make_writable(int fd);
27+
2228
/* acl_free takes multiple argument types.
2329
* Multiple cleanup functions are necessary. */
2430
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(acl_t, acl_free, NULL);
@@ -37,4 +43,13 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(gid_t*, acl_free_gid_tp, NULL);
3743
static inline int fd_add_uid_acl_permission(int fd, uid_t uid, unsigned mask) {
3844
return -EOPNOTSUPP;
3945
}
46+
47+
static inline int fd_acl_make_read_only(int fd) {
48+
return fd_acl_make_read_only_fallback(fd);
49+
}
50+
51+
static inline int fd_acl_make_writable(int fd) {
52+
return fd_acl_make_writable_fallback(fd);
53+
}
54+
4055
#endif

src/shared/meson.build

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# SPDX-License-Identifier: LGPL-2.1-or-later
22

33
shared_sources = files(
4+
'acl-util.c',
45
'acl-util.h',
56
'acpi-fpdt.c',
67
'acpi-fpdt.h',
@@ -359,7 +360,6 @@ syscall_list_h = custom_target(
359360

360361
if conf.get('HAVE_ACL') == 1
361362
shared_sources += files(
362-
'acl-util.c',
363363
'devnode-acl.c',
364364
)
365365
endif

src/test/test-acl-util.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "acl-util.h"
99
#include "errno-util.h"
1010
#include "fd-util.h"
11+
#include "fs-util.h"
1112
#include "format-util.h"
1213
#include "string-util.h"
1314
#include "tests.h"
@@ -69,4 +70,62 @@ TEST_RET(add_acls_for_user) {
6970
return 0;
7071
}
7172

73+
TEST(fd_acl_make_read_only) {
74+
_cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-empty.XXXXXX";
75+
_cleanup_close_ int fd = -EBADF;
76+
const char *cmd;
77+
struct stat st;
78+
79+
fd = mkostemp_safe(fn);
80+
assert_se(fd >= 0);
81+
82+
/* make it more exciting */
83+
(void) fd_add_uid_acl_permission(fd, 1, ACL_READ|ACL_WRITE|ACL_EXECUTE);
84+
85+
assert_se(fstat(fd, &st) >= 0);
86+
assert_se((st.st_mode & 0200) == 0200);
87+
88+
cmd = strjoina("getfacl -p ", fn);
89+
assert_se(system(cmd) == 0);
90+
91+
cmd = strjoina("stat ", fn);
92+
assert_se(system(cmd) == 0);
93+
94+
log_info("read-only");
95+
assert_se(fd_acl_make_read_only(fd));
96+
97+
assert_se(fstat(fd, &st) >= 0);
98+
assert_se((st.st_mode & 0222) == 0000);
99+
100+
cmd = strjoina("getfacl -p ", fn);
101+
assert_se(system(cmd) == 0);
102+
103+
cmd = strjoina("stat ", fn);
104+
assert_se(system(cmd) == 0);
105+
106+
log_info("writable");
107+
assert_se(fd_acl_make_writable(fd));
108+
109+
assert_se(fstat(fd, &st) >= 0);
110+
assert_se((st.st_mode & 0222) == 0200);
111+
112+
cmd = strjoina("getfacl -p ", fn);
113+
assert_se(system(cmd) == 0);
114+
115+
cmd = strjoina("stat ", fn);
116+
assert_se(system(cmd) == 0);
117+
118+
log_info("read-only");
119+
assert_se(fd_acl_make_read_only(fd));
120+
121+
assert_se(fstat(fd, &st) >= 0);
122+
assert_se((st.st_mode & 0222) == 0000);
123+
124+
cmd = strjoina("getfacl -p ", fn);
125+
assert_se(system(cmd) == 0);
126+
127+
cmd = strjoina("stat ", fn);
128+
assert_se(system(cmd) == 0);
129+
}
130+
72131
DEFINE_TEST_MAIN(LOG_INFO);

0 commit comments

Comments
 (0)