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

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