[gdm/wip/slave-connection: 30/33] worker: add reauthentication support



commit d1d1c8aedb5b9912c2dc96f1e79fcf575deaa845
Author: Ray Strode <rstrode redhat com>
Date:   Tue Jul 10 02:11:24 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                 |   20 +++
 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               |  104 ++++++++++++++++-
 daemon/gdm-session-enum-types.c.in |   42 +++++++
 daemon/gdm-session-enum-types.h.in |   24 ++++
 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               |  179 +++++++++++++++++++++++++---
 daemon/gdm-session.h               |   16 +++-
 daemon/gdm-session.xml             |   17 +++
 daemon/gdm-simple-slave.c          |  138 ++++++++++++++++++----
 daemon/gdm-slave.c                 |   52 ++++++---
 daemon/gdm-slave.h                 |   12 ++-
 daemon/gdm-slave.xml               |    2 +
 daemon/gdm-welcome-session.c       |   14 ++-
 daemon/gdm-xdmcp-chooser-slave.c   |   61 ++++++++--
 daemon/session-worker-main.c       |    5 +-
 data/gdm.conf.in                   |    4 +-
 23 files changed, 920 insertions(+), 81 deletions(-)
---
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 42aa2ff..c2f87bb 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -39,8 +39,15 @@ BUILT_SOURCES =					\
 	gdm-local-display-factory-glue.h	\
 	gdm-greeter-glue.h			\
 	gdm-session-glue.h			\
+	gdm-session-enum-types.h		\
 	$(NULL)
 
+gdm-session-enum-types.h: gdm-session-enum-types.h.in gdm-session.h
+	$(AM_V_GEN) glib-mkenums --template $^ > $@
+
+gdm-session-enum-types.c: gdm-session-enum-types.c.in gdm-session.h
+	$(AM_V_GEN) glib-mkenums --template $^ > $@
+
 gdm-display-glue.c gdm-display-glue.h: gdm-display.xml Makefile.am
 	$(AM_V_GEN)gdbus-codegen 					\
 		--c-namespace=GdmDBus					\
@@ -143,6 +150,8 @@ gdm_simple_slave_SOURCES = 		\
 	$(NULL)
 
 nodist_gdm_simple_slave_SOURCES = 	\
+	gdm-session-enum-types.c	\
+	gdm-session-enum-types.h	\
 	gdm-greeter-glue.c		\
 	gdm-greeter-glue.h		\
 	gdm-session-glue.c		\
@@ -191,6 +200,8 @@ gdm_xdmcp_chooser_slave_SOURCES = 		\
 nodist_gdm_xdmcp_chooser_slave_SOURCES = 	\
 	gdm-session-glue.c			\
 	gdm-session-glue.h			\
+	gdm-session-enum-types.c		\
+	gdm-session-enum-types.h		\
 	gdm-display-glue.c			\
 	gdm-display-glue.h			\
 	gdm-slave-glue.c			\
@@ -210,17 +221,26 @@ 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 =		\
 	gdm-session-glue.h			\
 	gdm-session-glue.c			\
+	gdm-session-enum-types.c		\
+	gdm-session-enum-types.h		\
 	$(NULL)
 
 if HAVE_LIBAUDIT
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 6141aca..8ae7e41 100644
--- a/daemon/gdm-display.c
+++ b/daemon/gdm-display.c
@@ -1250,6 +1250,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)
 {
@@ -1266,6 +1268,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 5c9e78a..9c9eca7 100644
--- a/daemon/gdm-manager.c
+++ b/daemon/gdm-manager.c
@@ -176,6 +176,81 @@ get_session_id_for_pid (GDBusConnection  *connection,
         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_CONSOLEKIT
+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",
+                                             g_variant_new ("(s)", session_id),
+                                             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_CONSOLEKIT
+        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,
@@ -207,6 +282,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;
@@ -217,7 +293,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);
@@ -229,6 +313,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);
@@ -243,7 +345,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-enum-types.c.in b/daemon/gdm-session-enum-types.c.in
new file mode 100644
index 0000000..c028690
--- /dev/null
+++ b/daemon/gdm-session-enum-types.c.in
@@ -0,0 +1,42 @@
+/*** BEGIN file-header ***/
+
+#include <glib-object.h>
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+#include "@filename@"
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name _get_type (void) G_GNUC_CONST;
+
+GType
+ enum_name@_get_type (void)
+{
+        static GType etype = 0;
+
+        if (G_UNLIKELY(etype == 0)) {
+                static const G Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+                { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+                { 0, NULL, NULL }
+        };
+
+        etype = g_ type@_register_static (g_intern_static_string ("@EnumName@"), values);
+    }
+
+    return etype;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+ /**/
+/*** END file-tail ***/
diff --git a/daemon/gdm-session-enum-types.h.in b/daemon/gdm-session-enum-types.h.in
new file mode 100644
index 0000000..b6934c1
--- /dev/null
+++ b/daemon/gdm-session-enum-types.h.in
@@ -0,0 +1,24 @@
+/*** BEGIN file-header ***/
+#ifndef GDM_SESSION_ENUM_TYPES_H
+#define GDM_SESSION_ENUM_TYPES_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name _get_type (void) G_GNUC_CONST;
+#define @ENUMPREFIX _TYPE_@ENUMSHORT@ (@enum_name _get_type ())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* GDM_SESSION_ENUM_TYPES_H */
+/*** END file-tail ***/
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 f44e2e1..6805c45 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,
@@ -1915,6 +1945,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,
@@ -1928,6 +1965,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;
@@ -1948,6 +1988,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;
@@ -2393,6 +2436,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);
@@ -2406,6 +2454,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,
@@ -2550,7 +2738,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);
 }
@@ -2565,6 +2757,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);
 }
 
@@ -2656,6 +2853,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.
@@ -2686,6 +2887,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
@@ -2695,6 +2911,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
@@ -2733,16 +2954,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 9ce2e64..0c5ac56 100644
--- a/daemon/gdm-session.c
+++ b/daemon/gdm-session.c
@@ -52,6 +52,7 @@
 #include "gdm-dbus-util.h"
 
 #include "gdm-session.h"
+#include "gdm-session-enum-types.h"
 #include "gdm-session-record.h"
 #include "gdm-session-worker-job.h"
 #include "gdm-common.h"
@@ -107,6 +108,9 @@ struct _GdmSessionPrivate
         char                *display_x11_authority_file;
         gboolean             display_is_local;
 
+        GdmSessionVerificationMode verification_mode;
+        uid_t                allowed_user;
+
         char                *fallback_session_name;
 
         GDBusServer         *worker_server;
@@ -116,6 +120,8 @@ struct _GdmSessionPrivate
 
 enum {
         PROP_0,
+        PROP_VERIFICATION_MODE,
+        PROP_ALLOWED_USER,
         PROP_DISPLAY_NAME,
         PROP_DISPLAY_HOSTNAME,
         PROP_DISPLAY_IS_LOCAL,
@@ -140,6 +146,7 @@ enum {
         SESSION_START_FAILED,
         SESSION_EXITED,
         SESSION_DIED,
+        REAUTHENTICATION_STARTED,
         LAST_SIGNAL
 };
 
@@ -349,7 +356,17 @@ 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:
+                gdm_dbus_user_verifier_emit_verification_complete (self->priv->user_verifier_interface,
+                                                                   service_name);
+                break;
+
+            case GDM_SESSION_VERIFICATION_MODE_LOGIN:
+                gdm_session_open_session (self, service_name);
+                break;
+        }
 
         return TRUE;
 }
@@ -809,6 +826,11 @@ gdm_session_handle_opened (GdmDBusWorkerManager  *worker_manager_interface,
         g_debug ("GdmSession: Emitting 'session-opened' signal");
         g_signal_emit (self, signals[SESSION_OPENED], 0, service_name, session_id);
 
+
+        g_warn_if_fail (self->priv->verification_mode == GDM_SESSION_VERIFICATION_MODE_LOGIN);
+        gdm_dbus_user_verifier_emit_verification_complete (self->priv->user_verifier_interface,
+                                                           service_name);
+
         return TRUE;
 }
 
@@ -893,6 +915,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,
@@ -966,9 +1004,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;
         }
 
@@ -1155,6 +1202,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,
@@ -1474,14 +1525,20 @@ on_outside_connection_closed (GDBusConnection *connection,
                               GError          *error,
                               GdmSession      *self)
 {
+        GCredentials *credentials;
+
         self->priv->outside_connections =
             g_list_remove (self->priv->outside_connections,
                             connection);
-        g_object_unref (connection);
+
+        credentials = g_dbus_connection_get_peer_credentials (connection);
 
         g_signal_emit (G_OBJECT (self),
                        signals [CLIENT_DISCONNECTED],
-                       0);
+                       0,
+                       credentials);
+
+        g_object_unref (connection);
 }
 
 static gboolean
@@ -1489,6 +1546,8 @@ handle_connection_from_outside (GDBusServer      *server,
                                 GDBusConnection  *connection,
                                 GdmSession       *self)
 {
+        GCredentials *credentials;
+
         g_debug ("GdmSession: Handling new connection from outside");
 
         self->priv->outside_connections =
@@ -1504,9 +1563,12 @@ handle_connection_from_outside (GDBusServer      *server,
         export_greeter_session_interface (self, connection);
         export_chooser_session_interface (self, connection);
 
+        credentials = g_dbus_connection_get_peer_credentials (connection);
+
         g_signal_emit (G_OBJECT (self),
                        signals [CLIENT_CONNECTED],
-                       0);
+                       0,
+                       credentials);
 
         return TRUE;
 }
@@ -1524,7 +1586,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);
@@ -2451,6 +2513,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)
 {
@@ -2686,6 +2766,20 @@ set_display_is_local (GdmSession *self,
 }
 
 static void
+set_verification_mode (GdmSession                 *self,
+                       GdmSessionVerificationMode  verification_mode)
+{
+        self->priv->verification_mode = verification_mode;
+}
+
+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,
@@ -2717,6 +2811,12 @@ gdm_session_set_property (GObject      *object,
         case PROP_DISPLAY_IS_LOCAL:
                 set_display_is_local (self, g_value_get_boolean (value));
                 break;
+        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;
@@ -2755,6 +2855,12 @@ gdm_session_get_property (GObject    *object,
         case PROP_DISPLAY_IS_LOCAL:
                 g_value_set_boolean (value, self->priv->display_is_local);
                 break;
+        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;
@@ -2938,6 +3044,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,
@@ -2956,9 +3075,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",
@@ -2967,9 +3088,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,
@@ -2995,6 +3118,24 @@ gdm_session_class_init (GdmSessionClass *session_class)
                               0);
 
         g_object_class_install_property (object_class,
+                                         PROP_VERIFICATION_MODE,
+                                         g_param_spec_enum ("verification-mode",
+                                                            "verification mode",
+                                                            "verification mode",
+                                                            GDM_TYPE_SESSION_VERIFICATION_MODE,
+                                                            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",
@@ -3048,16 +3189,20 @@ gdm_session_class_init (GdmSessionClass *session_class)
 }
 
 GdmSession *
-gdm_session_new (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)
+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)
 {
         GdmSession *self;
 
         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 22e8c35..2e6ea79 100644
--- a/daemon/gdm-session.h
+++ b/daemon/gdm-session.h
@@ -34,6 +34,12 @@ G_BEGIN_DECLS
 
 typedef struct _GdmSessionPrivate GdmSessionPrivate;
 
+typedef enum
+{
+        GDM_SESSION_VERIFICATION_MODE_LOGIN,
+        GDM_SESSION_VERIFICATION_MODE_REAUTHENTICATE
+} GdmSessionVerificationMode;
+
 typedef struct
 {
         GObject            parent;
@@ -67,6 +73,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,
@@ -77,12 +85,18 @@ typedef struct
 
 GType            gdm_session_get_type                 (void);
 
-GdmSession      *gdm_session_new                      (const char *display_name,
+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 0aee75a..f138395 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"/>
@@ -196,6 +207,9 @@
     <signal name="Ready">
       <arg name="service_name" type="s"/>
     </signal>
+    <signal name="ConversationStarted">
+      <arg name="service_name" type="s"/>
+    </signal>
     <signal name="ConversationStopped">
       <arg name="service_name" type="s"/>
     </signal>
@@ -242,6 +256,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.ChooserSession">
     <method name="SelectHostname">
diff --git a/daemon/gdm-simple-slave.c b/daemon/gdm-simple-slave.c
index 9668ac5..8b3e59f 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,31 @@ 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;
+
+        result = g_hash_table_lookup (slave->priv->open_session_requests,
+                                      GINT_TO_POINTER (pid_of_caller));
+
+        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 +565,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 +585,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 +620,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,
@@ -612,7 +642,9 @@ create_new_session (GdmSimpleSlave  *slave)
                 display_device = gdm_server_get_display_device (slave->priv->server);
         }
 
-        slave->priv->session = gdm_session_new (display_name,
+        slave->priv->session = gdm_session_new (GDM_SESSION_VERIFICATION_MODE_LOGIN,
+                                                greeter_uid,
+                                                display_name,
                                                 display_hostname,
                                                 display_device,
                                                 display_seat_id,
@@ -625,6 +657,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);
@@ -1119,35 +1155,80 @@ 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;
+        GPid             pid_of_caller;
 
-        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;
-        }
+        pid_of_caller = GPOINTER_TO_INT (g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (result)));
 
-        session = self->priv->session;
+        g_hash_table_remove (self->priv->open_session_requests,
+                             GINT_TO_POINTER (pid_of_caller));
 
-        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;
+        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;
         }
 
-        *address = gdm_session_get_server_address (session);
+        return g_strdup (address);
+}
 
-        return TRUE;
+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;
+
+        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);
+
+        g_hash_table_insert (self->priv->open_session_requests,
+                             GINT_TO_POINTER (pid_of_caller),
+                             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,
+                                                         _("The client can only connect once"));
+                } 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);
+
+        }
 }
 
 static gboolean
@@ -1255,6 +1336,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));
 }
@@ -1266,6 +1348,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
@@ -1282,6 +1372,8 @@ gdm_simple_slave_finalize (GObject *object)
 
         gdm_simple_slave_stop (GDM_SLAVE (slave));
 
+        g_object_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 db77dbf..741e8d3 100644
--- a/daemon/gdm-slave.c
+++ b/daemon/gdm-slave.c
@@ -1850,14 +1850,41 @@ 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);
+        }
+
+        g_object_unref (invocation);
+}
+
 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) {
@@ -1867,20 +1894,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 a937593..6e60b0b 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);
         gboolean (*get_private_connection) (GdmSlave    *slave,
                                             const char  *session_id,
                                             char       **address,
diff --git a/daemon/gdm-slave.xml b/daemon/gdm-slave.xml
index dbe5930..57371f0 100644
--- a/daemon/gdm-slave.xml
+++ b/daemon/gdm-slave.xml
@@ -7,6 +7,8 @@
     </method>
 
     <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 e488409..78fd178 100644
--- a/daemon/gdm-welcome-session.c
+++ b/daemon/gdm-welcome-session.c
@@ -779,6 +779,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);
@@ -787,7 +789,17 @@ gdm_welcome_session_start (GdmWelcomeSession *welcome_session)
                 return FALSE;
         }
 
-        welcome_session->priv->session = gdm_session_new (welcome_session->priv->x11_display_name,
+        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 (GDM_SESSION_VERIFICATION_MODE_LOGIN,
+                                                          uid,
+                                                          welcome_session->priv->x11_display_name,
                                                           welcome_session->priv->x11_display_hostname,
                                                           welcome_session->priv->x11_display_device,
                                                           welcome_session->priv->x11_display_seat_id,
diff --git a/daemon/gdm-xdmcp-chooser-slave.c b/daemon/gdm-xdmcp-chooser-slave.c
index 0fc3815..42cfce5 100644
--- a/daemon/gdm-xdmcp-chooser-slave.c
+++ b/daemon/gdm-xdmcp-chooser-slave.c
@@ -145,6 +145,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");
@@ -317,27 +319,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 *
@@ -371,6 +405,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;
 
         signals [HOSTNAME_SELECTED] =
                 g_signal_new ("hostname-selected",
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 9511432..ffe32a8 100644
--- a/data/gdm.conf.in
+++ b/data/gdm.conf.in
@@ -27,8 +27,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"/>
@@ -46,6 +44,8 @@
            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]