[gdm/wip/slave-connection: 18/24] worker: add reauthentication support



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

    worker: add reauthentication support
    
    This commit adds reauthentication support for
    screensavers and user switching 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 adds a way for programs to open a communication channel for user
    verification to already runnings sessions (so reauthentication happens
    in the context of the session).

 daemon/Makefile.am               |    7 +
 daemon/gdm-dbus-util.c           |   40 +++
 daemon/gdm-dbus-util.h           |    3 +
 daemon/gdm-display.c             |   39 +++
 daemon/gdm-display.h             |    9 +
 daemon/gdm-manager.c             |  525 +++++++++++++++++++++++++++++++++++++-
 daemon/gdm-manager.xml           |    4 +
 daemon/gdm-session-worker-job.c  |   28 ++
 daemon/gdm-session-worker-job.h  |    2 +
 daemon/gdm-session-worker.c      |  239 +++++++++++++++++-
 daemon/gdm-session-worker.h      |    3 +-
 daemon/gdm-session.c             |  321 ++++++++++++++++++-----
 daemon/gdm-session.h             |   14 +-
 daemon/gdm-session.xml           |   26 ++
 daemon/gdm-simple-slave.c        |  147 ++++++++++-
 daemon/gdm-slave.c               |   64 +++++
 daemon/gdm-slave.h               |   19 ++-
 daemon/gdm-slave.xml             |    9 +
 daemon/gdm-welcome-session.c     |   11 +
 daemon/gdm-xdmcp-chooser-slave.c |   14 +-
 daemon/session-worker-main.c     |    5 +-
 data/gdm.conf.in                 |    4 +-
 22 files changed, 1438 insertions(+), 95 deletions(-)
---
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index cd5e122..061b5a2 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -220,12 +220,19 @@ gdm_xdmcp_chooser_slave_LDADD = 		\
 
 gdm_session_worker_SOURCES = 			\
 	session-worker-main.c 			\
+	gdm-session.c				\
+	gdm-session.h				\
 	gdm-session-settings.h			\
 	gdm-session-settings.c			\
 	gdm-session-auditor.h			\
 	gdm-session-auditor.c			\
+	gdm-session-record.c			\
+	gdm-session-record.h			\
 	gdm-session-worker.h			\
 	gdm-session-worker.c			\
+	gdm-session-worker-job.c		\
+	gdm-dbus-util.c				\
+	gdm-dbus-util.h				\
 	$(NULL)
 
 nodist_gdm_session_worker_SOURCES =		\
diff --git a/daemon/gdm-dbus-util.c b/daemon/gdm-dbus-util.c
index e57f0a0..94361c6 100644
--- a/daemon/gdm-dbus-util.c
+++ b/daemon/gdm-dbus-util.c
@@ -163,3 +163,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 b3210b4..a9a42ac 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 0c6e756..25fff69 100644
--- a/daemon/gdm-display.c
+++ b/daemon/gdm-display.c
@@ -1254,6 +1254,8 @@ register_display (GdmDisplay *display)
 
 char *
 gdm_display_open_session_sync (GdmDisplay    *display,
+                               GPid           pid_of_caller,
+                               uid_t          uid_of_caller,
                                GCancellable  *cancellable,
                                GError       **error)
 {
@@ -1270,6 +1272,8 @@ gdm_display_open_session_sync (GdmDisplay    *display,
 
         address = NULL;
         ret = gdm_dbus_slave_call_open_session_sync (display->priv->slave_bus_proxy,
+                                                     (int) pid_of_caller,
+                                                     (int) uid_of_caller,
                                                      &address,
                                                      cancellable,
                                                      error);
@@ -1281,6 +1285,41 @@ gdm_display_open_session_sync (GdmDisplay    *display,
         return address;
 }
 
+char *
+gdm_display_open_reauthentication_channel_sync (GdmDisplay    *display,
+                                                const char    *username,
+                                                GPid           pid_of_caller,
+                                                uid_t          uid_of_caller,
+                                                GCancellable  *cancellable,
+                                                GError       **error)
+{
+        char *address;
+        int ret;
+
+        if (display->priv->slave_bus_proxy == NULL) {
+                g_set_error (error,
+                             G_DBUS_ERROR,
+                             G_DBUS_ERROR_ACCESS_DENIED,
+                             _("No session available yet"));
+                return NULL;
+        }
+
+        address = NULL;
+        ret = gdm_dbus_slave_call_open_reauthentication_channel_sync (display->priv->slave_bus_proxy,
+                                                                      username,
+                                                                      pid_of_caller,
+                                                                      uid_of_caller,
+                                                                      &address,
+                                                                      cancellable,
+                                                                      error);
+
+        if (!ret) {
+                return NULL;
+        }
+
+        return address;
+}
+
 /*
   dbus-send --system --print-reply --dest=org.gnome.DisplayManager /org/gnome/DisplayManager/Displays/1 org.freedesktop.DBus.Introspectable.Introspect
 */
diff --git a/daemon/gdm-display.h b/daemon/gdm-display.h
index 1f4d3db..5050eec 100644
--- a/daemon/gdm-display.h
+++ b/daemon/gdm-display.h
@@ -91,8 +91,17 @@ 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_open_reauthentication_channel_sync        (GdmDisplay    *display,
+                                                                           const char    *username,
+                                                                           GPid           pid_of_caller,
+                                                                           uid_t          uid_of_caller,
+                                                                           GCancellable  *cancellable,
+                                                                           GError       **error);
 char *              gdm_display_get_session_id                 (GdmDisplay *display);
 gboolean            gdm_display_create_authority               (GdmDisplay *display);
 gboolean            gdm_display_prepare                        (GdmDisplay *display);
diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c
index 2ae06b8..abff0f4 100644
--- a/daemon/gdm-manager.c
+++ b/daemon/gdm-manager.c
@@ -178,6 +178,319 @@ 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_CONSOLE_KIT
+static gboolean
+get_uid_for_consolekit_session_id (GDBusConnection  *connection,
+                                   const char       *session_id,
+                                   uid_t            *out_uid,
+                                   GError          **error)
+{
+        GVariant *reply;
+        guint32 uid;
+
+        reply = g_dbus_connection_call_sync (connection,
+                                             "org.freedesktop.ConsoleKit",
+                                             session_id,
+                                             "org.freedesktop.ConsoleKit.Session",
+                                             "GetUnixUser",
+                                             NULL,
+                                             G_VARIANT_TYPE ("(u)"),
+                                             G_DBUS_CALL_FLAGS_NONE,
+                                             -1,
+                                             NULL,
+                                             error);
+        if (reply == NULL) {
+                return FALSE;
+        }
+
+        g_variant_get (reply, "(u)", &uid);
+        g_variant_unref (reply);
+
+        *out_uid = (uid_t) uid;
+
+        return TRUE;
+}
+#endif
+
+static gboolean
+get_uid_for_session_id (GDBusConnection  *connection,
+                        const char       *session_id,
+                        uid_t            *uid,
+                        GError          **error)
+{
+#ifdef WITH_SYSTEMD
+        if (sd_booted () > 0) {
+                return get_uid_for_systemd_session_id (session_id, uid, error);
+        }
+#endif
+
+#ifdef WITH_CONSOLE_KIT
+        return get_uid_for_consolekit_session_id (connection, session_id, uid, error);
+#endif
+
+        return FALSE;
+}
+
+#ifdef WITH_SYSTEMD
+static char *
+get_session_id_for_user_on_seat_systemd (const char  *username,
+                                         const char  *seat,
+                                         GError     **error)
+{
+        struct passwd  *pwent;
+        uid_t           uid;
+        int             i;
+        char          **sessions;
+        char           *session = NULL;
+        int             ret;
+
+        pwent = NULL;
+        gdm_get_pwent_for_name (username, &pwent);
+
+        if (pwent == NULL) {
+                g_set_error (error,
+                             G_DBUS_ERROR,
+                             G_DBUS_ERROR_ACCESS_DENIED,
+                             _("Unable to look up UID of user %s"),
+                             username);
+                return NULL;
+        }
+
+        uid = pwent->pw_uid;
+
+        session = NULL;
+        ret = sd_seat_get_sessions (seat, &sessions, NULL, NULL);
+        if (ret < 0 || sessions == NULL) {
+                g_set_error (error,
+                             G_DBUS_ERROR,
+                             G_DBUS_ERROR_ACCESS_DENIED,
+                             "Error getting session ids from systemd: %s",
+                             ret < 0? g_strerror (-ret) : _("no sessions available"));
+                return NULL;
+        }
+
+        for (i = 0; sessions[i] != NULL; i++) {
+                char     *type;
+                char     *state;
+                gboolean  is_closing;
+                gboolean  is_x11;
+                uid_t     session_uid;
+
+                ret = sd_session_get_uid (sessions[i], &session_uid);
+
+                if (ret < 0) {
+                        g_warning ("GdmManager: could not fetch uid of session '%s': %s",
+                                   sessions[i], strerror (-ret));
+                        continue;
+                }
+
+                if (uid != session_uid) {
+                        continue;
+                }
+
+                ret = sd_session_get_type (sessions[i], &type);
+
+                if (ret < 0) {
+                        g_warning ("GdmManager: could not fetch type of session '%s': %s",
+                                   sessions[i], strerror (-ret));
+                        continue;
+                }
+
+                is_x11 = g_strcmp0 (type, "x11") == 0;
+                free (type);
+
+                if (!is_x11) {
+                        continue;
+                }
+
+                ret = sd_session_get_state (sessions[i], &state);
+                if (ret < 0) {
+                        g_warning ("GdmManager: could not fetch state of session '%s': %s",
+                                   sessions[i], strerror (-ret));
+                        continue;
+                }
+
+                is_closing = g_strcmp0 (state, "closing") == 0;
+
+                if (is_closing) {
+                        continue;
+                }
+
+                session = g_strdup (sessions[i]);
+                break;
+
+        }
+
+        if (session == NULL) {
+                g_debug ("GdmManager: There are no applicable sessions (%d looked at)", i);
+                g_set_error (error,
+                             G_DBUS_ERROR,
+                             G_DBUS_ERROR_ACCESS_DENIED,
+                             _("No sessions for %s available for reauthentication"),
+                             username);
+                return NULL;
+        }
+
+        g_debug ("GdmManager: session %s is available for reauthentication", session);
+
+        return session;
+}
+#endif
+
+#ifdef WITH_CONSOLE_KIT
+static char *
+get_session_id_for_user_on_seat_consolekit (GDBusConnection  *connection,
+                                            const char       *username,
+                                            const char       *seat,
+                                            GError          **error)
+{
+        GVariant       *reply;
+        GVariant       *array;
+        const gchar   **sessions;
+        char           *session = NULL;
+        struct passwd  *pwent;
+        uid_t           uid;
+        int             i;
+
+        pwent = NULL;
+        gdm_get_pwent_for_name (username, &pwent);
+
+        if (pwent == NULL) {
+                g_set_error (error,
+                             G_DBUS_ERROR,
+                             G_DBUS_ERROR_ACCESS_DENIED,
+                             _("Unable to look up UID of user %s"),
+                             username);
+                return NULL;
+        }
+
+        uid = pwent->pw_uid;
+
+        reply = g_dbus_connection_call_sync (connection,
+                                             "org.freedesktop.ConsoleKit",
+                                             "/org/freedesktop/ConsoleKit/Manager",
+                                             "org.freedesktop.ConsoleKit.Manager",
+                                             "GetSessionsForUnixUser",
+                                             g_variant_new ("(u)", (guint32) uid),
+                                             G_VARIANT_TYPE ("(ao)"),
+                                             G_DBUS_CALL_FLAGS_NONE,
+                                             -1,
+                                             NULL,
+                                             error);
+        if (reply == NULL) {
+                return NULL;
+        }
+
+        g_variant_get (reply, "(ao)", &array);
+
+        sessions = g_variant_get_objv (array, NULL);
+
+        for (i = 0; sessions[i] != NULL; i++) {
+                GVariant *reply2;
+                GError   *error2 = NULL;
+                gchar    *display;
+                gchar    *session_seat_id;
+
+                reply2 = g_dbus_connection_call_sync (connection,
+                                                     "org.freedesktop.ConsoleKit",
+                                                     sessions[i],
+                                                     "org.freedesktop.ConsoleKit.Session",
+                                                     "GetSeatId",
+                                                     NULL,
+                                                     G_VARIANT_TYPE ("(o)"),
+                                                     G_DBUS_CALL_FLAGS_NONE,
+                                                     -1,
+                                                     NULL,
+                                                     &error2);
+                if (reply2 == NULL) {
+                        continue;
+                }
+
+                g_variant_get (reply, "(o)", &session_seat_id);
+                g_variant_unref (reply2);
+
+                if (g_strcmp0 (seat, session_seat_id) != 0) {
+                        g_free (session_seat_id);
+                        continue;
+                }
+                g_free (session_seat_id);
+
+                reply2 = g_dbus_connection_call_sync (connection,
+                                                     "org.freedesktop.ConsoleKit",
+                                                     sessions[i],
+                                                     "org.freedesktop.ConsoleKit.Session",
+                                                     "GetX11DisplayDevice",
+                                                     NULL,
+                                                     G_VARIANT_TYPE ("(s)"),
+                                                     G_DBUS_CALL_FLAGS_NONE,
+                                                     -1,
+                                                     NULL,
+                                                     &error2);
+                if (reply2 == NULL) {
+                        continue;
+                }
+
+                g_variant_get (reply2, "(s)", &display);
+                g_variant_unref (reply2);
+
+                if (display[0] == '\0') {
+                        g_free (display);
+                        continue;
+                }
+
+                session = g_strdup (sessions[i]);
+                break;
+
+        }
+        g_free (sessions);
+        g_variant_unref (reply);
+
+        return session;
+}
+#endif
+
+static char *
+get_session_id_for_user_on_seat (GDBusConnection  *connection,
+                                 const char       *username,
+                                 const char       *seat,
+                                 GError          **error)
+{
+#ifdef WITH_SYSTEMD
+        if (sd_booted () > 0) {
+                return get_session_id_for_user_on_seat_systemd (username, seat, error);
+        }
+#endif
+
+#ifdef WITH_CONSOLE_KIT
+        return get_session_id_for_user_on_seat_consolekit (connection, username, seat, error);
+#endif
+
+        return NULL;
+}
+
 static gboolean
 lookup_by_session_id (const char *id,
                       GdmDisplay *display,
@@ -196,6 +509,102 @@ lookup_by_session_id (const char *id,
         return res;
 }
 
+#ifdef WITH_SYSTEMD
+static char *
+get_seat_id_for_pid_systemd (pid_t    pid,
+                             GError **error)
+{
+        char *session;
+        char *seat, *gseat;
+        int ret;
+
+        session = get_session_id_for_pid_systemd (pid, error);
+
+        if (session == NULL) {
+                return NULL;
+        }
+
+        seat = NULL;
+        ret = sd_session_get_seat (session, &seat);
+        free (session);
+
+        if (ret < 0) {
+                g_set_error (error,
+                             GDM_DISPLAY_ERROR,
+                             GDM_DISPLAY_ERROR_GETTING_SESSION_INFO,
+                             "Error getting seat id from systemd: %s",
+                             g_strerror (-ret));
+                return NULL;
+        }
+
+        if (seat != NULL) {
+                gseat = g_strdup (seat);
+                free (seat);
+
+                return gseat;
+        } else {
+                return NULL;
+        }
+}
+#endif
+
+#ifdef WITH_CONSOLE_KIT
+static char *
+get_seat_id_for_pid_consolekit (GDBusConnection  *connection,
+                                pid_t             pid,
+                                GError          **error)
+{
+        char *session;
+        GVariant *reply;
+        char *retval;
+
+        session = get_session_id_for_pid_consolekit (connection, pid, error);
+
+        if (session == NULL) {
+                return NULL;
+        }
+
+        reply = g_dbus_connection_call_sync (connection,
+                                             "org.freedesktop.ConsoleKit",
+                                             session,
+                                             "org.freedesktop.ConsoleKit.Session",
+                                             "GetSeatId",
+                                             NULL,
+                                             G_VARIANT_TYPE ("(o)"),
+                                             G_DBUS_CALL_FLAGS_NONE,
+                                             -1,
+                                             NULL, error);
+        g_free (session);
+
+        if (reply == NULL) {
+                return NULL;
+        }
+
+        g_variant_get (reply, "(o)", &retval);
+        g_variant_unref (reply);
+
+        return retval;
+}
+#endif
+
+static char *
+get_seat_id_for_pid (GDBusConnection  *connection,
+                     pid_t             pid,
+                     GError          **error)
+{
+#ifdef WITH_SYSTEMD
+        if (sd_booted () > 0) {
+                return get_seat_id_for_pid_systemd (pid, error);
+        }
+#endif
+
+#ifdef WITH_CONSOLE_KIT
+        return get_seat_id_for_pid_consolekit (connection, pid, error);
+#endif
+
+        return NULL;
+}
+
 static gboolean
 gdm_manager_handle_open_session (GdmDBusManager        *manager,
                                  GDBusMethodInvocation *invocation)
@@ -209,6 +618,7 @@ gdm_manager_handle_open_session (GdmDBusManager        *manager,
         char             *address;
         int               ret;
         GPid              pid;
+        uid_t             caller_uid, session_uid;
 
         sender = g_dbus_method_invocation_get_sender (invocation);
         error = NULL;
@@ -219,7 +629,15 @@ gdm_manager_handle_open_session (GdmDBusManager        *manager,
                 g_dbus_method_invocation_return_gerror (invocation, error);
                 g_error_free (error);
                 return TRUE;
+        }
+
+        ret = gdm_dbus_get_uid_for_name (sender, &caller_uid, &error);
 
+        if (!ret) {
+                g_prefix_error (&error, "Error while retrieving caller session id: ");
+                g_dbus_method_invocation_return_gerror (invocation, error);
+                g_error_free (error);
+                return TRUE;
         }
 
         connection = g_dbus_method_invocation_get_connection (invocation);
@@ -231,6 +649,24 @@ gdm_manager_handle_open_session (GdmDBusManager        *manager,
                 return TRUE;
         }
 
+        if (!get_uid_for_session_id (connection, session_id, &session_uid, &error)) {
+                g_prefix_error (&error, "Error while retrieving caller session id: ");
+                g_dbus_method_invocation_return_gerror (invocation, error);
+                g_error_free (error);
+                return TRUE;
+        }
+
+        if (caller_uid != session_uid) {
+                g_dbus_method_invocation_return_error_literal (invocation,
+                                                               G_DBUS_ERROR,
+                                                               G_DBUS_ERROR_ACCESS_DENIED,
+                                                               _("User doesn't own session"));
+                g_prefix_error (&error, "Error while retrieving caller session id: ");
+                g_dbus_method_invocation_return_gerror (invocation, error);
+                g_error_free (error);
+                return TRUE;
+        }
+
         display = gdm_display_store_find (self->priv->display_store,
                                           lookup_by_session_id,
                                           (gpointer) session_id);
@@ -245,7 +681,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);
@@ -261,10 +697,97 @@ gdm_manager_handle_open_session (GdmDBusManager        *manager,
         return TRUE;
 }
 
+static gboolean
+gdm_manager_handle_open_reauthentication_channel (GdmDBusManager        *manager,
+                                                  GDBusMethodInvocation *invocation,
+                                                  const char            *username)
+{
+        GdmManager       *self = GDM_MANAGER (manager);
+        GDBusConnection  *connection;
+        GdmDisplay       *display;
+        const char       *sender;
+        char             *seat_id;
+        char             *session_id = NULL;
+        GError           *error;
+        char             *address;
+        int               ret;
+        GPid              pid;
+        uid_t             caller_uid;
+
+        g_debug ("GdmManager: trying to open reauthentication channel for user %s", username);
+
+        sender = g_dbus_method_invocation_get_sender (invocation);
+        error = NULL;
+        ret = gdm_dbus_get_pid_for_name (sender, &pid, &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;
+
+        }
+
+        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);
+
+        seat_id = get_seat_id_for_pid (connection, pid, &error);
+
+        session_id = get_session_id_for_user_on_seat (connection, username, seat_id, &error);
+        if (session_id == NULL) {
+                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);
+        g_free (session_id);
+
+        if (display == NULL) {
+                g_dbus_method_invocation_return_error_literal (invocation,
+                                                               G_DBUS_ERROR,
+                                                               G_DBUS_ERROR_ACCESS_DENIED,
+                                                               _("No session available"));
+
+                return TRUE;
+        }
+
+        address = gdm_display_open_reauthentication_channel_sync (display,
+                                                                  username,
+                                                                  pid,
+                                                                  caller_uid,
+                                                                  NULL,
+                                                                  &error);
+
+        if (address == NULL) {
+                g_dbus_method_invocation_return_gerror (invocation, error);
+                g_error_free (error);
+                return TRUE;
+        }
+
+        gdm_dbus_manager_complete_open_reauthentication_channel (GDM_DBUS_MANAGER (manager),
+                                                                 invocation,
+                                                                 address);
+        g_free (address);
+
+        return TRUE;
+}
+
 static void
 manager_interface_init (GdmDBusManagerIface *interface)
 {
         interface->handle_open_session = gdm_manager_handle_open_session;
+        interface->handle_open_reauthentication_channel = gdm_manager_handle_open_reauthentication_channel;
 }
 
 static void
diff --git a/daemon/gdm-manager.xml b/daemon/gdm-manager.xml
index 8c1a94e..db35b38 100644
--- a/daemon/gdm-manager.xml
+++ b/daemon/gdm-manager.xml
@@ -4,5 +4,9 @@
     <method name="OpenSession">
       <arg name="address" direction="out" type="s"/>
     </method>
+    <method name="OpenReauthenticationChannel">
+      <arg name="username" direction="in" type="s"/>
+      <arg name="address" direction="out" type="s"/>
+    </method>
   </interface>
 </node>
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 f751606..190ca4f 100644
--- a/daemon/gdm-session-worker.c
+++ b/daemon/gdm-session-worker.c
@@ -50,6 +50,7 @@
 #include "gdm-log.h"
 #include "gdm-session-worker.h"
 #include "gdm-session-glue.h"
+#include "gdm-session.h"
 
 #if defined (HAVE_ADT)
 #include "gdm-session-solaris-auditor.h"
@@ -97,6 +98,15 @@ enum {
         GDM_SESSION_WORKER_STATE_SESSION_STARTED
 };
 
+typedef struct
+{
+        GdmSessionWorker *worker;
+        GdmSession       *session;
+        GPid              pid_of_caller;
+        uid_t             uid_of_caller;
+
+} ReauthenticationRequest;
+
 struct GdmSessionWorkerPrivate
 {
         int               state;
@@ -133,6 +143,7 @@ struct GdmSessionWorkerPrivate
         guint32           cancelled : 1;
         guint32           timed_out : 1;
         guint32           is_program_session : 1;
+        guint32           is_reauth_session : 1;
         guint32           display_is_local : 1;
         guint             state_change_idle_id;
 
@@ -140,6 +151,8 @@ struct GdmSessionWorkerPrivate
         GDBusConnection      *connection;
         GdmDBusWorkerManager *manager;
 
+        GHashTable         *reauthentication_requests;
+
         GdmSessionAuditor  *auditor;
         GdmSessionSettings *user_settings;
 };
@@ -147,6 +160,7 @@ struct GdmSessionWorkerPrivate
 enum {
         PROP_0,
         PROP_SERVER_ADDRESS,
+        PROP_IS_REAUTH_SESSION,
 };
 
 static void     gdm_session_worker_class_init   (GdmSessionWorkerClass *klass);
@@ -1213,6 +1227,13 @@ gdm_session_worker_authorize_user (GdmSessionWorker *worker,
                 }
         }
 
+        /* If the user is reauthenticating, then authorization isn't required to
+         * proceed, the user is already logged in after all.
+         */
+        if (worker->priv->is_reauth_session) {
+                error_code = PAM_SUCCESS;
+        }
+
         if (error_code != PAM_SUCCESS) {
                 g_debug ("GdmSessionWorker: user is not authorized to log in: %s",
                          pam_strerror (worker->priv->pam_handle, error_code));
@@ -1474,6 +1495,15 @@ gdm_session_worker_accredit_user (GdmSessionWorker  *worker,
 
         error_code = pam_setcred (worker->priv->pam_handle, worker->priv->cred_flags);
 
+        /* If the user is reauthenticating and they've made it this far, then there
+         * is no reason we should lock them out of their session.  They've already
+         * proved they are they same person who logged in, and that's all we care
+         * about.
+         */
+        if (worker->priv->is_reauth_session) {
+                error_code = PAM_SUCCESS;
+        }
+
         if (error_code != PAM_SUCCESS) {
                 g_set_error (error,
                              GDM_SESSION_WORKER_ERROR,
@@ -1917,6 +1947,13 @@ gdm_session_worker_set_server_address (GdmSessionWorker *worker,
 }
 
 static void
+gdm_session_worker_set_is_reauth_session (GdmSessionWorker *worker,
+                                          gboolean          is_reauth_session)
+{
+        worker->priv->is_reauth_session = is_reauth_session;
+}
+
+static void
 gdm_session_worker_set_property (GObject      *object,
                                 guint         prop_id,
                                 const GValue *value,
@@ -1930,6 +1967,9 @@ gdm_session_worker_set_property (GObject      *object,
         case PROP_SERVER_ADDRESS:
                 gdm_session_worker_set_server_address (self, g_value_get_string (value));
                 break;
+        case PROP_IS_REAUTH_SESSION:
+                gdm_session_worker_set_is_reauth_session (self, g_value_get_boolean (value));
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
@@ -1950,6 +1990,9 @@ gdm_session_worker_get_property (GObject    *object,
         case PROP_SERVER_ADDRESS:
                 g_value_set_string (value, self->priv->server_address);
                 break;
+        case PROP_IS_REAUTH_SESSION:
+                g_value_set_boolean (value, self->priv->is_reauth_session);
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
@@ -2395,6 +2438,11 @@ on_start_program (GdmDBusWorkerManager *proxy,
                 return;
         }
 
+        if (worker->priv->is_reauth_session) {
+                g_debug ("GdmSessionWorker: ignoring start program request in reauthentication session");
+                return;
+        }
+
         g_debug ("GdmSessionWorker: start program: %s", text);
 
         g_clear_pointer (&worker->priv->arguments, (GDestroyNotify) g_strfreev);
@@ -2408,6 +2456,156 @@ 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_verification_complete (GdmSession              *session,
+                          const char              *service_name,
+                          ReauthenticationRequest *request)
+{
+        GdmSessionWorker *worker;
+
+        worker = request->worker;
+
+        g_debug ("GdmSessionWorker: pid %d reauthenticated user %d with service '%s'",
+                 (int) request->pid_of_caller,
+                 (int) request->uid_of_caller,
+                 service_name);
+        gdm_session_reset (session);
+
+        gdm_dbus_worker_manager_call_reauthenticated_sync (worker->priv->manager,
+                                                           service_name,
+                                                           NULL,
+                                                           NULL);
+}
+
+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,
+                          "verification-complete",
+                          G_CALLBACK (on_verification_complete),
+                          request);
+
+        address = gdm_session_get_server_address (request->session);
+        gdm_dbus_worker_manager_call_reauthentication_started_sync (worker->priv->manager,
+                                                                    pid_of_caller,
+                                                                    address,
+                                                                    NULL,
+                                                                    NULL);
+        g_free (address);
+
+        return request;
+}
+
+static void
+on_start_reauthentication (GdmDBusWorkerManager *proxy,
+                           int                   pid_of_caller,
+                           int                   uid_of_caller,
+                           GdmSessionWorker     *worker)
+{
+        ReauthenticationRequest *request;
+
+        if (worker->priv->state != GDM_SESSION_WORKER_STATE_SESSION_STARTED) {
+                g_debug ("GdmSessionWorker: ignoring spurious start reauthentication while in state %s", get_state_name (worker->priv->state));
+                return;
+        }
+
+        g_debug ("GdmSessionWorker: start reauthentication");
+
+        request = reauthentication_request_new (worker, pid_of_caller, uid_of_caller);
+        g_hash_table_replace (worker->priv->reauthentication_requests,
+                              GINT_TO_POINTER (pid_of_caller),
+                              request);
+}
+
+static void
 on_setup (GdmDBusWorkerManager *proxy,
           const char           *service,
           const char           *x11_display_name,
@@ -2552,7 +2750,11 @@ on_establish_credentials (GdmDBusWorkerManager *manager,
                 return;
         }
 
-        worker->priv->cred_flags = PAM_ESTABLISH_CRED;
+        if (!worker->priv->is_reauth_session) {
+                worker->priv->cred_flags = PAM_ESTABLISH_CRED;
+        } else {
+                worker->priv->cred_flags = PAM_REINITIALIZE_CRED;
+        }
 
         queue_state_change (worker);
 }
@@ -2567,6 +2769,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);
 }
 
@@ -2660,6 +2867,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.
@@ -2690,6 +2901,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
@@ -2699,6 +2925,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
@@ -2737,16 +2968,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 aab06b6..0e389fa 100644
--- a/daemon/gdm-session.c
+++ b/daemon/gdm-session.c
@@ -73,6 +73,7 @@ typedef struct
         char                  *service_name;
         GDBusConnection       *worker_connection;
         GDBusMethodInvocation *starting_invocation;
+        char                  *starting_username;
         GDBusMethodInvocation *pending_invocation;
         GdmDBusWorkerManager  *worker_manager_interface;
         char                  *session_id;
@@ -114,6 +115,8 @@ struct _GdmSessionPrivate
 
         GdmSessionVerificationMode verification_mode;
 
+        uid_t                allowed_user;
+
         char                *fallback_session_name;
 
         GDBusServer         *worker_server;
@@ -124,6 +127,7 @@ struct _GdmSessionPrivate
 enum {
         PROP_0,
         PROP_VERIFICATION_MODE,
+        PROP_ALLOWED_USER,
         PROP_DISPLAY_NAME,
         PROP_DISPLAY_HOSTNAME,
         PROP_DISPLAY_IS_LOCAL,
@@ -143,11 +147,14 @@ enum {
         CLIENT_DISCONNECTED,
         CLIENT_READY_FOR_SESSION_TO_START,
         DISCONNECTED,
+        VERIFICATION_COMPLETE,
         SESSION_OPENED,
         SESSION_STARTED,
         SESSION_START_FAILED,
         SESSION_EXITED,
         SESSION_DIED,
+        REAUTHENTICATION_STARTED,
+        REAUTHENTICATED,
         LAST_SIGNAL
 };
 
@@ -226,6 +233,8 @@ report_problem_and_stop_conversation (GdmSession *self,
                 gdm_dbus_user_verifier_emit_problem (self->priv->user_verifier_interface,
                                                      service_name,
                                                      message);
+                gdm_dbus_user_verifier_emit_verification_failed (self->priv->user_verifier_interface,
+                                                                 service_name);
         }
 
         gdm_session_stop_conversation (self, service_name);
@@ -245,6 +254,8 @@ gdm_session_handle_service_unavailable (GdmDBusWorkerManager  *worker_manager_in
                 gdm_dbus_user_verifier_emit_service_unavailable (self->priv->user_verifier_interface,
                                                                  service_name,
                                                                  message);
+                gdm_dbus_user_verifier_emit_verification_failed (self->priv->user_verifier_interface,
+                                                                 service_name);
         }
 
         gdm_session_stop_conversation (self, service_name);
@@ -258,6 +269,16 @@ gdm_session_handle_setup_complete (GdmDBusWorkerManager  *worker_manager_interfa
                                    const char            *service_name,
                                    GdmSession            *self)
 {
+        GdmSessionConversation *conversation;
+
+        conversation = find_conversation_by_name (self, service_name);
+        if (conversation != NULL) {
+                if (conversation->starting_invocation != NULL) {
+                        g_dbus_method_invocation_return_value (conversation->starting_invocation,
+                                                               NULL);
+                        g_clear_object (&conversation->starting_invocation);
+                }
+        }
         gdm_dbus_worker_manager_complete_setup_complete (worker_manager_interface,
                                                          invocation);
 
@@ -278,6 +299,19 @@ gdm_session_handle_setup_failed (GdmDBusWorkerManager  *worker_manager_interface
                                  const char            *message,
                                  GdmSession            *self)
 {
+        GdmSessionConversation *conversation;
+
+        conversation = find_conversation_by_name (self, service_name);
+        if (conversation != NULL) {
+                if (conversation->starting_invocation != NULL) {
+                        g_dbus_method_invocation_return_dbus_error (conversation->starting_invocation,
+                                                                    "org.gnome.DisplayManager.Session.Error.SetupFailed",
+                                                                    message);
+
+                        g_clear_object (&conversation->starting_invocation);
+                }
+        }
+
         gdm_dbus_worker_manager_complete_setup_failed (worker_manager_interface,
                                                        invocation);
 
@@ -361,7 +395,23 @@ 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) {
+                        g_signal_emit (self, signals[VERIFICATION_COMPLETE], 0, service_name);
+                        gdm_dbus_user_verifier_emit_verification_complete (self->priv->user_verifier_interface,
+                                                                           service_name);
+                }
+                break;
+
+            case GDM_SESSION_VERIFICATION_MODE_LOGIN:
+            case GDM_SESSION_VERIFICATION_MODE_CHOOSER:
+                gdm_session_open_session (self, service_name);
+                break;
+            default:
+                break;
+        }
 
         return TRUE;
 }
@@ -376,8 +426,6 @@ gdm_session_handle_accreditation_failed (GdmDBusWorkerManager  *worker_manager_i
         gdm_dbus_worker_manager_complete_accreditation_failed (worker_manager_interface,
                                                                invocation);
 
-#warning emit TRY_TO_MIGRATE or something like that.  The idea is, we don't care if accreditation fails if the user is already logged in.  though maybe we should rethink how user switching is done so that its done in the same way unlock will be done, then we won't even try to accredit in the first place.
-
         report_problem_and_stop_conversation (self, service_name, message);
         return TRUE;
 }
@@ -831,14 +879,15 @@ gdm_session_handle_opened (GdmDBusWorkerManager  *worker_manager_interface,
                                                       service_name);
         }
 
-        g_debug ("GdmSession: Emitting 'session-opened' signal");
-        g_signal_emit (self, signals[SESSION_OPENED], 0, service_name, session_id);
-
         if (self->priv->user_verifier_interface != NULL) {
+                g_signal_emit (self, signals[VERIFICATION_COMPLETE], 0, service_name);
                 gdm_dbus_user_verifier_emit_verification_complete (self->priv->user_verifier_interface,
                                                                    service_name);
         }
 
+        g_debug ("GdmSession: Emitting 'session-opened' signal");
+        g_signal_emit (self, signals[SESSION_OPENED], 0, service_name, session_id);
+
         return TRUE;
 }
 
@@ -923,6 +972,35 @@ 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_reauthenticated (GdmDBusWorkerManager  *worker_manager_interface,
+                                    GDBusMethodInvocation *invocation,
+                                    const char            *service_name,
+                                    GdmSession            *self)
+{
+        gdm_dbus_worker_manager_complete_reauthentication_started (worker_manager_interface,
+                                                                   invocation);
+
+        g_debug ("GdmSession: Emitting 'reauthenticated' signal ");
+        g_signal_emit (self, signals[REAUTHENTICATED], 0, service_name);
+        return TRUE;
+}
+
+static gboolean
 gdm_session_handle_saved_language_name_read (GdmDBusWorkerManager  *worker_manager_interface,
                                              GDBusMethodInvocation *invocation,
                                              const char            *language_name,
@@ -1000,9 +1078,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;
         }
 
@@ -1085,6 +1172,23 @@ register_worker (GdmDBusWorkerManager  *worker_manager_interface,
         g_debug ("GdmSession: Emitting conversation-started signal");
         g_signal_emit (self, signals[CONVERSATION_STARTED], 0, conversation->service_name);
 
+        if (self->priv->user_verifier_interface != NULL) {
+                gdm_dbus_user_verifier_emit_conversation_started (self->priv->user_verifier_interface,
+                                                                  conversation->service_name);
+        }
+
+        if (conversation->starting_invocation != NULL) {
+                if (conversation->starting_username != NULL) {
+                        gdm_session_setup_for_user (self, conversation->service_name, conversation->starting_username);
+
+                        g_clear_pointer (&conversation->starting_username,
+                                         (GDestroyNotify)
+                                         g_free);
+                } else {
+                        gdm_session_setup (self, conversation->service_name);
+                }
+        }
+
         g_debug ("GdmSession: Conversation started");
 
         return TRUE;
@@ -1189,6 +1293,14 @@ 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_signal_connect (worker_manager_interface,
+                          "handle-reauthenticated",
+                          G_CALLBACK (gdm_session_handle_reauthenticated),
+                          self);
 
         g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (worker_manager_interface),
                                           connection,
@@ -1249,8 +1361,7 @@ gdm_session_handle_client_begin_verification (GdmDBusUserVerifier    *user_verif
 
         if (conversation != NULL) {
                 conversation->starting_invocation = g_object_ref (invocation);
-
-                gdm_session_setup (self, service_name);
+                conversation->starting_username = NULL;
         } else {
                 g_dbus_method_invocation_return_error (conversation->starting_invocation,
                                                        G_DBUS_ERROR,
@@ -1276,8 +1387,7 @@ gdm_session_handle_client_begin_verification_for_user (GdmDBusUserVerifier    *u
 
         if (conversation != NULL) {
                 conversation->starting_invocation = g_object_ref (invocation);
-
-                gdm_session_setup_for_user (self, service_name, username);
+                conversation->starting_username = g_strdup (username);
         } else {
                 g_dbus_method_invocation_return_error (conversation->starting_invocation,
                                                        G_DBUS_ERROR,
@@ -1534,16 +1644,26 @@ on_outside_connection_closed (GDBusConnection *connection,
                               GError          *error,
                               GdmSession      *self)
 {
+        GCredentials *credentials;
+        GPid          pid_of_client;
+
         g_debug ("GdmSession: external connection closed");
 
         self->priv->outside_connections =
             g_list_remove (self->priv->outside_connections,
                             connection);
-        g_object_unref (connection);
+
+        credentials = g_dbus_connection_get_peer_credentials (connection);
+        pid_of_client = credentials_get_unix_pid (credentials);
 
         g_signal_emit (G_OBJECT (self),
                        signals [CLIENT_DISCONNECTED],
-                       0);
+                       0,
+                       credentials,
+                       (guint)
+                       pid_of_client);
+
+        g_object_unref (connection);
 }
 
 static gboolean
@@ -1551,6 +1671,9 @@ handle_connection_from_outside (GDBusServer      *server,
                                 GDBusConnection  *connection,
                                 GdmSession       *self)
 {
+        GCredentials *credentials;
+        GPid          pid_of_client;
+
         g_debug ("GdmSession: Handling new connection from outside");
 
         self->priv->outside_connections =
@@ -1573,15 +1696,24 @@ handle_connection_from_outside (GDBusServer      *server,
                 case GDM_SESSION_VERIFICATION_MODE_CHOOSER:
                         export_chooser_interface (self, connection);
                 break;
+
+                default:
+                break;
         }
 
         if (!self->priv->display_is_local) {
                 export_remote_greeter_interface (self, connection);
         }
 
+        credentials = g_dbus_connection_get_peer_credentials (connection);
+        pid_of_client = credentials_get_unix_pid (credentials);
+
         g_signal_emit (G_OBJECT (self),
                        signals [CLIENT_CONNECTED],
-                       0);
+                       0,
+                       credentials,
+                       (guint)
+                       pid_of_client);
 
         return TRUE;
 }
@@ -1599,7 +1731,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);
@@ -1623,48 +1755,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;
         }
 
@@ -1686,7 +1785,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);
@@ -1717,6 +1816,7 @@ free_conversation (GdmSessionConversation *conversation)
         }
 
         g_free (conversation->service_name);
+        g_clear_object (&conversation->session);
         g_free (conversation);
 }
 
@@ -1760,11 +1860,6 @@ worker_started (GdmSessionWorkerJob    *job,
 {
         g_debug ("GdmSession: Worker job started");
 
-        if (conversation->starting_invocation != NULL) {
-                g_dbus_method_invocation_return_value (conversation->starting_invocation,
-                                                       NULL);
-                g_clear_object (&conversation->starting_invocation);
-        }
 }
 
 static void
@@ -1785,6 +1880,10 @@ worker_exited (GdmSessionWorkerJob    *job,
 
         g_debug ("GdmSession: Emitting conversation-stopped signal");
         g_signal_emit (self, signals[CONVERSATION_STOPPED], 0, conversation->service_name);
+        if (self->priv->user_verifier_interface != NULL) {
+                gdm_dbus_user_verifier_emit_conversation_stopped (self->priv->user_verifier_interface,
+                                                                  conversation->service_name);
+        }
         g_object_unref (conversation->job);
 
         if (conversation->is_stopping) {
@@ -1813,6 +1912,10 @@ worker_died (GdmSessionWorkerJob    *job,
 
         g_debug ("GdmSession: Emitting conversation-stopped signal");
         g_signal_emit (self, signals[CONVERSATION_STOPPED], 0, conversation->service_name);
+        if (self->priv->user_verifier_interface != NULL) {
+                gdm_dbus_user_verifier_emit_conversation_stopped (self->priv->user_verifier_interface,
+                                                                  conversation->service_name);
+        }
         g_object_unref (conversation->job);
 
         if (conversation->is_stopping) {
@@ -1831,7 +1934,7 @@ start_conversation (GdmSession *self,
         char                   *job_name;
 
         conversation = g_new0 (GdmSessionConversation, 1);
-        conversation->session = self;
+        conversation->session = g_object_ref (self);
         conversation->service_name = g_strdup (service_name);
         conversation->worker_pid = -1;
         conversation->job = gdm_session_worker_job_new ();
@@ -2463,10 +2566,6 @@ gdm_session_close (GdmSession *self)
         g_list_free_full (self->priv->pending_worker_connections, g_object_unref);
         self->priv->pending_worker_connections = NULL;
 
-        g_clear_object (&self->priv->user_verifier_interface);
-        g_clear_object (&self->priv->greeter_interface);
-        g_clear_object (&self->priv->chooser_interface);
-
         g_free (self->priv->selected_user);
         self->priv->selected_user = NULL;
 
@@ -2543,6 +2642,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)
 {
@@ -2777,7 +2894,7 @@ set_display_is_local (GdmSession *self,
         self->priv->display_is_local = is_local;
 }
 
- static void
+static void
 set_verification_mode (GdmSession                 *self,
                        GdmSessionVerificationMode  verification_mode)
 {
@@ -2785,6 +2902,13 @@ set_verification_mode (GdmSession                 *self,
 }
 
 static void
+set_allowed_user (GdmSession *self,
+                  uid_t       allowed_user)
+{
+        self->priv->allowed_user = allowed_user;
+}
+
+static void
 gdm_session_set_property (GObject      *object,
                           guint         prop_id,
                           const GValue *value,
@@ -2819,6 +2943,9 @@ gdm_session_set_property (GObject      *object,
         case PROP_VERIFICATION_MODE:
                 set_verification_mode (self, g_value_get_enum (value));
                 break;
+        case PROP_ALLOWED_USER:
+                set_allowed_user (self, g_value_get_uint (value));
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
@@ -2860,6 +2987,9 @@ gdm_session_get_property (GObject    *object,
         case PROP_VERIFICATION_MODE:
                 g_value_set_enum (value, self->priv->verification_mode);
                 break;
+        case PROP_ALLOWED_USER:
+                g_value_set_uint (value, self->priv->allowed_user);
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
@@ -2877,6 +3007,10 @@ gdm_session_dispose (GObject *object)
 
         gdm_session_close (self);
 
+        g_clear_object (&self->priv->user_verifier_interface);
+        g_clear_object (&self->priv->greeter_interface);
+        g_clear_object (&self->priv->chooser_interface);
+
         g_free (self->priv->display_name);
         self->priv->display_name = NULL;
 
@@ -2986,6 +3120,17 @@ gdm_session_class_init (GdmSessionClass *session_class)
                               G_TYPE_NONE,
                               1,
                               G_TYPE_STRING);
+        signals [VERIFICATION_COMPLETE] =
+                g_signal_new ("verification-complete",
+                              GDM_TYPE_SESSION,
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmSessionClass, verification_complete),
+                              NULL,
+                              NULL,
+                              NULL,
+                              G_TYPE_NONE,
+                              1,
+                              G_TYPE_STRING);
         signals [SESSION_OPENED] =
                 g_signal_new ("session-opened",
                               GDM_TYPE_SESSION,
@@ -3043,6 +3188,30 @@ 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 [REAUTHENTICATED] =
+                g_signal_new ("reauthenticated",
+                              GDM_TYPE_SESSION,
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmSessionClass, reauthenticated),
+                              NULL,
+                              NULL,
+                              NULL,
+                              G_TYPE_NONE,
+                              1,
+                              G_TYPE_STRING);
         signals [CANCELLED] =
                 g_signal_new ("cancelled",
                               GDM_TYPE_SESSION,
@@ -3061,9 +3230,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",
@@ -3072,9 +3243,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,
@@ -3119,6 +3292,16 @@ gdm_session_class_init (GdmSessionClass *session_class)
                                                             GDM_SESSION_VERIFICATION_MODE_LOGIN,
                                                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
         g_object_class_install_property (object_class,
+                                         PROP_ALLOWED_USER,
+                                         g_param_spec_uint ("allowed-user",
+                                                            "allowed user",
+                                                            "allowed user ",
+                                                            0,
+                                                            G_MAXUINT,
+                                                            0,
+                                                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+        g_object_class_install_property (object_class,
                                          PROP_DISPLAY_NAME,
                                          g_param_spec_string ("display-name",
                                                               "display name",
@@ -3173,6 +3356,7 @@ gdm_session_class_init (GdmSessionClass *session_class)
 
 GdmSession *
 gdm_session_new (GdmSessionVerificationMode  verification_mode,
+                 uid_t                       allowed_user,
                  const char                 *display_name,
                  const char                 *display_hostname,
                  const char                 *display_device,
@@ -3184,6 +3368,7 @@ gdm_session_new (GdmSessionVerificationMode  verification_mode,
 
         self = g_object_new (GDM_TYPE_SESSION,
                              "verification-mode", verification_mode,
+                             "allowed-user", (guint) allowed_user,
                              "display-name", display_name,
                              "display-hostname", display_hostname,
                              "display-device", display_device,
diff --git a/daemon/gdm-session.h b/daemon/gdm-session.h
index ad5f885..3a1d0fd 100644
--- a/daemon/gdm-session.h
+++ b/daemon/gdm-session.h
@@ -37,7 +37,8 @@ typedef struct _GdmSessionPrivate GdmSessionPrivate;
 typedef enum
 {
         GDM_SESSION_VERIFICATION_MODE_LOGIN,
-        GDM_SESSION_VERIFICATION_MODE_CHOOSER
+        GDM_SESSION_VERIFICATION_MODE_CHOOSER,
+        GDM_SESSION_VERIFICATION_MODE_REAUTHENTICATE
 } GdmSessionVerificationMode;
 
 typedef struct
@@ -59,6 +60,8 @@ typedef struct
         void (* client_connected)            (GdmSession   *session);
         void (* client_disconnected)         (GdmSession   *session);
         void (* disconnected)                (GdmSession   *session);
+        void (* verification_complete)       (GdmSession   *session,
+                                              const char   *service_name);
         void (* session_opened)              (GdmSession   *session,
                                               const char   *service_name,
                                               const char   *session_id);
@@ -73,6 +76,10 @@ typedef struct
                                               int           exit_code);
         void (* session_died)                (GdmSession   *session,
                                               int           signal_number);
+        void (* reauthentication_started)    (GdmSession   *session,
+                                              GPid          pid_of_caller);
+        void (* reauthenticated)             (GdmSession   *session,
+                                              const char   *service_name);
         void (* conversation_started)        (GdmSession   *session,
                                               const char   *service_name);
         void (* conversation_stopped)        (GdmSession   *session,
@@ -84,12 +91,17 @@ typedef struct
 GType            gdm_session_get_type                 (void);
 
 GdmSession      *gdm_session_new                      (GdmSessionVerificationMode verification_mode,
+                                                       uid_t       allowed_user,
                                                        const char *display_name,
                                                        const char *display_hostname,
                                                        const char *display_device,
                                                        const char *display_seat_id,
                                                        const char *display_x11_authority_file,
                                                        gboolean    display_is_local);
+uid_t             gdm_session_get_allowed_user       (GdmSession     *session);
+void              gdm_session_start_reauthentication (GdmSession *session,
+                                                      GPid        pid_of_caller,
+                                                      uid_t       uid_of_caller);
 
 char             *gdm_session_get_server_address          (GdmSession     *session);
 char             *gdm_session_get_username                (GdmSession     *session);
diff --git a/daemon/gdm-session.xml b/daemon/gdm-session.xml
index ad20adf..15f83f9 100644
--- a/daemon/gdm-session.xml
+++ b/daemon/gdm-session.xml
@@ -67,6 +67,13 @@
       <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="Reauthenticated">
+      <arg name="service_name" 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 +150,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" />
@@ -171,6 +182,15 @@
     </method>
     <method name="Cancel">
     </method>
+    <signal name="ConversationStarted">
+      <arg name="service_name" type="s"/>
+    </signal>
+    <signal name="ConversationStopped">
+      <arg name="service_name" type="s"/>
+    </signal>
+    <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"/>
@@ -193,6 +213,9 @@
       <arg name="service_name" type="s"/>
       <arg name="message" type="s"/>
     </signal>
+    <signal name="VerificationFailed">
+      <arg name="service_name" type="s"/>
+    </signal>
     <signal name="VerificationComplete">
       <arg name="service_name" type="s"/>
     </signal>
@@ -230,6 +253,9 @@
     <signal name="SessionOpened">
       <arg name="service_name" type="s"/>
     </signal>
+    <signal name="Reauthenticated">
+      <arg name="service_name" type="s"/>
+    </signal>
   </interface>
   <interface name="org.gnome.DisplayManager.RemoteGreeter">
     <method name="Disconnect" />
diff --git a/daemon/gdm-simple-slave.c b/daemon/gdm-simple-slave.c
index be6aa27..4dc5009 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_reauthentication_requests;
+
         guint              start_session_when_ready : 1;
         guint              waiting_to_start_session : 1;
         guint              session_is_running : 1;
@@ -411,6 +413,14 @@ queue_start_session (GdmSimpleSlave *slave,
 }
 
 static void
+on_session_reauthenticated (GdmSession       *session,
+                            const char       *service_name,
+                            GdmSimpleSlave   *slave)
+{
+        try_migrate_session (slave);
+}
+
+static void
 on_session_opened (GdmSession       *session,
                    const char       *service_name,
                    const char       *session_id,
@@ -484,12 +494,36 @@ start_autologin_conversation_if_necessary (GdmSimpleSlave  *slave)
 }
 
 static void
+on_session_reauthentication_started (GdmSession      *session,
+                                     int              pid_of_caller,
+                                     const char      *address,
+                                     GdmSimpleSlave  *slave)
+{
+        GSimpleAsyncResult *result;
+        gpointer            source_tag;
+
+        g_debug ("GdmSimpleSlave: reauthentication started");
+
+        source_tag = GINT_TO_POINTER (pid_of_caller);
+
+        result = g_hash_table_lookup (slave->priv->open_reauthentication_requests,
+                                      source_tag);
+
+        if (result != NULL) {
+                g_simple_async_result_set_op_res_gpointer (result,
+                                                           g_strdup (address),
+                                                           (GDestroyNotify)
+                                                           g_free);
+                g_simple_async_result_complete_in_idle (result);
+        }
+}
+
+static void
 on_session_client_ready_for_session_to_start (GdmSession      *session,
                                               const char      *service_name,
                                               gboolean         client_is_ready,
                                               GdmSimpleSlave  *slave)
 {
-
         if (client_is_ready) {
                 g_debug ("GdmSimpleSlave: Will start session when ready");
         } else {
@@ -508,6 +542,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;
@@ -526,6 +562,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;
@@ -559,9 +597,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,
@@ -577,6 +620,7 @@ create_new_session (GdmSimpleSlave  *slave)
         }
 
         slave->priv->session = gdm_session_new (GDM_SESSION_VERIFICATION_MODE_LOGIN,
+                                                greeter_uid,
                                                 display_name,
                                                 display_hostname,
                                                 display_device,
@@ -590,6 +634,14 @@ 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,
+                          "reauthenticated",
+                          G_CALLBACK (on_session_reauthenticated),
+                          slave);
+        g_signal_connect (slave->priv->session,
                           "client-ready-for-session-to-start",
                           G_CALLBACK (on_session_client_ready_for_session_to_start),
                           slave);
@@ -1040,36 +1092,98 @@ gdm_simple_slave_run (GdmSimpleSlave *slave)
 }
 
 static gboolean
-gdm_simple_slave_open_session (GdmSlave   *slave,
-                               char      **address,
-                               GError    **error)
+gdm_simple_slave_open_session (GdmSlave  *slave,
+                               GPid       pid_of_caller,
+                               uid_t      uid_of_caller,
+                               char     **address,
+                               GError   **error)
 {
-        GdmSimpleSlave  *self = GDM_SIMPLE_SLAVE (slave);
-        GdmSession      *session;
+        GdmSimpleSlave     *self = GDM_SIMPLE_SLAVE (slave);
+        uid_t               allowed_user;
 
         if (self->priv->session_is_running) {
                 g_set_error (error,
                              G_DBUS_ERROR,
                              G_DBUS_ERROR_ACCESS_DENIED,
-                             _("Greeter access only currently supported"));
+                             _("Can only be called before user is logged in"));
                 return FALSE;
         }
 
-        session = self->priv->session;
+        allowed_user = gdm_session_get_allowed_user (self->priv->session);
 
-        if (gdm_session_client_is_connected (session)) {
+        if (uid_of_caller != allowed_user) {
                 g_set_error (error,
                              G_DBUS_ERROR,
                              G_DBUS_ERROR_ACCESS_DENIED,
-                             _("Currently, only one client can be connected at once"));
+                             _("Caller not GDM"));
                 return FALSE;
         }
 
-        *address = gdm_session_get_server_address (session);
+        *address = gdm_session_get_server_address (self->priv->session);
 
         return TRUE;
 }
 
+static char *
+gdm_simple_slave_open_reauthentication_channel_finish (GdmSlave      *slave,
+                                                       GAsyncResult  *result,
+                                                       GError       **error)
+{
+        GdmSimpleSlave  *self = GDM_SIMPLE_SLAVE (slave);
+        const char      *address;
+        GPid             pid_of_caller;
+
+        pid_of_caller = GPOINTER_TO_INT (g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (result)));
+
+        g_hash_table_remove (self->priv->open_reauthentication_requests,
+                             GINT_TO_POINTER (pid_of_caller));
+
+        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_simple_slave_open_reauthentication_channel (GdmSlave             *slave,
+                                                const char           *username,
+                                                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_reauthentication_requests,
+                             GINT_TO_POINTER (pid_of_caller),
+                             g_object_ref (result));
+
+        if (!self->priv->session_is_running) {
+                g_simple_async_result_set_error (result,
+                                                 G_DBUS_ERROR,
+                                                 G_DBUS_ERROR_ACCESS_DENIED,
+                                                 _("User not logged in"));
+                g_simple_async_result_complete_in_idle (result);
+
+        } else {
+                gdm_session_start_reauthentication (self->priv->session,
+                                                    pid_of_caller,
+                                                    uid_of_caller);
+        }
+}
+
 static gboolean
 gdm_simple_slave_start (GdmSlave *slave)
 {
@@ -1175,6 +1289,8 @@ 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_reauthentication_channel = gdm_simple_slave_open_reauthentication_channel;
+        slave_class->open_reauthentication_channel_finish = gdm_simple_slave_open_reauthentication_channel_finish;
 
         g_type_class_add_private (klass, sizeof (GdmSimpleSlavePrivate));
 }
@@ -1186,6 +1302,13 @@ gdm_simple_slave_init (GdmSimpleSlave *slave)
 #ifdef  HAVE_LOGINDEVPERM
         slave->priv->use_logindevperm = FALSE;
 #endif
+
+        slave->priv->open_reauthentication_requests = g_hash_table_new_full (NULL,
+                                                                             NULL,
+                                                                             (GDestroyNotify)
+                                                                             NULL,
+                                                                             (GDestroyNotify)
+                                                                             g_object_unref);
 }
 
 static void
@@ -1202,6 +1325,8 @@ gdm_simple_slave_finalize (GObject *object)
 
         gdm_simple_slave_stop (GDM_SLAVE (slave));
 
+        g_hash_table_unref (slave->priv->open_reauthentication_requests);
+
         if (slave->priv->greeter_reset_id > 0) {
                 g_source_remove (slave->priv->greeter_reset_id);
                 slave->priv->greeter_reset_id = 0;
diff --git a/daemon/gdm-slave.c b/daemon/gdm-slave.c
index fbee296..0869097 100644
--- a/daemon/gdm-slave.c
+++ b/daemon/gdm-slave.c
@@ -1870,6 +1870,8 @@ gdm_slave_get_property (GObject    *object,
 static gboolean
 handle_open_session (GdmDBusSlave          *skeleton,
                      GDBusMethodInvocation *invocation,
+                     int                    pid_of_caller,
+                     int                    uid_of_caller,
                      GdmSlave              *slave)
 {
         GError        *error;
@@ -1887,6 +1889,8 @@ handle_open_session (GdmDBusSlave          *skeleton,
         error = NULL;
         address = NULL;
         if (!slave_class->open_session (slave,
+                                        (GPid) pid_of_caller,
+                                        (uid_t) uid_of_caller,
                                         &address,
                                         &error)) {
                 g_dbus_method_invocation_return_gerror (invocation, error);
@@ -1897,6 +1901,62 @@ handle_open_session (GdmDBusSlave          *skeleton,
         gdm_dbus_slave_complete_open_session (skeleton, invocation, address);
 
         g_free (address);
+        return TRUE;
+}
+
+static void
+on_reauthentication_channel_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_reauthentication_channel_finish != NULL);
+
+        error = NULL;
+        address = slave_class->open_reauthentication_channel_finish (slave, result, &error);
+
+        if (address == NULL) {
+                g_dbus_method_invocation_return_gerror (invocation, error);
+        } else {
+                gdm_dbus_slave_complete_open_reauthentication_channel (slave->priv->skeleton,
+                                                                       invocation,
+                                                                       address);
+        }
+
+        g_object_unref (invocation);
+}
+
+static gboolean
+handle_open_reauthentication_channel (GdmDBusSlave          *skeleton,
+                                      GDBusMethodInvocation *invocation,
+                                      const char            *username,
+                                      GPid                   pid_of_caller,
+                                      uid_t                  uid_of_caller,
+                                      GdmSlave              *slave)
+{
+        GdmSlaveClass *slave_class;
+
+        slave_class = GDM_SLAVE_GET_CLASS (slave);
+        if (slave_class->open_reauthentication_channel == NULL) {
+                g_dbus_method_invocation_return_dbus_error (invocation,
+                                                            "org.gnome.DisplayManager.Slave.Unsupported",
+                                                            "Connections to the slave are not supported by this slave");
+                return TRUE;
+        }
+
+        slave_class->open_reauthentication_channel (slave,
+                                                    username,
+                                                    pid_of_caller,
+                                                    uid_of_caller,
+                                                    (GAsyncReadyCallback)
+                                                    on_reauthentication_channel_opened,
+                                                    g_object_ref (invocation),
+                                                    NULL);
 
         return TRUE;
 }
@@ -1922,6 +1982,10 @@ register_slave (GdmSlave *slave)
                           "handle-open-session",
                           G_CALLBACK (handle_open_session),
                           slave);
+        g_signal_connect (slave->priv->skeleton,
+                          "handle-open-reauthentication-channel",
+                          G_CALLBACK (handle_open_reauthentication_channel),
+                          slave);
 
         g_object_bind_property (G_OBJECT (slave),
                                 "session-id",
diff --git a/daemon/gdm-slave.h b/daemon/gdm-slave.h
index ee3dcbc..dc04677 100644
--- a/daemon/gdm-slave.h
+++ b/daemon/gdm-slave.h
@@ -50,9 +50,22 @@ typedef struct
         gboolean (*start) (GdmSlave *slave);
         gboolean (*stop)  (GdmSlave *slave);
 
-        gboolean (*open_session) (GdmSlave    *slave,
-                                  char       **address,
-                                  GError     **error);
+        gboolean     (*open_session)       (GdmSlave   *slave,
+                                            GPid        pid_of_caller,
+                                            uid_t       uid_of_caller,
+                                            char      **address,
+                                            GError    **error);
+
+        void     (*open_reauthentication_channel) (GdmSlave            *slave,
+                                                   const char          *username,
+                                                   GPid                 pid_of_caller,
+                                                   uid_t                uid_of_caller,
+                                                   GAsyncReadyCallback  callback,
+                                                   gpointer             user_data,
+                                                   GCancellable        *cancellable);
+        char *   (*open_reauthentication_channel_finish) (GdmSlave      *slave,
+                                                          GAsyncResult  *result,
+                                                          GError       **error);
 
         /* signals */
         void (*stopped) (GdmSlave *slave);
diff --git a/daemon/gdm-slave.xml b/daemon/gdm-slave.xml
index b6e57cf..547c44e 100644
--- a/daemon/gdm-slave.xml
+++ b/daemon/gdm-slave.xml
@@ -2,6 +2,15 @@
 <node>
   <interface name="org.gnome.DisplayManager.Slave">
     <method name="OpenSession">
+      <arg name="pid_of_caller" type="i" direction="in" />
+      <arg name="uid_of_caller" type="i" direction="in" />
+      <arg name="address" type="s" direction="out" />
+    </method>
+
+    <method name="OpenReauthenticationChannel">
+      <arg name="username" type="s" direction="in" />
+      <arg name="pid_of_caller" type="i" direction="in" />
+      <arg name="uid_of_caller" type="i" direction="in" />
       <arg name="address" type="s" direction="out" />
     </method>
 
diff --git a/daemon/gdm-welcome-session.c b/daemon/gdm-welcome-session.c
index e8947b5..3f3cd66 100644
--- a/daemon/gdm-welcome-session.c
+++ b/daemon/gdm-welcome-session.c
@@ -785,6 +785,8 @@ gboolean
 gdm_welcome_session_start (GdmWelcomeSession *welcome_session)
 {
         gboolean          res;
+        struct passwd *passwd_entry;
+        uid_t uid;
 
         g_debug ("GdmWelcomeSession: Starting welcome...");
         res = start_dbus_daemon (welcome_session);
@@ -793,7 +795,16 @@ gdm_welcome_session_start (GdmWelcomeSession *welcome_session)
                 return FALSE;
         }
 
+        res = gdm_get_pwent_for_name (welcome_session->priv->user_name,
+                                      &passwd_entry);
+
+        if (!res) {
+                return FALSE;
+        }
+
+        uid = passwd_entry->pw_uid;
         welcome_session->priv->session = gdm_session_new (welcome_session->priv->verification_mode,
+                                                          uid,
                                                           welcome_session->priv->x11_display_name,
                                                           welcome_session->priv->x11_display_hostname,
                                                           welcome_session->priv->x11_display_device,
diff --git a/daemon/gdm-xdmcp-chooser-slave.c b/daemon/gdm-xdmcp-chooser-slave.c
index c8bc586..17ce057 100644
--- a/daemon/gdm-xdmcp-chooser-slave.c
+++ b/daemon/gdm-xdmcp-chooser-slave.c
@@ -155,6 +155,8 @@ on_chooser_disconnected (GdmSession           *session,
 
 static void
 on_chooser_connected (GdmSession           *session,
+                      GCredentials         *credentials,
+                      GPid                  pid_of_client,
                       GdmXdmcpChooserSlave *slave)
 {
         g_debug ("GdmXdmcpChooserSlave: Chooser connected");
@@ -332,12 +334,14 @@ gdm_xdmcp_chooser_slave_stop (GdmSlave *slave)
 }
 
 static gboolean
-gdm_xdmcp_chooser_slave_open_session (GdmSlave   *slave,
-                                      char      **address,
-                                      GError    **error)
+gdm_xdmcp_chooser_slave_open_session (GdmSlave  *slave,
+                                      GPid       pid_of_caller,
+                                      uid_t      uid_of_caller,
+                                      char     **address,
+                                      GError   **error)
 {
         GdmXdmcpChooserSlave *self = GDM_XDMCP_CHOOSER_SLAVE (slave);
-        GdmSession      *session;
+        GdmSession           *session;
 
         session = gdm_welcome_session_get_session (GDM_WELCOME_SESSION (self->priv->chooser));
 
@@ -346,11 +350,11 @@ gdm_xdmcp_chooser_slave_open_session (GdmSlave   *slave,
                              G_DBUS_ERROR,
                              G_DBUS_ERROR_ACCESS_DENIED,
                              _("Currently, only one client can be connected at once"));
+
                 return FALSE;
         }
 
         *address = gdm_session_get_server_address (session);
-
         return TRUE;
 }
 
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]