Submitted By:            Douglas R. Reno <renodr at linuxfromscratch dot org>
Date:                    2019-02-21
Initial Package Version: 3.30.2
Upstream Status:         Applied
Origin:                  Upstream
Description:             Fixes CVE-2019-3825, a security bug in which any user
                         can unlock another user's session by using Timed Login.

diff -Naurp gdm-3.30.2.orig/daemon/gdm-manager.c gdm-3.30.2/daemon/gdm-manager.c
--- gdm-3.30.2.orig/daemon/gdm-manager.c	2018-11-06 15:26:11.000000000 -0600
+++ gdm-3.30.2/daemon/gdm-manager.c	2019-02-21 00:17:36.294486152 -0600
@@ -336,23 +336,40 @@ find_session_for_user_on_seat (GdmManage
 
         for (node = manager->priv->user_sessions; node != NULL; node = node->next) {
                 GdmSession *candidate_session = node->data;
-                const char *candidate_username, *candidate_seat_id;
+                const char *candidate_username, *candidate_seat_id, *candidate_session_id;
 
-                if (candidate_session == dont_count_session)
+                candidate_session_id = gdm_session_get_session_id (candidate_session);
+
+                if (candidate_session == dont_count_session) {
+                        g_debug ("GdmSession: Ignoring session %s as requested",
+                                 candidate_session_id);
                         continue;
+                }
 
-                if (!gdm_session_is_running (candidate_session))
+                if (!gdm_session_is_running (candidate_session)) {
+                        g_debug ("GdmSession: Ignoring session %s as it isn't running",
+                                 candidate_session_id);
                         continue;
+                }
 
                 candidate_username = gdm_session_get_username (candidate_session);
                 candidate_seat_id = gdm_session_get_display_seat_id (candidate_session);
 
+                g_debug ("GdmManager: Considering session %s on seat %s belonging to user %s",
+                         candidate_session_id,
+                         candidate_seat_id,
+                         candidate_username);
+
                 if (g_strcmp0 (candidate_username, username) == 0 &&
                     g_strcmp0 (candidate_seat_id, seat_id) == 0) {
+                        g_debug ("GdmManager: yes, found session %s", candidate_session_id);
                         return candidate_session;
                 }
+
+                g_debug ("GdmManager: no, will not use session %s", candidate_session_id);
         }
 
+        g_debug ("GdmManager: no matching sessions found");
         return NULL;
 }
 
@@ -836,8 +853,12 @@ gdm_manager_handle_open_session (GdmDBus
 #endif
         if (session == NULL) {
                 session = get_user_session_for_display (display);
+                g_debug ("GdmSession: Considering session %s for username %s",
+                         gdm_session_get_session_id (session),
+                         gdm_session_get_username (session));
 
                 if (gdm_session_is_running (session)) {
+                        g_debug ("GdmSession: the session is running, and therefore can't be used");
                         g_dbus_method_invocation_return_error_literal (invocation,
                                                                        G_DBUS_ERROR,
                                                                        G_DBUS_ERROR_ACCESS_DENIED,
@@ -1013,6 +1034,10 @@ open_temporary_reauthentication_channel
                                    environment);
         g_strfreev (environment);
 
+        g_debug ("GdmSession: Created session for temporary reauthentication channel for user %d (seat %s)",
+                 (int) uid,
+                 seat_id);
+
         g_object_set_data_full (G_OBJECT (session),
                                 "caller-session-id",
                                 g_strdup (session_id),
@@ -1092,11 +1117,13 @@ gdm_manager_handle_open_reauthentication
         }
 
         if (is_login_screen) {
+                g_debug ("GdmManager: looking for login screen session for user %s on seat %s", username, seat_id);
                 session = find_session_for_user_on_seat (self,
                                                          username,
                                                          seat_id,
                                                          NULL);
         } else {
+                g_debug ("GdmManager: looking for user session on display");
                 session = get_user_session_for_display (display);
         }
 
@@ -1815,7 +1842,8 @@ on_start_user_session (StartUserSessionO
                                                  session_id);
 
 
-                if (g_strcmp0 (operation->service_name, "gdm-autologin") == 0) {
+                if (g_strcmp0 (operation->service_name, "gdm-autologin") == 0 && 
+                    !gdm_session_client_is_connected (operation->session)) {
                         /* remove the unused prepared greeter display since we're not going
                          * to have a greeter */
                         gdm_display_store_remove (self->priv->display_store, display);
@@ -2049,7 +2077,15 @@ on_session_client_connected (GdmSession
         gboolean enabled;
         gboolean allow_timed_login = FALSE;
 
-        g_debug ("GdmManager: client connected");
+        g_debug ("GdmManager: client with pid %d connected", (int) pid_of_client);
+
+        if (gdm_session_is_running (session)) {
+               const char *session_username;
+               session_username = gdm_session_get_username (session);
+               g_debug ("GdmManager: ignoring connection, since session already running (for user %s)",
+                        session_username);
+               return;
+        }
 
         display = get_display_for_user_session (session);
 
@@ -2095,7 +2131,7 @@ on_session_client_disconnected (GdmSessi
                                 GPid          pid_of_client,
                                 GdmManager   *manager)
 {
-        g_debug ("GdmManager: client disconnected");
+        g_debug ("GdmManager: client with pid %d disconnected", (int) pid_of_client);
 }
 
 typedef struct
@@ -2162,9 +2198,10 @@ on_session_conversation_started (GdmSess
         gboolean    enabled;
         char       *username;
 
-        g_debug ("GdmManager: session conversation started for service %s", service_name);
+        g_debug ("GdmManager: session conversation started for service %s on session", service_name);
 
         if (g_strcmp0 (service_name, "gdm-autologin") != 0) {
+                g_debug ("GdmManager: ignoring session conversation since it's not automatic login conversation");
                 return;
         }
 
@@ -2274,6 +2311,12 @@ create_user_session_for_display (GdmMana
                                    display_auth_file,
                                    display_is_local,
                                    NULL);
+
+        g_debug ("GdmSession: Created user session for user %d on display %s (seat %s)",
+                 (int) allowed_user,
+                 display_id,
+                 display_seat_id);
+
         g_free (display_name);
         g_free (remote_hostname);
         g_free (display_auth_file);
diff -Naurp gdm-3.30.2.orig/daemon/gdm-session.c gdm-3.30.2/daemon/gdm-session.c
--- gdm-3.30.2.orig/daemon/gdm-session.c	2018-11-06 15:26:31.000000000 -0600
+++ gdm-3.30.2/daemon/gdm-session.c	2019-02-21 00:16:22.685376490 -0600
@@ -654,7 +654,10 @@ gdm_session_select_user (GdmSession *sel
                          const char *text)
 {
 
-        g_debug ("GdmSession: Setting user: '%s'", text);
+        g_debug ("GdmSession: selecting user '%s' for session '%s' (%p)",
+                 text,
+                 gdm_session_get_session_id (self),
+                 self);
 
         g_free (self->priv->selected_user);
         self->priv->selected_user = g_strdup (text);
@@ -1411,6 +1414,21 @@ gdm_session_handle_client_select_session
                                           const char             *session,
                                           GdmSession             *self)
 {
+        if (gdm_session_is_running (self)) {
+               const char *username;
+
+               username = gdm_session_get_username (self);
+               g_debug("GdmSession: refusing to select session %s since it's already running (for user %s)",
+                       session,
+                       username);
+               g_dbus_method_invocation_return_error (invocation,
+                                                      G_DBUS_ERROR,
+                                                      G_DBUS_ERROR_INVALID_ARGS,
+                                                      "Session already running for user %s",
+                                                      username);
+               return TRUE;
+        }
+
         if (self->priv->greeter_interface != NULL) {
                 gdm_dbus_greeter_complete_select_session (greeter_interface,
                                                           invocation);
@@ -1425,10 +1443,27 @@ gdm_session_handle_client_select_user (G
                                        const char            *username,
                                        GdmSession            *self)
 {
+        if (gdm_session_is_running (self)) {
+           const char *session_username;
+
+           session_username = gdm_session_get_username (self);
+           g_debug ("GdmSession: refusing to select user %s, since session (%p) already running (for user %s)",
+                    username,
+                    self,
+                    session_username);
+           g_dbus_method_invocation_return_error (invocation,
+                                                  G_DBUS_ERROR,
+                                                  G_DBUS_ERROR_INVALID_ARGS,
+                                                  "Session already running for user %s",
+                                                  session_username);
+           return TRUE;
+        }
+
         if (self->priv->greeter_interface != NULL) {
                 gdm_dbus_greeter_complete_select_user (greeter_interface,
                                                        invocation);
         }
+        g_debug ("GdmSession: client selected user '%s' on session (%p)", username, self);
         gdm_session_select_user (self, username);
         return TRUE;
 }
@@ -1440,6 +1475,20 @@ gdm_session_handle_client_start_session_
                                                     gboolean               client_is_ready,
                                                     GdmSession            *self)
 {
+        if (gdm_session_is_running (self)) {
+               const char *username;
+
+               username = gdm_session_get_username (self);
+               g_debug ("GdmSession: refusing to start session (%p), since it's already running (for user %s)",
+                        self,
+                        username);
+               g_dbus_method_invocation_return_error (invocation,
+                                                      G_DBUS_ERROR,
+                                                      G_DBUS_ERROR_INVALID_ARGS,
+                                                      "Session already running for user %s",
+                                                      username);
+               return TRUE;
+        }
 
         if (self->priv->greeter_interface != NULL) {
                 gdm_dbus_greeter_complete_start_session_when_ready (greeter_interface,
@@ -1458,6 +1507,20 @@ gdm_session_handle_get_timed_login_detai
                                             GDBusMethodInvocation *invocation,
                                             GdmSession            *self)
 {
+        if (gdm_session_is_running (self)) {
+               const char *username;
+
+               username = gdm_session_get_username (self);
+               g_debug("GdmSession: refusing to give timed login details, session (%p) already running (for user %s)",
+                       self,
+                       username);
+               g_dbus_method_invocation_return_error (invocation,
+                                                      G_DBUS_ERROR,
+                                                      G_DBUS_ERROR_INVALID_ARGS,
+                                                      "Session already running for user %s",
+                                                      username);
+               return TRUE;
+        }
 
         if (self->priv->greeter_interface != NULL) {
                 gdm_dbus_greeter_complete_get_timed_login_details (greeter_interface,
@@ -1480,12 +1543,31 @@ gdm_session_handle_client_begin_auto_log
                                             const char            *username,
                                             GdmSession            *self)
 {
+        const char *session_username;
+
+        if (gdm_session_is_running (self)) {
+                session_username = gdm_session_get_username (self);
+                g_debug ("GdmSession: refusing auto login operation, session (%p) already running for user %s (%s requested)",
+                         self,
+                         session_username,
+                         username);
+                g_dbus_method_invocation_return_error (invocation,
+                                                       G_DBUS_ERROR,
+                                                       G_DBUS_ERROR_INVALID_ARGS,
+                                                       "Session already owned by user %s",
+                                                       session_username);
+                return TRUE;
+        }
+
         if (self->priv->greeter_interface != NULL) {
                 gdm_dbus_greeter_complete_begin_auto_login (greeter_interface,
                                                             invocation);
         }
 
-        g_debug ("GdmSession: begin auto login for user '%s'", username);
+        g_debug ("GdmSession: client requesting automatic login for user '%s' on session '%s' (%p)",
+                 username,
+                 gdm_session_get_session_id (self),
+                 self);
 
         gdm_session_setup_for_user (self, "gdm-autologin", username);
 
@@ -1788,7 +1870,9 @@ setup_outside_server (GdmSession *self)
         GDBusServer *server;
         GError *error = NULL;
 
-        g_debug ("GdmSession: Creating D-Bus server for greeters and such");
+        g_debug ("GdmSession: Creating D-Bus server for greeters and such for session %s (%p)",
+                 gdm_session_get_session_id(self),
+                 self);
 
         observer = g_dbus_auth_observer_new ();
         g_signal_connect_object (observer,
@@ -2172,7 +2256,7 @@ gdm_session_start_conversation (GdmSessi
                 conversation->job = NULL;
         }
 
-        g_debug ("GdmSession: starting conversation %s", service_name);
+        g_debug ("GdmSession: starting conversation %s for session (%p)", service_name, self);
 
         conversation = start_conversation (self, service_name);
 
@@ -2331,6 +2415,11 @@ gdm_session_setup_for_user (GdmSession *
 
         update_session_type (self);
 
+        g_debug ("GdmSession: Set up service %s for username %s on session (%p)",
+                 service_name,
+                 username,
+                 self);
+
         gdm_session_select_user (self, username);
 
         self->priv->is_program_session = FALSE;
@@ -2961,6 +3050,10 @@ gdm_session_start_reauthentication (GdmS
 
         g_return_if_fail (conversation != NULL);
 
+        g_debug ("GdmSession: starting reauthentication for session %s for client with pid %d",
+                 conversation->session_id,
+                 (int) uid_of_caller);
+
         conversation->reauth_pid_of_caller = pid_of_caller;
 
         gdm_dbus_worker_call_start_reauthentication (conversation->worker_proxy,
