]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
logind: add ability to upgrade session class from 'user-incomplete' to 'user'
authorLennart Poettering <lennart@poettering.net>
Mon, 27 Nov 2023 16:48:37 +0000 (17:48 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 14 Feb 2024 14:00:46 +0000 (15:00 +0100)
man/org.freedesktop.login1.xml
src/login/logind-dbus.c
src/login/logind-session-dbus.c
src/login/logind-session-dbus.h
src/login/logind-session.c
src/login/logind-session.h
src/login/org.freedesktop.login1.conf

index 8b84f227ff1ff7ea19e7c98511a08fb0715c9418..ea466531607475c96c6b83defaa2338e5b24f930 100644 (file)
@@ -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 = '...';
index 199c7f9e9b8c754ee2b5b602088fe528855012cc..933b69542a1c884f87532d6c661ddded330fe977 100644 (file)
@@ -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);
                 }
index 7217b8147642c188d959c77d826bff94cf3f22e5..bd8618ba5057bbcf7ee5d7fef3d32c90153ae73e 100644 (file)
@@ -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,
index 751ca86c0dbcfe74e93bef6e68e604b1714e7079..a02656293821532dbf2c5d8a6b3210e18e71510b 100644 (file)
@@ -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);
index ffdd42002aeedd93ca97f50e4597adc8ebff3c77..0a2f04b021d442cd7b860d549ec7db97061607c1 100644 (file)
@@ -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;
 
index 34cf9b075bce3a3375f2c7cf5184a51cdbcdccad..7d8c03af868d6e9beca322d52ecb711f40ad3702 100644 (file)
@@ -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);
index e49496fce840be9deb22f9dbf3942b510b6d7ca8..9b59e9ce556c88f331d48dff2446cbbad5948636 100644 (file)
                        send_interface="org.freedesktop.login1.Session"
                        send_member="SetType"/>
 
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Session"
+                       send_member="SetClass"/>
+
                 <allow send_destination="org.freedesktop.login1"
                        send_interface="org.freedesktop.login1.Session"
                        send_member="TakeDevice"/>