From 41c4a69653f7ca48a449de172e73acd43eda81a4 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 23 Sep 2025 09:56:09 +0900 Subject: [PATCH 1/2] udev: move devnode_acl() back to libshared This effectively reverts 1abb592f2f886913492e4967cc96816c167177a9. No functional change, preparation for the next commit. --- src/shared/acl-util.c | 94 ++++++++++++++++++++++++++++++++ src/shared/acl-util.h | 6 +++ src/udev/udev-builtin-uaccess.c | 96 --------------------------------- 3 files changed, 100 insertions(+), 96 deletions(-) diff --git a/src/shared/acl-util.c b/src/shared/acl-util.c index 03bfcca9b6..0a5c8c9f92 100644 --- a/src/shared/acl-util.c +++ b/src/shared/acl-util.c @@ -6,12 +6,106 @@ #include "alloc-util.h" #include "errno-util.h" #include "extract-word.h" +#include "fd-util.h" #include "string-util.h" #include "strv.h" #include "user-util.h" #if HAVE_ACL +int devnode_acl(int fd, uid_t uid) { + bool changed = false, found = false; + int r; + + assert(fd >= 0); + + _cleanup_(acl_freep) acl_t acl = NULL; + acl = acl_get_file(FORMAT_PROC_FD_PATH(fd), ACL_TYPE_ACCESS); + if (!acl) + return -errno; + + acl_entry_t entry; + for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); + r > 0; + r = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry)) { + + acl_tag_t tag; + if (acl_get_tag_type(entry, &tag) < 0) + return -errno; + + if (tag != ACL_USER) + continue; + + if (uid > 0) { + uid_t *u = acl_get_qualifier(entry); + if (!u) + return -errno; + + if (*u == uid) { + acl_permset_t permset; + if (acl_get_permset(entry, &permset) < 0) + return -errno; + + int rd = acl_get_perm(permset, ACL_READ); + if (rd < 0) + return -errno; + + int wt = acl_get_perm(permset, ACL_WRITE); + if (wt < 0) + return -errno; + + if (!rd || !wt) { + if (acl_add_perm(permset, ACL_READ|ACL_WRITE) < 0) + return -errno; + + changed = true; + } + + found = true; + continue; + } + } + + if (acl_delete_entry(acl, entry) < 0) + return -errno; + + changed = true; + } + if (r < 0) + return -errno; + + if (!found && uid > 0) { + if (acl_create_entry(&acl, &entry) < 0) + return -errno; + + if (acl_set_tag_type(entry, ACL_USER) < 0) + return -errno; + + if (acl_set_qualifier(entry, &uid) < 0) + return -errno; + + acl_permset_t permset; + if (acl_get_permset(entry, &permset) < 0) + return -errno; + + if (acl_add_perm(permset, ACL_READ|ACL_WRITE) < 0) + return -errno; + + changed = true; + } + + if (!changed) + return 0; + + if (acl_calc_mask(&acl) < 0) + return -errno; + + if (acl_set_file(FORMAT_PROC_FD_PATH(fd), ACL_TYPE_ACCESS, acl) < 0) + return -errno; + + return 0; +} + static int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *ret_entry) { acl_entry_t i; int r; diff --git a/src/shared/acl-util.h b/src/shared/acl-util.h index 5e28f7167f..3e23fe3938 100644 --- a/src/shared/acl-util.h +++ b/src/shared/acl-util.h @@ -10,6 +10,8 @@ int fd_acl_make_writable_fallback(int fd); #include /* IWYU pragma: export */ #include /* IWYU pragma: export */ +int devnode_acl(int fd, uid_t uid); + int calc_acl_mask_if_needed(acl_t *acl_p); int add_base_acls_if_needed(acl_t *acl_p, const char *path); int acl_search_groups(const char* path, char ***ret_groups); @@ -40,6 +42,10 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(gid_t*, acl_free_gid_tp, NULL); #define ACL_WRITE 0x02 #define ACL_EXECUTE 0x01 +static inline int devnode_acl(int fd, uid_t uid) { + return -EOPNOTSUPP; +} + static inline int fd_add_uid_acl_permission(int fd, uid_t uid, unsigned mask) { return -EOPNOTSUPP; } diff --git a/src/udev/udev-builtin-uaccess.c b/src/udev/udev-builtin-uaccess.c index c382f957cf..e73ec7c03d 100644 --- a/src/udev/udev-builtin-uaccess.c +++ b/src/udev/udev-builtin-uaccess.c @@ -1,7 +1,4 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * manage device node user ACL - */ #include "sd-login.h" @@ -12,99 +9,6 @@ #include "login-util.h" #include "udev-builtin.h" -static int devnode_acl(int fd, uid_t uid) { - bool changed = false, found = false; - int r; - - assert(fd >= 0); - - _cleanup_(acl_freep) acl_t acl = NULL; - acl = acl_get_file(FORMAT_PROC_FD_PATH(fd), ACL_TYPE_ACCESS); - if (!acl) - return -errno; - - acl_entry_t entry; - for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); - r > 0; - r = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry)) { - - acl_tag_t tag; - if (acl_get_tag_type(entry, &tag) < 0) - return -errno; - - if (tag != ACL_USER) - continue; - - if (uid > 0) { - uid_t *u = acl_get_qualifier(entry); - if (!u) - return -errno; - - if (*u == uid) { - acl_permset_t permset; - if (acl_get_permset(entry, &permset) < 0) - return -errno; - - int rd = acl_get_perm(permset, ACL_READ); - if (rd < 0) - return -errno; - - int wt = acl_get_perm(permset, ACL_WRITE); - if (wt < 0) - return -errno; - - if (!rd || !wt) { - if (acl_add_perm(permset, ACL_READ|ACL_WRITE) < 0) - return -errno; - - changed = true; - } - - found = true; - continue; - } - } - - if (acl_delete_entry(acl, entry) < 0) - return -errno; - - changed = true; - } - if (r < 0) - return -errno; - - if (!found && uid > 0) { - if (acl_create_entry(&acl, &entry) < 0) - return -errno; - - if (acl_set_tag_type(entry, ACL_USER) < 0) - return -errno; - - if (acl_set_qualifier(entry, &uid) < 0) - return -errno; - - acl_permset_t permset; - if (acl_get_permset(entry, &permset) < 0) - return -errno; - - if (acl_add_perm(permset, ACL_READ|ACL_WRITE) < 0) - return -errno; - - changed = true; - } - - if (!changed) - return 0; - - if (acl_calc_mask(&acl) < 0) - return -errno; - - if (acl_set_file(FORMAT_PROC_FD_PATH(fd), ACL_TYPE_ACCESS, acl) < 0) - return -errno; - - return 0; -} - static int builtin_uaccess(UdevEvent *event, int argc, char *argv[]) { sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev); int r, k; From 2c762d90cfe7d67f10af87986ed2e6f0005eabfb Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 23 Sep 2025 10:17:47 +0900 Subject: [PATCH 2/2] login: update ACL on static device nodes again In the commit c960ca2be1cfd183675df581f049a0c022c1c802, the logic of updating ACL on device node was moved from logind to udevd, but at that time, mistakenly removed the logic for static nodes. Fixes a regression caused by c960ca2be1cfd183675df581f049a0c022c1c802 (v258). Fixes #39043. --- src/login/logind-seat.c | 92 ++++++++++++++++++++++++++++++++++++++++- src/login/meson.build | 1 + 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c index db476af251..d30cc6974f 100644 --- a/src/login/logind-seat.c +++ b/src/login/logind-seat.c @@ -6,8 +6,10 @@ #include "sd-messages.h" +#include "acl-util.h" #include "alloc-util.h" #include "device-util.h" +#include "dirent-util.h" #include "errno-util.h" #include "fd-util.h" #include "format-util.h" @@ -26,6 +28,7 @@ #include "mkdir-label.h" #include "path-util.h" #include "set.h" +#include "stat-util.h" #include "stdio-util.h" #include "string-util.h" #include "terminal-util.h" @@ -321,12 +324,88 @@ static int seat_trigger_devices(Seat *s) { return r; } - seat_triggered_uevents_done(s); return 0; } +static int static_node_acl(Seat *s) { +#if HAVE_ACL + int r, ret = 0; + uid_t uid; + + assert(s); + + if (s->active) + uid = s->active->user->user_record->uid; + else + uid = 0; + + _cleanup_closedir_ DIR *dir = opendir("/run/udev/static_node-tags/uaccess/"); + if (!dir) { + if (errno == ENOENT) + return 0; + + return log_debug_errno(errno, "Failed to open /run/udev/static_node-tags/uaccess/: %m"); + } + + FOREACH_DIRENT(de, dir, return -errno) { + _cleanup_close_ int fd = RET_NERRNO(openat(dirfd(dir), de->d_name, O_CLOEXEC|O_PATH)); + if (ERRNO_IS_NEG_DEVICE_ABSENT_OR_EMPTY(fd)) + continue; + if (fd < 0) { + RET_GATHER(ret, log_debug_errno(fd, "Failed to open '/run/udev/static_node-tags/uaccess/%s': %m", de->d_name)); + continue; + } + + struct stat st; + if (fstat(fd, &st) < 0) { + RET_GATHER(ret, log_debug_errno(errno, "Failed to stat '/run/udev/static_node-tags/uaccess/%s': %m", de->d_name)); + continue; + } + + r = stat_verify_device_node(&st); + if (r < 0) { + RET_GATHER(ret, log_debug_errno(fd, "'/run/udev/static_node-tags/uaccess/%s' points to a non-device node: %m", de->d_name)); + continue; + } + + _cleanup_(sd_device_unrefp) sd_device *dev = NULL; + r = sd_device_new_from_stat_rdev(&dev, &st); + if (r >= 0) { + log_device_debug(dev, "'/run/udev/static_node-tags/uaccess/%s' points to a non-static device node, ignoring.", de->d_name); + continue; + } + if (!ERRNO_IS_NEG_DEVICE_ABSENT_OR_EMPTY(r)) + log_debug_errno(r, "Failed to check if '/run/udev/static_node-tags/uaccess/%s' points to a static device node, ignoring: %m", de->d_name); + + r = devnode_acl(fd, uid); + if (r >= 0 || r == -ENOENT) + continue; + + /* de->d_name is escaped, like "snd\x2ftimer", hence let's use the path to node, if possible. */ + _cleanup_free_ char *node = NULL; + (void) fd_get_path(fd, &node); + + if (uid != 0) { + RET_GATHER(ret, log_debug_errno(r, "Failed to apply ACL on '%s': %m", node ?: de->d_name)); + + /* Better be safe than sorry and reset ACL */ + r = devnode_acl(fd, /* uid = */ 0); + if (r >= 0 || r == -ENOENT) + continue; + } + if (r < 0) + RET_GATHER(ret, log_debug_errno(r, "Failed to flush ACL on '%s': %m", node ?: de->d_name)); + } + + return ret; +#else + return 0; +#endif +} + int seat_set_active(Seat *s, Session *session) { Session *old_active; + int r; assert(s); assert(!session || session->seat == s); @@ -358,7 +437,16 @@ int seat_set_active(Seat *s, Session *session) { session_send_changed(old_active, "Active"); } - return seat_trigger_devices(s); + r = seat_trigger_devices(s); + if (r < 0) + return r; + + r = static_node_acl(s); + if (r < 0) + return r; + + seat_triggered_uevents_done(s); + return 0; } static Session* seat_get_position(Seat *s, unsigned pos) { diff --git a/src/login/meson.build b/src/login/meson.build index 0ad9beb167..954b7e1f3a 100644 --- a/src/login/meson.build +++ b/src/login/meson.build @@ -49,6 +49,7 @@ executables += [ 'include_directories' : [libexec_template['include_directories'], include_directories('.')], 'extract' : systemd_logind_extract_sources, 'dependencies' : [ + libacl, threads, ], },