diff --git a/man/org.freedesktop.login1.xml b/man/org.freedesktop.login1.xml
index b447ae70f6..cc4a146fff 100644
--- a/man/org.freedesktop.login1.xml
+++ b/man/org.freedesktop.login1.xml
@@ -1246,6 +1246,8 @@ node /org/freedesktop/login1/session/1 {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s RemoteUser = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly b ExtraDeviceAccess = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s Service = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s Desktop = '...';
@@ -1348,6 +1350,8 @@ node /org/freedesktop/login1/session/1 {
+
+
@@ -1537,6 +1541,10 @@ node /org/freedesktop/login1/session/1 {
RemoteHost and RemoteUser encode the remote host and user
if this is a remote session, or an empty string otherwise.
+ ExtraDeviceAccess encodes whether the session is granted access to additional
+ hardware devices, typically useful for for graphical, remote session. If true, the session is granted
+ access to all devices tagged with xaccess in udev.
+
Service encodes the PAM service name that registered the session.
Desktop describes the desktop environment running in the session (if
@@ -1671,6 +1679,7 @@ node /org/freedesktop/login1/session/1 {
SetTTY() was added in version 254.
SetClass() was added in version 256.
LeaderPIDFDId was added in version 258.
+ ExtraDeviceAccess was added in version 260.
diff --git a/man/pam_systemd.xml b/man/pam_systemd.xml
index 5b6f2fa732..ee3060d670 100644
--- a/man/pam_systemd.xml
+++ b/man/pam_systemd.xml
@@ -404,6 +404,17 @@
+
+
+ $XDG_SESSION_EXTRA_DEVICE_ACCESS
+
+ Whether or not the session shall be granted additional hardware device access,
+ typically useful for graphical, remote session. If true, the session is granted access to all
+ devices tagged with xaccess in udev. Typically, rendering device nodes of
+ the GPU are tagged like this.
+
+
+
If not set, pam_systemd will initialize
diff --git a/man/sd-login.xml b/man/sd-login.xml
index d052024779..47ea4680b6 100644
--- a/man/sd-login.xml
+++ b/man/sd-login.xml
@@ -182,6 +182,25 @@
+
+ Tag uaccess
+
+ When set, access to this device is tied to an active seat.
+ As the session on the seat becomes active or inactive, access to the device
+ is updated accordingly.
+
+
+
+ Tag xaccess
+
+ When set, access to this device is granted to sessions
+ created with ExtraDeviceAccess. This is typically useful
+ for graphical, remote sessions. As the ExtraDeviceAccess
+ sessions open and close, access to the device is updated accordingly.
+
+
+
+
Property ID_SEAT
diff --git a/man/sd_session_is_active.xml b/man/sd_session_is_active.xml
index 92cd669dc4..3a3935cfef 100644
--- a/man/sd_session_is_active.xml
+++ b/man/sd_session_is_active.xml
@@ -34,6 +34,7 @@
sd_session_get_remote_host
sd_session_get_remote_user
sd_session_get_leader
+ sd_session_has_extra_device_access
Determine state of a specific session
@@ -140,6 +141,11 @@
const char *session
unsigned int *vt
+
+
+ int sd_session_has_extra_device_access
+ const char *session
+
@@ -274,6 +280,12 @@
session identifier. This function will return an error if the seat
does not support VTs.
+ sd_session_has_extra_device_access() may
+ be used to determine whether the session is granted access to
+ additional hardware devices, typically useful for for graphical,
+ remote session. If true, the session is granted access to all
+ devices tagged with xaccess in udev.
+
If the session parameter of any of these
functions is passed as NULL, the operation is
executed for the session the calling process is a member of, if
@@ -284,9 +296,10 @@
Return Value
If the test succeeds,
- sd_session_is_active() and
- sd_session_is_remote() return a
- positive integer; if it fails, 0. On success,
+ sd_session_is_active(),
+ sd_session_is_remote(), and
+ sd_session_has_extra_device_access()
+ return a positive integer; if it fails, 0. On success,
sd_session_get_state(),
sd_session_get_uid(),
sd_session_get_username(),
@@ -353,6 +366,7 @@
sd_session_get_username(),
sd_session_get_start_time(), and
sd_session_get_leader() were added in version 254.
+ sd_session_has_extra_device_access() was added in version 260.
diff --git a/rules.d/70-uaccess.rules.in b/rules.d/70-uaccess.rules.in
index 96b61cec32..84ee00dcb0 100644
--- a/rules.d/70-uaccess.rules.in
+++ b/rules.d/70-uaccess.rules.in
@@ -46,7 +46,7 @@ SUBSYSTEM=="firewire", TEST=="units", ENV{IEEE1394_UNIT_FUNCTION_VIDEO}=="1", TA
SUBSYSTEM=="drm", KERNEL=="card*", TAG+="uaccess"
{% if GROUP_RENDER_UACCESS %}
# DRI render nodes
-SUBSYSTEM=="drm", KERNEL=="renderD*", TAG+="uaccess"
+SUBSYSTEM=="drm", KERNEL=="renderD*", TAG+="uaccess", TAG+="xaccess"
{% endif %}
{% if DEV_KVM_UACCESS %}
# KVM
diff --git a/rules.d/73-seat-late.rules.in b/rules.d/73-seat-late.rules.in
index 7cda2b08ce..2e451ce600 100644
--- a/rules.d/73-seat-late.rules.in
+++ b/rules.d/73-seat-late.rules.in
@@ -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"
diff --git a/src/core/manager.c b/src/core/manager.c
index 277935bab2..fec1106565 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -697,6 +697,7 @@ int manager_default_environment(Manager *m) {
"XDG_SESSION_CLASS",
"XDG_SESSION_TYPE",
"XDG_SESSION_DESKTOP",
+ "XDG_SESSION_EXTRA_DEVICE_ACCESS",
"XDG_SEAT",
"XDG_VTNR");
}
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index a1fa794264..735aa78214 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -1085,3 +1085,8 @@ global:
sd_event_get_exit_on_idle;
sd_varlink_is_connected;
} LIBSYSTEMD_258;
+
+LIBSYSTEMD_260 {
+global:
+ sd_session_has_extra_device_access;
+} LIBSYSTEMD_259;
diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c
index f65fb2077e..bf214ab661 100644
--- a/src/libsystemd/sd-login/sd-login.c
+++ b/src/libsystemd/sd-login/sd-login.c
@@ -677,6 +677,25 @@ _public_ int sd_session_is_remote(const char *session) {
return parse_boolean(s);
}
+_public_ int sd_session_has_extra_device_access(const char *session) {
+ _cleanup_free_ char *p = NULL, *s = NULL;
+ int r;
+
+ r = file_of_session(session, &p);
+ if (r < 0)
+ return r;
+
+ r = parse_env_file(/* f= */ NULL, p, "EXTRA_DEVICE_ACCESS", &s);
+ if (r == -ENOENT)
+ return -ENXIO;
+ if (r < 0)
+ return r;
+ if (isempty(s))
+ return -ENODATA;
+
+ return parse_boolean(s);
+}
+
_public_ int sd_session_get_state(const char *session, char **ret_state) {
_cleanup_free_ char *p = NULL, *s = NULL;
int r;
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index ab113afbab..8ccaa9a1cd 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -898,6 +898,7 @@ int manager_create_session(
bool remote,
const char *remote_user,
const char *remote_host,
+ bool extra_device_access,
Session **ret_session) {
bool mangle_class = false;
@@ -1004,6 +1005,7 @@ int manager_create_session(
session->original_type = session->type = type;
session->remote = remote;
+ session->extra_device_access = extra_device_access;
session->vtnr = vtnr;
session->class = class;
@@ -1227,6 +1229,7 @@ static int manager_create_session_by_bus(
remote,
remote_user,
remote_host,
+ /* extra_device_access= */ false,
&session);
if (r == -EBUSY)
return sd_bus_error_set(error, BUS_ERROR_SESSION_BUSY, "Already running in a session or user slice");
diff --git a/src/login/logind-dbus.h b/src/login/logind-dbus.h
index febc78aa4b..e254e62195 100644
--- a/src/login/logind-dbus.h
+++ b/src/login/logind-dbus.h
@@ -57,6 +57,7 @@ int manager_create_session(
bool remote,
const char *remote_user,
const char *remote_host,
+ bool extra_device_access,
Session **ret_session);
extern const BusObjectImplementation manager_object;
diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c
index cc71efb60e..77f006a479 100644
--- a/src/login/logind-seat.c
+++ b/src/login/logind-seat.c
@@ -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;
}
diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c
index e6513949b1..21d16dbd8d 100644
--- a/src/login/logind-session-dbus.c
+++ b/src/login/logind-session-dbus.c
@@ -985,6 +985,7 @@ static const sd_bus_vtable session_vtable[] = {
SD_BUS_PROPERTY("Remote", "b", bus_property_get_bool, offsetof(Session, remote), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RemoteHost", "s", NULL, offsetof(Session, remote_host), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RemoteUser", "s", NULL, offsetof(Session, remote_user), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("ExtraDeviceAccess", "b", bus_property_get_bool, offsetof(Session, extra_device_access), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Session, service), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Desktop", "s", NULL, offsetof(Session, desktop), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Session, scope), SD_BUS_VTABLE_PROPERTY_CONST),
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index b30252cd09..2b2c36b1d4 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -9,6 +9,7 @@
#include
#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;
@@ -307,12 +349,14 @@ int session_save(Session *s) {
"IS_DISPLAY=%s\n"
"STATE=%s\n"
"REMOTE=%s\n"
+ "EXTRA_DEVICE_ACCESS=%s\n"
"LEADER_FD_SAVED=%s\n",
s->user->user_record->uid,
one_zero(session_is_active(s)),
one_zero(s->user->display == s),
session_state_to_string(session_get_state(s)),
one_zero(s->remote),
+ one_zero(s->extra_device_access),
one_zero(s->leader_fd_saved));
env_file_fputs_assignment(f, "USER=", s->user->user_record->user_name);
@@ -453,6 +497,7 @@ static int session_load_leader(Session *s, uint64_t pidfdid) {
int session_load(Session *s) {
_cleanup_free_ char *remote = NULL,
+ *extra_device_access = NULL,
*seat = NULL,
*tty_validity = NULL,
*vtnr = NULL,
@@ -478,34 +523,35 @@ int session_load(Session *s) {
assert(s);
r = parse_env_file(NULL, s->state_file,
- "REMOTE", &remote,
- "SCOPE", &s->scope,
- "SCOPE_JOB", &s->scope_job,
- "FIFO", &fifo_path,
- "SEAT", &seat,
- "TTY", &s->tty,
- "TTY_VALIDITY", &tty_validity,
- "DISPLAY", &s->display,
- "REMOTE_HOST", &s->remote_host,
- "REMOTE_USER", &s->remote_user,
- "SERVICE", &s->service,
- "DESKTOP", &s->desktop,
- "VTNR", &vtnr,
- "STATE", &state,
- "POSITION", &position,
- "LEADER", &leader_pid,
- "LEADER_FD_SAVED", &leader_fd_saved,
- "LEADER_PIDFDID", &leader_pidfdid,
- "TYPE", &type,
- "ORIGINAL_TYPE", &original_type,
- "CLASS", &class,
- "UID", &uid,
- "REALTIME", &realtime,
- "MONOTONIC", &monotonic,
- "CONTROLLER", &controller,
- "ACTIVE", &active,
- "DEVICES", &devices,
- "IS_DISPLAY", &is_display);
+ "REMOTE", &remote,
+ "EXTRA_DEVICE_ACCESS", &extra_device_access,
+ "SCOPE", &s->scope,
+ "SCOPE_JOB", &s->scope_job,
+ "FIFO", &fifo_path,
+ "SEAT", &seat,
+ "TTY", &s->tty,
+ "TTY_VALIDITY", &tty_validity,
+ "DISPLAY", &s->display,
+ "REMOTE_HOST", &s->remote_host,
+ "REMOTE_USER", &s->remote_user,
+ "SERVICE", &s->service,
+ "DESKTOP", &s->desktop,
+ "VTNR", &vtnr,
+ "STATE", &state,
+ "POSITION", &position,
+ "LEADER", &leader_pid,
+ "LEADER_FD_SAVED", &leader_fd_saved,
+ "LEADER_PIDFDID", &leader_pidfdid,
+ "TYPE", &type,
+ "ORIGINAL_TYPE", &original_type,
+ "CLASS", &class,
+ "UID", &uid,
+ "REALTIME", &realtime,
+ "MONOTONIC", &monotonic,
+ "CONTROLLER", &controller,
+ "ACTIVE", &active,
+ "DEVICES", &devices,
+ "IS_DISPLAY", &is_display);
if (r < 0)
return log_error_errno(r, "Failed to read %s: %m", s->state_file);
@@ -539,6 +585,12 @@ int session_load(Session *s) {
s->remote = k;
}
+ if (extra_device_access) {
+ k = parse_boolean(extra_device_access);
+ if (k >= 0)
+ s->extra_device_access = k;
+ }
+
if (vtnr)
(void) safe_atou(vtnr, &s->vtnr);
@@ -863,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");
@@ -953,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;
}
diff --git a/src/login/logind-session.h b/src/login/logind-session.h
index d339006f17..ddebd43e55 100644
--- a/src/login/logind-session.h
+++ b/src/login/logind-session.h
@@ -121,6 +121,7 @@ typedef struct Session {
char *remote_host;
char *service;
char *desktop;
+ bool extra_device_access;
char *scope;
char *scope_job;
diff --git a/src/login/logind-varlink.c b/src/login/logind-varlink.c
index a17f64271e..0016f276f3 100644
--- a/src/login/logind-varlink.c
+++ b/src/login/logind-varlink.c
@@ -145,6 +145,7 @@ typedef struct CreateSessionParameters {
int remote;
const char *remote_user;
const char *remote_host;
+ bool extra_device_access;
} CreateSessionParameters;
static void create_session_parameters_done(CreateSessionParameters *p) {
@@ -156,19 +157,20 @@ static int vl_method_create_session(sd_varlink *link, sd_json_variant *parameter
int r;
static const sd_json_dispatch_field dispatch_table[] = {
- { "UID", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(CreateSessionParameters, uid), SD_JSON_MANDATORY },
- { "PID", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_pidref, offsetof(CreateSessionParameters, pid), SD_JSON_STRICT },
- { "Service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, service), 0 },
- { "Type", SD_JSON_VARIANT_STRING, json_dispatch_session_type, offsetof(CreateSessionParameters, type), SD_JSON_MANDATORY },
- { "Class", SD_JSON_VARIANT_STRING, json_dispatch_session_class, offsetof(CreateSessionParameters, class), SD_JSON_MANDATORY },
- { "Desktop", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, desktop), SD_JSON_STRICT },
- { "Seat", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, seat), 0 },
- { "VTNr", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, offsetof(CreateSessionParameters, vtnr), 0 },
- { "TTY", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, tty), 0 },
- { "Display", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, display), 0 },
- { "Remote", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(CreateSessionParameters, remote), 0 },
- { "RemoteUser", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, remote_user), 0 },
- { "RemoteHost", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, remote_host), 0 },
+ { "UID", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(CreateSessionParameters, uid), SD_JSON_MANDATORY },
+ { "PID", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_pidref, offsetof(CreateSessionParameters, pid), SD_JSON_STRICT },
+ { "Service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, service), 0 },
+ { "Type", SD_JSON_VARIANT_STRING, json_dispatch_session_type, offsetof(CreateSessionParameters, type), SD_JSON_MANDATORY },
+ { "Class", SD_JSON_VARIANT_STRING, json_dispatch_session_class, offsetof(CreateSessionParameters, class), SD_JSON_MANDATORY },
+ { "Desktop", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, desktop), SD_JSON_STRICT },
+ { "Seat", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, seat), 0 },
+ { "VTNr", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, offsetof(CreateSessionParameters, vtnr), 0 },
+ { "TTY", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, tty), 0 },
+ { "Display", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, display), 0 },
+ { "Remote", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(CreateSessionParameters, remote), 0 },
+ { "RemoteUser", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, remote_user), 0 },
+ { "RemoteHost", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, remote_host), 0 },
+ { "ExtraDeviceAccess", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(CreateSessionParameters, extra_device_access), 0 },
{}
};
@@ -178,6 +180,7 @@ static int vl_method_create_session(sd_varlink *link, sd_json_variant *parameter
.class = _SESSION_CLASS_INVALID,
.type = _SESSION_TYPE_INVALID,
.remote = -1,
+ .extra_device_access = false,
};
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
@@ -264,6 +267,7 @@ static int vl_method_create_session(sd_varlink *link, sd_json_variant *parameter
p.remote,
p.remote_user,
p.remote_host,
+ p.extra_device_access,
&session);
if (r == -EBUSY)
return sd_varlink_error(link, "io.systemd.Login.AlreadySessionMember", /* parameters= */ NULL);
diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c
index ea4cf8193b..00b93a3dc4 100644
--- a/src/login/pam_systemd.c
+++ b/src/login/pam_systemd.c
@@ -814,6 +814,7 @@ typedef struct SessionContext {
uint32_t vtnr;
const char *tty;
const char *display;
+ bool extra_device_access;
bool remote;
const char *remote_user;
const char *remote_host;
@@ -1148,7 +1149,8 @@ static int register_session(
JSON_BUILD_PAIR_STRING_NON_EMPTY("Display", c->display),
SD_JSON_BUILD_PAIR_BOOLEAN("Remote", c->remote),
JSON_BUILD_PAIR_STRING_NON_EMPTY("RemoteUser", c->remote_user),
- JSON_BUILD_PAIR_STRING_NON_EMPTY("RemoteHost", c->remote_host));
+ JSON_BUILD_PAIR_STRING_NON_EMPTY("RemoteHost", c->remote_host),
+ JSON_BUILD_PAIR_CONDITION_BOOLEAN(c->extra_device_access, "ExtraDeviceAccess", c->extra_device_access));
if (r < 0)
return pam_syslog_errno(pamh, LOG_ERR, r,
"Failed to issue io.systemd.Login.CreateSession varlink call: %m");
@@ -1315,6 +1317,10 @@ static int register_session(
if (r != PAM_SUCCESS)
return r;
+ r = update_environment(pamh, "XDG_SESSION_EXTRA_DEVICE_ACCESS", one_zero(c->extra_device_access));
+ if (r != PAM_SUCCESS)
+ return r;
+
r = update_environment(pamh, "XDG_SEAT", real_seat);
if (r != PAM_SUCCESS)
return r;
@@ -1781,6 +1787,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
c.desktop = getenv_harder(pamh, "XDG_SESSION_DESKTOP", desktop_pam);
c.area = getenv_harder(pamh, "XDG_AREA", area_pam);
c.incomplete = getenv_harder_bool(pamh, "XDG_SESSION_INCOMPLETE", false);
+ c.extra_device_access = getenv_harder_bool(pamh, "XDG_SESSION_EXTRA_DEVICE_ACCESS", false);
r = pam_get_data_many(
pamh,
diff --git a/src/shared/acl-util.c b/src/shared/acl-util.c
index 24384e64ad..ae4684414c 100644
--- a/src/shared/acl-util.c
+++ b/src/shared/acl-util.c
@@ -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;
diff --git a/src/shared/acl-util.h b/src/shared/acl-util.h
index 0e0423ad25..1b74101ae4 100644
--- a/src/shared/acl-util.h
+++ b/src/shared/acl-util.h
@@ -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;
}
diff --git a/src/shared/varlink-io.systemd.Login.c b/src/shared/varlink-io.systemd.Login.c
index 92cb2dfd75..fe49a9221f 100644
--- a/src/shared/varlink-io.systemd.Login.c
+++ b/src/shared/varlink-io.systemd.Login.c
@@ -65,6 +65,10 @@ static SD_VARLINK_DEFINE_METHOD(
SD_VARLINK_DEFINE_INPUT(RemoteUser, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Host name of the remote host"),
SD_VARLINK_DEFINE_INPUT(RemoteHost, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("If true this session is granted access to additional hardware devices, "
+ "typically useful for remote, graphical sessions. "
+ "This adds access for all devices tagged with \"xaccess\" in udev."),
+ SD_VARLINK_DEFINE_INPUT(ExtraDeviceAccess, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The identifier string of the session of the user."),
SD_VARLINK_DEFINE_OUTPUT(Id, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("The runtime path ($XDG_RUNTIME_DIR) of the user."),
diff --git a/src/systemd/sd-login.h b/src/systemd/sd-login.h
index 70f05cf306..042d753913 100644
--- a/src/systemd/sd-login.h
+++ b/src/systemd/sd-login.h
@@ -150,6 +150,9 @@ int sd_session_is_active(const char *session);
/* Return 1 if the session is remote. */
int sd_session_is_remote(const char *session);
+/* Return 1 if the session is granted extra device access. */
+int sd_session_has_extra_device_access(const char *session);
+
/* Get state from session. Possible states: online, active, closing.
* This function is a more generic version of sd_session_is_active(). */
int sd_session_get_state(const char *session, char **ret_state);
diff --git a/src/udev/udev-builtin-uaccess.c b/src/udev/udev-builtin-uaccess.c
index fcbf267465..e77fc70909 100644
--- a/src/udev/udev-builtin-uaccess.c
+++ b/src/udev/udev-builtin-uaccess.c
@@ -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"));