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

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