From: Michal Sekletar Date: Fri, 6 Oct 2023 10:26:44 +0000 (+0200) Subject: logind: introduce CreateSessionWithPIDFD() X-Git-Tag: v255-rc1~128 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=76f2191d8eb54d7b9e39ab230c9c62b8a8c42265;p=thirdparty%2Fsystemd.git logind: introduce CreateSessionWithPIDFD() This new D-Bus API uses pidfd to refer to the session leader. Also, pam_systemd will try to make use of it when pidfd support is available. --- diff --git a/man/org.freedesktop.login1.xml b/man/org.freedesktop.login1.xml index 954518992be..2c1b9118eaa 100644 --- a/man/org.freedesktop.login1.xml +++ b/man/org.freedesktop.login1.xml @@ -80,6 +80,30 @@ node /org/freedesktop/login1 { out u vtnr, out b existing); @org.freedesktop.systemd1.Privileged("true") + CreateSessionWithPIDFD(in u uid, + in h pidfd, + in s service, + in s type, + in s class, + in s desktop, + in s seat_id, + in u vtnr, + in s tty, + in s display, + in b remote, + in s remote_user, + in s remote_host, + in t flags, + in a(sv) properties, + out s session_id, + out o object_path, + out s runtime_path, + out h fifo_fd, + out u uid, + out s seat_id, + out u vtnr, + out b existing); + @org.freedesktop.systemd1.Privileged("true") ReleaseSession(in s session_id); ActivateSession(in s session_id); ActivateSessionOnSeat(in s session_id, @@ -294,6 +318,8 @@ node /org/freedesktop/login1 { + + @@ -525,10 +551,10 @@ node /org/freedesktop/login1 { structures consisting of what, who, why, mode, uid (user ID), and pid (process ID). - CreateSession() and ReleaseSession() may be used to - open or close login sessions. These calls should never be invoked directly by - clients. Creating/closing sessions is exclusively the job of PAM and its - pam_systemd8 + CreateSession(), CreateSessionWithPIDFD(), and + ReleaseSession() may be used to open or close login sessions. These calls should + never be invoked directly by clients. Creating/closing sessions is exclusively the job + of PAM and its pam_systemd8 module. ActivateSession() brings the session with the specified ID into the @@ -1520,7 +1546,8 @@ node /org/freedesktop/login1/session/1 { HandleSuspendKeyLongPress, and HandleHibernateKeyLongPress were added in version 251. StopIdleSessionUSec was added in version 252. - PrepareForShutdownWithMetadata was added in version 255. + PrepareForShutdownWithMetadata and + CreateSessionWithPIDFD() were added in version 255. Session Objects diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index f45a944f17e..dc06d67fef7 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -677,36 +677,73 @@ static int method_list_inhibitors(sd_bus_message *message, void *userdata, sd_bu return sd_bus_send(NULL, reply, NULL); } -static int method_create_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { - const char *service, *type, *class, *cseat, *tty, *display, *remote_user, *remote_host, *desktop; +static int create_session( + sd_bus_message *message, + void *userdata, + sd_bus_error *error, + uid_t uid, + pid_t pid, + int pidfd, + const char *service, + const char *type, + const char *class, + const char *desktop, + const char *cseat, + uint32_t vtnr, + const char *tty, + const char *display, + int remote, + const char *remote_user, + const char *remote_host, + uint64_t flags) { + + _cleanup_(pidref_done) PidRef leader = PIDREF_NULL; + Manager *m = ASSERT_PTR(userdata); _cleanup_free_ char *id = NULL; Session *session = NULL; uint32_t audit_id = 0; - Manager *m = ASSERT_PTR(userdata); User *user = NULL; Seat *seat = NULL; - pid_t leader; - uid_t uid; - int remote; - uint32_t vtnr = 0; SessionType t; SessionClass c; int r; assert(message); - assert_cc(sizeof(pid_t) == sizeof(uint32_t)); - assert_cc(sizeof(uid_t) == sizeof(uint32_t)); - - r = sd_bus_message_read(message, "uusssssussbss", - &uid, &leader, &service, &type, &class, &desktop, &cseat, - &vtnr, &tty, &display, &remote, &remote_user, &remote_host); - if (r < 0) - return r; - if (!uid_is_valid(uid)) return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID"); - if (leader < 0 || leader == 1 || leader == getpid_cached()) + + if (flags != 0) + return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Flags must be zero."); + + if (pidfd >= 0) { + r = pidref_set_pidfd(&leader, pidfd); + if (r < 0) + return r; + } else if (pid == 0) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + pid_t p; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_pid(creds, &p); + if (r < 0) + return r; + + r = pidref_set_pid(&leader, p); + if (r < 0) + return r; + } else { + assert(pid > 0); + + r = pidref_set_pid(&leader, pid); + if (r < 0) + return r; + } + + if (leader.pid == 1 || leader.pid == getpid_cached()) return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID"); if (isempty(type)) @@ -805,21 +842,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus c = SESSION_USER; } - if (leader == 0) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_pid(creds, (pid_t*) &leader); - if (r < 0) - return r; - } - /* Check if we are already in a logind session. Or if we are in user@.service * which is a special PAM session that avoids creating a logind session. */ - r = manager_get_user_by_pid(m, leader, NULL); + r = manager_get_user_by_pid(m, leader.pid, NULL); if (r < 0) return r; if (r > 0) @@ -848,7 +873,7 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus "Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.", m->sessions_max); - (void) audit_session_from_pid(leader, &audit_id); + (void) audit_session_from_pid(leader.pid, &audit_id); if (audit_session_is_valid(audit_id)) { /* Keep our session IDs and the audit session IDs in sync */ @@ -890,7 +915,7 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus goto fail; session_set_user(session, user); - r = session_set_leader(session, leader); + r = session_set_leader_consume(session, TAKE_PIDREF(leader)); if (r < 0) goto fail; @@ -984,6 +1009,107 @@ fail: return r; } +static int method_create_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { + const char *service, *type, *class, *cseat, *tty, *display, *remote_user, *remote_host, *desktop; + pid_t leader; + uid_t uid; + int remote; + uint32_t vtnr = 0; + int r; + + assert(message); + + assert_cc(sizeof(pid_t) == sizeof(uint32_t)); + assert_cc(sizeof(uid_t) == sizeof(uint32_t)); + + r = sd_bus_message_read(message, + "uusssssussbss", + &uid, + &leader, + &service, + &type, + &class, + &desktop, + &cseat, + &vtnr, + &tty, + &display, + &remote, + &remote_user, + &remote_host); + if (r < 0) + return r; + + return create_session( + message, + userdata, + error, + uid, + leader, + /* pidfd = */ -EBADF, + service, + type, + class, + desktop, + cseat, + vtnr, + tty, + display, + remote, + remote_user, + remote_host, + /* flags = */ 0); +} + +static int method_create_session_pidfd(sd_bus_message *message, void *userdata, sd_bus_error *error) { + const char *service, *type, *class, *cseat, *tty, *display, *remote_user, *remote_host, *desktop; + int leaderfd = -EBADF; + uid_t uid; + int remote; + uint32_t vtnr = 0; + uint64_t flags; + int r; + + r = sd_bus_message_read(message, + "uhsssssussbsst", + &uid, + &leaderfd, + &service, + &type, + &class, + &desktop, + &cseat, + &vtnr, + &tty, + &display, + &remote, + &remote_user, + &remote_host, + &flags); + if (r < 0) + return r; + + return create_session( + message, + userdata, + error, + uid, + /* pid = */ 0, + leaderfd, + service, + type, + class, + desktop, + cseat, + vtnr, + tty, + display, + remote, + remote_user, + remote_host, + flags); +} + static int method_release_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = ASSERT_PTR(userdata); Session *session; @@ -3494,6 +3620,32 @@ static const sd_bus_vtable manager_vtable[] = { "b", existing), method_create_session, 0), + SD_BUS_METHOD_WITH_ARGS("CreateSessionWithPIDFD", + SD_BUS_ARGS("u", uid, + "h", pidfd, + "s", service, + "s", type, + "s", class, + "s", desktop, + "s", seat_id, + "u", vtnr, + "s", tty, + "s", display, + "b", remote, + "s", remote_user, + "s", remote_host, + "t", flags, + "a(sv)", properties), + SD_BUS_RESULT("s", session_id, + "o", object_path, + "s", runtime_path, + "h", fifo_fd, + "u", uid, + "s", seat_id, + "u", vtnr, + "b", existing), + method_create_session_pidfd, + 0), SD_BUS_METHOD_WITH_ARGS("ReleaseSession", SD_BUS_ARGS("s", session_id), SD_BUS_NO_RESULT, diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c index fe52cfa64e1..d9d6cb28566 100644 --- a/src/login/logind-session-dbus.c +++ b/src/login/logind-session-dbus.c @@ -810,6 +810,7 @@ int session_send_create_reply(Session *s, sd_bus_error *error) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL; _cleanup_close_ int fifo_fd = -EBADF; _cleanup_free_ char *p = NULL; + int r; assert(s); @@ -830,6 +831,10 @@ int session_send_create_reply(Session *s, sd_bus_error *error) { if (fifo_fd < 0) return fifo_fd; + r = session_watch_pidfd(s); + if (r < 0) + return r; + /* Update the session state file before we notify the client about the result. */ session_save(s); diff --git a/src/login/logind-session.c b/src/login/logind-session.c index 91361a8f6fb..8c8c5da6fb9 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -170,22 +170,16 @@ void session_set_user(Session *s, User *u) { user_update_last_session_timer(u); } -int session_set_leader(Session *s, pid_t pid) { - _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; +int session_set_leader_consume(Session *s, PidRef _leader) { + _cleanup_(pidref_done) PidRef pidref = _leader; int r; assert(s); + assert(pidref_is_set(&pidref)); - if (!pid_is_valid(pid)) - return -EINVAL; - - if (s->leader.pid == pid) + if (pidref_equal(&s->leader, &pidref)) return 0; - r = pidref_set_pid(&pidref, pid); - if (r < 0) - return r; - r = hashmap_put(s->manager->sessions_by_leader, PID_TO_PTR(pidref.pid), s); if (r < 0) return r; @@ -523,13 +517,13 @@ int session_load(Session *s) { } if (leader) { - pid_t pid; + _cleanup_(pidref_done) PidRef p = PIDREF_NULL; - r = parse_pid(leader, &pid); + r = pidref_set_pidstr(&p, leader); if (r < 0) log_debug_errno(r, "Failed to parse leader PID of session: %s", leader); else { - r = session_set_leader(s, pid); + r = session_set_leader_consume(s, TAKE_PIDREF(p)); if (r < 0) log_warning_errno(r, "Failed to set session leader PID, ignoring: %m"); } @@ -884,6 +878,7 @@ int session_stop(Session *s, bool force) { return 0; s->timer_event_source = sd_event_source_unref(s->timer_event_source); + s->leader_pidfd_event_source = sd_event_source_unref(s->leader_pidfd_event_source); if (s->seat) seat_evict_position(s->seat, s); @@ -921,6 +916,7 @@ int session_finalize(Session *s) { LOG_MESSAGE("Removed session %s.", s->id)); s->timer_event_source = sd_event_source_unref(s->timer_event_source); + s->leader_pidfd_event_source = sd_event_source_unref(s->leader_pidfd_event_source); if (s->seat) seat_evict_position(s->seat, s); @@ -945,6 +941,8 @@ int session_finalize(Session *s) { seat_save(s->seat); } + pidref_done(&s->leader); + user_save(s->user); user_send_changed(s->user, "Display", NULL); @@ -1226,6 +1224,36 @@ static void session_remove_fifo(Session *s) { } } +static int session_dispatch_leader_pidfd(sd_event_source *es, int fd, uint32_t revents, void *userdata) { + Session *s = ASSERT_PTR(userdata); + + assert(s->leader.fd == fd); + session_stop(s, /* force= */ false); + + return 1; +} + +int session_watch_pidfd(Session *s) { + int r; + + assert(s); + + if (s->leader.fd < 0) + return 0; + + r = sd_event_add_io(s->manager->event, &s->leader_pidfd_event_source, s->leader.fd, EPOLLIN, session_dispatch_leader_pidfd, s); + if (r < 0) + return r; + + r = sd_event_source_set_priority(s->leader_pidfd_event_source, SD_EVENT_PRIORITY_IMPORTANT); + if (r < 0) + return r; + + (void) sd_event_source_set_description(s->leader_pidfd_event_source, "session-pidfd"); + + return 0; +} + bool session_may_gc(Session *s, bool drop_not_started) { int r; @@ -1237,6 +1265,12 @@ bool session_may_gc(Session *s, bool drop_not_started) { if (!s->user) return true; + r = pidref_is_alive(&s->leader); + if (r < 0) + log_debug_errno(r, "Unable to determine if leader PID " PID_FMT " is still alive, assuming not.", s->leader.pid); + if (r > 0) + return false; + if (s->fifo_fd >= 0) { if (pipe_eof(s->fifo_fd) <= 0) return false; @@ -1282,7 +1316,7 @@ SessionState session_get_state(Session *s) { if (s->stopping || s->timer_event_source) return SESSION_CLOSING; - if (s->scope_job || s->fifo_fd < 0) + if (s->scope_job || (!pidref_is_set(&s->leader) && s->fifo_fd < 0)) return SESSION_OPENING; if (session_is_active(s)) diff --git a/src/login/logind-session.h b/src/login/logind-session.h index 7a4e66ff153..8b63843ed81 100644 --- a/src/login/logind-session.h +++ b/src/login/logind-session.h @@ -95,6 +95,7 @@ struct Session { char *fifo_path; sd_event_source *fifo_event_source; + sd_event_source *leader_pidfd_event_source; bool idle_hint; dual_timestamp idle_hint_timestamp; @@ -130,7 +131,7 @@ Session* session_free(Session *s); DEFINE_TRIVIAL_CLEANUP_FUNC(Session *, session_free); void session_set_user(Session *s, User *u); -int session_set_leader(Session *s, pid_t pid); +int session_set_leader_consume(Session *s, PidRef _leader); bool session_may_gc(Session *s, bool drop_not_started); void session_add_to_gc_queue(Session *s); int session_activate(Session *s); @@ -143,6 +144,7 @@ void session_set_type(Session *s, SessionType t); int session_set_display(Session *s, const char *display); int session_set_tty(Session *s, const char *tty); int session_create_fifo(Session *s); +int session_watch_pidfd(Session *s); int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error); int session_stop(Session *s, bool force); int session_finalize(Session *s); diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index 0c5d51c3a34..b8da266e277 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -34,6 +34,7 @@ #include "locale-util.h" #include "login-util.h" #include "macro.h" +#include "missing_syscall.h" #include "pam-util.h" #include "parse-util.h" #include "path-util.h" @@ -780,6 +781,101 @@ static uint64_t pick_default_capability_ambient_set( (streq_ptr(service, "systemd-user") || !isempty(seat)) ? (UINT64_C(1) << CAP_WAKE_ALARM) : UINT64_MAX; } +typedef struct SessionContext { + const uid_t uid; + const pid_t pid; + const char *service; + const char *type; + const char *class; + const char *desktop; + const char *seat; + const uint32_t vtnr; + const char *tty; + const char *display; + const bool remote; + const char *remote_user; + const char *remote_host; + const char *memory_max; + const char *tasks_max; + const char *cpu_weight; + const char *io_weight; + const char *runtime_max_sec; +} SessionContext; + +static int create_session_message(sd_bus *bus, pam_handle_t *handle, const SessionContext *context, bool avoid_pidfd, sd_bus_message **ret) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r, pidfd = -EBADFD; + + assert(bus); + assert(handle); + assert(context); + + if (!avoid_pidfd) { + pidfd = pidfd_open(getpid_cached(), 0); + if (pidfd < 0 && !ERRNO_IS_NOT_SUPPORTED(errno)) + return -errno; + } + + r = bus_message_new_method_call(bus, &m, bus_login_mgr, pidfd >= 0 ? "CreateSessionWithPIDFD" : "CreateSession"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, + pidfd >= 0 ? "uhsssssussbss" : "uusssssussbss", + (uint32_t) context->uid, + pidfd >= 0 ? pidfd : context->pid, + context->service, + context->type, + context->class, + context->desktop, + context->seat, + context->vtnr, + context->tty, + context->display, + context->remote, + context->remote_user, + context->remote_host); + if (r < 0) + return r; + + if (pidfd >= 0) { + r = sd_bus_message_append(m, "t", UINT64_C(0)); + if (r < 0) + return r; + } + + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return r; + + r = append_session_memory_max(handle, m, context->memory_max); + if (r != PAM_SUCCESS) + return r; + + r = append_session_runtime_max_sec(handle, m, context->runtime_max_sec); + if (r != PAM_SUCCESS) + return r; + + r = append_session_tasks_max(handle, m, context->tasks_max); + if (r != PAM_SUCCESS) + return r; + + r = append_session_cpu_weight(handle, m, context->cpu_weight); + if (r != PAM_SUCCESS) + return r; + + r = append_session_io_weight(handle, m, context->io_weight); + if (r != PAM_SUCCESS) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + *ret = TAKE_PTR(m); + return 0; +} + _public_ PAM_EXTERN int pam_sm_open_session( pam_handle_t *handle, int flags, @@ -963,52 +1059,32 @@ _public_ PAM_EXTERN int pam_sm_open_session( "memory_max=%s tasks_max=%s cpu_weight=%s io_weight=%s runtime_max_sec=%s", strna(memory_max), strna(tasks_max), strna(cpu_weight), strna(io_weight), strna(runtime_max_sec)); - r = bus_message_new_method_call(bus, &m, bus_login_mgr, "CreateSession"); - if (r < 0) - return pam_bus_log_create_error(handle, r); - - r = sd_bus_message_append(m, "uusssssussbss", - (uint32_t) ur->uid, - 0, - service, - type, - class, - desktop, - seat, - vtnr, - tty, - display, - remote, - remote_user, - remote_host); - if (r < 0) - return pam_bus_log_create_error(handle, r); - - r = sd_bus_message_open_container(m, 'a', "(sv)"); - if (r < 0) - return pam_bus_log_create_error(handle, r); - - r = append_session_memory_max(handle, m, memory_max); - if (r != PAM_SUCCESS) - return r; - - r = append_session_runtime_max_sec(handle, m, runtime_max_sec); - if (r != PAM_SUCCESS) - return r; - - r = append_session_tasks_max(handle, m, tasks_max); - if (r != PAM_SUCCESS) - return r; - - r = append_session_cpu_weight(handle, m, cpu_weight); - if (r != PAM_SUCCESS) - return r; - - r = append_session_io_weight(handle, m, io_weight); - if (r != PAM_SUCCESS) - return r; - - r = sd_bus_message_close_container(m); + const SessionContext context = { + .uid = ur->uid, + .pid = 0, + .service = service, + .type = type, + .class = class, + .desktop = desktop, + .seat = seat, + .vtnr = vtnr, + .tty = tty, + .display = display, + .remote = remote, + .remote_user = remote_user, + .remote_host = remote_host, + .memory_max = memory_max, + .tasks_max = tasks_max, + .cpu_weight = cpu_weight, + .io_weight = io_weight, + .runtime_max_sec = runtime_max_sec, + }; + + r = create_session_message(bus, + handle, + &context, + false /* avoid_pidfd = */, + &m); if (r < 0) return pam_bus_log_create_error(handle, r); @@ -1019,7 +1095,23 @@ _public_ PAM_EXTERN int pam_sm_open_session( "Not creating session: %s", bus_error_message(&error, r)); /* We are already in a session, don't do anything */ goto success; - } else { + } else if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) { + pam_debug_syslog(handle, debug, + "CreateSessionWithPIDFD() API is not available, retrying with CreateSession()."); + + m = sd_bus_message_unref(m); + r = create_session_message(bus, + handle, + &context, + true /* avoid_pidfd = */, + &m); + if (r < 0) + return pam_bus_log_create_error(handle, r); + + sd_bus_error_free(&error); + r = sd_bus_call(bus, m, LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply); + } + if (r < 0) { pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error, r)); return PAM_SESSION_ERR;