[gdm/wip/slave-connection: 32/38] worker: add reauthentication support



commit 37bde00a189a732ac9f9badfe7ce2a02d033c8c5
Author: Ray Strode <rstrode redhat com>
Date:   Mon Jul 9 21:33:10 2012 -0400

    worker: add reauthentication support
    
    This commit starts to flesh out reauthentication support for
    screensavers to use.
    
    1) It adds a "verification mode" argument to the GdmSession constructor
    that tweaks the behavior of how the session worker acts to fit login or
    unlock scenarios better.
    
    2) It makes it so that when programs running within an existing session
    attempt to open a communication channel for user verification, they are
    granted access to a session object that's managed by the already running
    worker process (so reauthentication happens in the context of the
    session).
    
    3) It adds an "allowed user" property to the gdm session object, so the
    only user given access to a particular session is the owner of that
    session.

 daemon/Makefile.am               |    7 +
 daemon/gdm-dbus-util.c           |   40 +++++++
 daemon/gdm-dbus-util.h           |    3 +
 daemon/gdm-display.c             |    4 +
 daemon/gdm-display.h             |    2 +
 daemon/gdm-manager.c             |  109 ++++++++++++++++++-
 daemon/gdm-session-worker-job.c  |   28 +++++
 daemon/gdm-session-worker-job.h  |    2 +
 daemon/gdm-session-worker.c      |  229 +++++++++++++++++++++++++++++++++++++-
 daemon/gdm-session-worker.h      |    3 +-
 daemon/gdm-session.c             |  195 ++++++++++++++++++++++++--------
 daemon/gdm-session.h             |   10 ++-
 daemon/gdm-session.xml           |   14 +++
 daemon/gdm-simple-slave.c        |  154 ++++++++++++++++++++++----
 daemon/gdm-slave.c               |   50 ++++++---
 daemon/gdm-slave.h               |   12 ++-
 daemon/gdm-slave.xml             |    2 +
 daemon/gdm-welcome-session.c     |   11 ++
 daemon/gdm-xdmcp-chooser-slave.c |   61 ++++++++--
 daemon/session-worker-main.c     |    5 +-
 data/gdm.conf.in                 |    4 +-
 21 files changed, 833 insertions(+), 112 deletions(-)
---
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index cd5e122..061b5a2 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -220,12 +220,19 @@ gdm_xdmcp_chooser_slave_LDADD = 		\
 
 gdm_session_worker_SOURCES = 			\
 	session-worker-main.c 			\
+	gdm-session.c				\
+	gdm-session.h				\
 	gdm-session-settings.h			\
 	gdm-session-settings.c			\
 	gdm-session-auditor.h			\
 	gdm-session-auditor.c			\
+	gdm-session-record.c			\
+	gdm-session-record.h			\
 	gdm-session-worker.h			\
 	gdm-session-worker.c			\
+	gdm-session-worker-job.c		\
+	gdm-dbus-util.c				\
+	gdm-dbus-util.h				\
 	$(NULL)
 
 nodist_gdm_session_worker_SOURCES =		\
diff --git a/daemon/gdm-dbus-util.c b/daemon/gdm-dbus-util.c
index ebaa43f..5f177ba 100644
--- a/daemon/gdm-dbus-util.c
+++ b/daemon/gdm-dbus-util.c
@@ -161,3 +161,43 @@ gdm_dbus_get_pid_for_name (const char  *system_bus_name,
 
         return retval;
 }
+
+gboolean
+gdm_dbus_get_uid_for_name (const char  *system_bus_name,
+                           uid_t       *out_uid,
+                           GError     **error)
+{
+        GDBusConnection *bus;
+        GVariant *reply;
+        gboolean retval = FALSE;
+        unsigned int v;
+
+        bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
+        if (bus == NULL) {
+                return FALSE;
+        }
+
+        reply = g_dbus_connection_call_sync (bus,
+                                             "org.freedesktop.DBus",
+                                             "/org/freedesktop/DBus",
+                                             "org.freedesktop.DBus",
+                                             "GetConnectionUnixUser",
+                                             g_variant_new ("(s)", system_bus_name),
+                                             G_VARIANT_TYPE ("(u)"),
+                                             G_DBUS_CALL_FLAGS_NONE,
+                                             -1,
+                                             NULL, error);
+        if (reply == NULL) {
+                goto out;
+        }
+
+        g_variant_get (reply, "(u)", &v);
+        *out_uid = v;
+        g_variant_unref (reply);
+
+        retval = TRUE;
+ out:
+        g_object_unref (bus);
+
+        return retval;
+}
diff --git a/daemon/gdm-dbus-util.h b/daemon/gdm-dbus-util.h
index d83dea7..8bcaea0 100644
--- a/daemon/gdm-dbus-util.h
+++ b/daemon/gdm-dbus-util.h
@@ -32,4 +32,7 @@ gboolean gdm_dbus_get_pid_for_name (const char  *system_bus_name,
                                     pid_t       *out_pid,
                                     GError     **error);
 
+gboolean gdm_dbus_get_uid_for_name (const char  *system_bus_name,
+                                    uid_t       *out_uid,
+                                    GError     **error);
 #endif
diff --git a/daemon/gdm-display.c b/daemon/gdm-display.c
index f1877ab..6ef724c 100644
--- a/daemon/gdm-display.c
+++ b/daemon/gdm-display.c
@@ -1254,6 +1254,8 @@ register_display (GdmDisplay *display)
 
 char *
 gdm_display_open_session_sync (GdmDisplay    *display,
+                               GPid           pid_of_caller,
+                               uid_t          uid_of_caller,
                                GCancellable  *cancellable,
                                GError       **error)
 {
@@ -1270,6 +1272,8 @@ gdm_display_open_session_sync (GdmDisplay    *display,
 
         address = NULL;
         ret = gdm_dbus_slave_call_open_session_sync (display->priv->slave_bus_proxy,
+                                                     (int) pid_of_caller,
+                                                     (int) uid_of_caller,
                                                      &address,
                                                      cancellable,
                                                      error);
diff --git a/daemon/gdm-display.h b/daemon/gdm-display.h
index 1f4d3db..a66003a 100644
--- a/daemon/gdm-display.h
+++ b/daemon/gdm-display.h
@@ -91,6 +91,8 @@ GType               gdm_display_get_type                       (void);
 int                 gdm_display_get_status                     (GdmDisplay *display);
 time_t              gdm_display_get_creation_time              (GdmDisplay *display);
 char *              gdm_display_open_session_sync              (GdmDisplay    *display,
+                                                                GPid           pid_of_caller,
+                                                                uid_t          uid_of_caller,
                                                                 GCancellable  *cancellable,
                                                                 GError       **error);
 char *              gdm_display_get_session_id                 (GdmDisplay *display);
diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c
index aed036c..16a0cc9 100644
--- a/daemon/gdm-manager.c
+++ b/daemon/gdm-manager.c
@@ -130,7 +130,7 @@ get_session_id_for_pid_systemd (pid_t    pid,
 }
 #endif
 
-#ifdef WITH_CONSOLEKIT
+#ifdef WITH_CONSOLE_KIT
 static char *
 get_session_id_for_pid_consolekit (GDBusConnection  *connection,
                                    pid_t             pid,
@@ -171,13 +171,89 @@ get_session_id_for_pid (GDBusConnection  *connection,
         }
 #endif
 
-#ifdef WITH_CONSOLEKIT
+#ifdef WITH_CONSOLE_KIT
         return get_session_id_for_pid_consolekit (connection, pid, error);
 #endif
 
         return NULL;
 }
 
+#ifdef WITH_SYSTEMD
+static gboolean
+get_uid_for_systemd_session_id (const char  *session_id,
+                                uid_t       *uid,
+                                GError     **error)
+{
+        int ret;
+
+        ret = sd_session_get_uid (session_id, uid);
+        if (ret < 0) {
+                g_set_error (error,
+                             GDM_DISPLAY_ERROR,
+                             GDM_DISPLAY_ERROR_GETTING_SESSION_INFO,
+                             "Error getting uid for session id %s from systemd: %s",
+                             session_id,
+                             g_strerror (-ret));
+                return FALSE;
+        }
+
+        return TRUE;
+}
+#endif
+
+#ifdef WITH_CONSOLE_KIT
+static gboolean
+get_uid_for_consolekit_session_id (GDBusConnection  *connection,
+                                   const char       *session_id,
+                                   uid_t            *out_uid,
+                                   GError          **error)
+{
+        GVariant *reply;
+        guint32 uid;
+
+        reply = g_dbus_connection_call_sync (connection,
+                                             "org.freedesktop.ConsoleKit",
+                                             session_id,
+                                             "org.freedesktop.ConsoleKit.Session",
+                                             "GetUnixUser",
+                                             NULL,
+                                             G_VARIANT_TYPE ("(u)"),
+                                             G_DBUS_CALL_FLAGS_NONE,
+                                             -1,
+                                             NULL,
+                                             error);
+        if (reply == NULL) {
+                return FALSE;
+        }
+
+        g_variant_get (reply, "(u)", &uid);
+        g_variant_unref (reply);
+
+        *out_uid = (uid_t) uid;
+
+        return TRUE;
+}
+#endif
+
+static gboolean
+get_uid_for_session_id (GDBusConnection  *connection,
+                        const char       *session_id,
+                        uid_t            *uid,
+                        GError          **error)
+{
+#ifdef WITH_SYSTEMD
+        if (sd_booted () > 0) {
+                return get_uid_for_systemd_session_id (session_id, uid, error);
+        }
+#endif
+
+#ifdef WITH_CONSOLE_KIT
+        return get_uid_for_consolekit_session_id (connection, session_id, uid, error);
+#endif
+
+        return FALSE;
+}
+
 static gboolean
 lookup_by_session_id (const char *id,
                       GdmDisplay *display,
@@ -209,6 +285,7 @@ gdm_manager_handle_open_session (GdmDBusManager        *manager,
         char             *address;
         int               ret;
         GPid              pid;
+        uid_t             caller_uid, session_uid;
 
         sender = g_dbus_method_invocation_get_sender (invocation);
         error = NULL;
@@ -219,7 +296,15 @@ gdm_manager_handle_open_session (GdmDBusManager        *manager,
                 g_dbus_method_invocation_return_gerror (invocation, error);
                 g_error_free (error);
                 return TRUE;
+        }
+
+        ret = gdm_dbus_get_uid_for_name (sender, &caller_uid, &error);
 
+        if (!ret) {
+                g_prefix_error (&error, "Error while retrieving caller session id: ");
+                g_dbus_method_invocation_return_gerror (invocation, error);
+                g_error_free (error);
+                return TRUE;
         }
 
         connection = g_dbus_method_invocation_get_connection (invocation);
@@ -231,6 +316,24 @@ gdm_manager_handle_open_session (GdmDBusManager        *manager,
                 return TRUE;
         }
 
+        if (!get_uid_for_session_id (connection, session_id, &session_uid, &error)) {
+                g_prefix_error (&error, "Error while retrieving caller session id: ");
+                g_dbus_method_invocation_return_gerror (invocation, error);
+                g_error_free (error);
+                return TRUE;
+        }
+
+        if (caller_uid != session_uid) {
+                g_dbus_method_invocation_return_error_literal (invocation,
+                                                               G_DBUS_ERROR,
+                                                               G_DBUS_ERROR_ACCESS_DENIED,
+                                                               _("User doesn't own session"));
+                g_prefix_error (&error, "Error while retrieving caller session id: ");
+                g_dbus_method_invocation_return_gerror (invocation, error);
+                g_error_free (error);
+                return TRUE;
+        }
+
         display = gdm_display_store_find (self->priv->display_store,
                                           lookup_by_session_id,
                                           (gpointer) session_id);
@@ -245,7 +348,7 @@ gdm_manager_handle_open_session (GdmDBusManager        *manager,
                 return TRUE;
         }
 
-        address = gdm_display_open_session_sync (display, NULL, &error);
+        address = gdm_display_open_session_sync (display, pid, session_uid, NULL, &error);
 
         if (address == NULL) {
                 g_dbus_method_invocation_return_gerror (invocation, error);
diff --git a/daemon/gdm-session-worker-job.c b/daemon/gdm-session-worker-job.c
index 35a506a..1aa6787 100644
--- a/daemon/gdm-session-worker-job.c
+++ b/daemon/gdm-session-worker-job.c
@@ -52,6 +52,7 @@ struct GdmSessionWorkerJobPrivate
 {
         char           *command;
         GPid            pid;
+        gboolean        for_reauth;
 
         guint           child_watch_id;
 
@@ -61,6 +62,7 @@ struct GdmSessionWorkerJobPrivate
 enum {
         PROP_0,
         PROP_SERVER_ADDRESS,
+        PROP_FOR_REAUTH,
 };
 
 enum {
@@ -194,6 +196,10 @@ get_job_environment (GdmSessionWorkerJob *job)
 
         g_hash_table_insert (hash, g_strdup ("GDM_SESSION_DBUS_ADDRESS"), g_strdup (job->priv->server_address));
 
+        if (job->priv->for_reauth) {
+                g_hash_table_insert (hash, g_strdup ("GDM_SESSION_FOR_REAUTH"), g_strdup ("1"));
+        }
+
         g_hash_table_foreach (hash, (GHFunc)listify_hash, env);
         g_hash_table_destroy (hash);
 
@@ -349,6 +355,15 @@ gdm_session_worker_job_set_server_address (GdmSessionWorkerJob *session_worker_j
         session_worker_job->priv->server_address = g_strdup (address);
 }
 
+void
+gdm_session_worker_job_set_for_reauth (GdmSessionWorkerJob *session_worker_job,
+                                       gboolean             for_reauth)
+{
+        g_return_if_fail (GDM_IS_SESSION_WORKER_JOB (session_worker_job));
+
+        session_worker_job->priv->for_reauth = for_reauth;
+}
+
 static void
 gdm_session_worker_job_set_property (GObject      *object,
                                      guint         prop_id,
@@ -363,6 +378,9 @@ gdm_session_worker_job_set_property (GObject      *object,
         case PROP_SERVER_ADDRESS:
                 gdm_session_worker_job_set_server_address (self, g_value_get_string (value));
                 break;
+        case PROP_FOR_REAUTH:
+                gdm_session_worker_job_set_for_reauth (self, g_value_get_boolean (value));
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
@@ -383,6 +401,9 @@ gdm_session_worker_job_get_property (GObject    *object,
         case PROP_SERVER_ADDRESS:
                 g_value_set_string (value, self->priv->server_address);
                 break;
+        case PROP_FOR_REAUTH:
+                g_value_set_boolean (value, self->priv->for_reauth);
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
@@ -422,6 +443,13 @@ gdm_session_worker_job_class_init (GdmSessionWorkerJobClass *klass)
                                                               "server address",
                                                               NULL,
                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+        g_object_class_install_property (object_class,
+                                         PROP_FOR_REAUTH,
+                                         g_param_spec_boolean ("for-reauth",
+                                                               "for reauth",
+                                                               "for reauth",
+                                                               FALSE,
+                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
         signals [STARTED] =
                 g_signal_new ("started",
                               G_OBJECT_CLASS_TYPE (object_class),
diff --git a/daemon/gdm-session-worker-job.h b/daemon/gdm-session-worker-job.h
index ab102a9..7b256de 100644
--- a/daemon/gdm-session-worker-job.h
+++ b/daemon/gdm-session-worker-job.h
@@ -57,6 +57,8 @@ GType                   gdm_session_worker_job_get_type           (void);
 GdmSessionWorkerJob *   gdm_session_worker_job_new                (void);
 void                    gdm_session_worker_job_set_server_address (GdmSessionWorkerJob *session_worker_job,
                                                                    const char          *server_address);
+void                    gdm_session_worker_job_set_for_reauth (GdmSessionWorkerJob *session_worker_job,
+                                                               gboolean             for_reauth);
 gboolean                gdm_session_worker_job_start              (GdmSessionWorkerJob *session_worker_job,
                                                                    const char          *name);
 void                    gdm_session_worker_job_stop               (GdmSessionWorkerJob *session_worker_job);
diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c
index 45c24c9..9545814 100644
--- a/daemon/gdm-session-worker.c
+++ b/daemon/gdm-session-worker.c
@@ -50,6 +50,7 @@
 #include "gdm-log.h"
 #include "gdm-session-worker.h"
 #include "gdm-session-glue.h"
+#include "gdm-session.h"
 
 #if defined (HAVE_ADT)
 #include "gdm-session-solaris-auditor.h"
@@ -97,6 +98,15 @@ enum {
         GDM_SESSION_WORKER_STATE_SESSION_STARTED
 };
 
+typedef struct
+{
+        GdmSessionWorker *worker;
+        GdmSession       *session;
+        GPid              pid_of_caller;
+        uid_t             uid_of_caller;
+
+} ReauthenticationRequest;
+
 struct GdmSessionWorkerPrivate
 {
         int               state;
@@ -133,6 +143,7 @@ struct GdmSessionWorkerPrivate
         guint32           cancelled : 1;
         guint32           timed_out : 1;
         guint32           is_program_session : 1;
+        guint32           is_reauth_session : 1;
         guint32           display_is_local : 1;
         guint             state_change_idle_id;
 
@@ -140,6 +151,8 @@ struct GdmSessionWorkerPrivate
         GDBusConnection      *connection;
         GdmDBusWorkerManager *manager;
 
+        GHashTable         *reauthentication_requests;
+
         GdmSessionAuditor  *auditor;
         GdmSessionSettings *user_settings;
 };
@@ -147,6 +160,7 @@ struct GdmSessionWorkerPrivate
 enum {
         PROP_0,
         PROP_SERVER_ADDRESS,
+        PROP_IS_REAUTH_SESSION,
 };
 
 static void     gdm_session_worker_class_init   (GdmSessionWorkerClass *klass);
@@ -1213,6 +1227,13 @@ gdm_session_worker_authorize_user (GdmSessionWorker *worker,
                 }
         }
 
+        /* If the user is reauthenticating, then authorization isn't required to
+         * proceed, the user is already logged in after all.
+         */
+        if (worker->priv->is_reauth_session) {
+                error_code = PAM_SUCCESS;
+        }
+
         if (error_code != PAM_SUCCESS) {
                 g_debug ("GdmSessionWorker: user is not authorized to log in: %s",
                          pam_strerror (worker->priv->pam_handle, error_code));
@@ -1474,6 +1495,15 @@ gdm_session_worker_accredit_user (GdmSessionWorker  *worker,
 
         error_code = pam_setcred (worker->priv->pam_handle, worker->priv->cred_flags);
 
+        /* If the user is reauthenticating and they've made it this far, then there
+         * is no reason we should lock them out of their session.  They've already
+         * proved they are they same person who logged in, and that's all we care
+         * about.
+         */
+        if (worker->priv->is_reauth_session) {
+                error_code = PAM_SUCCESS;
+        }
+
         if (error_code != PAM_SUCCESS) {
                 g_set_error (error,
                              GDM_SESSION_WORKER_ERROR,
@@ -1917,6 +1947,13 @@ gdm_session_worker_set_server_address (GdmSessionWorker *worker,
 }
 
 static void
+gdm_session_worker_set_is_reauth_session (GdmSessionWorker *worker,
+                                          gboolean          is_reauth_session)
+{
+        worker->priv->is_reauth_session = is_reauth_session;
+}
+
+static void
 gdm_session_worker_set_property (GObject      *object,
                                 guint         prop_id,
                                 const GValue *value,
@@ -1930,6 +1967,9 @@ gdm_session_worker_set_property (GObject      *object,
         case PROP_SERVER_ADDRESS:
                 gdm_session_worker_set_server_address (self, g_value_get_string (value));
                 break;
+        case PROP_IS_REAUTH_SESSION:
+                gdm_session_worker_set_is_reauth_session (self, g_value_get_boolean (value));
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
@@ -1950,6 +1990,9 @@ gdm_session_worker_get_property (GObject    *object,
         case PROP_SERVER_ADDRESS:
                 g_value_set_string (value, self->priv->server_address);
                 break;
+        case PROP_IS_REAUTH_SESSION:
+                g_value_set_boolean (value, self->priv->is_reauth_session);
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
@@ -2395,6 +2438,11 @@ on_start_program (GdmDBusWorkerManager *proxy,
                 return;
         }
 
+        if (worker->priv->is_reauth_session) {
+                g_debug ("GdmSessionWorker: ignoring start program request in reauthentication session");
+                return;
+        }
+
         g_debug ("GdmSessionWorker: start program: %s", text);
 
         g_clear_pointer (&worker->priv->arguments, (GDestroyNotify) g_strfreev);
@@ -2408,6 +2456,146 @@ on_start_program (GdmDBusWorkerManager *proxy,
 }
 
 static void
+on_reauthentication_client_connected (GdmSession              *session,
+                                      GCredentials            *credentials,
+                                      GPid                     pid_of_client,
+                                      ReauthenticationRequest *request)
+{
+        g_debug ("GdmSessionWorker: client connected to reauthentication server");
+}
+
+static void
+on_reauthentication_client_disconnected (GdmSession              *session,
+                                         GCredentials            *credentials,
+                                         GPid                     pid_of_client,
+                                         ReauthenticationRequest *request)
+{
+        GdmSessionWorker *worker;
+
+        g_debug ("GdmSessionWorker: client disconnected from reauthentication server");
+
+        worker = request->worker;
+        g_hash_table_remove (worker->priv->reauthentication_requests,
+                             GINT_TO_POINTER (pid_of_client));
+}
+
+static void
+on_reauthentication_cancelled (GdmSession              *session,
+                               ReauthenticationRequest *request)
+{
+        g_debug ("GdmSessionWorker: client cancelled reauthentication request");
+        gdm_session_reset (session);
+}
+
+static void
+on_reauthentication_conversation_started (GdmSession              *session,
+                                          const char              *service_name,
+                                          ReauthenticationRequest *request)
+{
+        g_debug ("GdmSessionWorker: reauthentication service '%s' stopped",
+                 service_name);
+}
+
+static void
+on_reauthentication_conversation_stopped (GdmSession              *session,
+                                          const char              *service_name,
+                                          ReauthenticationRequest *request)
+{
+        g_debug ("GdmSessionWorker: reauthentication service '%s' started",
+                 service_name);
+}
+
+static void
+on_reauthenticated (GdmSession              *session,
+                    const char              *service_name,
+                    ReauthenticationRequest *request)
+{
+        g_debug ("GdmSessionWorker: pid %d reauthenticated user %d with service '%s'",
+                 (int) request->pid_of_caller,
+                 (int) request->uid_of_caller,
+                 service_name);
+}
+
+static ReauthenticationRequest *
+reauthentication_request_new (GdmSessionWorker *worker,
+                              GPid              pid_of_caller,
+                              uid_t             uid_of_caller)
+{
+        ReauthenticationRequest *request;
+        char *address;
+
+        request = g_slice_new (ReauthenticationRequest);
+
+        request->worker = worker;
+        request->pid_of_caller = pid_of_caller;
+        request->uid_of_caller = uid_of_caller;
+        request->session = gdm_session_new (GDM_SESSION_VERIFICATION_MODE_REAUTHENTICATE,
+                                            uid_of_caller,
+                                            worker->priv->x11_display_name,
+                                            worker->priv->hostname,
+                                            worker->priv->display_device,
+                                            worker->priv->display_seat_id,
+                                            worker->priv->x11_authority_file,
+                                            worker->priv->display_is_local);
+
+        g_signal_connect (request->session,
+                          "client-connected",
+                          G_CALLBACK (on_reauthentication_client_connected),
+                          request);
+        g_signal_connect (request->session,
+                          "client-disconnected",
+                          G_CALLBACK (on_reauthentication_client_disconnected),
+                          request);
+        g_signal_connect (request->session,
+                          "cancelled",
+                          G_CALLBACK (on_reauthentication_cancelled),
+                          request);
+        g_signal_connect (request->session,
+                          "conversation-started",
+                          G_CALLBACK (on_reauthentication_conversation_started),
+                          request);
+        g_signal_connect (request->session,
+                          "conversation-stopped",
+                          G_CALLBACK (on_reauthentication_conversation_stopped),
+                          request);
+        g_signal_connect (request->session,
+                          "reauthenticated",
+                          G_CALLBACK (on_reauthenticated),
+                          request);
+
+        address = gdm_session_get_server_address (request->session);
+        gdm_dbus_worker_manager_call_reauthentication_started_sync (worker->priv->manager,
+                                                                    pid_of_caller,
+                                                                    address,
+                                                                    NULL,
+                                                                    NULL);
+        g_free (address);
+
+        return request;
+}
+
+static void
+on_start_reauthentication (GdmDBusWorkerManager *proxy,
+                           int                   pid_of_caller,
+                           int                   uid_of_caller,
+                           GdmSessionWorker     *worker)
+{
+        ReauthenticationRequest *request;
+
+        if (worker->priv->state != GDM_SESSION_WORKER_STATE_SESSION_STARTED) {
+                g_debug ("GdmSessionWorker: ignoring spurious start reauthentication while in state %s", get_state_name (worker->priv->state));
+                return;
+        }
+
+        g_debug ("GdmSessionWorker: start reauthentication");
+
+        request = reauthentication_request_new (worker, pid_of_caller, uid_of_caller);
+        g_hash_table_replace (worker->priv->reauthentication_requests,
+                              GINT_TO_POINTER (pid_of_caller),
+                              request);
+}
+
+static void
 on_setup (GdmDBusWorkerManager *proxy,
           const char           *service,
           const char           *x11_display_name,
@@ -2552,7 +2740,11 @@ on_establish_credentials (GdmDBusWorkerManager *manager,
                 return;
         }
 
-        worker->priv->cred_flags = PAM_ESTABLISH_CRED;
+        if (!worker->priv->is_reauth_session) {
+                worker->priv->cred_flags = PAM_ESTABLISH_CRED;
+        } else {
+                worker->priv->cred_flags = PAM_REINITIALIZE_CRED;
+        }
 
         queue_state_change (worker);
 }
@@ -2567,6 +2759,11 @@ on_open_session (GdmDBusWorkerManager *manager,
                 return;
         }
 
+        if (worker->priv->is_reauth_session) {
+                g_debug ("GdmSessionWorker: ignoring open session request in reauthentication session");
+                return;
+        }
+
         queue_state_change (worker);
 }
 
@@ -2658,6 +2855,10 @@ gdm_session_worker_constructor (GType                  type,
                           "start-program",
                           G_CALLBACK (on_start_program),
                           worker);
+        g_signal_connect (worker->priv->manager,
+                          "start-reauthentication",
+                          G_CALLBACK (on_start_reauthentication),
+                          worker);
 
         /* Send an initial Hello message so that the session can associate
          * the conversation we manage with our pid.
@@ -2688,6 +2889,21 @@ gdm_session_worker_class_init (GdmSessionWorkerClass *klass)
                                                               "server address",
                                                               NULL,
                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+        g_object_class_install_property (object_class,
+                                         PROP_IS_REAUTH_SESSION,
+                                         g_param_spec_boolean ("is-reauth-session",
+                                                               "is reauth session",
+                                                               "is reauth session",
+                                                              FALSE,
+                                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+}
+
+static void
+reauthentication_request_free (ReauthenticationRequest *request)
+{
+        g_clear_object (&request->session);
+        g_slice_free (ReauthenticationRequest, request);
 }
 
 static void
@@ -2697,6 +2913,11 @@ gdm_session_worker_init (GdmSessionWorker *worker)
         worker->priv = GDM_SESSION_WORKER_GET_PRIVATE (worker);
 
         worker->priv->user_settings = gdm_session_settings_new ();
+        worker->priv->reauthentication_requests = g_hash_table_new_full (NULL,
+                                                                         NULL,
+                                                                         NULL,
+                                                                         (GDestroyNotify)
+                                                                         reauthentication_request_free);
 }
 
 static void
@@ -2735,16 +2956,20 @@ gdm_session_worker_finalize (GObject *object)
         g_free (worker->priv->server_address);
         g_strfreev (worker->priv->arguments);
 
+        g_hash_table_unref (worker->priv->reauthentication_requests);
+
         G_OBJECT_CLASS (gdm_session_worker_parent_class)->finalize (object);
 }
 
 GdmSessionWorker *
-gdm_session_worker_new (const char *address)
+gdm_session_worker_new (const char *address,
+                        gboolean    is_reauth_session)
 {
         GObject *object;
 
         object = g_object_new (GDM_TYPE_SESSION_WORKER,
                                "server-address", address,
+                               "is-reauth-session", is_reauth_session,
                                NULL);
 
         return GDM_SESSION_WORKER (object);
diff --git a/daemon/gdm-session-worker.h b/daemon/gdm-session-worker.h
index b1c8285..da5ec3f 100644
--- a/daemon/gdm-session-worker.h
+++ b/daemon/gdm-session-worker.h
@@ -65,7 +65,8 @@ typedef struct
 GType              gdm_session_worker_get_type                 (void);
 GQuark             gdm_session_worker_error_quark              (void);
 
-GdmSessionWorker * gdm_session_worker_new                      (const char *server_address) G_GNUC_MALLOC;
+GdmSessionWorker * gdm_session_worker_new                      (const char *server_address,
+                                                                gboolean    is_for_reauth) G_GNUC_MALLOC;
 
 G_END_DECLS
 
diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c
index 6b69530..861462c 100644
--- a/daemon/gdm-session.c
+++ b/daemon/gdm-session.c
@@ -113,6 +113,8 @@ struct _GdmSessionPrivate
 
         GdmSessionVerificationMode verification_mode;
 
+        uid_t                allowed_user;
+
         char                *fallback_session_name;
 
         GDBusServer         *worker_server;
@@ -123,6 +125,7 @@ struct _GdmSessionPrivate
 enum {
         PROP_0,
         PROP_VERIFICATION_MODE,
+        PROP_ALLOWED_USER,
         PROP_DISPLAY_NAME,
         PROP_DISPLAY_HOSTNAME,
         PROP_DISPLAY_IS_LOCAL,
@@ -147,6 +150,7 @@ enum {
         SESSION_START_FAILED,
         SESSION_EXITED,
         SESSION_DIED,
+        REAUTHENTICATION_STARTED,
         LAST_SIGNAL
 };
 
@@ -360,7 +364,22 @@ gdm_session_handle_accredited (GdmDBusWorkerManager  *worker_manager_interface,
         gdm_dbus_worker_manager_complete_accredited (worker_manager_interface,
                                                      invocation);
 
-        gdm_session_open_session (self, service_name);
+
+        switch (self->priv->verification_mode) {
+            case GDM_SESSION_VERIFICATION_MODE_REAUTHENTICATE:
+                if (self->priv->user_verifier_interface != NULL) {
+                        gdm_dbus_user_verifier_emit_verification_complete (self->priv->user_verifier_interface,
+                                                                           service_name);
+                }
+                break;
+
+            case GDM_SESSION_VERIFICATION_MODE_LOGIN:
+            case GDM_SESSION_VERIFICATION_MODE_CHOOSER:
+                gdm_session_open_session (self, service_name);
+                break;
+            default:
+                break;
+        }
 
         return TRUE;
 }
@@ -922,6 +941,22 @@ gdm_session_handle_session_exited_or_died (GdmDBusWorkerManager  *worker_manager
 }
 
 static gboolean
+gdm_session_handle_reauthentication_started (GdmDBusWorkerManager  *worker_manager_interface,
+                                             GDBusMethodInvocation *invocation,
+                                             int                    pid_of_caller,
+                                             const char            *address,
+                                             GdmSession            *self)
+{
+        gdm_dbus_worker_manager_complete_reauthentication_started (worker_manager_interface,
+                                                                   invocation);
+
+        g_debug ("GdmSession: Emitting 'reauthentication-started' signal for caller pid '%d'", pid_of_caller);
+        g_signal_emit (self, signals[REAUTHENTICATION_STARTED], 0, pid_of_caller, address);
+
+        return TRUE;
+}
+
+static gboolean
 gdm_session_handle_saved_language_name_read (GdmDBusWorkerManager  *worker_manager_interface,
                                              GDBusMethodInvocation *invocation,
                                              const char            *language_name,
@@ -999,9 +1034,18 @@ find_conversation_by_pid (GdmSession *self,
 static gboolean
 allow_worker_function (GDBusAuthObserver *observer,
                        GIOStream         *stream,
-                       GCredentials      *credentials)
+                       GCredentials      *credentials,
+                       GdmSession        *self)
 {
-        if (g_credentials_get_unix_user (credentials, NULL) == 0) {
+        uid_t connecting_user;
+
+        connecting_user = g_credentials_get_unix_user (credentials, NULL);
+
+        if (connecting_user == 0) {
+                return TRUE;
+        }
+
+        if (connecting_user == self->priv->allowed_user) {
                 return TRUE;
         }
 
@@ -1188,6 +1232,10 @@ export_worker_manager_interface (GdmSession      *self,
                           "handle-session-exited",
                           G_CALLBACK (gdm_session_handle_session_exited_or_died),
                           self);
+        g_signal_connect (worker_manager_interface,
+                          "handle-reauthentication-started",
+                          G_CALLBACK (gdm_session_handle_reauthentication_started),
+                          self);
 
         g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (worker_manager_interface),
                                           connection,
@@ -1541,16 +1589,26 @@ on_outside_connection_closed (GDBusConnection *connection,
                               GError          *error,
                               GdmSession      *self)
 {
+        GCredentials *credentials;
+        GPid          pid_of_client;
+
         g_debug ("GdmSession: external connection closed");
 
         self->priv->outside_connections =
             g_list_remove (self->priv->outside_connections,
                             connection);
-        g_object_unref (connection);
+
+        credentials = g_dbus_connection_get_peer_credentials (connection);
+        pid_of_client = credentials_get_unix_pid (credentials);
 
         g_signal_emit (G_OBJECT (self),
                        signals [CLIENT_DISCONNECTED],
-                       0);
+                       0,
+                       credentials,
+                       (guint)
+                       pid_of_client);
+
+        g_object_unref (connection);
 }
 
 static gboolean
@@ -1558,6 +1616,9 @@ handle_connection_from_outside (GDBusServer      *server,
                                 GDBusConnection  *connection,
                                 GdmSession       *self)
 {
+        GCredentials *credentials;
+        GPid          pid_of_client;
+
         g_debug ("GdmSession: Handling new connection from outside");
 
         self->priv->outside_connections =
@@ -1579,15 +1640,24 @@ handle_connection_from_outside (GDBusServer      *server,
                 case GDM_SESSION_VERIFICATION_MODE_CHOOSER:
                         export_chooser_interface (self, connection);
                 break;
+
+                default:
+                break;
         }
 
         if (!self->priv->display_is_local) {
                 export_remote_greeter_interface (self, connection);
         }
 
+        credentials = g_dbus_connection_get_peer_credentials (connection);
+        pid_of_client = credentials_get_unix_pid (credentials);
+
         g_signal_emit (G_OBJECT (self),
                        signals [CLIENT_CONNECTED],
-                       0);
+                       0,
+                       credentials,
+                       (guint)
+                       pid_of_client);
 
         return TRUE;
 }
@@ -1605,7 +1675,7 @@ setup_worker_server (GdmSession *self)
         g_signal_connect (observer,
                           "authorize-authenticated-peer",
                           G_CALLBACK (allow_worker_function),
-                          NULL);
+                          self);
 
         server = gdm_dbus_setup_private_server (observer, &error);
         g_object_unref (observer);
@@ -1629,48 +1699,15 @@ setup_worker_server (GdmSession *self)
 }
 
 static gboolean
-_get_uid_and_gid_for_user (const char *username,
-                           uid_t      *uid,
-                           gid_t      *gid)
-{
-        struct passwd *passwd_entry;
-
-        g_assert (username != NULL);
-
-        errno = 0;
-        gdm_get_pwent_for_name (username, &passwd_entry);
-
-        if (passwd_entry == NULL) {
-                return FALSE;
-        }
-
-        if (uid != NULL) {
-                *uid = passwd_entry->pw_uid;
-        }
-
-        if (gid != NULL) {
-                *gid = passwd_entry->pw_gid;
-        }
-
-        return TRUE;
-}
-
-static gboolean
 allow_user_function (GDBusAuthObserver *observer,
                      GIOStream         *stream,
-                     GCredentials      *credentials)
+                     GCredentials      *credentials,
+                     GdmSession        *self)
 {
-        uid_t uid;
-        gid_t gid;
         uid_t client_uid;
 
-        if (!_get_uid_and_gid_for_user (GDM_USERNAME, &uid, &gid)) {
-                g_debug ("GdmSession: Unable to determine uid for gdm user");
-                return FALSE;
-        }
-
         client_uid = g_credentials_get_unix_user (credentials, NULL);
-        if (uid == client_uid) {
+        if (client_uid == self->priv->allowed_user) {
                 return TRUE;
         }
 
@@ -1692,7 +1729,7 @@ setup_outside_server (GdmSession *self)
         g_signal_connect (observer,
                           "authorize-authenticated-peer",
                           G_CALLBACK (allow_user_function),
-                          NULL);
+                          self);
 
         server = gdm_dbus_setup_private_server (observer, &error);
         g_object_unref (observer);
@@ -2540,6 +2577,24 @@ gdm_session_client_is_connected (GdmSession *self)
         return self->priv->outside_connections != NULL;
 }
 
+uid_t
+gdm_session_get_allowed_user (GdmSession *self)
+{
+        return self->priv->allowed_user;
+}
+
+void
+gdm_session_start_reauthentication (GdmSession *session,
+                                    GPid        pid_of_caller,
+                                    uid_t       uid_of_caller)
+{
+        g_return_if_fail (session->priv->session_conversation != NULL);
+
+        gdm_dbus_worker_manager_emit_start_reauthentication (session->priv->session_conversation->worker_manager_interface,
+                                                             (int) pid_of_caller,
+                                                             (int) uid_of_caller);
+}
+
 char *
 gdm_session_get_server_address (GdmSession *self)
 {
@@ -2774,7 +2829,7 @@ set_display_is_local (GdmSession *self,
         self->priv->display_is_local = is_local;
 }
 
- static void
+static void
 set_verification_mode (GdmSession                 *self,
                        GdmSessionVerificationMode  verification_mode)
 {
@@ -2782,6 +2837,13 @@ set_verification_mode (GdmSession                 *self,
 }
 
 static void
+set_allowed_user (GdmSession *self,
+                  uid_t       allowed_user)
+{
+        self->priv->allowed_user = allowed_user;
+}
+
+static void
 gdm_session_set_property (GObject      *object,
                           guint         prop_id,
                           const GValue *value,
@@ -2816,6 +2878,9 @@ gdm_session_set_property (GObject      *object,
         case PROP_VERIFICATION_MODE:
                 set_verification_mode (self, g_value_get_enum (value));
                 break;
+        case PROP_ALLOWED_USER:
+                set_allowed_user (self, g_value_get_uint (value));
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
@@ -2857,6 +2922,9 @@ gdm_session_get_property (GObject    *object,
         case PROP_VERIFICATION_MODE:
                 g_value_set_enum (value, self->priv->verification_mode);
                 break;
+        case PROP_ALLOWED_USER:
+                g_value_set_uint (value, self->priv->allowed_user);
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
@@ -3040,6 +3108,19 @@ gdm_session_class_init (GdmSessionClass *session_class)
                               G_TYPE_NONE,
                               1,
                               G_TYPE_INT);
+
+        signals [REAUTHENTICATION_STARTED] =
+                g_signal_new ("reauthentication-started",
+                              GDM_TYPE_SESSION,
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmSessionClass, reauthentication_started),
+                              NULL,
+                              NULL,
+                              NULL,
+                              G_TYPE_NONE,
+                              2,
+                              G_TYPE_INT,
+                              G_TYPE_STRING);
         signals [CANCELLED] =
                 g_signal_new ("cancelled",
                               GDM_TYPE_SESSION,
@@ -3058,9 +3139,11 @@ gdm_session_class_init (GdmSessionClass *session_class)
                               G_STRUCT_OFFSET (GdmSessionClass, client_connected),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__VOID,
+                              NULL,
                               G_TYPE_NONE,
-                              0);
+                              2,
+                              G_TYPE_CREDENTIALS,
+                              G_TYPE_UINT);
 
         signals [CLIENT_DISCONNECTED] =
                 g_signal_new ("client-disconnected",
@@ -3069,9 +3152,11 @@ gdm_session_class_init (GdmSessionClass *session_class)
                               G_STRUCT_OFFSET (GdmSessionClass, client_disconnected),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__VOID,
+                              NULL,
                               G_TYPE_NONE,
-                              0);
+                              2,
+                              G_TYPE_CREDENTIALS,
+                              G_TYPE_UINT);
         signals [CLIENT_READY_FOR_SESSION_TO_START] =
                 g_signal_new ("client-ready-for-session-to-start",
                               GDM_TYPE_SESSION,
@@ -3116,6 +3201,16 @@ gdm_session_class_init (GdmSessionClass *session_class)
                                                             GDM_SESSION_VERIFICATION_MODE_LOGIN,
                                                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
         g_object_class_install_property (object_class,
+                                         PROP_ALLOWED_USER,
+                                         g_param_spec_uint ("allowed-user",
+                                                            "allowed user",
+                                                            "allowed user ",
+                                                            0,
+                                                            G_MAXUINT,
+                                                            0,
+                                                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+        g_object_class_install_property (object_class,
                                          PROP_DISPLAY_NAME,
                                          g_param_spec_string ("display-name",
                                                               "display name",
@@ -3170,6 +3265,7 @@ gdm_session_class_init (GdmSessionClass *session_class)
 
 GdmSession *
 gdm_session_new (GdmSessionVerificationMode  verification_mode,
+                 uid_t                       allowed_user,
                  const char                 *display_name,
                  const char                 *display_hostname,
                  const char                 *display_device,
@@ -3181,6 +3277,7 @@ gdm_session_new (GdmSessionVerificationMode  verification_mode,
 
         self = g_object_new (GDM_TYPE_SESSION,
                              "verification-mode", verification_mode,
+                             "allowed-user", (guint) allowed_user,
                              "display-name", display_name,
                              "display-hostname", display_hostname,
                              "display-device", display_device,
diff --git a/daemon/gdm-session.h b/daemon/gdm-session.h
index ad5f885..b16d981 100644
--- a/daemon/gdm-session.h
+++ b/daemon/gdm-session.h
@@ -37,7 +37,8 @@ typedef struct _GdmSessionPrivate GdmSessionPrivate;
 typedef enum
 {
         GDM_SESSION_VERIFICATION_MODE_LOGIN,
-        GDM_SESSION_VERIFICATION_MODE_CHOOSER
+        GDM_SESSION_VERIFICATION_MODE_CHOOSER,
+        GDM_SESSION_VERIFICATION_MODE_REAUTHENTICATE
 } GdmSessionVerificationMode;
 
 typedef struct
@@ -73,6 +74,8 @@ typedef struct
                                               int           exit_code);
         void (* session_died)                (GdmSession   *session,
                                               int           signal_number);
+        void (* reauthentication_started)    (GdmSession   *session,
+                                              GPid          pid_of_caller);
         void (* conversation_started)        (GdmSession   *session,
                                               const char   *service_name);
         void (* conversation_stopped)        (GdmSession   *session,
@@ -84,12 +87,17 @@ typedef struct
 GType            gdm_session_get_type                 (void);
 
 GdmSession      *gdm_session_new                      (GdmSessionVerificationMode verification_mode,
+                                                       uid_t       allowed_user,
                                                        const char *display_name,
                                                        const char *display_hostname,
                                                        const char *display_device,
                                                        const char *display_seat_id,
                                                        const char *display_x11_authority_file,
                                                        gboolean    display_is_local);
+uid_t             gdm_session_get_allowed_user       (GdmSession     *session);
+void              gdm_session_start_reauthentication (GdmSession *session,
+                                                      GPid        pid_of_caller,
+                                                      uid_t       uid_of_caller);
 
 char             *gdm_session_get_server_address          (GdmSession     *session);
 char             *gdm_session_get_username                (GdmSession     *session);
diff --git a/daemon/gdm-session.xml b/daemon/gdm-session.xml
index 2ef1ff8..ca597d2 100644
--- a/daemon/gdm-session.xml
+++ b/daemon/gdm-session.xml
@@ -67,6 +67,10 @@
       <arg name="service_name" direction="in" type="s"/>
       <arg name="problem" direction="in" type="s"/>
     </method>
+    <method name="ReauthenticationStarted">
+      <arg name="pid_of_caller" direction="in" type="i"/>
+      <arg name="address" direction="in" type="s"/>
+    </method>
     <method name="Opened">
       <arg name="service_name" direction="in" type="s"/>
       <arg name="session_id" direction="in" type="s"/>
@@ -143,6 +147,10 @@
     <signal name="StartSession">
       <arg name="service_name" type="s"/>
     </signal>
+    <signal name="StartReauthentication">
+      <arg name="pid_of_caller" type="i"/>
+      <arg name="uid_of_caller" type="i"/>
+    </signal>
     <signal name="SetEnvironmentVariable">
       <arg name="name" type="s" />
       <arg name="value" type="s" />
@@ -177,6 +185,9 @@
     </method>
     <method name="Cancel">
     </method>
+    <signal name="ReauthenticationStarted">
+      <arg name="pid_of_caller" type="i"/>
+    </signal>
     <signal name="Info">
       <arg name="service_name" type="s"/>
       <arg name="info" type="s"/>
@@ -242,6 +253,9 @@
     <signal name="SessionOpened">
       <arg name="service_name" type="s"/>
     </signal>
+    <signal name="Reauthenticated">
+      <arg name="service_name" type="s"/>
+    </signal>
   </interface>
   <interface name="org.gnome.DisplayManager.RemoteGreeter">
     <method name="Disconnect" />
diff --git a/daemon/gdm-simple-slave.c b/daemon/gdm-simple-slave.c
index 54a7bcb..41b6f04 100644
--- a/daemon/gdm-simple-slave.c
+++ b/daemon/gdm-simple-slave.c
@@ -79,6 +79,8 @@ struct GdmSimpleSlavePrivate
         GdmSession        *session;
         GdmGreeterSession *greeter;
 
+        GHashTable        *open_session_requests;
+
         guint              start_session_when_ready : 1;
         guint              waiting_to_start_session : 1;
         guint              session_is_running : 1;
@@ -520,12 +522,34 @@ start_autologin_conversation_if_necessary (GdmSimpleSlave  *slave)
 }
 
 static void
+on_session_reauthentication_started (GdmSession      *session,
+                                     int              pid_of_caller,
+                                     const char      *address,
+                                     GdmSimpleSlave  *slave)
+{
+        GSimpleAsyncResult *result;
+        gpointer            source_tag;
+
+        source_tag = GINT_TO_POINTER (pid_of_caller);
+
+        result = g_hash_table_lookup (slave->priv->open_session_requests,
+                                      source_tag);
+
+        if (result != NULL) {
+                g_simple_async_result_set_op_res_gpointer (result,
+                                                           g_strdup (address),
+                                                           (GDestroyNotify)
+                                                           g_free);
+                g_simple_async_result_complete_in_idle (result);
+        }
+}
+
+static void
 on_session_client_ready_for_session_to_start (GdmSession      *session,
                                               const char      *service_name,
                                               gboolean         client_is_ready,
                                               GdmSimpleSlave  *slave)
 {
-
         if (client_is_ready) {
                 g_debug ("GdmSimpleSlave: Will start session when ready");
         } else {
@@ -544,6 +568,8 @@ on_session_client_ready_for_session_to_start (GdmSession      *session,
 }
 static void
 on_session_client_connected (GdmSession          *session,
+                             GCredentials        *credentials,
+                             GPid                 pid_of_client,
                              GdmSimpleSlave      *slave)
 {
         gboolean display_is_local;
@@ -562,6 +588,8 @@ on_session_client_connected (GdmSession          *session,
 
 static void
 on_session_client_disconnected (GdmSession          *session,
+                                GCredentials        *credentials,
+                                GPid                 pid_of_client,
                                 GdmSimpleSlave      *slave)
 {
         gboolean display_is_local;
@@ -595,9 +623,14 @@ create_new_session (GdmSimpleSlave  *slave)
         char          *display_device;
         char          *display_seat_id;
         char          *display_x11_authority_file;
+        GdmSession    *greeter_session;
+        uid_t          greeter_uid;
 
         g_debug ("GdmSimpleSlave: Creating new session");
 
+        greeter_session = gdm_welcome_session_get_session (GDM_WELCOME_SESSION (slave->priv->greeter));
+        greeter_uid = gdm_session_get_allowed_user (greeter_session);
+
         g_object_get (slave,
                       "display-id", &display_id,
                       "display-name", &display_name,
@@ -613,6 +646,7 @@ create_new_session (GdmSimpleSlave  *slave)
         }
 
         slave->priv->session = gdm_session_new (GDM_SESSION_VERIFICATION_MODE_LOGIN,
+                                                greeter_uid,
                                                 display_name,
                                                 display_hostname,
                                                 display_device,
@@ -626,6 +660,10 @@ create_new_session (GdmSimpleSlave  *slave)
         g_free (display_hostname);
 
         g_signal_connect (slave->priv->session,
+                          "reauthentication-started",
+                          G_CALLBACK (on_session_reauthentication_started),
+                          slave);
+        g_signal_connect (slave->priv->session,
                           "client-ready-for-session-to-start",
                           G_CALLBACK (on_session_client_ready_for_session_to_start),
                           slave);
@@ -1117,35 +1155,98 @@ gdm_simple_slave_run (GdmSimpleSlave *slave)
         return TRUE;
 }
 
-static gboolean
-gdm_simple_slave_open_session (GdmSlave   *slave,
-                               char      **address,
-                               GError    **error)
+static char *
+gdm_simple_slave_open_session_finish (GdmSlave      *slave,
+                                      GAsyncResult  *result,
+                                      GError       **error)
 {
         GdmSimpleSlave  *self = GDM_SIMPLE_SLAVE (slave);
-        GdmSession      *session;
+        const char      *address;
+        gpointer         source_tag;
 
-        if (self->priv->session_is_running) {
-                g_set_error (error,
-                             G_DBUS_ERROR,
-                             G_DBUS_ERROR_ACCESS_DENIED,
-                             _("Greeter access only currently supported"));
-                return FALSE;
+        source_tag = g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (result));
+        g_hash_table_remove (self->priv->open_session_requests,
+                             source_tag);
+
+        address = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
+
+        if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) {
+                return NULL;
         }
 
-        session = self->priv->session;
+        return g_strdup (address);
+}
 
-        if (gdm_session_client_is_connected (session)) {
-                g_set_error (error,
-                             G_DBUS_ERROR,
-                             G_DBUS_ERROR_ACCESS_DENIED,
-                             _("Currently, only one client can be connected at once"));
-                return FALSE;
+static void
+gdm_simple_slave_open_session (GdmSlave             *slave,
+                               GPid                  pid_of_caller,
+                               uid_t                 uid_of_caller,
+                               GAsyncReadyCallback   callback,
+                               gpointer              user_data,
+                               GCancellable         *cancellable)
+{
+        GdmSimpleSlave     *self = GDM_SIMPLE_SLAVE (slave);
+        GSimpleAsyncResult *result;
+        gpointer            source_tag;
+
+        source_tag = GINT_TO_POINTER (pid_of_caller);
+
+        result = g_hash_table_lookup (self->priv->open_session_requests,
+                                      source_tag);
+
+        if (result != NULL) {
+                g_simple_async_result_set_error (result,
+                                                 G_DBUS_ERROR,
+                                                 G_DBUS_ERROR_ACCESS_DENIED,
+                                                 _("The client can only connect once"));
+                g_simple_async_result_complete_in_idle (result);
+                return;
         }
 
-        *address = gdm_session_get_server_address (session);
+        result = g_simple_async_result_new (G_OBJECT (slave),
+                                            callback,
+                                            user_data,
+                                            source_tag);
 
-        return TRUE;
+        g_simple_async_result_set_check_cancellable (result, cancellable);
+
+        g_hash_table_insert (self->priv->open_session_requests,
+                             source_tag,
+                             result);
+
+        if (!self->priv->session_is_running) {
+                uid_t allowed_user;
+
+                allowed_user = gdm_session_get_allowed_user (self->priv->session);
+
+                if (uid_of_caller != allowed_user) {
+                        g_simple_async_result_set_error (result,
+                                                         G_DBUS_ERROR,
+                                                         G_DBUS_ERROR_ACCESS_DENIED,
+                                                         _("Caller not GDM"));
+                } else if (gdm_session_client_is_connected (self->priv->session)) {
+                        g_simple_async_result_set_error (result,
+                                                         G_DBUS_ERROR,
+                                                         G_DBUS_ERROR_ACCESS_DENIED,
+                                                         _("Only one client can connect before session is started"));
+                } else {
+                        char *address;
+
+                        address = gdm_session_get_server_address (self->priv->session);
+
+                        g_simple_async_result_set_op_res_gpointer (result,
+                                                                   address,
+                                                                   (GDestroyNotify)
+                                                                   g_free);
+                }
+
+                g_simple_async_result_complete_in_idle (result);
+
+        } else {
+                gdm_session_start_reauthentication (self->priv->session,
+                                                    pid_of_caller,
+                                                    uid_of_caller);
+        }
 }
 
 static gboolean
@@ -1253,6 +1354,7 @@ gdm_simple_slave_class_init (GdmSimpleSlaveClass *klass)
         slave_class->start = gdm_simple_slave_start;
         slave_class->stop = gdm_simple_slave_stop;
         slave_class->open_session = gdm_simple_slave_open_session;
+        slave_class->open_session_finish = gdm_simple_slave_open_session_finish;
 
         g_type_class_add_private (klass, sizeof (GdmSimpleSlavePrivate));
 }
@@ -1264,6 +1366,14 @@ gdm_simple_slave_init (GdmSimpleSlave *slave)
 #ifdef  HAVE_LOGINDEVPERM
         slave->priv->use_logindevperm = FALSE;
 #endif
+
+        slave->priv->open_session_requests = g_hash_table_new_full (NULL,
+                                                                    NULL,
+                                                                    (GDestroyNotify)
+                                                                    NULL,
+                                                                    (GDestroyNotify)
+                                                                    g_object_unref);
+
 }
 
 static void
@@ -1280,6 +1390,8 @@ gdm_simple_slave_finalize (GObject *object)
 
         gdm_simple_slave_stop (GDM_SLAVE (slave));
 
+        g_hash_table_unref (slave->priv->open_session_requests);
+
         if (slave->priv->greeter_reset_id > 0) {
                 g_source_remove (slave->priv->greeter_reset_id);
                 slave->priv->greeter_reset_id = 0;
diff --git a/daemon/gdm-slave.c b/daemon/gdm-slave.c
index fbee296..0739953 100644
--- a/daemon/gdm-slave.c
+++ b/daemon/gdm-slave.c
@@ -1867,14 +1867,39 @@ gdm_slave_get_property (GObject    *object,
         }
 }
 
+static void
+on_session_opened (GdmSlave              *slave,
+                   GAsyncResult          *result,
+                   GDBusMethodInvocation *invocation)
+{
+        GdmSlaveClass      *slave_class;
+        GError             *error;
+        char               *address;
+
+        slave_class = GDM_SLAVE_GET_CLASS (slave);
+
+        g_assert (slave_class->open_session_finish != NULL);
+
+        error = NULL;
+        address = slave_class->open_session_finish (slave, result, &error);
+
+        if (address == NULL) {
+                g_dbus_method_invocation_return_gerror (invocation, error);
+        } else {
+                gdm_dbus_slave_complete_open_session (slave->priv->skeleton,
+                                                      invocation,
+                                                      address);
+        }
+}
+
 static gboolean
 handle_open_session (GdmDBusSlave          *skeleton,
                      GDBusMethodInvocation *invocation,
+                     int                    pid_of_caller,
+                     int                    uid_of_caller,
                      GdmSlave              *slave)
 {
-        GError        *error;
         GdmSlaveClass *slave_class;
-        char          *address;
 
         slave_class = GDM_SLAVE_GET_CLASS (slave);
         if (slave_class->open_session == NULL) {
@@ -1884,20 +1909,13 @@ handle_open_session (GdmDBusSlave          *skeleton,
                 return TRUE;
         }
 
-        error = NULL;
-        address = NULL;
-        if (!slave_class->open_session (slave,
-                                        &address,
-                                        &error)) {
-                g_dbus_method_invocation_return_gerror (invocation, error);
-                g_error_free (error);
-                return TRUE;
-        }
-
-        gdm_dbus_slave_complete_open_session (skeleton, invocation, address);
-
-        g_free (address);
-
+        slave_class->open_session (slave,
+                                   (GPid) pid_of_caller,
+                                   (uid_t) uid_of_caller,
+                                   (GAsyncReadyCallback)
+                                   on_session_opened,
+                                   invocation,
+                                   NULL);
         return TRUE;
 }
 
diff --git a/daemon/gdm-slave.h b/daemon/gdm-slave.h
index ee3dcbc..c84c47a 100644
--- a/daemon/gdm-slave.h
+++ b/daemon/gdm-slave.h
@@ -50,9 +50,15 @@ typedef struct
         gboolean (*start) (GdmSlave *slave);
         gboolean (*stop)  (GdmSlave *slave);
 
-        gboolean (*open_session) (GdmSlave    *slave,
-                                  char       **address,
-                                  GError     **error);
+        void     (*open_session)       (GdmSlave             *slave,
+                                        GPid                  pid_of_caller,
+                                        uid_t                 uid_of_caller,
+                                        GAsyncReadyCallback   callback,
+                                        gpointer              user_data,
+                                        GCancellable         *cancellable);
+        char   * (*open_session_finish) (GdmSlave      *slave,
+                                         GAsyncResult  *result,
+                                         GError       **error);
 
         /* signals */
         void (*stopped) (GdmSlave *slave);
diff --git a/daemon/gdm-slave.xml b/daemon/gdm-slave.xml
index b6e57cf..f8b7086 100644
--- a/daemon/gdm-slave.xml
+++ b/daemon/gdm-slave.xml
@@ -2,6 +2,8 @@
 <node>
   <interface name="org.gnome.DisplayManager.Slave">
     <method name="OpenSession">
+      <arg name="pid_of_caller" type="i" direction="in" />
+      <arg name="uid_of_caller" type="i" direction="in" />
       <arg name="address" type="s" direction="out" />
     </method>
 
diff --git a/daemon/gdm-welcome-session.c b/daemon/gdm-welcome-session.c
index e8947b5..3f3cd66 100644
--- a/daemon/gdm-welcome-session.c
+++ b/daemon/gdm-welcome-session.c
@@ -785,6 +785,8 @@ gboolean
 gdm_welcome_session_start (GdmWelcomeSession *welcome_session)
 {
         gboolean          res;
+        struct passwd *passwd_entry;
+        uid_t uid;
 
         g_debug ("GdmWelcomeSession: Starting welcome...");
         res = start_dbus_daemon (welcome_session);
@@ -793,7 +795,16 @@ gdm_welcome_session_start (GdmWelcomeSession *welcome_session)
                 return FALSE;
         }
 
+        res = gdm_get_pwent_for_name (welcome_session->priv->user_name,
+                                      &passwd_entry);
+
+        if (!res) {
+                return FALSE;
+        }
+
+        uid = passwd_entry->pw_uid;
         welcome_session->priv->session = gdm_session_new (welcome_session->priv->verification_mode,
+                                                          uid,
                                                           welcome_session->priv->x11_display_name,
                                                           welcome_session->priv->x11_display_hostname,
                                                           welcome_session->priv->x11_display_device,
diff --git a/daemon/gdm-xdmcp-chooser-slave.c b/daemon/gdm-xdmcp-chooser-slave.c
index c8bc586..982192c 100644
--- a/daemon/gdm-xdmcp-chooser-slave.c
+++ b/daemon/gdm-xdmcp-chooser-slave.c
@@ -155,6 +155,8 @@ on_chooser_disconnected (GdmSession           *session,
 
 static void
 on_chooser_connected (GdmSession           *session,
+                      GCredentials         *credentials,
+                      GPid                  pid_of_client,
                       GdmXdmcpChooserSlave *slave)
 {
         g_debug ("GdmXdmcpChooserSlave: Chooser connected");
@@ -331,27 +333,59 @@ gdm_xdmcp_chooser_slave_stop (GdmSlave *slave)
         return TRUE;
 }
 
-static gboolean
-gdm_xdmcp_chooser_slave_open_session (GdmSlave   *slave,
-                                      char      **address,
-                                      GError    **error)
+static char *
+gdm_xdmcp_chooser_slave_open_session_finish (GdmSlave      *slave,
+                                             GAsyncResult  *result,
+                                             GError       **error)
+{
+        const char           *address;
+
+        address = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
+
+        if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) {
+                return NULL;
+        }
+
+        return g_strdup (address);
+}
+
+static void
+gdm_xdmcp_chooser_slave_open_session (GdmSlave            *slave,
+                                      GPid                 pid_of_caller,
+                                      uid_t                uid_of_caller,
+                                      GAsyncReadyCallback  callback,
+                                      gpointer             user_data,
+                                      GCancellable        *cancellable)
 {
         GdmXdmcpChooserSlave *self = GDM_XDMCP_CHOOSER_SLAVE (slave);
-        GdmSession      *session;
+        GdmSession           *session;
+        GSimpleAsyncResult   *result;
+
+        result = g_simple_async_result_new (G_OBJECT (slave),
+                                            callback,
+                                            user_data,
+                                            GINT_TO_POINTER (pid_of_caller));
+        g_simple_async_result_set_check_cancellable (result, cancellable);
 
         session = gdm_welcome_session_get_session (GDM_WELCOME_SESSION (self->priv->chooser));
 
         if (gdm_session_client_is_connected (session)) {
-                g_set_error (error,
-                             G_DBUS_ERROR,
-                             G_DBUS_ERROR_ACCESS_DENIED,
-                             _("Currently, only one client can be connected at once"));
-                return FALSE;
-        }
+                g_simple_async_result_set_error (result,
+                                                 G_DBUS_ERROR,
+                                                 G_DBUS_ERROR_ACCESS_DENIED,
+                                                 _("Currently, only one client can be connected at once"));
+        } else {
+                char *address;
 
-        *address = gdm_session_get_server_address (session);
+                address = gdm_session_get_server_address (session);
 
-        return TRUE;
+                g_simple_async_result_set_op_res_gpointer (result,
+                                                           address,
+                                                           (GDestroyNotify)
+                                                           g_free);
+        }
+
+        g_simple_async_result_complete_in_idle (result);
 }
 
 static GObject *
@@ -384,6 +418,7 @@ gdm_xdmcp_chooser_slave_class_init (GdmXdmcpChooserSlaveClass *klass)
         slave_class->start = gdm_xdmcp_chooser_slave_start;
         slave_class->stop = gdm_xdmcp_chooser_slave_stop;
         slave_class->open_session = gdm_xdmcp_chooser_slave_open_session;
+        slave_class->open_session_finish = gdm_xdmcp_chooser_slave_open_session_finish;
 
         g_type_class_add_private (klass, sizeof (GdmXdmcpChooserSlavePrivate));
 }
diff --git a/daemon/session-worker-main.c b/daemon/session-worker-main.c
index 94496b5..7a385ed 100644
--- a/daemon/session-worker-main.c
+++ b/daemon/session-worker-main.c
@@ -137,6 +137,7 @@ main (int    argc,
         GdmSessionWorker *worker;
         GdmSignalHandler *signal_handler;
         const char       *address;
+        gboolean          is_for_reauth;
         static GOptionEntry entries []   = {
                 { NULL }
         };
@@ -176,7 +177,9 @@ main (int    argc,
                 exit (1);
         }
 
-        worker = gdm_session_worker_new (address);
+        is_for_reauth = g_getenv ("GDM_SESSION_FOR_REAUTH") != NULL;
+
+        worker = gdm_session_worker_new (address, is_for_reauth);
 
         main_loop = g_main_loop_new (NULL, FALSE);
 
diff --git a/data/gdm.conf.in b/data/gdm.conf.in
index b455971..ec20260 100644
--- a/data/gdm.conf.in
+++ b/data/gdm.conf.in
@@ -24,8 +24,6 @@
 
   <policy context="default">
     <deny send_destination="org.gnome.DisplayManager"
-          send_interface="org.gnome.DisplayManager.Manager"/>
-    <deny send_destination="org.gnome.DisplayManager"
           send_interface="org.gnome.DisplayManager.Display"/>
     <deny send_destination="org.gnome.DisplayManager"
           send_interface="org.gnome.DisplayManager.LocalDisplayFactory"/>
@@ -40,6 +38,8 @@
     <allow send_destination="org.gnome.DisplayManager"
            send_interface="org.freedesktop.DBus.ObjectManager"/>
     <allow send_destination="org.gnome.DisplayManager"
+           send_interface="org.gnome.DisplayManager.Manager"/>
+    <allow send_destination="org.gnome.DisplayManager"
            send_interface="org.gnome.DisplayManager.Display"
            send_member="GetId"/>
     <allow send_destination="org.gnome.DisplayManager"



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]