From 6e9bf0ad29f4027586f854763c27a35fedcb75df Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 27 Nov 2023 17:48:37 +0100 Subject: [PATCH] logind: add ability to upgrade session class from 'user-incomplete' to 'user' --- man/org.freedesktop.login1.xml | 1 - src/login/logind-dbus.c | 16 ++++-- src/login/logind-session-dbus.c | 82 ++++++++++++++++++++++++++- src/login/logind-session-dbus.h | 1 + src/login/logind-session.c | 15 +++++ src/login/logind-session.h | 4 +- src/login/org.freedesktop.login1.conf | 4 ++ 7 files changed, 114 insertions(+), 9 deletions(-) diff --git a/man/org.freedesktop.login1.xml b/man/org.freedesktop.login1.xml index 8b84f227ff..ea46653160 100644 --- a/man/org.freedesktop.login1.xml +++ b/man/org.freedesktop.login1.xml @@ -1202,7 +1202,6 @@ node /org/freedesktop/login1/session/1 { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly u Audit = ...; readonly s Type = '...'; - @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s Class = '...'; readonly b Active = ...; readonly s State = '...'; diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 199c7f9e9b..933b69542a 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -4038,22 +4038,26 @@ const BusObjectImplementation manager_object = { &user_object), }; -static int session_jobs_reply(Session *s, uint32_t jid, const char *unit, const char *result) { +static void session_jobs_reply(Session *s, uint32_t jid, const char *unit, const char *result) { assert(s); assert(unit); if (!s->started) - return 0; + return; if (result && !streq(result, "done")) { _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL; sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Job %u for unit '%s' failed with '%s'", jid, unit, result); - return session_send_create_reply(s, &e); + + (void) session_send_create_reply(s, &e); + (void) session_send_upgrade_reply(s, &e); + return; } - return session_send_create_reply(s, NULL); + (void) session_send_create_reply(s, /* error= */ NULL); + (void) session_send_upgrade_reply(s, /* error= */ NULL); } int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -4089,7 +4093,7 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err if (session) { if (streq_ptr(path, session->scope_job)) { session->scope_job = mfree(session->scope_job); - (void) session_jobs_reply(session, id, unit, result); + session_jobs_reply(session, id, unit, result); session_save(session); user_save(session->user); @@ -4106,7 +4110,7 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err LIST_FOREACH(sessions_by_user, s, user->sessions) /* Don't propagate user service failures to the client */ - (void) session_jobs_reply(s, id, unit, /* error = */ NULL); + session_jobs_reply(s, id, unit, /* error = */ NULL /* don't propagate user service failures to the client */); user_save(user); } diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c index 7217b81476..bd8618ba50 100644 --- a/src/login/logind-session-dbus.c +++ b/src/login/logind-session-dbus.c @@ -404,6 +404,62 @@ static int method_set_type(sd_bus_message *message, void *userdata, sd_bus_error return sd_bus_reply_method_return(message, NULL); } +static int method_set_class(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + Session *s = ASSERT_PTR(userdata); + SessionClass class; + const char *c; + uid_t uid; + int r; + + assert(message); + + r = sd_bus_message_read(message, "s", &c); + if (r < 0) + return r; + + class = session_class_from_string(c); + if (class < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Invalid session class '%s'", c); + + /* For now, we'll allow only upgrades user-incomplete → user */ + if (class != SESSION_USER) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Class may only be set to 'user', refusing."); + if (s->class == SESSION_USER) /* No change, shortcut */ + return sd_bus_reply_method_return(message, NULL); + if (s->class != SESSION_USER_INCOMPLETE) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Only sessions with class 'user-incomplete' may change class, refusing."); + + if (s->upgrade_message) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Set session class operation already in progress, refsuing."); + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_euid(creds, &uid); + if (r < 0) + return r; + + if (uid != 0 && uid != s->user->user_record->uid) + return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may change its class"); + + session_set_class(s, class); + + sd_bus_message_unref(s->upgrade_message); + s->upgrade_message = sd_bus_message_ref(message); + + r = session_send_upgrade_reply(s, /* error= */ NULL); + if (r < 0) + return r; + + return 1; +} + static int method_set_display(sd_bus_message *message, void *userdata, sd_bus_error *error) { Session *s = ASSERT_PTR(userdata); const char *display; @@ -875,6 +931,25 @@ int session_send_create_reply(Session *s, sd_bus_error *error) { false); } +int session_send_upgrade_reply(Session *s, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL; + assert(s); + + if (!s->upgrade_message) + return 0; + + if (!sd_bus_error_is_set(error) && !session_ready(s)) + return 0; + + c = TAKE_PTR(s->upgrade_message); + if (error) + return sd_bus_reply_method_error(c, error); + + session_save(s); + + return sd_bus_reply_method_return(c, NULL); +} + static const sd_bus_vtable session_vtable[] = { SD_BUS_VTABLE_START(0), @@ -895,7 +970,7 @@ static const sd_bus_vtable session_vtable[] = { SD_BUS_PROPERTY("Leader", "u", bus_property_get_pid, offsetof(Session, leader.pid), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Audit", "u", NULL, offsetof(Session, audit_id), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Session, type), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Session, class), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Session, class), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("Active", "b", property_get_active, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("State", "s", property_get_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), @@ -953,6 +1028,11 @@ static const sd_bus_vtable session_vtable[] = { SD_BUS_NO_RESULT, method_set_type, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("SetClass", + SD_BUS_ARGS("s", class), + SD_BUS_NO_RESULT, + method_set_class, + SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD_WITH_ARGS("SetDisplay", SD_BUS_ARGS("s", display), SD_BUS_NO_RESULT, diff --git a/src/login/logind-session-dbus.h b/src/login/logind-session-dbus.h index 751ca86c0d..a026562938 100644 --- a/src/login/logind-session-dbus.h +++ b/src/login/logind-session-dbus.h @@ -16,6 +16,7 @@ int session_send_lock(Session *s, bool lock); int session_send_lock_all(Manager *m, bool lock); int session_send_create_reply(Session *s, sd_bus_error *error); +int session_send_upgrade_reply(Session *s, sd_bus_error *error); int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/login/logind-session.c b/src/login/logind-session.c index ffdd42002a..0a2f04b021 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -187,6 +187,7 @@ Session* session_free(Session *s) { session_reset_leader(s, /* keep_fdstore = */ true); sd_bus_message_unref(s->create_message); + sd_bus_message_unref(s->upgrade_message); free(s->tty); free(s->display); @@ -1210,6 +1211,20 @@ void session_set_type(Session *s, SessionType t) { (void) session_send_changed(s, "Type", NULL); } +void session_set_class(Session *s, SessionClass c) { + assert(s); + + if (s->class == c) + return; + + s->class = c; + (void) session_save(s); + (void) session_send_changed(s, "Class", NULL); + + /* This class change might mean we need the per-user session manager now. Try to start it */ + user_start_service_manager(s->user); +} + int session_set_display(Session *s, const char *display) { int r; diff --git a/src/login/logind-session.h b/src/login/logind-session.h index 34cf9b075b..7d8c03af86 100644 --- a/src/login/logind-session.h +++ b/src/login/logind-session.h @@ -146,7 +146,8 @@ struct Session { bool was_active:1; - sd_bus_message *create_message; + sd_bus_message *create_message; /* The D-Bus message used to create the session, which we haven't responded to yet */ + sd_bus_message *upgrade_message; /* The D-Bus message used to upgrade the session class user-incomplete → user, wich we haven't responded to yet */ /* Set up when a client requested to release the session via the bus */ sd_event_source *timer_event_source; @@ -179,6 +180,7 @@ int session_set_idle_hint(Session *s, bool b); int session_get_locked_hint(Session *s); int session_set_locked_hint(Session *s, bool b); void session_set_type(Session *s, SessionType t); +void session_set_class(Session *s, SessionClass c); int session_set_display(Session *s, const char *display); int session_set_tty(Session *s, const char *tty); int session_create_fifo(Session *s); diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf index e49496fce8..9b59e9ce55 100644 --- a/src/login/org.freedesktop.login1.conf +++ b/src/login/org.freedesktop.login1.conf @@ -334,6 +334,10 @@ send_interface="org.freedesktop.login1.Session" send_member="SetType"/> + + -- 2.25.1