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"));