Blame SOURCES/0043-gdm-session-worker-Drop-login_vt-assuming-it-is-GDM_.patch

400dab
From 476230f7b721781c682d26983c9a2fd82afc45e1 Mon Sep 17 00:00:00 2001
400dab
From: Benjamin Berg <bberg@redhat.com>
400dab
Date: Wed, 25 Sep 2019 14:51:40 +0200
400dab
Subject: [PATCH 43/51] gdm-session-worker: Drop login_vt assuming it is
400dab
 GDM_INITIAL_VT
400dab
400dab
When a session ends, its "session worker" is closed. Since
400dab
3e8220921bb608afd06ed677104fd2244b901a28 (3.33.4), we uninitialise PAM
400dab
when this happens. As part of this procedure, we jump back to the login
400dab
screen, if the screen being killed is not itself the login screen.
400dab
400dab
This has broken fast user switching. It goes like this - this
400dab
explanation is a bit complicated, bear with us:
400dab
400dab
We want to jump back to the login screen when a normal user session
400dab
ends, so that people can log in again. We do not want to do this when a
400dab
login screen itself ends. When session workers start up, they query for
400dab
the *currently active VT* and save this in `login_vt`. Then later on, we
400dab
check if our session ID is the same as `login_vt`, and jump to
400dab
`login_vt` if they are different - this means that it was a user session
400dab
not a login session. Querying the currently active VT is fine for the
400dab
first greeter, but when initiating a user switch it's wrong as this
400dab
gives the user VT.
400dab
400dab
GDM greeters are killed once they have spawned a session. They are
400dab
associated with a logind session, and therefore a PAM session. There are
400dab
some actions performed when unregistering PAM sessions, including the
400dab
previously mentioned VT jump. Before
400dab
3e8220921bb608afd06ed677104fd2244b901a28 we only uninitialised PAM when
400dab
the session itself exited so the bug was masked, but now (since this
400dab
commit), if the login screen's *worker* exits first - as happens in the
400dab
normal case when GDM kills it - we also do this uninitialisation. Since
400dab
we falsely recorded the login screen as the first user's VT, this means
400dab
that checking `login_vt != session_vt` returns `TRUE` and we jump back
400dab
to the previous user's session immediately after logging into the new
400dab
session: fast user switching is broken.
400dab
400dab
Since the work on shutting down the GDM session has been finished, we
400dab
can assume that the login_vt is always on GDM_INITIAL_VT (see
400dab
example c71bc5d6c3bc2ec448b5c72ce9a811d9c0c7905e
400dab
"local-display-factory: Remove initial VT is in use check" and
400dab
39fb4ff64e6a0653e70a3bfab31da47b49227d59 "manager: don't run autologin
400dab
display on tty1"). So simply replace all usages of login_vt with
400dab
GDM_INITIAL_VT to solve the above problem.
400dab
400dab
Note that in the case where ENABLE_USER_DISPLAY_SERVER is not enabled,
400dab
the login_vt is always the same as the session_vt. We can simply remove
400dab
the VT switching magic there and everything should be working as
400dab
expected.
400dab
400dab
This is a simpler version of the patch by Iain Lane <iainl@gnome.org>,
400dab
taking into account that we can make the assumption about the login_vt.
400dab
400dab
Closes #515
400dab
---
400dab
 daemon/gdm-session-worker.c | 43 +++++++++----------------------------
400dab
 1 file changed, 10 insertions(+), 33 deletions(-)
400dab
400dab
diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c
400dab
index b4befaa83..0bd78cfaf 100644
400dab
--- a/daemon/gdm-session-worker.c
400dab
+++ b/daemon/gdm-session-worker.c
400dab
@@ -119,61 +119,60 @@ typedef struct
400dab
 
400dab
 } ReauthenticationRequest;
400dab
 
400dab
 struct GdmSessionWorkerPrivate
400dab
 {
400dab
         GdmSessionWorkerState state;
400dab
 
400dab
         int               exit_code;
400dab
 
400dab
         pam_handle_t     *pam_handle;
400dab
 
400dab
         GPid              child_pid;
400dab
         guint             child_watch_id;
400dab
 
400dab
         /* from Setup */
400dab
         char             *service;
400dab
         char             *x11_display_name;
400dab
         char             *x11_authority_file;
400dab
         char             *display_device;
400dab
         char             *display_seat_id;
400dab
         char             *hostname;
400dab
         char             *username;
400dab
         char             *log_file;
400dab
         char             *session_id;
400dab
         uid_t             uid;
400dab
         gid_t             gid;
400dab
         gboolean          password_is_required;
400dab
         char            **extensions;
400dab
 
400dab
         int               cred_flags;
400dab
-        int               login_vt;
400dab
         int               session_vt;
400dab
         int               session_tty_fd;
400dab
 
400dab
         char            **arguments;
400dab
         guint32           cancelled : 1;
400dab
         guint32           timed_out : 1;
400dab
         guint32           is_program_session : 1;
400dab
         guint32           is_reauth_session : 1;
400dab
         guint32           display_is_local : 1;
400dab
         guint32           display_is_initial : 1;
400dab
         guint             state_change_idle_id;
400dab
         GdmSessionDisplayMode display_mode;
400dab
 
400dab
         char                 *server_address;
400dab
         GDBusConnection      *connection;
400dab
         GdmDBusWorkerManager *manager;
400dab
 
400dab
         GHashTable         *reauthentication_requests;
400dab
 
400dab
         GdmSessionAuditor  *auditor;
400dab
         GdmSessionSettings *user_settings;
400dab
 
400dab
         GDBusMethodInvocation *pending_invocation;
400dab
 };
400dab
 
400dab
 #ifdef SUPPORTS_PAM_EXTENSIONS
400dab
 static char gdm_pam_extension_environment_block[_POSIX_ARG_MAX];
400dab
 
400dab
 static const char * const
400dab
 gdm_supported_pam_extensions[] = {
400dab
@@ -1029,141 +1028,120 @@ gdm_session_worker_set_state (GdmSessionWorker      *worker,
400dab
 
400dab
 static void
400dab
 gdm_session_worker_uninitialize_pam (GdmSessionWorker *worker,
400dab
                                      int               status)
400dab
 {
400dab
         g_debug ("GdmSessionWorker: uninitializing PAM");
400dab
 
400dab
         if (worker->priv->pam_handle == NULL)
400dab
                 return;
400dab
 
400dab
         gdm_session_worker_get_username (worker, NULL);
400dab
 
400dab
         if (worker->priv->state >= GDM_SESSION_WORKER_STATE_SESSION_OPENED) {
400dab
                 pam_close_session (worker->priv->pam_handle, 0);
400dab
                 gdm_session_auditor_report_logout (worker->priv->auditor);
400dab
         } else {
400dab
                 gdm_session_auditor_report_login_failure (worker->priv->auditor,
400dab
                                                           status,
400dab
                                                           pam_strerror (worker->priv->pam_handle, status));
400dab
         }
400dab
 
400dab
         if (worker->priv->state >= GDM_SESSION_WORKER_STATE_ACCREDITED) {
400dab
                 pam_setcred (worker->priv->pam_handle, PAM_DELETE_CRED);
400dab
         }
400dab
 
400dab
         pam_end (worker->priv->pam_handle, status);
400dab
         worker->priv->pam_handle = NULL;
400dab
 
400dab
         gdm_session_worker_stop_auditor (worker);
400dab
 
400dab
+        /* If user-display-server is not enabled the login_vt is always
400dab
+         * identical to the session_vt. So in that case we never need to
400dab
+         * do a VT switch. */
400dab
+#ifdef ENABLE_USER_DISPLAY_SERVER
400dab
         if (g_strcmp0 (worker->priv->display_seat_id, "seat0") == 0) {
400dab
-                if (worker->priv->login_vt != worker->priv->session_vt) {
400dab
-                        jump_to_vt (worker, worker->priv->login_vt);
400dab
+                /* Switch to the login VT if we are not the login screen. */
400dab
+                if (worker->priv->session_vt != GDM_INITIAL_VT) {
400dab
+                        jump_to_vt (worker, GDM_INITIAL_VT);
400dab
                 }
400dab
         }
400dab
+#endif
400dab
 
400dab
-        worker->priv->login_vt = 0;
400dab
         worker->priv->session_vt = 0;
400dab
 
400dab
         g_debug ("GdmSessionWorker: state NONE");
400dab
         gdm_session_worker_set_state (worker, GDM_SESSION_WORKER_STATE_NONE);
400dab
 }
400dab
 
400dab
 static char *
400dab
 _get_tty_for_pam (const char *x11_display_name,
400dab
                   const char *display_device)
400dab
 {
400dab
 #ifdef __sun
400dab
         return g_strdup (display_device);
400dab
 #else
400dab
         return g_strdup (x11_display_name);
400dab
 #endif
400dab
 }
400dab
 
400dab
 #ifdef PAM_XAUTHDATA
400dab
 static struct pam_xauth_data *
400dab
 _get_xauth_for_pam (const char *x11_authority_file)
400dab
 {
400dab
         FILE                  *fh;
400dab
         Xauth                 *auth = NULL;
400dab
         struct pam_xauth_data *retval = NULL;
400dab
         gsize                  len = sizeof (*retval) + 1;
400dab
 
400dab
         fh = fopen (x11_authority_file, "r");
400dab
         if (fh) {
400dab
                 auth = XauReadAuth (fh);
400dab
                 fclose (fh);
400dab
         }
400dab
         if (auth) {
400dab
                 len += auth->name_length + auth->data_length;
400dab
                 retval = g_malloc0 (len);
400dab
         }
400dab
         if (retval) {
400dab
                 retval->namelen = auth->name_length;
400dab
                 retval->name = (char *) (retval + 1);
400dab
                 memcpy (retval->name, auth->name, auth->name_length);
400dab
                 retval->datalen = auth->data_length;
400dab
                 retval->data = retval->name + auth->name_length + 1;
400dab
                 memcpy (retval->data, auth->data, auth->data_length);
400dab
         }
400dab
         XauDisposeAuth (auth);
400dab
         return retval;
400dab
 }
400dab
 #endif
400dab
 
400dab
-static gboolean
400dab
-ensure_login_vt (GdmSessionWorker *worker)
400dab
-{
400dab
-        int fd;
400dab
-        struct vt_stat vt_state = { 0 };
400dab
-        gboolean got_login_vt = FALSE;
400dab
-
400dab
-        fd = open ("/dev/tty0", O_RDWR | O_NOCTTY);
400dab
-
400dab
-        if (fd < 0) {
400dab
-                g_debug ("GdmSessionWorker: couldn't open VT master: %m");
400dab
-                return FALSE;
400dab
-        }
400dab
-
400dab
-        if (ioctl (fd, VT_GETSTATE, &vt_state) < 0) {
400dab
-                g_debug ("GdmSessionWorker: couldn't get current VT: %m");
400dab
-                goto out;
400dab
-        }
400dab
-
400dab
-        worker->priv->login_vt = vt_state.v_active;
400dab
-        got_login_vt = TRUE;
400dab
-out:
400dab
-        close (fd);
400dab
-        return got_login_vt;
400dab
-}
400dab
-
400dab
 static gboolean
400dab
 gdm_session_worker_initialize_pam (GdmSessionWorker   *worker,
400dab
                                    const char         *service,
400dab
                                    const char * const *extensions,
400dab
                                    const char         *username,
400dab
                                    const char         *hostname,
400dab
                                    gboolean            display_is_local,
400dab
                                    const char         *x11_display_name,
400dab
                                    const char         *x11_authority_file,
400dab
                                    const char         *display_device,
400dab
                                    const char         *seat_id,
400dab
                                    GError            **error)
400dab
 {
400dab
         struct pam_conv        pam_conversation;
400dab
         int                    error_code;
400dab
         char tty_string[256];
400dab
 
400dab
         g_assert (worker->priv->pam_handle == NULL);
400dab
 
400dab
         g_debug ("GdmSessionWorker: initializing PAM; service=%s username=%s seat=%s",
400dab
                  service ? service : "(null)",
400dab
                  username ? username : "(null)",
400dab
                  seat_id ? seat_id : "(null)");
400dab
 
400dab
 #ifdef SUPPORTS_PAM_EXTENSIONS
400dab
         if (extensions != NULL) {
400dab
                 GDM_PAM_EXTENSION_ADVERTISE_SUPPORTED_EXTENSIONS (gdm_pam_extension_environment_block, extensions);
400dab
         }
400dab
 #endif
400dab
 
400dab
@@ -1204,64 +1182,63 @@ gdm_session_worker_initialize_pam (GdmSessionWorker   *worker,
400dab
         }
400dab
 
400dab
         /* set RHOST */
400dab
         if (hostname != NULL && hostname[0] != '\0') {
400dab
                 error_code = pam_set_item (worker->priv->pam_handle, PAM_RHOST, hostname);
400dab
                 g_debug ("error informing authentication system of user's hostname %s: %s",
400dab
                          hostname,
400dab
                          pam_strerror (worker->priv->pam_handle, error_code));
400dab
 
400dab
                 if (error_code != PAM_SUCCESS) {
400dab
                         g_set_error (error,
400dab
                                      GDM_SESSION_WORKER_ERROR,
400dab
                                      GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
400dab
                                      "%s", "");
400dab
                         goto out;
400dab
                 }
400dab
         }
400dab
 
400dab
         /* set seat ID */
400dab
         if (seat_id != NULL && seat_id[0] != '\0') {
400dab
                 gdm_session_worker_set_environment_variable (worker, "XDG_SEAT", seat_id);
400dab
         }
400dab
 
400dab
         if (strcmp (service, "gdm-launch-environment") == 0) {
400dab
                 gdm_session_worker_set_environment_variable (worker, "XDG_SESSION_CLASS", "greeter");
400dab
         }
400dab
 
400dab
         g_debug ("GdmSessionWorker: state SETUP_COMPLETE");
400dab
         gdm_session_worker_set_state (worker, GDM_SESSION_WORKER_STATE_SETUP_COMPLETE);
400dab
 
400dab
-        /* Temporarily set PAM_TTY with the currently active VT (login screen) 
400dab
+        /* Temporarily set PAM_TTY with the login VT,
400dab
            PAM_TTY will be reset with the users VT right before the user session is opened */
400dab
-        ensure_login_vt (worker);
400dab
-        g_snprintf (tty_string, 256, "/dev/tty%d", worker->priv->login_vt);
400dab
+        g_snprintf (tty_string, 256, "/dev/tty%d", GDM_INITIAL_VT);
400dab
         pam_set_item (worker->priv->pam_handle, PAM_TTY, tty_string);
400dab
         if (!display_is_local)
400dab
                 worker->priv->password_is_required = TRUE;
400dab
 
400dab
  out:
400dab
         if (error_code != PAM_SUCCESS) {
400dab
                 gdm_session_worker_uninitialize_pam (worker, error_code);
400dab
                 return FALSE;
400dab
         }
400dab
 
400dab
         return TRUE;
400dab
 }
400dab
 
400dab
 static gboolean
400dab
 gdm_session_worker_authenticate_user (GdmSessionWorker *worker,
400dab
                                       gboolean          password_is_required,
400dab
                                       GError          **error)
400dab
 {
400dab
         int error_code;
400dab
         int authentication_flags;
400dab
 
400dab
         g_debug ("GdmSessionWorker: authenticating user %s", worker->priv->username);
400dab
 
400dab
         authentication_flags = 0;
400dab
 
400dab
         if (password_is_required) {
400dab
                 authentication_flags |= PAM_DISALLOW_NULL_AUTHTOK;
400dab
         }
400dab
 
400dab
         /* blocking call, does the actual conversation */
400dab
-- 
400dab
2.27.0
400dab