]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
logind: introduce CreateSessionWithPIDFD()
authorMichal Sekletar <msekleta@redhat.com>
Fri, 6 Oct 2023 10:26:44 +0000 (12:26 +0200)
committerLuca Boccassi <luca.boccassi@gmail.com>
Thu, 26 Oct 2023 13:28:48 +0000 (14:28 +0100)
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.

man/org.freedesktop.login1.xml
src/login/logind-dbus.c
src/login/logind-session-dbus.c
src/login/logind-session.c
src/login/logind-session.h
src/login/pam_systemd.c

index 954518992be5dd5880da631694b2f661fea64a2a..2c1b9118eaad2776e9e19a6d36cde1cc019e95dd 100644 (file)
@@ -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 {
 
     <variablelist class="dbus-method" generated="True" extra-ref="CreateSession()"/>
 
+    <variablelist class="dbus-method" generated="True" extra-ref="CreateSessionWithPIDFD()"/>
+
     <variablelist class="dbus-method" generated="True" extra-ref="ReleaseSession()"/>
 
     <variablelist class="dbus-method" generated="True" extra-ref="ActivateSession()"/>
@@ -525,10 +551,10 @@ node /org/freedesktop/login1 {
       structures consisting of <varname>what</varname>, <varname>who</varname>, <varname>why</varname>,
       <varname>mode</varname>, <varname>uid</varname> (user ID), and <varname>pid</varname> (process ID).</para>
 
-      <para><function>CreateSession()</function> and <function>ReleaseSession()</function> may be used to
-      open or close login sessions. These calls should <emphasis>never</emphasis> be invoked directly by
-      clients. Creating/closing sessions is exclusively the job of PAM and its
-      <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      <para><function>CreateSession()</function>, <function>CreateSessionWithPIDFD()</function>, and
+      <function>ReleaseSession()</function> may be used to open or close login sessions. These calls should
+      <emphasis>never</emphasis> be invoked directly by clients. Creating/closing sessions is exclusively the job
+      of PAM and its <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
       module.</para>
 
       <para><function>ActivateSession()</function> brings the session with the specified ID into the
@@ -1520,7 +1546,8 @@ node /org/freedesktop/login1/session/1 {
       <varname>HandleSuspendKeyLongPress</varname>, and
       <varname>HandleHibernateKeyLongPress</varname> were added in version 251.</para>
       <para><varname>StopIdleSessionUSec</varname> was added in version 252.</para>
-      <para><function>PrepareForShutdownWithMetadata</function> was added in version 255.</para>
+      <para><function>PrepareForShutdownWithMetadata</function> and
+      <function>CreateSessionWithPIDFD()</function> were added in version 255.</para>
     </refsect2>
     <refsect2>
       <title>Session Objects</title>
index f45a944f17ef88f643599d52841aec7c1bfa6a49..dc06d67fef741963d1ff54d7e0ffd9972eb5e74d 100644 (file)
@@ -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,
index fe52cfa64e1064833f0bd585679869f70163b3a8..d9d6cb285667600d97ae5db12760f5eb6954ddae 100644 (file)
@@ -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);
 
index 91361a8f6fb3ecdaeb42856156b72c02f7eb2608..8c8c5da6fb909edadc9a06ab24191238a00b5a7e 100644 (file)
@@ -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))
index 7a4e66ff153f1dd5dc8b3d9010ffaa851b53d3f2..8b63843ed8127211750bca0e7b052a305fa53794 100644 (file)
@@ -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);
index 0c5d51c3a3446771dcebd0b071e11ddb6367b3af..b8da266e277cc21d486e77c47d06a0ddea2359ac 100644 (file)
@@ -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;