core/exec-credential: fix credentials plain dir exchanging (#40108)

Follow-ups for #39637

Split out from #40093
This commit is contained in:
Mike Yuan
2025-12-17 00:25:11 +01:00
committed by GitHub
5 changed files with 131 additions and 16 deletions
+7 -1
View File
@@ -980,7 +980,13 @@ static int setup_credentials_plain_dir(
r = RET_NERRNO(rename(workspace, cred_dir));
if (r >= 0)
workspace_rm = NULL;
if (r == -EEXIST) {
if (IN_SET(r, -ENOTEMPTY, -EEXIST)) {
_cleanup_close_ int old_dfd = open(cred_dir, O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
if (old_dfd < 0)
return log_debug_errno(errno, "Failed to open credentials dir '%s': %m", cred_dir);
(void) fd_acl_make_writable(old_dfd);
log_debug_errno(r, "Credential dir '%s' already populated, exchanging with workspace.", cred_dir);
r = RET_NERRNO(renameat2(AT_FDCWD, workspace, AT_FDCWD, cred_dir, RENAME_EXCHANGE));
}
+93 -6
View File
@@ -668,15 +668,34 @@ int fd_add_uid_acl_permission(
return 0;
}
#endif
static int fd_acl_make_read_only_fallback(int fd) {
struct stat st;
assert(fd >= 0);
if (fstat(fd, &st) < 0)
return -errno;
if ((st.st_mode & 0222) == 0)
return 0;
if (fchmod(fd, st.st_mode & 0555) < 0)
return -errno;
return 1;
}
int fd_acl_make_read_only(int fd) {
assert(fd >= 0);
#if HAVE_ACL
_cleanup_(acl_freep) acl_t acl = NULL;
bool changed = false;
acl_entry_t i;
int r;
assert(fd >= 0);
/* Safely drops all W bits from all relevant ACL entries of the file, without changing entries which
* are masked by the ACL mask */
@@ -734,13 +753,13 @@ int fd_acl_make_read_only(int fd) {
maybe_fallback:
if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
return r;
#endif
/* No ACLs? Then just update the regular mode_t */
return fd_acl_make_read_only_fallback(fd);
}
#endif
int fd_acl_make_read_only_fallback(int fd) {
static int fd_acl_make_writable_fallback(int fd) {
struct stat st;
assert(fd >= 0);
@@ -748,15 +767,83 @@ int fd_acl_make_read_only_fallback(int fd) {
if (fstat(fd, &st) < 0)
return -errno;
if ((st.st_mode & 0222) == 0)
if ((st.st_mode & 0200) != 0) /* already set */
return 0;
if (fchmod(fd, st.st_mode & 0555) < 0)
if (fchmod(fd, (st.st_mode & 07777) | 0200) < 0)
return -errno;
return 1;
}
int fd_acl_make_writable(int fd) {
assert(fd >= 0);
#if HAVE_ACL
_cleanup_(acl_freep) acl_t acl = NULL;
acl_entry_t i;
int r;
/* Safely adds the writable bit to the owner's ACL entry of this inode. (And only the owner's! – This
* not the obvious inverse of fd_acl_make_read_only() hence!) */
r = dlopen_libacl();
if (r < 0)
goto maybe_fallback;
acl = sym_acl_get_fd(fd);
if (!acl) {
r = -errno;
goto maybe_fallback;
}
for (r = sym_acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
r > 0;
r = sym_acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
acl_permset_t permset;
acl_tag_t tag;
int b;
if (sym_acl_get_tag_type(i, &tag) < 0)
return -errno;
if (tag != ACL_USER_OBJ)
continue;
if (sym_acl_get_permset(i, &permset) < 0)
return -errno;
b = sym_acl_get_perm(permset, ACL_WRITE);
if (b < 0)
return -errno;
if (b)
return 0; /* Already set? Then there's nothing to do. */
if (sym_acl_add_perm(permset, ACL_WRITE) < 0)
return -errno;
break;
}
if (r < 0)
return -errno;
if (sym_acl_set_fd(fd, acl) < 0) {
r = -errno;
goto maybe_fallback;
}
return 1;
maybe_fallback:
if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
return r;
#endif
/* No ACLs? Then just update the regular mode_t */
return fd_acl_make_writable_fallback(fd);
}
int inode_type_can_acl(mode_t mode) {
return IN_SET(mode & S_IFMT, S_IFSOCK, S_IFREG, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO);
}
+4 -9
View File
@@ -3,8 +3,6 @@
#include "shared-forward.h"
int fd_acl_make_read_only_fallback(int fd);
#if HAVE_ACL
#include <acl/libacl.h> /* IWYU pragma: export */
#include <sys/acl.h> /* IWYU pragma: export */
@@ -54,8 +52,6 @@ int parse_acl(
int acls_for_file(const char *path, acl_type_t type, acl_t acl, acl_t *ret);
int fd_add_uid_acl_permission(int fd, uid_t uid, unsigned mask);
int fd_acl_make_read_only(int fd);
/* acl_free() takes multiple argument types. Multiple cleanup functions are necessary. */
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(acl_t, sym_acl_free, acl_freep, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(char*, sym_acl_free, acl_free_charpp, NULL);
@@ -68,7 +64,7 @@ static inline int acl_set_perm(acl_permset_t ps, acl_perm_t p, bool b) {
#else
typedef void *acl_t;
typedef void* acl_t;
typedef int acl_tag_t;
typedef unsigned acl_type_t;
@@ -100,10 +96,9 @@ static inline int devnode_acl(int fd, uid_t uid) {
static inline int fd_add_uid_acl_permission(int fd, uid_t uid, unsigned mask) {
return -EOPNOTSUPP;
}
static inline int fd_acl_make_read_only(int fd) {
return fd_acl_make_read_only_fallback(fd);
}
#endif
int fd_acl_make_read_only(int fd);
int fd_acl_make_writable(int fd);
int inode_type_can_acl(mode_t mode);
+24
View File
@@ -112,6 +112,30 @@ TEST_RET(fd_acl_make_read_only) {
cmd = strjoina("stat ", fn);
ASSERT_OK_ZERO_ERRNO(system(cmd));
log_info("writable");
ASSERT_OK_POSITIVE(fd_acl_make_writable(fd));
ASSERT_OK_ERRNO(fstat(fd, &st));
ASSERT_EQ(st.st_mode & 0222, 0200u);
cmd = strjoina("getfacl -p ", fn);
ASSERT_OK_ZERO_ERRNO(system(cmd));
cmd = strjoina("stat ", fn);
ASSERT_OK_ZERO_ERRNO(system(cmd));
log_info("read-only");
ASSERT_OK_POSITIVE(fd_acl_make_read_only(fd));
ASSERT_OK_ERRNO(fstat(fd, &st));
ASSERT_EQ(st.st_mode & 0222, 0000u);
cmd = strjoina("getfacl -p ", fn);
ASSERT_OK_ZERO_ERRNO(system(cmd));
cmd = strjoina("stat ", fn);
ASSERT_OK_ZERO_ERRNO(system(cmd));
return 0;
}
+3
View File
@@ -554,6 +554,9 @@ run0 -u testuser --pipe mkdir -p /home/testuser/.config/credstore.encrypted
run0 -u testuser --pipe systemd-creds encrypt --user --name=brummbaer - /home/testuser/.config/credstore.encrypted/brummbaer < /tmp/brummbaer.data
run0 -u testuser --pipe systemd-run --user --pipe -p ImportCredential=brummbaer systemd-creds cat brummbaer | cmp /tmp/brummbaer.data
# https://github.com/systemd/systemd/pull/40108
run0 -u testuser --pipe systemd-run --user --wait -p ImportCredential=brummbaer -p ExecStartPre='ls -l $CREDENTIALS_DIRECTORY' bash -c 'systemd-creds cat brummbaer | cmp /tmp/brummbaer.data'
# https://github.com/systemd/systemd/pull/39651
TESTUSER_CRED_DIR="/run/user/$(id -u testuser)/credentials"