]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev: Grant sessions access to devices tagged with xaccess
authorAlessandro Astone <alessandro.astone@canonical.com>
Wed, 26 Nov 2025 16:26:53 +0000 (17:26 +0100)
committerAlessandro Astone <alessandro.astone@canonical.com>
Fri, 6 Feb 2026 15:20:18 +0000 (16:20 +0100)
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.

rules.d/73-seat-late.rules.in
src/login/logind-seat.c
src/login/logind-session.c
src/shared/acl-util.c
src/shared/acl-util.h
src/udev/udev-builtin-uaccess.c

index 7cda2b08ceda1627098acb381af0037f634d3307..2e451ce600cdbd4b9c2af60afd89c92ed65ab797 100644 (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"
index cc71efb60edcb64a8b782ac28397c70d8d468baf..77f006a479d29cab2f8f3f2c7ab3ed1f115f65b6 100644 (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;
                 }
index 3724de793dccaf92b905d44ea5d5969d7bce818b..2b2c36b1d4654711dc78f44ce0283d1dc67b6f8d 100644 (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;
 }
 
index 24384e64add7348faec7f675335060d8d6bc0755..ae4684414ca8e9c60e84209ae0a732fb5e329e07 100644 (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;
 
index 0e0423ad25130bed2aa1e32a4286d95139cd939f..1b74101ae44a6f0783e8fe41b554b266bb89bf06 100644 (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;
 }
 
index fcbf2674652971826acedc15f3053f44e7426b5c..e77fc709094a17af08380cd44c0f1bdcabb14517 100644 (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);
+                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();
+                }
+        }
 
-                goto reset;
+        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, uid);
+        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"));