udev: Grant sessions access to devices tagged with xaccess

Grant access to devices tagged with "xaccess" on session start, if the session
was created with XDG_SESSION_EXTRA_DEVICE_ACCESS=1.

udev-builtin-uaccess is refactored to grant multiple users access to a device,
taking into account the device's seat and all the active EXTRA_DEVICE_ACCESS
sessions.
This commit is contained in:
Alessandro Astone
2026-02-06 16:20:18 +01:00
parent 87840e144b
commit 5a198ad6f8
6 changed files with 140 additions and 30 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ ENV{ID_SEAT}=="", IMPORT{parent}="ID_SEAT"
ENV{ID_SEAT}!="", TAG+="$env{ID_SEAT}"
{% if HAVE_ACL %}
TAG=="uaccess", ENV{MAJOR}!="", RUN{builtin}+="uaccess"
TAG=="uaccess|xaccess", ENV{MAJOR}!="", RUN{builtin}+="uaccess"
{% endif %}
LABEL="seat_late_end"
+10 -8
View File
@@ -35,6 +35,7 @@
#include "tmpfile-util.h"
#include "udev-util.h"
#include "user-record.h"
#include "user-util.h"
int seat_new(Manager *m, const char *id, Seat **ret) {
_cleanup_(seat_freep) Seat *s = NULL;
@@ -330,14 +331,15 @@ static int seat_trigger_devices(Seat *s) {
static int static_node_acl(Seat *s) {
#if HAVE_ACL
int r, ret = 0;
uid_t uid;
_cleanup_set_free_ Set *uids = NULL;
assert(s);
if (s->active)
uid = s->active->user->user_record->uid;
else
uid = 0;
if (s->active) {
r = set_ensure_put(&uids, NULL, UID_TO_PTR(s->active->user->user_record->uid));
if (r < 0)
return log_oom();
}
_cleanup_closedir_ DIR *dir = opendir("/run/udev/static_node-tags/uaccess/");
if (!dir) {
@@ -377,7 +379,7 @@ static int static_node_acl(Seat *s) {
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);
r = devnode_acl(fd, uids);
if (r >= 0 || r == -ENOENT)
continue;
@@ -385,11 +387,11 @@ static int static_node_acl(Seat *s) {
_cleanup_free_ char *node = NULL;
(void) fd_get_path(fd, &node);
if (uid != 0) {
if (!set_isempty(uids)) {
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);
r = devnode_acl(fd, /* uids= */ NULL);
if (r >= 0 || r == -ENOENT)
continue;
}
+48
View File
@@ -9,6 +9,7 @@
#include <unistd.h>
#include "sd-bus.h"
#include "sd-device.h"
#include "sd-event.h"
#include "sd-messages.h"
#include "sd-varlink.h"
@@ -18,6 +19,7 @@
#include "bus-error.h"
#include "bus-util.h"
#include "daemon-util.h"
#include "device-util.h"
#include "devnum-util.h"
#include "env-file.h"
#include "errno-util.h"
@@ -276,6 +278,46 @@ static void session_save_devices(Session *s, FILE *f) {
}
}
static int trigger_xaccess(void) {
int r;
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
r = sd_device_enumerator_new(&e);
if (r < 0)
return r;
r = sd_device_enumerator_add_match_tag(e, "xaccess");
if (r < 0)
return r;
FOREACH_DEVICE(e, d) {
/* Verify that the tag is still in place. */
r = sd_device_has_current_tag(d, "xaccess");
if (r < 0)
return r;
if (r == 0)
continue;
/* In case people mistag devices without nodes, we need to ignore this. */
r = sd_device_get_devname(d, NULL);
if (r == -ENOENT)
continue;
if (r < 0)
return r;
sd_id128_t uuid;
r = sd_device_trigger_with_uuid(d, SD_DEVICE_CHANGE, &uuid);
if (r < 0) {
log_device_debug_errno(d, r, "Failed to trigger 'change' event, ignoring: %m");
continue;
}
log_device_debug(d, "Triggered synthetic event (ACTION=change, UUID=%s).", SD_ID128_TO_UUID_STRING(uuid));
}
return 0;
}
int session_save(Session *s) {
int r;
@@ -873,6 +915,9 @@ int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error) {
if (s->seat)
(void) seat_save(s->seat);
if (s->extra_device_access)
(void) trigger_xaccess();
/* Send signals */
(void) session_send_signal(s, true);
(void) user_send_changed(s->user, "Display");
@@ -963,6 +1008,9 @@ int session_stop(Session *s, bool force) {
(void) session_save(s);
(void) user_save(s->user);
if (s->extra_device_access)
(void) trigger_xaccess();
return r;
}
+20 -6
View File
@@ -8,6 +8,7 @@
#include "errno-util.h"
#include "extract-word.h"
#include "fd-util.h"
#include "set.h"
#include "string-util.h"
#include "strv.h"
#include "user-util.h"
@@ -80,8 +81,9 @@ int dlopen_libacl(void) {
DLSYM_ARG(acl_to_any_text));
}
int devnode_acl(int fd, uid_t uid) {
bool changed = false, found = false;
int devnode_acl(int fd, const Set *uids) {
_cleanup_set_free_ Set *found = NULL;
bool changed = false;
int r;
assert(fd >= 0);
@@ -107,12 +109,12 @@ int devnode_acl(int fd, uid_t uid) {
if (tag != ACL_USER)
continue;
if (uid > 0) {
if (!set_isempty(uids)) {
uid_t *u = sym_acl_get_qualifier(entry);
if (!u)
return -errno;
if (*u == uid) {
if (set_contains(uids, UID_TO_PTR(*u))) {
acl_permset_t permset;
if (sym_acl_get_permset(entry, &permset) < 0)
return -errno;
@@ -132,7 +134,10 @@ int devnode_acl(int fd, uid_t uid) {
changed = true;
}
found = true;
r = set_ensure_put(&found, NULL, UID_TO_PTR(*u));
if (r < 0)
return r;
continue;
}
}
@@ -145,7 +150,16 @@ int devnode_acl(int fd, uid_t uid) {
if (r < 0)
return -errno;
if (!found && uid > 0) {
void *p;
SET_FOREACH(p, uids) {
uid_t uid = PTR_TO_UID(p);
if (uid == 0)
continue;
if (set_contains(found, UID_TO_PTR(uid)))
continue;
if (sym_acl_create_entry(&acl, &entry) < 0)
return -errno;
+2 -2
View File
@@ -38,7 +38,7 @@ extern DLSYM_PROTOTYPE(acl_to_any_text);
int dlopen_libacl(void);
int devnode_acl(int fd, uid_t uid);
int devnode_acl(int fd, const Set *uids);
int calc_acl_mask_if_needed(acl_t *acl_p);
int add_base_acls_if_needed(acl_t *acl_p, const char *path);
@@ -89,7 +89,7 @@ static inline int dlopen_libacl(void) {
return -EOPNOTSUPP;
}
static inline int devnode_acl(int fd, uid_t uid) {
static inline int devnode_acl(int fd, const Set *uids) {
return -EOPNOTSUPP;
}
+59 -13
View File
@@ -3,15 +3,23 @@
#include "sd-login.h"
#include "acl-util.h"
#include "alloc-util.h"
#include "device-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "login-util.h"
#include "set.h"
#include "string-util.h"
#include "strv.h"
#include "udev-builtin.h"
#include "user-util.h"
static int builtin_uaccess(UdevEvent *event, int argc, char *argv[]) {
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
int r, k;
_cleanup_strv_free_ char **sessions = NULL;
_cleanup_set_free_ Set *uids = NULL;
uid_t uid;
int r = 0, k;
if (event->event_mode != EVENT_UDEV_WORKER) {
log_device_debug(dev, "Running in test mode, skipping execution of 'uaccess' builtin command.");
@@ -33,24 +41,62 @@ static int builtin_uaccess(UdevEvent *event, int argc, char *argv[]) {
return ignore ? 0 : fd;
}
const char *seat;
r = device_get_seat(dev, &seat);
r = sd_device_has_tag(dev, "uaccess");
if (r < 0)
return log_device_error_errno(dev, r, "Failed to get seat: %m");
return log_device_error_errno(dev, r, "Failed to query uaccess tag: %m");
uid_t uid;
r = sd_seat_get_active(seat, /* ret_session= */ NULL, &uid);
if (r < 0) {
if (r > 0) {
const char *seat;
r = device_get_seat(dev, &seat);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to get seat: %m");
r = sd_seat_get_active(seat, /* ret_session= */ NULL, &uid);
if (IN_SET(r, -ENXIO, -ENODATA))
/* No active session on this seat */
r = 0;
else
log_device_error_errno(dev, r, "Failed to determine active user on seat %s: %m", seat);
goto reset;
else if (r < 0)
log_device_error_errno(dev, r, "Failed to determine active user on seat %s, ignoring: %m", seat);
else {
if (set_ensure_put(&uids, NULL, UID_TO_PTR(uid)) < 0)
return log_oom();
}
}
r = devnode_acl(fd, uid);
r = sd_device_has_tag(dev, "xaccess");
if (r < 0)
return log_device_error_errno(dev, r, "Failed to query device xaccess tag: %m");
if (r > 0) {
r = sd_get_sessions(&sessions);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to list sessions: %m");
STRV_FOREACH(s, sessions) {
_cleanup_free_ char *state = NULL;
if (sd_session_get_state(*s, &state) < 0) {
log_device_debug_errno(dev, r, "Failed to query state for session %s, ignoring: %m", *s);
continue;
}
if (streq(state, "closing"))
continue;
r = sd_session_has_extra_device_access(*s);
if (r < 0) {
log_device_debug_errno(dev, r, "Failed to query extra device access for session %s, ignoring: %m", *s);
continue;
}
if (r == 0)
continue;
if (sd_session_get_uid(*s, &uid) < 0) {
log_device_debug_errno(dev, r, "Failed to query uid for session %s, ignoring: %m", *s);
continue;
}
if (set_ensure_put(&uids, NULL, UID_TO_PTR(uid)) < 0)
return log_oom();
}
}
r = devnode_acl(fd, uids);
if (r < 0) {
log_device_full_errno(dev, r == -ENOENT ? LOG_DEBUG : LOG_ERR, r, "Failed to apply ACL: %m");
goto reset;
@@ -60,7 +106,7 @@ static int builtin_uaccess(UdevEvent *event, int argc, char *argv[]) {
reset:
/* Better be safe than sorry and reset ACL */
k = devnode_acl(fd, /* uid= */ 0);
k = devnode_acl(fd, /* uids= */ NULL);
if (k < 0)
RET_GATHER(r, log_device_full_errno(dev, k == -ENOENT ? LOG_DEBUG : LOG_ERR, k, "Failed to flush ACLs: %m"));