|
|
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 |
|