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



commit 157927cdfa9c69ad26143c896c1c4f614bbef476
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                 |   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               |  230 +++++++++++++++++++++++++++---------
 daemon/gdm-session.h               |   16 +++-
 daemon/gdm-session.xml             |   17 +++
 daemon/gdm-simple-slave.c          |  142 +++++++++++++++++++----
 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, 934 insertions(+), 122 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 917df4a..386ad06 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 413996a..9c7b893 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"
@@ -109,6 +110,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;
@@ -118,6 +122,8 @@ struct _GdmSessionPrivate
 
 enum {
         PROP_0,
+        PROP_VERIFICATION_MODE,
+        PROP_ALLOWED_USER,
         PROP_DISPLAY_NAME,
         PROP_DISPLAY_HOSTNAME,
         PROP_DISPLAY_IS_LOCAL,
@@ -142,6 +148,7 @@ enum {
         SESSION_START_FAILED,
         SESSION_EXITED,
         SESSION_DIED,
+        REAUTHENTICATION_STARTED,
         LAST_SIGNAL
 };
 
@@ -355,7 +362,19 @@ 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:
+                gdm_session_open_session (self, service_name);
+                break;
+        }
 
         return TRUE;
 }
@@ -828,6 +847,8 @@ 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);
+
         if (self->priv->user_verifier_interface != NULL) {
                 gdm_dbus_user_verifier_emit_verification_complete (self->priv->user_verifier_interface,
                                                                    service_name);
@@ -917,6 +938,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,
@@ -994,9 +1031,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;
         }
 
@@ -1186,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,
@@ -1539,16 +1589,22 @@ on_outside_connection_closed (GDBusConnection *connection,
                               GError          *error,
                               GdmSession      *self)
 {
+        GCredentials *credentials;
+
         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);
 
         g_signal_emit (G_OBJECT (self),
                        signals [CLIENT_DISCONNECTED],
-                       0);
+                       0,
+                       credentials);
+
+        g_object_unref (connection);
 }
 
 static gboolean
@@ -1556,6 +1612,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 =
@@ -1568,15 +1626,21 @@ handle_connection_from_outside (GDBusServer      *server,
                           self);
 
         export_user_verifier_interface (self, connection);
-        export_greeter_interface (self, connection);
-        if (self->priv->is_local) {
-                export_remote_greeter_interface (self, connection);
+
+        if (self->priv->verification_mode == GDM_SESSION_VERIFICATION_MODE_LOGIN) {
+                export_greeter_interface (self, connection);
+                if (self->priv->display_is_local) {
+                        export_remote_greeter_interface (self, connection);
+                }
+                export_chooser_interface (self, connection);
         }
-        export_chooser_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;
 }
@@ -1594,7 +1658,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);
@@ -1618,48 +1682,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;
         }
 
@@ -1681,7 +1712,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);
@@ -2529,6 +2560,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)
 {
@@ -2764,6 +2813,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,
@@ -2795,6 +2858,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;
@@ -2833,6 +2902,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;
@@ -3016,6 +3091,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,
@@ -3034,9 +3122,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",
@@ -3045,9 +3135,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,
@@ -3073,6 +3165,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",
@@ -3126,16 +3236,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 2ef1ff8..d0986ae 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="ConversationStarted">
       <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.RemoteGreeter">
     <method name="Disconnect" />
diff --git a/daemon/gdm-simple-slave.c b/daemon/gdm-simple-slave.c
index 15e76da..0927709 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);
@@ -1116,35 +1152,84 @@ 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);
+
+        } else {
+                gdm_session_start_reauthentication (self->priv->session,
+                                                    pid_of_caller,
+                                                    uid_of_caller);
+        }
 }
 
 static gboolean
@@ -1252,6 +1337,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));
 }
@@ -1263,6 +1349,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
@@ -1279,6 +1373,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 c3e6a63..faf1797 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 64ad511..ac4d3e8 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 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 7ae5ecb..4fafd40 100644
--- a/daemon/gdm-welcome-session.c
+++ b/daemon/gdm-welcome-session.c
@@ -782,6 +782,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);
@@ -790,7 +792,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 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]