]> rtime.felk.cvut.cz Git - sojka/lightdm.git/commitdiff
Restructure session code so the PAM authentication is run in its own process.
authorRobert Ancell <robert.ancell@canonical.com>
Thu, 1 Mar 2012 03:29:29 +0000 (14:29 +1100)
committerRobert Ancell <robert.ancell@canonical.com>
Thu, 1 Mar 2012 03:29:29 +0000 (14:29 +1100)
20 files changed:
NEWS
src/Makefile.am
src/display.c
src/greeter.c
src/greeter.h
src/lightdm.c
src/pam-session.c [deleted file]
src/pam-session.h [deleted file]
src/seat-xdmcp-session.c
src/seat-xlocal.c
src/seat-xremote.c
src/seat-xvnc.c
src/seat.c
src/seat.h
src/session-child.c [new file with mode: 0644]
src/session-child.h [new file with mode: 0644]
src/session.c
src/session.h
src/xsession.c
tests/Makefile.am

diff --git a/NEWS b/NEWS
index db86688b8329f33982a78f219c0ae25c2d70347a..69c27c6c66c4e43824d7a40081b53afbf894a2cf 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,8 @@ Overview of changes in lightdm 1.1.4
 
     * Change session directory once user permissions are set so it works
       on NFS filesystems that don't allow root to access files.
+    * Restructure session code so the PAM authentication is run in it's
+      own process.
 
 Overview of changes in lightdm 1.1.3
 
index b66f197ecbdfefb49df08c981a3a9ff7799a2dbb..4cf2863ab88b94c86d9e3e6ad64576cd81e17496 100644 (file)
@@ -28,8 +28,6 @@ lightdm_SOURCES = \
        lightdm.c \
        ldm-marshal.c \
        ldm-marshal.h \
-       pam-session.c \
-       pam-session.h \
        plymouth.c \
        plymouth.h \
        privileges.c \
@@ -48,6 +46,8 @@ lightdm_SOURCES = \
        seat-xvnc.h \
        session.c \
        session.h \
+       session-child.c \
+       session-child.h \
        vnc-server.c \
        vnc-server.h \
        vt.c \
index f572f3ba612314e952d80e8b33855bc5fc850d46..da6949ddb59c9d53952ff3b15e3b57b965d8082c 100644 (file)
@@ -18,7 +18,6 @@
 #include "display.h"
 #include "configuration.h"
 #include "ldm-marshal.h"
-#include "pam-session.h"
 #include "greeter.h"
 
 enum
@@ -71,9 +70,6 @@ struct DisplayPrivate
     /* PAM service to authenticate against for automatic logins */
     gchar *pam_autologin_service;
   
-    /* TRUE if a session should be started on greeter quit */
-    gboolean start_session_on_greeter_quit;
-
     /* TRUE if in a user session */
     gboolean in_user_session;
 
@@ -110,8 +106,8 @@ struct DisplayPrivate
 
 G_DEFINE_TYPE (Display, display, G_TYPE_OBJECT);
 
-static gboolean start_greeter_session (Display *display);
-static gboolean start_user_session (Display *display, PAMSession *authentication);
+static void greeter_session_stopped_cb (Session *session, Display *display);
+static void user_session_stopped_cb (Session *session, Display *display);
 
 Display *
 display_new (DisplayServer *display_server)
@@ -138,7 +134,7 @@ display_get_username (Display *display)
     if (!display->priv->session || !display->priv->in_user_session)
         return NULL;
 
-    return user_get_name (session_get_user (display->priv->session));
+    return session_get_username (display->priv->session);
 }
 
 Session *
@@ -212,6 +208,16 @@ display_set_user_session (Display *display, const gchar *session_name)
     display->priv->user_session = g_strdup (session_name);
 }
 
+static void
+display_set_is_ready (Display *display)
+{
+    if (display->priv->is_ready)
+        return;
+
+    display->priv->is_ready = TRUE;
+    g_signal_emit (display, signals[READY], 0);
+}
+
 static gboolean
 switch_to_user (Display *display, User *user)
 {
@@ -236,261 +242,14 @@ get_guest_username (Display *display)
     return username;
 }
 
-static void
-autologin_authentication_result_cb (PAMSession *authentication, int result, Display *display)
-{
-    g_signal_handlers_disconnect_matched (authentication, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
-    if (display->priv->stopping)
-        return;
-
-    gboolean started_session = FALSE;
-
-    if (result == PAM_SUCCESS)
-    {
-        User *user;
-        const gchar *session_name;
-
-        user = pam_session_get_user (authentication);
-
-        if (user)
-        {
-            g_debug ("User %s authorized", pam_session_get_username (authentication));
-
-            session_name = user_get_xsession (user);
-            if (session_name)
-            {
-                g_debug ("Using session %s", session_name);
-                display_set_user_session (display, session_name);
-            }
-
-            started_session = start_user_session (display, authentication);
-            if (!started_session)
-                g_debug ("Failed to start autologin session");
-        }
-        else
-            g_debug ("User %s was authorized, but no account of that name exists", pam_session_get_username (authentication));
-    }
-    else
-        g_debug ("Autologin failed authentication");
-
-    if (!started_session && display->priv->start_greeter_if_fail)
-    {
-        display_set_autologin_user (display, NULL, FALSE, 0);
-        if (display->priv->autologin_user)
-            display_set_select_user_hint (display, display->priv->autologin_user, FALSE);
-        started_session = start_greeter_session (display);
-    }
-
-    if (!started_session)
-        display_stop (display);
-}
-
-static gboolean
-autologin (Display *display, const gchar *username, const gchar *service, gboolean start_greeter_if_fail)
-{
-    gboolean result;
-    PAMSession *authentication;
-    GError *error = NULL;
-
-    display->priv->start_greeter_if_fail = start_greeter_if_fail;
-
-    display->priv->in_user_session = TRUE;
-    authentication = pam_session_new (service, username);
-    pam_session_set_interactive (authentication, FALSE);
-    g_signal_connect (authentication, "authentication-result", G_CALLBACK (autologin_authentication_result_cb), display);
-
-    result = pam_session_authenticate (authentication, &error);
-    if (error)
-        g_debug ("Failed to start autologin session for %s: %s", username, error->message);
-    if (!result)
-    {
-        g_signal_handlers_disconnect_matched (authentication, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
-        g_object_unref (authentication);
-    }
-    g_clear_error (&error);
-
-    return result;
-}
-
-static gboolean
-autologin_guest (Display *display, const gchar *service, gboolean start_greeter_if_fail)
-{
-    gchar *username;
-    gboolean result;
-
-    username = get_guest_username (display);
-    if (!username)
-    {
-        g_debug ("Can't autologin guest, no guest account");
-        return FALSE;
-    }
-
-    result = autologin (display, username, service, start_greeter_if_fail);
-    g_free (username);
-
-    return result;
-}
-
-static gboolean
-cleanup_after_session (Display *display)
-{
-    g_signal_handlers_disconnect_matched (display->priv->session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
-    g_object_unref (display->priv->session);
-    display->priv->session = NULL;
-
-    if (display->priv->stopping)
-    {
-        display_stop (display);
-        return TRUE;
-    }
-
-    return FALSE;
-}
-
-static void
-greeter_session_stopped_cb (Session *session, Display *display)
-{
-    gboolean started_session = FALSE;
-
-    g_debug ("Greeter quit");
-
-    if (cleanup_after_session (display))
-        return;
-
-    if (!display->priv->display_server)
-        return;
-
-    /* Start the session for the authenticated user */
-    if (display->priv->start_session_on_greeter_quit)
-    {
-        if (greeter_get_guest_authenticated (display->priv->greeter))
-        {
-            started_session = autologin_guest (display, display->priv->pam_autologin_service, FALSE);
-            if (!started_session)
-                g_debug ("Failed to start guest session");
-        }
-        else
-        {
-            display->priv->in_user_session = TRUE;
-            started_session = start_user_session (display, greeter_get_authentication (display->priv->greeter));
-            if (!started_session)
-                g_debug ("Failed to start user session");
-        }
-    }
-
-    g_signal_handlers_disconnect_matched (display->priv->greeter, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
-    g_object_unref (display->priv->greeter);
-    display->priv->greeter = NULL;
-
-    if (!started_session)
-        display_stop (display);
-}
-
-static void
-user_session_stopped_cb (Session *session, Display *display)
-{
-    g_debug ("User session quit");
-
-    if (cleanup_after_session (display))
-        return;
-
-    /* This display has ended */
-    display_stop (display);
-}
-
 static Session *
-create_session (Display *display, PAMSession *authentication, const gchar *session_name, gboolean is_greeter)
+create_session (Display *display)
 {
-    gchar *sessions_dir, *filename, *path, *command = NULL;
-    GKeyFile *session_desktop_file;
-    gint argc;
-    gchar **argv;
     Session *session;
-    gboolean result;
-    GError *error = NULL;
-
-    g_debug ("Starting session %s as user %s", session_name, pam_session_get_username (authentication));
-
-    // FIXME: This is X specific, move into xsession.c
-    if (is_greeter)
-        sessions_dir = config_get_string (config_get_instance (), "LightDM", "xgreeters-directory");
-    else
-        sessions_dir = config_get_string (config_get_instance (), "LightDM", "xsessions-directory");
-    filename = g_strdup_printf ("%s.desktop", session_name);
-    path = g_build_filename (sessions_dir, filename, NULL);
-    g_free (sessions_dir);
-    g_free (filename);
-
-    session_desktop_file = g_key_file_new ();
-    result = g_key_file_load_from_file (session_desktop_file, path, G_KEY_FILE_NONE, &error);
-    if (error)
-        g_debug ("Failed to load session file %s: %s:", path, error->message);
-    g_clear_error (&error);
-    if (result)
-    {
-        command = g_key_file_get_string (session_desktop_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
-        if (!command)
-            g_debug ("No command in session file %s", path);
-    }
-    g_key_file_free (session_desktop_file);
-    g_free (path);
-    if (!command)
-        return NULL;
-
-    result = g_shell_parse_argv (command, &argc, &argv, &error);
-    if (error)
-        g_debug ("Invalid session command '%s': %s", command, error->message);
-    g_clear_error (&error);
-    g_free (command);
-    if (!result)
-        return NULL;
-
-    /* Convert to full path */
-    path = g_find_program_in_path (argv[0]);
-    if (path)
-    {
-        g_free (argv[0]);
-        argv[0] = path;
-    }
-    command = g_strjoinv (" ", argv);
-
-    if (display->priv->session_wrapper && !is_greeter)
-    {
-        gchar *wrapper;
-
-        wrapper = g_find_program_in_path (display->priv->session_wrapper);
-        if (wrapper)
-        {
-            gchar *t = command;
-            command = g_strdup_printf ("%s '%s'", wrapper, command);
-            g_free (t);
-            g_free (wrapper);
-        }
-    }
-
-    /* for a guest session, run command through the wrapper covered by MAC */
-    if (display->priv->autologin_guest)
-    {
-        gchar *t = command;
-        command = g_strdup_printf (PKGLIBEXEC_DIR "/lightdm-guest-session-wrapper %s", command);
-        g_debug("Guest session, running session command through wrapper: %s", command);
-        g_free (t);
-    }
 
     g_signal_emit (display, signals[CREATE_SESSION], 0, &session);
-    g_return_val_if_fail (session != NULL, NULL);
-
-    if (is_greeter)
-        g_signal_connect_after (session, "stopped", G_CALLBACK (greeter_session_stopped_cb), display);
-    else
-        g_signal_connect_after (session, "stopped", G_CALLBACK (user_session_stopped_cb), display);
-    session_set_is_greeter (session, is_greeter);
-    session_set_authentication (session, authentication);
-    session_set_command (session, command);
-    g_free (command);
-
-    session_set_env (session, "DESKTOP_SESSION", session_name); // FIXME: Apparently deprecated?
-    session_set_env (session, "GDMSESSION", session_name); // FIXME: Not cross-desktop
+    if (!session)
+        return NULL;
 
     /* Connect using the session bus */
     if (getuid () != 0)
@@ -524,13 +283,39 @@ create_session (Display *display, PAMSession *authentication, const gchar *sessi
 }
 
 static void
-display_set_is_ready (Display *display)
+destroy_session (Display *display)
 {
-    if (display->priv->is_ready)
+    if (!display->priv->session)
         return;
 
-    display->priv->is_ready = TRUE;
-    g_signal_emit (display, signals[READY], 0);
+    g_signal_handlers_disconnect_matched (display->priv->session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
+    session_stop (display->priv->session);
+    g_object_unref (display->priv->session);
+    display->priv->session = NULL;
+}
+
+static void
+greeter_authentication_complete_cb (Session *session, Display *display)
+{
+    gboolean result = FALSE;
+
+    if (display->priv->stopping)
+        return;
+
+    if (session_get_is_authenticated (session))
+    {
+        g_debug ("Greeter authorized");
+        g_signal_emit (display, signals[START_GREETER], 0, &result);
+        result = !result;
+    }
+    else
+        g_debug ("Greeter failed authentication");
+
+    if (!result)
+    {
+        g_debug ("Greeter failed to start");
+        display_stop (display);
+    }
 }
 
 static void
@@ -541,10 +326,10 @@ greeter_connected_cb (Greeter *greeter, Display *display)
     display_set_is_ready (display);
 }
 
-static PAMSession *
+static Session *
 greeter_start_authentication_cb (Greeter *greeter, const gchar *username, Display *display)
 {
-    return pam_session_new (display->priv->pam_service, username);
+    return create_session (display);
 }
 
 static gboolean
@@ -555,7 +340,7 @@ greeter_start_session_cb (Greeter *greeter, const gchar *session_name, Display *
     {
         User *user;
 
-        user = pam_session_get_user (greeter_get_authentication (greeter));
+        user = session_get_user (greeter_get_authentication_session (greeter));
         session_name = user_get_xsession (user);
     }
 
@@ -577,74 +362,30 @@ greeter_start_session_cb (Greeter *greeter, const gchar *session_name, Display *
     }
     else
     {
-       if (switch_to_user (display, pam_session_get_user (greeter_get_authentication (display->priv->greeter))))
-           return TRUE;
+        if (switch_to_user (display, session_get_user (greeter_get_authentication_session (display->priv->greeter))))
+            return TRUE;
     }
 
     /* Stop the greeter, the session will start when the greeter has quit */
     g_debug ("Stopping greeter");
-    display->priv->start_session_on_greeter_quit = TRUE;
     session_stop (display->priv->session);
 
     return TRUE;
 }
 
 static gboolean
-start_greeter_session (Display *display)
+start_greeter (Display *display)
 {
-    User *user;
-    gchar *log_dir, *filename, *log_filename;
-    PAMSession *authentication;
-    gboolean start_result;
-
-    g_return_val_if_fail (display->priv->session == NULL, FALSE);
-    g_return_val_if_fail (display->priv->greeter == NULL, FALSE);
-
-    g_debug ("Starting greeter session");
-
-    if (getuid () != 0)
-        user = accounts_get_current_user ();
-    else
-    {
-        gchar *greeter_user;
-
-        greeter_user = config_get_string (config_get_instance (), "LightDM", "greeter-user");
-        if (!greeter_user)
-        {
-            g_warning ("Greeter must not be run as root");
-            return FALSE;
-        }
-
-        user = accounts_get_user_by_name (greeter_user);
-        if (!user)
-            g_debug ("Unable to start greeter, user %s does not exist", greeter_user);
-        g_free (greeter_user);
-        if (!user)
-            return FALSE;
-    }
-    display->priv->in_user_session = FALSE;
-
-    /* Authenticate as the requested user */
-    authentication = pam_session_new (display->priv->pam_service, user_get_name (user));
-    g_object_unref (user);
-
-    display->priv->session = create_session (display, authentication, display->priv->greeter_session, TRUE);
-    g_object_unref (authentication);
-  
-    if (!display->priv->session)
-        return FALSE;
-
-    log_dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
-    filename = g_strdup_printf ("%s-greeter.log", display_server_get_name (display->priv->display_server));
-    log_filename = g_build_filename (log_dir, filename, NULL);
-    g_free (log_dir);
-    g_free (filename);
+    gchar *greeter_user;
+    gboolean result;
 
-    g_debug ("Logging to %s", log_filename);
-    session_set_log_file (display->priv->session, log_filename, FALSE);
-    g_free (log_filename);
+    destroy_session (display);
+    display->priv->session = create_session (display);
+    session_set_class (display->priv->session, XDG_SESSION_CLASS_GREETER);
+    g_signal_connect (display->priv->session, "authentication-complete", G_CALLBACK (greeter_authentication_complete_cb), display);
 
-    display->priv->greeter = greeter_new (display->priv->session);
+    /* Make communication link to greeter that will run on this session */
+    display->priv->greeter = greeter_new (display->priv->session, display->priv->pam_service);
     g_signal_connect (G_OBJECT (display->priv->greeter), "connected", G_CALLBACK (greeter_connected_cb), display);
     g_signal_connect (G_OBJECT (display->priv->greeter), "start-authentication", G_CALLBACK (greeter_start_authentication_cb), display);
     g_signal_connect (G_OBJECT (display->priv->greeter), "start-session", G_CALLBACK (greeter_start_session_cb), display);
@@ -669,83 +410,310 @@ start_greeter_session (Display *display)
     if (display->priv->greeter_is_lock)
         greeter_set_hint (display->priv->greeter, "lock-screen", "true");
 
-    start_result = FALSE;
-    g_signal_emit (display, signals[START_GREETER], 0, &start_result);
+    /* Run greeter as unprivileged user */
+    if (getuid () != 0)
+    {
+        User *user;
+        user = accounts_get_current_user ();
+        greeter_user = g_strdup (user_get_name (user));
+        g_object_unref (user);
+    }
+    else
+    {
+        greeter_user = config_get_string (config_get_instance (), "LightDM", "greeter-user");
+        if (!greeter_user)
+        {
+            g_warning ("Greeter must not be run as root");
+            display_stop (display);
+            return FALSE;
+        }
+    }
+
+    g_signal_connect_after (display->priv->session, "stopped", G_CALLBACK (greeter_session_stopped_cb), display);
+    result = session_start (display->priv->session, display->priv->pam_service, greeter_user, FALSE, FALSE);
+    g_free (greeter_user);
+
+    if (!result)
+        display_stop (display);
 
-    return !start_result;
+    return result;
 }
 
-static gboolean
-display_start_greeter (Display *display)
+static void
+autologin_authentication_complete_cb (Session *session, Display *display)
 {
-    if (!greeter_start (display->priv->greeter))
+    gboolean result = FALSE;
+
+    if (display->priv->stopping)
+        return;
+
+    if (session_get_is_authenticated (session))
     {
-        g_debug ("Failed to start greeter protocol");
+        const gchar *session_name;
 
-        g_signal_handlers_disconnect_matched (display->priv->greeter, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
-        g_object_unref (display->priv->greeter);
-        display->priv->greeter = NULL;
+        g_debug ("Autologin user %s authorized", session_get_username (session));
 
-        return TRUE;
+        session_name = user_get_xsession (session_get_user (session));
+        if (session_name)
+        {
+            g_debug ("Autologin using session %s", session_name);
+            display_set_user_session (display, session_name);
+        }
+
+        g_signal_emit (display, signals[START_SESSION], 0, &result);
+        result = !result;
     }
-  
-    if (!session_start (SESSION (display->priv->session)))
-    {     
-        g_debug ("Failed to start greeter session");
-        return TRUE;
+    else
+        g_debug ("Autologin failed authentication");
+
+    if (!result)
+    {
+        if (display->priv->start_greeter_if_fail)
+            start_greeter (display);
+        else
+            display_stop (display);
     }
+}
 
-    return FALSE;
+static gboolean
+autologin (Display *display, const gchar *username, const gchar *service, gboolean start_greeter_if_fail)
+{
+    display->priv->start_greeter_if_fail = start_greeter_if_fail;
+
+    display->priv->in_user_session = TRUE;
+    destroy_session (display);
+    display->priv->session = create_session (display);
+    g_signal_connect (display->priv->session, "authentication-complete", G_CALLBACK (autologin_authentication_complete_cb), display);
+    g_signal_connect_after (display->priv->session, "stopped", G_CALLBACK (user_session_stopped_cb), display);
+    return session_start (display->priv->session, service, username, TRUE, FALSE);
 }
 
 static gboolean
-start_user_session (Display *display, PAMSession *authentication)
+autologin_guest (Display *display, const gchar *service, gboolean start_greeter_if_fail)
 {
-    User *user;
-    gchar *log_filename;
+    gchar *username;
     gboolean result;
 
-    g_return_val_if_fail (display->priv->session == NULL, FALSE);
+    username = get_guest_username (display);
+    if (!username)
+    {
+        g_debug ("Can't autologin guest, no guest account");
+        return FALSE;
+    }
 
-    g_debug ("Starting user session");
+    result = autologin (display, username, service, start_greeter_if_fail);
+    g_free (username);
 
-    user = pam_session_get_user (authentication);
+    return result;
+}
 
-    /* Update user's xsession setting */
-    user_set_xsession (user, display->priv->user_session);
-    display->priv->session = create_session (display, authentication, display->priv->user_session, FALSE);
+static gchar **
+get_session_command (const gchar *filename)
+{
+    GKeyFile *session_desktop_file;
+    gboolean result;
+    int argc;
+    gchar *command = NULL, **argv, *path;
+    GError *error = NULL;
 
-    if (!display->priv->session)
-        return FALSE;
+    /* Read the command from the .desktop file */
+    session_desktop_file = g_key_file_new ();
+    result = g_key_file_load_from_file (session_desktop_file, filename, G_KEY_FILE_NONE, &error);
+    if (error)
+        g_debug ("Failed to load session file %s: %s", filename, error->message);
+    g_clear_error (&error);
+    if (result)
+    {
+        command = g_key_file_get_string (session_desktop_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
+        if (!command)
+            g_debug ("No command in session file %s", filename);
+    }
+    g_key_file_free (session_desktop_file);
+
+    if (!command)
+        return NULL;
 
-    // FIXME: Copy old error file
-    log_filename = g_build_filename (user_get_home_directory (user), ".xsession-errors", NULL);
+    /* Split command into an array listing and make command absolute */
+    result = g_shell_parse_argv (command, &argc, &argv, &error);
+    if (error)
+        g_debug ("Invalid session command '%s': %s", command, error->message);
+    g_clear_error (&error);
+    g_free (command);
+    if (!result)
+        return NULL;
+    path = g_find_program_in_path (argv[0]);
+    if (path)
+    {
+        g_free (argv[0]);
+        argv[0] = path;
+    }
   
-    /* Delete existing log file if it exists - a bug in 1.0.0 would cause this file to be written as root */
-    unlink (log_filename);
+    return argv;
+}
 
-    g_debug ("Logging to %s", log_filename);    
-    session_set_log_file (display->priv->session, log_filename, TRUE);
-    g_free (log_filename);
+static void
+greeter_session_stopped_cb (Session *session, Display *display)
+{
+    gboolean result = FALSE;
+
+    g_debug ("Greeter quit");
+
+    g_signal_handlers_disconnect_matched (display->priv->session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
+    g_object_unref (display->priv->session);
+    display->priv->session = NULL;
+
+    if (display->priv->stopping)
+    {
+        display_stop (display);
+        return;
+    }
+
+    if (!display->priv->display_server)
+        return;
+
+    /* Start the session for the authenticated user */
+    if (greeter_get_start_session (display->priv->greeter))
+    {
+        /* If guest, then start a new autologin guest session (so can setup account) */
+        if (greeter_get_guest_authenticated (display->priv->greeter))
+            result = autologin_guest (display, display->priv->pam_autologin_service, FALSE);
+        /* Otherwise, use the session the greeter has authenticated */
+        else
+        {
+            destroy_session (display);
+            display->priv->session = g_object_ref (greeter_get_authentication_session (display->priv->greeter));
+            g_signal_connect_after (display->priv->session, "stopped", G_CALLBACK (user_session_stopped_cb), display);
+            display->priv->in_user_session = TRUE;
+            g_signal_emit (display, signals[START_SESSION], 0, &result);
+            result = !result;
+        }
+    }
 
-    g_signal_emit (display, signals[START_SESSION], 0, &result);
-    result = !result;
+    /* Destroy the greeter */
+    g_signal_handlers_disconnect_matched (display->priv->greeter, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
+    g_object_unref (display->priv->greeter);
+    display->priv->greeter = NULL;
 
     if (!result)
     {
-        g_object_unref (display->priv->session);
-        display->priv->session = NULL;
+        g_debug ("Failed to start greeter");
+        display_stop (display);
     }
+}
 
-    return result;
+static gboolean
+display_start_greeter (Display *display)
+{
+    gchar *log_dir, *filename, *log_filename, *sessions_dir, *path;
+    gchar **argv;
+
+    /* Log the output of the greeter to a system location */
+    log_dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
+    filename = g_strdup_printf ("%s-greeter.log", display_server_get_name (display->priv->display_server));
+    log_filename = g_build_filename (log_dir, filename, NULL);
+    g_free (log_dir);
+    g_free (filename);
+    g_debug ("Logging to %s", log_filename);
+    session_set_log_file (display->priv->session, log_filename);
+    g_free (log_filename);
+
+    /* Load the greeter session information */
+    sessions_dir = config_get_string (config_get_instance (), "LightDM", "xgreeters-directory");
+    filename = g_strdup_printf ("%s.desktop", display->priv->greeter_session);
+    path = g_build_filename (sessions_dir, filename, NULL);
+    g_free (sessions_dir);
+    g_free (filename);
+    argv = get_session_command (path);
+    g_free (path);
+    if (!argv)
+        return TRUE;
+
+    session_run (display->priv->session, argv);
+
+    return FALSE;
+}
+
+static void
+user_session_stopped_cb (Session *session, Display *display)
+{
+    g_debug ("User session quit");
+
+    g_signal_handlers_disconnect_matched (display->priv->session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
+    g_object_unref (display->priv->session);
+    display->priv->session = NULL;
+
+    /* This display has ended */
+    display_stop (display);
+}
+
+static void
+prepend_argv (gchar ***argv, const gchar *value)
+{
+    gchar **old_argv, **new_argv;
+    gint i;
+
+    old_argv = *argv;
+    new_argv = g_malloc (sizeof (gchar *) * (g_strv_length (*argv) + 2));
+    new_argv[0] = g_strdup (value);
+    for (i = 0; old_argv[i]; i++)
+        new_argv[i + 1] = old_argv[i];
+    new_argv[i + 1] = NULL;
+
+    g_free (*argv);
+    *argv = new_argv;
 }
 
 static gboolean
 display_start_session (Display *display)
 {
-    if (!session_start (SESSION (display->priv->session)))
+    User *user;
+    gchar *filename, *sessions_dir, *path;
+    gchar **argv;
+
+    user = session_get_user (display->priv->session);
+
+    /* Store this session name so we automatically use it next time */
+    user_set_xsession (user, display->priv->user_session);
+
+    /* Find the command to run for the selected session */
+    // FIXME: This is X specific, move into xsession.c
+    sessions_dir = config_get_string (config_get_instance (), "LightDM", "xsessions-directory");
+    filename = g_strdup_printf ("%s.desktop", display->priv->user_session);
+    path = g_build_filename (sessions_dir, filename, NULL);
+    g_free (sessions_dir);
+    g_free (filename);
+    argv = get_session_command (path);
+    g_free (path);
+    if (!argv)
         return TRUE;
+  
+    session_set_env (display->priv->session, "DESKTOP_SESSION", display->priv->user_session); // FIXME: Apparently deprecated?
+    session_set_env (display->priv->session, "GDMSESSION", display->priv->user_session); // FIXME: Not cross-desktop
+
+    /* If configured, run sessions through a wrapper */
+    if (display->priv->session_wrapper)
+    {
+        gchar *wrapper = g_find_program_in_path (display->priv->session_wrapper);
+        if (wrapper)
+            prepend_argv (&argv, wrapper);
+        else
+            g_warning ("Session wrapper %s not in the path", display->priv->session_wrapper);
+        g_free (wrapper);
+    }
+
+    /* Run a guest session through the wrapper covered by MAC */
+    if (display->priv->autologin_guest)
+    {
+        gchar *wrapper = g_build_filename (PKGLIBEXEC_DIR, "lightdm-guest-session-wrapper", NULL);
+        g_debug ("Running guest session through wrapper: %s", wrapper);
+        prepend_argv (&argv, wrapper);
+        g_free (wrapper);
+    }
+
+    g_debug ("Starting session %s as user %s", display->priv->user_session, session_get_username (display->priv->session));
+
+    session_run (display->priv->session, argv);
+    g_strfreev (argv);
 
     // FIXME: Wait for session to indicate it is ready (maybe)
     display_set_is_ready (display);
@@ -769,8 +737,7 @@ display_server_stopped_cb (DisplayServer *server, Display *display)
 static void
 display_server_ready_cb (DisplayServer *display_server, Display *display)
 {
-    gboolean result;
-    gboolean started_session = FALSE;
+    gboolean result = FALSE;
 
     g_signal_emit (display, signals[DISPLAY_SERVER_READY], 0, &result);
     if (!result)
@@ -783,45 +750,33 @@ display_server_ready_cb (DisplayServer *display_server, Display *display)
     if (!display_server_get_start_local_sessions (display_server))
         return;
 
-    /* Automatically log in */
+    /* Automatically start requested user session */
+    result = FALSE;
     if (display->priv->autologin_guest)
     {
         g_debug ("Automatically logging in as guest");
-        started_session = autologin_guest (display, display->priv->pam_autologin_service, TRUE);
-        if (!started_session)
-            g_debug ("Failed to autologin as guest");
+        result = autologin_guest (display, display->priv->pam_autologin_service, TRUE);
     }
     else if (display->priv->autologin_user)
     {
         g_debug ("Automatically logging in user %s", display->priv->autologin_user);
-        started_session = autologin (display, display->priv->autologin_user, display->priv->pam_autologin_service, TRUE);
-        if (!started_session)
-            g_debug ("Failed to autologin user %s", display->priv->autologin_user);
+        result = autologin (display, display->priv->autologin_user, display->priv->pam_autologin_service, TRUE);
     }
     else if (display->priv->select_user_hint)
     {
         g_debug ("Logging in user %s", display->priv->select_user_hint);
-        started_session = autologin (display, display->priv->select_user_hint, display->priv->pam_service, TRUE);
-        if (!started_session)
-            g_debug ("Failed to login user %s", display->priv->select_user_hint);
-    }
-    else if (display->priv->select_guest_hint)
-    {
-        g_debug ("Logging in as guest");
-        started_session = autologin_guest (display, display->priv->pam_service, TRUE);
-        if (!started_session)
-            g_debug ("Failed login as guest");
+        result = autologin (display, display->priv->select_user_hint, display->priv->pam_service, TRUE);
     }
 
-    /* Finally start a greeter */
-    if (!started_session)
+    /* If no session started, start a greeter */
+    if (!result)
     {
-        started_session = start_greeter_session (display);
-        if (!started_session)
-            g_debug ("Failed to start greeter");
+        g_debug ("Starting greeter");      
+        result = start_greeter (display);
     }
 
-    if (!started_session)
+    /* If nothing started, then the display can't work */
+    if (!result)
         display_stop (display);
 }
 
@@ -948,15 +903,24 @@ display_finalize (GObject *object)
     self = DISPLAY (object);
 
     if (self->priv->display_server)
+    {
+        g_signal_handlers_disconnect_matched (self->priv->display_server, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
         g_object_unref (self->priv->display_server);
+    }
     g_free (self->priv->greeter_session);
     if (self->priv->greeter)
+    {
+        g_signal_handlers_disconnect_matched (self->priv->greeter, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
         g_object_unref (self->priv->greeter);
+    }
     g_free (self->priv->session_wrapper);
     g_free (self->priv->pam_service);
     g_free (self->priv->pam_autologin_service);
     if (self->priv->session)
+    {
+        g_signal_handlers_disconnect_matched (self->priv->session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);      
         g_object_unref (self->priv->session);
+    }
     g_free (self->priv->autologin_user);
     g_free (self->priv->select_user_hint);
     g_free (self->priv->user_session);
index 10fc31fbcaf91f901f6ae18e9d8042f9f2c4b75e..434a6bd3c1846f8b4413adc2d79f4348aa422fe7 100644 (file)
@@ -14,6 +14,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <fcntl.h>
 
 #include "greeter.h"
 #include "ldm-marshal.h"
@@ -31,6 +32,9 @@ struct GreeterPrivate
     /* Session running on */
     Session *session;
 
+    /* PAM service to authenticate with */
+    gchar *pam_service;
+
     /* Buffer for data read from greeter */
     guint8 *read_buffer;
     gsize n_read;
@@ -45,7 +49,10 @@ struct GreeterPrivate
     guint32 authentication_sequence_number;
 
     /* PAM session being constructed by the greeter */
-    PAMSession *authentication;
+    Session *authentication_session;
+
+    /* TRUE if a user has been authenticated and the session requested to start */
+    gboolean start_session;
 
     /* TRUE if can log into guest accounts */
     gboolean allow_guest;
@@ -54,6 +61,8 @@ struct GreeterPrivate
     gboolean guest_account_authenticated;
 
     /* Communication channels to communicate with */
+    int to_greeter_pipe[2];
+    int from_greeter_pipe[2];
     GIOChannel *to_greeter_channel;
     GIOChannel *from_greeter_channel;
 };
@@ -81,12 +90,42 @@ typedef enum
     SERVER_MESSAGE_SESSION_RESULT
 } ServerMessage;
 
+static gboolean read_cb (GIOChannel *source, GIOCondition condition, gpointer data);
+
 Greeter *
-greeter_new (Session *session)
+greeter_new (Session *session, const gchar *pam_service)
 {
-    Greeter *greeter = g_object_new (GREETER_TYPE, NULL);
+    gchar *value;
+    Greeter *greeter;
 
+    greeter = g_object_new (GREETER_TYPE, NULL);
     greeter->priv->session = g_object_ref (session);
+    greeter->priv->pam_service = g_strdup (pam_service);
+
+    /* Create a pipe to talk with the greeter */
+    if (pipe (greeter->priv->to_greeter_pipe) != 0 || pipe (greeter->priv->from_greeter_pipe) != 0)
+    {
+        g_warning ("Failed to create pipes: %s", strerror (errno));
+        //return;
+    }
+    greeter->priv->to_greeter_channel = g_io_channel_unix_new (greeter->priv->to_greeter_pipe[1]);
+    g_io_channel_set_encoding (greeter->priv->to_greeter_channel, NULL, NULL);
+    greeter->priv->from_greeter_channel = g_io_channel_unix_new (greeter->priv->from_greeter_pipe[0]);
+    g_io_channel_set_encoding (greeter->priv->from_greeter_channel, NULL, NULL);
+    g_io_channel_set_buffered (greeter->priv->from_greeter_channel, FALSE);
+    g_io_add_watch (greeter->priv->from_greeter_channel, G_IO_IN | G_IO_HUP, read_cb, greeter);
+
+    /* Let the greeter session know how to communicate with the daemon */
+    value = g_strdup_printf ("%d", greeter->priv->from_greeter_pipe[1]);
+    session_set_env (greeter->priv->session, "LIGHTDM_TO_SERVER_FD", value);
+    g_free (value);
+    value = g_strdup_printf ("%d", greeter->priv->to_greeter_pipe[0]);
+    session_set_env (greeter->priv->session, "LIGHTDM_FROM_SERVER_FD", value);
+    g_free (value);
+
+    /* Don't allow the daemon end of the pipes to be accessed in child processes */
+    fcntl (greeter->priv->to_greeter_pipe[1], F_SETFD, FD_CLOEXEC);
+    fcntl (greeter->priv->from_greeter_pipe[0], F_SETFD, FD_CLOEXEC);
 
     return greeter;
 }
@@ -115,15 +154,12 @@ int_length ()
 static void
 write_message (Greeter *greeter, guint8 *message, gsize message_length)
 {
-    GIOStatus status;
     GError *error = NULL;
 
-    status = g_io_channel_write_chars (greeter->priv->to_greeter_channel, (gchar *) message, message_length, NULL, &error);
+    g_io_channel_write_chars (greeter->priv->to_greeter_channel, (gchar *) message, message_length, NULL, &error);
     if (error)
         g_warning ("Error writing to greeter: %s", error->message);
     g_clear_error (&error);
-    if (status == G_IO_STATUS_NORMAL)
-        g_debug ("Wrote %zi bytes to greeter", message_length);
     g_io_channel_flush (greeter->priv->to_greeter_channel, NULL);
 }
 
@@ -204,30 +240,35 @@ handle_connect (Greeter *greeter, const gchar *version)
 }
 
 static void
-pam_messages_cb (PAMSession *authentication, int num_msg, const struct pam_message **msg, Greeter *greeter)
+pam_messages_cb (Session *session, Greeter *greeter)
 {
     int i;
     guint32 size;
     guint8 message[MAX_MESSAGE_LENGTH];
+    const struct pam_message *messages;
+    int messages_length;
     gsize offset = 0;
     int n_prompts = 0;
 
+    messages = session_get_messages (session);
+    messages_length = session_get_messages_length (session);
+
     /* Respond to d-bus query with messages */
-    g_debug ("Prompt greeter with %d message(s)", num_msg);
-    size = int_length () + string_length (pam_session_get_username (authentication)) + int_length ();
-    for (i = 0; i < num_msg; i++)
-        size += int_length () + string_length (msg[i]->msg);
+    g_debug ("Prompt greeter with %d message(s)", messages_length);
+    size = int_length () + string_length (session_get_username (session)) + int_length ();
+    for (i = 0; i < messages_length; i++)
+        size += int_length () + string_length (messages[i].msg);
   
     write_header (message, MAX_MESSAGE_LENGTH, SERVER_MESSAGE_PROMPT_AUTHENTICATION, size, &offset);
     write_int (message, MAX_MESSAGE_LENGTH, greeter->priv->authentication_sequence_number, &offset);
-    write_string (message, MAX_MESSAGE_LENGTH, pam_session_get_username (authentication), &offset);
-    write_int (message, MAX_MESSAGE_LENGTH, num_msg, &offset);
-    for (i = 0; i < num_msg; i++)
+    write_string (message, MAX_MESSAGE_LENGTH, session_get_username (session), &offset);
+    write_int (message, MAX_MESSAGE_LENGTH, messages_length, &offset);
+    for (i = 0; i < messages_length; i++)
     {
-        write_int (message, MAX_MESSAGE_LENGTH, msg[i]->msg_style, &offset);
-        write_string (message, MAX_MESSAGE_LENGTH, msg[i]->msg, &offset);
+        write_int (message, MAX_MESSAGE_LENGTH, messages[i].msg_style, &offset);
+        write_string (message, MAX_MESSAGE_LENGTH, messages[i].msg, &offset);
 
-        if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF || msg[i]->msg_style == PAM_PROMPT_ECHO_ON)
+        if (messages[i].msg_style == PAM_PROMPT_ECHO_OFF || messages[i].msg_style == PAM_PROMPT_ECHO_ON)
             n_prompts++;
     }
     write_message (greeter, message, offset);
@@ -237,8 +278,8 @@ pam_messages_cb (PAMSession *authentication, int num_msg, const struct pam_messa
     if (n_prompts == 0)
     {
         struct pam_response *response;
-        response = calloc (num_msg, sizeof (struct pam_response));
-        pam_session_respond (greeter->priv->authentication, response);
+        response = calloc (messages_length, sizeof (struct pam_response));
+        session_respond (greeter->priv->authentication_session, response);
     }
 }
 
@@ -256,33 +297,36 @@ send_end_authentication (Greeter *greeter, guint32 sequence_number, const gchar
 }
 
 static void
-authentication_result_cb (PAMSession *authentication, int result, Greeter *greeter)
+authentication_complete_cb (Session *session, Greeter *greeter)
 {
-    g_debug ("Authenticate result for user %s: %s", pam_session_get_username (authentication), pam_session_strerror (authentication, result));
+    int result;
+
+    g_debug ("Authenticate result for user %s: %s", session_get_username (session), session_get_authentication_result_string (session));
 
-    if (result == PAM_SUCCESS)
+    result = session_get_authentication_result (session);
+    if (session_get_is_authenticated (session))
     {
-        if (pam_session_get_user (authentication))
-            g_debug ("User %s authorized", pam_session_get_username (authentication));
+        if (session_get_user (session))
+            g_debug ("User %s authorized", session_get_username (session));
         else
         {
-            g_debug ("User %s authorized, but no account of that name exists", pam_session_get_username (authentication));          
+            g_debug ("User %s authorized, but no account of that name exists", session_get_username (session));          
             result = PAM_USER_UNKNOWN;
         }
     }
 
-    send_end_authentication (greeter, greeter->priv->authentication_sequence_number, pam_session_get_username (authentication), result);
+    send_end_authentication (greeter, greeter->priv->authentication_sequence_number, session_get_username (session), result);
 }
 
 static void
 reset_session (Greeter *greeter)
 {
-    if (greeter->priv->authentication)
+    if (greeter->priv->authentication_session)
     {
-        g_signal_handlers_disconnect_matched (greeter->priv->authentication, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, greeter);
-        pam_session_cancel (greeter->priv->authentication);
-        g_object_unref (greeter->priv->authentication);
-        greeter->priv->authentication = NULL;
+        g_signal_handlers_disconnect_matched (greeter->priv->authentication_session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, greeter);
+        session_stop (greeter->priv->authentication_session);
+        g_object_unref (greeter->priv->authentication_session);
+        greeter->priv->authentication_session = NULL;
     }
 
     greeter->priv->guest_account_authenticated = FALSE;
@@ -291,9 +335,6 @@ reset_session (Greeter *greeter)
 static void
 handle_login (Greeter *greeter, guint32 sequence_number, const gchar *username)
 {
-    gboolean result;
-    GError *error = NULL;
-
     if (username[0] == '\0')
     {
         g_debug ("Greeter start authentication");
@@ -305,25 +346,18 @@ handle_login (Greeter *greeter, guint32 sequence_number, const gchar *username)
     reset_session (greeter);
 
     greeter->priv->authentication_sequence_number = sequence_number;
-    g_signal_emit (greeter, signals[START_AUTHENTICATION], 0, username, &greeter->priv->authentication);
-    if (!greeter->priv->authentication)
+    g_signal_emit (greeter, signals[START_AUTHENTICATION], 0, username, &greeter->priv->authentication_session);
+    if (!greeter->priv->authentication_session)
     {
         send_end_authentication (greeter, sequence_number, "", PAM_USER_UNKNOWN);
         return;
     }
 
-    g_signal_connect (G_OBJECT (greeter->priv->authentication), "got-messages", G_CALLBACK (pam_messages_cb), greeter);
-    g_signal_connect (G_OBJECT (greeter->priv->authentication), "authentication-result", G_CALLBACK (authentication_result_cb), greeter);
+    g_signal_connect (G_OBJECT (greeter->priv->authentication_session), "got-messages", G_CALLBACK (pam_messages_cb), greeter);
+    g_signal_connect (G_OBJECT (greeter->priv->authentication_session), "authentication-complete", G_CALLBACK (authentication_complete_cb), greeter);
 
-    pam_session_set_item (greeter->priv->authentication, PAM_TTY,
-                          session_get_env (greeter->priv->session, "DISPLAY"));
-
-    result = pam_session_authenticate (greeter->priv->authentication, &error);
-    if (error)
-        g_debug ("Failed to start authentication: %s", error->message);
-    if (!result)
-        send_end_authentication (greeter, sequence_number, "", PAM_SYSTEM_ERR);
-    g_clear_error (&error);
+    /* Run the session process */
+    session_start (greeter->priv->authentication_session, greeter->priv->pam_service, username, TRUE, TRUE);
 }
 
 static void
@@ -347,38 +381,38 @@ handle_login_as_guest (Greeter *greeter, guint32 sequence_number)
 static void
 handle_continue_authentication (Greeter *greeter, gchar **secrets)
 {
-    int num_messages;
-    const struct pam_message **messages;
+    int messages_length;
+    const struct pam_message *messages;
     struct pam_response *response;
     int i, j, n_prompts = 0;
 
     /* Not in authentication */
-    if (greeter->priv->authentication == NULL)
+    if (greeter->priv->authentication_session == NULL)
         return;
 
-    num_messages = pam_session_get_num_messages (greeter->priv->authentication);
-    messages = pam_session_get_messages (greeter->priv->authentication);
+    messages_length = session_get_messages_length (greeter->priv->authentication_session);
+    messages = session_get_messages (greeter->priv->authentication_session);
 
     /* Check correct number of responses */
-    for (i = 0; i < num_messages; i++)
+    for (i = 0; i < messages_length; i++)
     {
-        int msg_style = messages[i]->msg_style;
+        int msg_style = messages[i].msg_style;
         if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
             n_prompts++;
     }
     if (g_strv_length (secrets) != n_prompts)
     {
-        pam_session_cancel (greeter->priv->authentication);
+        session_respond_error (greeter->priv->authentication_session, PAM_CONV_ERR);
         return;
     }
 
     g_debug ("Continue authentication");
 
     /* Build response */
-    response = calloc (num_messages, sizeof (struct pam_response));
-    for (i = 0, j = 0; i < num_messages; i++)
+    response = calloc (messages_length, sizeof (struct pam_response));
+    for (i = 0, j = 0; i < messages_length; i++)
     {
-        int msg_style = messages[i]->msg_style;
+        int msg_style = messages[i].msg_style;
         if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
         {
             response[i].resp = strdup (secrets[j]); // FIXME: Need to convert from UTF-8
@@ -386,19 +420,18 @@ handle_continue_authentication (Greeter *greeter, gchar **secrets)
         }
     }
 
-    pam_session_respond (greeter->priv->authentication, response);
+    session_respond (greeter->priv->authentication_session, response);
 }
 
 static void
 handle_cancel_authentication (Greeter *greeter)
 {
     /* Not in authentication */
-    if (greeter->priv->authentication == NULL)
+    if (greeter->priv->authentication_session == NULL)
         return;
 
     g_debug ("Cancel authentication");
-
-    pam_session_cancel (greeter->priv->authentication);
+    reset_session (greeter);
 }
 
 static void
@@ -411,12 +444,13 @@ handle_start_session (Greeter *greeter, const gchar *session)
     if (strcmp (session, "") == 0)
         session = NULL;
 
-    if (greeter->priv->guest_account_authenticated || pam_session_get_is_authenticated (greeter->priv->authentication))
+    if (greeter->priv->guest_account_authenticated || session_get_is_authenticated (greeter->priv->authentication_session))
     {
         if (session)
             g_debug ("Greeter requests session %s", session);
         else
-            g_debug ("Greeter requests default session");     
+            g_debug ("Greeter requests default session");
+        greeter->priv->start_session = TRUE;
         g_signal_emit (greeter, signals[START_SESSION], 0, session, &result);
     }
     else
@@ -435,7 +469,7 @@ handle_set_language (Greeter *greeter, const gchar *language)
 {
     User *user;
 
-    if (!greeter->priv->guest_account_authenticated && !pam_session_get_is_authenticated (greeter->priv->authentication))
+    if (!greeter->priv->guest_account_authenticated && !session_get_is_authenticated (greeter->priv->authentication_session))
     {
         g_debug ("Ignoring set language request, user is not authorized");
         return;
@@ -449,7 +483,7 @@ handle_set_language (Greeter *greeter, const gchar *language)
     }
 
     g_debug ("Greeter sets language %s", language);
-    user = pam_session_get_user (greeter->priv->authentication);
+    user = session_get_user (greeter->priv->authentication_session);
     user_set_language (user, language);
 }
 
@@ -526,11 +560,6 @@ read_cb (GIOChannel *source, GIOCondition condition, gpointer data)
     if (status != G_IO_STATUS_NORMAL)
         return TRUE;
 
-    g_debug ("Read %zi bytes from greeter", n_read);
-    /*for (i = 0; i < n_read; i++)
-       g_print ("%02X ", greeter->priv->read_buffer[greeter->priv->n_read+i]);
-    g_print ("\n");*/
-
     greeter->priv->n_read += n_read;
     if (greeter->priv->n_read != n_to_read)
         return TRUE;
@@ -600,40 +629,6 @@ read_cb (GIOChannel *source, GIOCondition condition, gpointer data)
     return TRUE;
 }
 
-gboolean
-greeter_start (Greeter *greeter)
-{
-    int to_greeter_pipe[2], from_greeter_pipe[2];
-    gint fd;
-    gchar *value;
-
-    if (pipe (to_greeter_pipe) != 0 || 
-        pipe (from_greeter_pipe) != 0)
-    {
-        g_warning ("Failed to create pipes: %s", strerror (errno));            
-        return FALSE;
-    }
-
-    greeter->priv->to_greeter_channel = g_io_channel_unix_new (to_greeter_pipe[1]);
-    g_io_channel_set_encoding (greeter->priv->to_greeter_channel, NULL, NULL);
-    greeter->priv->from_greeter_channel = g_io_channel_unix_new (from_greeter_pipe[0]);
-    g_io_channel_set_encoding (greeter->priv->from_greeter_channel, NULL, NULL);
-    g_io_channel_set_buffered (greeter->priv->from_greeter_channel, FALSE);
-    g_io_add_watch (greeter->priv->from_greeter_channel, G_IO_IN | G_IO_HUP, read_cb, greeter);
-
-    fd = from_greeter_pipe[1];
-    value = g_strdup_printf ("%d", fd);
-    session_set_env (greeter->priv->session, "LIGHTDM_TO_SERVER_FD", value);
-    g_free (value);
-
-    fd = to_greeter_pipe[0];
-    value = g_strdup_printf ("%d", fd);
-    session_set_env (greeter->priv->session, "LIGHTDM_FROM_SERVER_FD", value);
-    g_free (value);
-
-    return TRUE;
-}
-
 gboolean
 greeter_get_guest_authenticated (Greeter *greeter)
 {
@@ -641,14 +636,21 @@ greeter_get_guest_authenticated (Greeter *greeter)
     return greeter->priv->guest_account_authenticated;
 }
 
-PAMSession *
-greeter_get_authentication (Greeter *greeter)
+Session *
+greeter_get_authentication_session (Greeter *greeter)
 {
     g_return_val_if_fail (greeter != NULL, NULL);
-    return greeter->priv->authentication;
+    return greeter->priv->authentication_session;
+}
+
+gboolean
+greeter_get_start_session (Greeter *greeter)
+{
+    g_return_val_if_fail (greeter != NULL, FALSE);
+    return greeter->priv->start_session;
 }
 
-static PAMSession *
+static Session *
 greeter_real_start_authentication (Greeter *greeter, const gchar *username)
 {
     return NULL;
@@ -675,14 +677,15 @@ greeter_finalize (GObject *object)
 
     self = GREETER (object);
 
+    g_signal_handlers_disconnect_matched (self->priv->session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);    
     g_object_unref (self->priv->session);
+    g_free (self->priv->pam_service);
     g_free (self->priv->read_buffer);
     g_hash_table_unref (self->priv->hints);
-    if (self->priv->authentication)
+    if (self->priv->authentication_session)
     {
-        g_signal_handlers_disconnect_matched (self->priv->authentication, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
-        pam_session_cancel (self->priv->authentication);
-        g_object_unref (self->priv->authentication);
+        g_signal_handlers_disconnect_matched (self->priv->authentication_session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
+        g_object_unref (self->priv->authentication_session);
     }
     if (self->priv->to_greeter_channel)
         g_io_channel_unref (self->priv->to_greeter_channel);
@@ -718,7 +721,7 @@ greeter_class_init (GreeterClass *klass)
                       g_signal_accumulator_first_wins,
                       NULL,
                       ldm_marshal_OBJECT__STRING,
-                      PAM_SESSION_TYPE, 1, G_TYPE_STRING);
+                      SESSION_TYPE, 1, G_TYPE_STRING);
 
     signals[START_SESSION] =
         g_signal_new ("start-session",
index 4694698ed85799d58a2112f8d264ecacde8b51d2..56eea002b930014cdc33086e14984a5da1b0609a 100644 (file)
@@ -13,7 +13,6 @@
 #define _GREETER_H_
 
 #include "session.h"
-#include "pam-session.h"
 
 G_BEGIN_DECLS
 
@@ -32,23 +31,23 @@ typedef struct
 {
     GObjectClass parent_class;
     void (*connected)(Greeter *greeter);
-    PAMSession *(*start_authentication)(Greeter *greeter, const gchar *username);
+    Session *(*start_authentication)(Greeter *greeter, const gchar *username);
     gboolean (*start_session)(Greeter *greeter, const gchar *session, gboolean is_guest);
 } GreeterClass;
 
 GType greeter_get_type (void);
 
-Greeter *greeter_new (Session *session);
+Greeter *greeter_new (Session *session, const gchar *pam_service);
 
 void greeter_set_allow_guest (Greeter *greeter, gboolean allow_guest);
 
 void greeter_set_hint (Greeter *greeter, const gchar *name, const gchar *value);
 
-gboolean greeter_start (Greeter *greeter);
-
 gboolean greeter_get_guest_authenticated (Greeter *greeter);
 
-PAMSession *greeter_get_authentication (Greeter *greeter);
+Session *greeter_get_authentication_session (Greeter *greeter);
+
+gboolean greeter_get_start_session (Greeter *greeter);
 
 G_END_DECLS
 
index c853650ae5f7eb53bdd72cdd7a76ba7e3bb637cd..4bc5c0c1b5316da5239a16c4c8e2f9147b876303 100644 (file)
@@ -27,8 +27,8 @@
 #include "seat-xdmcp-session.h"
 #include "seat-xvnc.h"
 #include "xserver.h"
-#include "pam-session.h"
 #include "process.h"
+#include "session-child.h"
 
 static gchar *config_path = NULL;
 static GMainLoop *loop = NULL;
@@ -463,7 +463,7 @@ handle_session_get_property (GDBusConnection       *connection,
     if (g_strcmp0 (property_name, "Seat") == 0)
         return g_variant_new_object_path (entry ? entry->parent_path : "");
     else if (g_strcmp0 (property_name, "UserName") == 0)
-        return g_variant_new_string (user_get_name (session_get_user (session)));
+        return g_variant_new_string (session_get_username (session));
 
     return NULL;
 }
@@ -486,7 +486,7 @@ handle_session_call (GDBusConnection       *connection,
 
         seat = get_seat_for_session (session);
         /* FIXME: Should only allow locks if have a session on this seat */
-        seat_lock (seat, user_get_name (session_get_user (session)));
+        seat_lock (seat, session_get_username (session));
         g_dbus_method_invocation_return_value (invocation, NULL);
     }
 }
@@ -534,13 +534,13 @@ start_session_cb (Display *display, Seat *seat)
 
     session = display_get_session (display);
 
+    /* Set environment variables when session runs */
     seat_entry = g_hash_table_lookup (seat_bus_entries, seat);
     session_set_env (session, "XDG_SEAT_PATH", seat_entry->path);
-
     path = g_strdup_printf ("/org/freedesktop/DisplayManager/Session%d", session_index);
     session_index++;
     session_set_env (session, "XDG_SESSION_PATH", path);
-    g_free (path);
+    g_object_set_data_full (G_OBJECT (session), "XDG_SESSION_PATH", path, g_free);
 
     return FALSE;
 }
@@ -568,7 +568,7 @@ session_started_cb (Display *display, Seat *seat)
     g_signal_connect (session, "stopped", G_CALLBACK (session_stopped_cb), seat);
 
     seat_entry = g_hash_table_lookup (seat_bus_entries, seat);
-    entry = bus_entry_new (session_get_env (session, "XDG_SESSION_PATH"), seat_entry ? seat_entry->path : NULL, "SessionRemoved");
+    entry = bus_entry_new (g_object_get_data (G_OBJECT (session), "XDG_SESSION_PATH"), seat_entry ? seat_entry->path : NULL, "SessionRemoved");
     g_hash_table_insert (session_bus_entries, g_object_ref (session), entry);
 
     g_debug ("Registering session with bus path %s", entry->path);
@@ -860,6 +860,10 @@ main (int argc, char **argv)
     };
     GError *error = NULL;
 
+    /* When lightdm starts sessions it needs to run itself in a new mode */
+    if (argc >= 2 && strcmp (argv[1], "--session-child") == 0)
+        return session_child_run (argc, argv);
+
     g_type_init ();
     loop = g_main_loop_new (NULL, FALSE);
 
diff --git a/src/pam-session.c b/src/pam-session.c
deleted file mode 100644 (file)
index 9dead91..0000000
+++ /dev/null
@@ -1,472 +0,0 @@
-/*
- * Copyright (C) 2010-2011 Robert Ancell.
- * Author: Robert Ancell <robert.ancell@canonical.com>
- * 
- * This program is free software: you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later
- * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
- * license.
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "ldm-marshal.h"
-#include "pam-session.h"
-
-enum {
-    AUTHENTICATION_STARTED,
-    GOT_MESSAGES,
-    AUTHENTICATION_RESULT,
-    LAST_SIGNAL
-};
-static guint signals[LAST_SIGNAL] = { 0 };
-
-struct PAMSessionPrivate
-{
-    /* Service to authenticate against */
-    gchar *service;
-
-    /* User being authenticated */
-    User *user;
-
-    /* TRUE if can handle provide interaction with PAM */
-    gboolean interactive;
-
-    /* Authentication thread */
-    GThread *authentication_thread;
-  
-    /* TRUE if the thread is being intentionally stopped */
-    gboolean stop_thread;
-
-    /* Messages requested */
-    int num_messages;
-    const struct pam_message **messages;
-    int authentication_result;
-
-    /* Queue to feed responses to the authentication thread */
-    GAsyncQueue *authentication_response_queue;
-
-    /* Authentication handle */
-    pam_handle_t *pam_handle;
-  
-    /* TRUE if in an authentication */
-    gboolean in_authentication;
-
-    /* TRUE if is authenticated */
-    gboolean is_authenticated;
-
-    /* TRUE if in a session */
-    gboolean in_session;
-};
-
-G_DEFINE_TYPE (PAMSession, pam_session, G_TYPE_OBJECT);
-
-static int pam_conv_cb (int num_msg, const struct pam_message **msg, struct pam_response **resp, void *app_data);
-
-PAMSession *
-pam_session_new (const gchar *service, const gchar *username)
-{
-    PAMSession *self = g_object_new (PAM_SESSION_TYPE, NULL);
-    struct pam_conv conversation = { pam_conv_cb, self };
-    int result;
-
-    self->priv->service = g_strdup (service);
-
-    result = pam_start (self->priv->service, username, &conversation, &self->priv->pam_handle);
-    g_debug ("pam_start(\"%s\", \"%s\") -> (%p, %d)",
-             self->priv->service,
-             username,
-             self->priv->pam_handle,
-             result);
-
-    return self;
-}
-
-void
-pam_session_set_interactive (PAMSession *session, gboolean interactive)
-{
-    g_return_if_fail (session != NULL);
-    session->priv->interactive = interactive;
-}
-
-gboolean
-pam_session_get_interactive (PAMSession *session)
-{
-    g_return_val_if_fail (session != NULL, FALSE);
-    return session->priv->interactive;
-}
-
-gboolean
-pam_session_get_is_authenticated (PAMSession *session)
-{
-    g_return_val_if_fail (session != NULL, FALSE);
-    return session->priv->is_authenticated;
-}
-
-gboolean
-pam_session_set_item (PAMSession *session, int item_type, const gchar *value)
-{
-    int result = PAM_SUCCESS;
-
-    g_return_val_if_fail (session != NULL, FALSE);
-    g_return_val_if_fail (value != NULL, FALSE);
-
-    result = pam_set_item (session->priv->pam_handle, item_type, value);
-    g_debug ("pam_set_item(%p, %d, \"%s\") -> %d (%s)",
-             session->priv->pam_handle,
-             item_type,
-             value,
-             result,
-             pam_strerror (session->priv->pam_handle, result));
-
-    return result == PAM_SUCCESS;
-}
-
-gboolean
-pam_session_open (PAMSession *session)
-{
-    int result = PAM_SUCCESS;
-
-    g_return_val_if_fail (session != NULL, FALSE);
-
-    session->priv->in_session = TRUE;
-
-    if (getuid () == 0)
-    {
-        result = pam_open_session (session->priv->pam_handle, 0);
-        g_debug ("pam_open_session(%p, 0) -> %d (%s)",
-                 session->priv->pam_handle,
-                 result,
-                 pam_strerror (session->priv->pam_handle, result));
-    }
-
-    return result == PAM_SUCCESS;
-}
-
-gboolean
-pam_session_setup (PAMSession *session)
-{
-    int result;
-
-    result = pam_setcred (session->priv->pam_handle, PAM_ESTABLISH_CRED);
-    g_debug ("pam_setcred(%p, PAM_ESTABLISH_CRED) -> %d (%s)",
-             session->priv->pam_handle,
-             result,
-             pam_strerror (session->priv->pam_handle, result));
-
-    return result == PAM_SUCCESS;
-}
-
-gboolean
-pam_session_get_in_session (PAMSession *session)
-{
-    g_return_val_if_fail (session != NULL, FALSE);
-    return session->priv->in_session;
-}
-
-static gboolean
-notify_messages_cb (gpointer data)
-{
-    PAMSession *session = data;
-
-    g_signal_emit (G_OBJECT (session), signals[GOT_MESSAGES], 0, session->priv->num_messages, session->priv->messages);
-
-    return FALSE;
-}
-
-static int
-pam_conv_cb (int num_msg, const struct pam_message **msg,
-             struct pam_response **resp, void *app_data)
-{
-    PAMSession *session = app_data;
-    struct pam_response *response;
-
-    /* For some reason after cancelling we still end up here so check for stop as well */
-    if (session->priv->stop_thread)
-        return PAM_CONV_ERR;
-
-    /* If not interactive then fail the authentication */
-    if (!session->priv->interactive)
-        return PAM_CONV_ERR;
-
-    /* Notify user */
-    session->priv->num_messages = num_msg;
-    session->priv->messages = msg;
-    g_idle_add (notify_messages_cb, session);
-
-    /* Wait for response */
-    response = g_async_queue_pop (session->priv->authentication_response_queue);
-    session->priv->num_messages = 0;
-    session->priv->messages = NULL;
-
-    /* Cancelled by user */
-    if (session->priv->stop_thread)
-        return PAM_CONV_ERR;
-
-    *resp = response;
-
-    return PAM_SUCCESS;
-}
-
-static void
-report_result (PAMSession *session, int result)
-{
-    session->priv->in_authentication = FALSE;
-    session->priv->is_authenticated = result == PAM_SUCCESS;
-    g_signal_emit (G_OBJECT (session), signals[AUTHENTICATION_RESULT], 0, result);
-}
-
-static gboolean
-notify_auth_complete_cb (gpointer data)
-{
-    PAMSession *session = data;
-    int result;
-
-    result = session->priv->authentication_result;
-    session->priv->authentication_result = 0;
-
-    g_thread_join (session->priv->authentication_thread);
-    session->priv->authentication_thread = NULL;
-    g_async_queue_unref (session->priv->authentication_response_queue);
-    session->priv->authentication_response_queue = NULL;
-
-    report_result (session, result);
-
-    /* Authentication was cancelled */
-    if (session->priv->stop_thread)
-        pam_session_cancel (session);
-
-    /* The thread is complete, drop the reference */
-    g_object_unref (session);
-
-    return FALSE;
-}
-
-static gpointer
-authenticate_cb (gpointer data)
-{
-    PAMSession *session = data;
-
-    session->priv->authentication_result = pam_authenticate (session->priv->pam_handle, 0);
-    g_debug ("pam_authenticate(%p, 0) -> %d (%s)",
-             session->priv->pam_handle,
-             session->priv->authentication_result,
-             pam_strerror (session->priv->pam_handle, session->priv->authentication_result));
-
-    if (session->priv->authentication_result == PAM_SUCCESS)
-    {
-        session->priv->authentication_result = pam_acct_mgmt (session->priv->pam_handle, 0);
-        g_debug ("pam_acct_mgmt(%p, 0) -> %d (%s)",
-                 session->priv->pam_handle,
-                 session->priv->authentication_result,
-                 pam_strerror (session->priv->pam_handle, session->priv->authentication_result));
-
-        if (session->priv->authentication_result == PAM_NEW_AUTHTOK_REQD)
-        {
-            session->priv->authentication_result = pam_chauthtok (session->priv->pam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
-            g_debug ("pam_chauthtok(%p, PAM_CHANGE_EXPIRED_AUTHTOK) -> %d (%s)",
-                     session->priv->pam_handle,
-                     session->priv->authentication_result,
-                     pam_strerror (session->priv->pam_handle, session->priv->authentication_result));
-        }
-    }
-
-    /* Notify user */
-    g_idle_add (notify_auth_complete_cb, session);
-
-    return NULL;
-}
-
-gboolean
-pam_session_authenticate (PAMSession *session, GError **error)
-{
-    g_return_val_if_fail (session != NULL, FALSE);
-    g_return_val_if_fail (session->priv->in_authentication == FALSE, FALSE);
-    g_return_val_if_fail (session->priv->is_authenticated == FALSE, FALSE);
-
-    session->priv->in_authentication = TRUE;
-    g_signal_emit (G_OBJECT (session), signals[AUTHENTICATION_STARTED], 0);
-
-    /* Hold a reference to this object while the thread may access it */
-    g_object_ref (session);
-
-    /* Start thread */
-    session->priv->authentication_response_queue = g_async_queue_new ();
-    session->priv->authentication_thread = g_thread_create (authenticate_cb, session, TRUE, error);
-    if (!session->priv->authentication_thread)
-        return FALSE;
-
-    return TRUE;
-}
-
-const gchar *
-pam_session_strerror (PAMSession *session, int error)
-{
-    g_return_val_if_fail (session != NULL, NULL);
-    return pam_strerror (session->priv->pam_handle, error);
-}
-
-const gchar *
-pam_session_get_username (PAMSession *session)
-{
-    const char *username;
-    g_return_val_if_fail (session != NULL, NULL);
-    pam_get_item (session->priv->pam_handle, PAM_USER, (const void **) &username);
-    return username;
-}
-
-User *
-pam_session_get_user (PAMSession *session)
-{
-    g_return_val_if_fail (session != NULL, NULL);
-
-    if (!session->priv->user && pam_session_get_username (session))
-        session->priv->user = accounts_get_user_by_name (pam_session_get_username (session));
-
-    return session->priv->user;
-}
-
-const struct pam_message **
-pam_session_get_messages (PAMSession *session)
-{
-    g_return_val_if_fail (session != NULL, NULL);
-    return session->priv->messages;  
-}
-
-gint
-pam_session_get_num_messages (PAMSession *session)
-{
-    g_return_val_if_fail (session != NULL, 0);
-    return session->priv->num_messages;
-}
-
-void
-pam_session_respond (PAMSession *session, struct pam_response *response)
-{
-    g_return_if_fail (session != NULL);
-    g_return_if_fail (session->priv->authentication_thread != NULL);
-    g_async_queue_push (session->priv->authentication_response_queue, response);
-}
-
-void
-pam_session_cancel (PAMSession *session)
-{
-    g_return_if_fail (session != NULL);
-
-    /* If authenticating cancel first */
-    if (session->priv->authentication_thread)
-    {
-        session->priv->stop_thread = TRUE;
-        g_async_queue_push (session->priv->authentication_response_queue, GINT_TO_POINTER (-1));
-    }
-}
-
-const gchar *
-pam_session_getenv (PAMSession *session, const gchar *name)
-{
-    g_return_val_if_fail (session != NULL, NULL);
-    return pam_getenv (session->priv->pam_handle, name);
-}
-
-gchar **
-pam_session_get_envlist (PAMSession *session)
-{
-    g_return_val_if_fail (session != NULL, NULL);
-    return pam_getenvlist (session->priv->pam_handle);
-}
-
-void
-pam_session_close (PAMSession *session)
-{
-    int result;
-
-    g_return_if_fail (session != NULL);
-
-    session->priv->in_session = FALSE;
-
-    if (getuid () == 0)
-    {
-        g_return_if_fail (session->priv->pam_handle != NULL);
-
-        result = pam_close_session (session->priv->pam_handle, 0);
-        g_debug ("pam_close_session(%p) -> %d (%s)",
-                 session->priv->pam_handle,
-                 result,
-                 pam_strerror (session->priv->pam_handle, result));
-
-        result = pam_setcred (session->priv->pam_handle, PAM_DELETE_CRED);
-        g_debug ("pam_setcred(%p, PAM_DELETE_CRED) -> %d (%s)",
-                 session->priv->pam_handle,
-                 result,
-                 pam_strerror (session->priv->pam_handle, result));
-
-        result = pam_end (session->priv->pam_handle, PAM_SUCCESS);
-        g_debug ("pam_end(%p) -> %d",
-                 session->priv->pam_handle,
-                 result);
-
-        session->priv->pam_handle = NULL;
-    }
-}
-
-static void
-pam_session_init (PAMSession *session)
-{
-    session->priv = G_TYPE_INSTANCE_GET_PRIVATE (session, PAM_SESSION_TYPE, PAMSessionPrivate);
-    session->priv->interactive = TRUE;
-}
-
-static void
-pam_session_finalize (GObject *object)
-{
-    PAMSession *self;
-
-    self = PAM_SESSION (object);
-
-    g_free (self->priv->service);
-    if (self->priv->user)
-        g_object_unref (self->priv->user);
-    if (self->priv->pam_handle)
-        pam_end (self->priv->pam_handle, PAM_SUCCESS);
-
-    G_OBJECT_CLASS (pam_session_parent_class)->finalize (object);
-}
-
-static void
-pam_session_class_init (PAMSessionClass *klass)
-{
-    GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-    object_class->finalize = pam_session_finalize;  
-
-    g_type_class_add_private (klass, sizeof (PAMSessionPrivate));
-
-    signals[AUTHENTICATION_STARTED] =
-        g_signal_new ("authentication-started",
-                      G_TYPE_FROM_CLASS (klass),
-                      G_SIGNAL_RUN_LAST,
-                      G_STRUCT_OFFSET (PAMSessionClass, authentication_started),
-                      NULL, NULL,
-                      g_cclosure_marshal_VOID__VOID,
-                      G_TYPE_NONE, 0);
-
-    signals[GOT_MESSAGES] =
-        g_signal_new ("got-messages",
-                      G_TYPE_FROM_CLASS (klass),
-                      G_SIGNAL_RUN_LAST,
-                      G_STRUCT_OFFSET (PAMSessionClass, got_messages),
-                      NULL, NULL,
-                      ldm_marshal_VOID__INT_POINTER,
-                      G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_POINTER);
-
-    signals[AUTHENTICATION_RESULT] =
-        g_signal_new ("authentication-result",
-                      G_TYPE_FROM_CLASS (klass),
-                      G_SIGNAL_RUN_LAST,
-                      G_STRUCT_OFFSET (PAMSessionClass, authentication_result),
-                      NULL, NULL,
-                      g_cclosure_marshal_VOID__INT,
-                      G_TYPE_NONE, 1, G_TYPE_INT);
-}
diff --git a/src/pam-session.h b/src/pam-session.h
deleted file mode 100644 (file)
index 2f58369..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2010-2011 Robert Ancell.
- * Author: Robert Ancell <robert.ancell@canonical.com>
- * 
- * This program is free software: you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later
- * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
- * license.
- */
-
-#ifndef _PAM_SESSION_H_
-#define _PAM_SESSION_H_
-
-#include <glib-object.h>
-#include <security/pam_appl.h>
-
-#include "accounts.h"
-
-G_BEGIN_DECLS
-
-#define PAM_SESSION_TYPE (pam_session_get_type())
-#define PAM_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PAM_SESSION_TYPE, PAMSession));
-
-typedef struct PAMSessionPrivate PAMSessionPrivate;
-
-typedef struct
-{
-    GObject         parent_instance;
-    PAMSessionPrivate *priv;
-} PAMSession;
-
-typedef struct
-{
-    GObjectClass parent_class;
-
-    void (*authentication_started)(PAMSession *pam_session);  
-    void (*got_messages)(PAMSession *pam_session, int num_msg, const struct pam_message **msg);
-    void (*authentication_result)(PAMSession *pam_session, int result);
-} PAMSessionClass;
-
-GType pam_session_get_type (void);
-
-void pam_session_set_use_pam (void);
-
-void pam_session_set_use_passwd_file (gchar *passwd_file);
-
-PAMSession *pam_session_new (const gchar *service, const gchar *username);
-
-void pam_session_set_interactive (PAMSession *session, gboolean interactive);
-
-gboolean pam_session_get_interactive (PAMSession *session);
-
-gboolean pam_session_authenticate (PAMSession *session, GError **error);
-
-gboolean pam_session_get_is_authenticated (PAMSession *session);
-
-gboolean pam_session_set_item (PAMSession *session, int item_type, const gchar *value);
-
-gboolean pam_session_open (PAMSession *session);
-
-gboolean pam_session_setup (PAMSession *session);
-
-gboolean pam_session_get_in_session (PAMSession *session);
-
-const gchar *pam_session_strerror (PAMSession *session, int error);
-
-const gchar *pam_session_get_username (PAMSession *session);
-
-User *pam_session_get_user (PAMSession *session);
-
-const struct pam_message **pam_session_get_messages (PAMSession *session);
-
-gint pam_session_get_num_messages (PAMSession *session);
-
-void pam_session_respond (PAMSession *session, struct pam_response *response);
-
-void pam_session_cancel (PAMSession *session);
-
-const gchar *pam_session_getenv (PAMSession *session, const gchar *name);
-
-gchar **pam_session_get_envlist (PAMSession *session);
-
-void pam_session_close (PAMSession *session);
-
-G_END_DECLS
-
-#endif /* _PAM_SESSION_H_ */
index 65a95e16d65d9b37a8f65ab4c60405b7bfd9a7cb..720a3704b35e9a5d245069aa95495d92b5c65563 100644 (file)
@@ -58,9 +58,7 @@ seat_xdmcp_session_create_session (Seat *seat, Display *display)
     xserver = XSERVER_REMOTE (display_get_display_server (display));
 
     session = xsession_new (XSERVER (xserver));
-    session_set_console_kit_parameter (SESSION (session), "remote-host-name", g_variant_new_string (xserver_get_hostname (XSERVER (xserver))));
-    // FIXME: Mark local seats as such
-    session_set_console_kit_parameter (SESSION (session), "is-local", g_variant_new_boolean (FALSE));
+    session_set_remote_host_name (SESSION (session), xserver_get_hostname (XSERVER (xserver)));
 
     return SESSION (session);
 }
index 15607ea551c049074ee24b55b4241624b4eb24a4..a1fb2e019f4714aec4e4e4ca77ab4ff27940ca95 100644 (file)
@@ -115,9 +115,8 @@ seat_xlocal_create_session (Seat *seat, Display *display)
 
     session = xsession_new (XSERVER (xserver));
     tty = g_strdup_printf ("/dev/tty%d", xserver_local_get_vt (xserver));
-    session_set_console_kit_parameter (SESSION (session), "x11-display-device", g_variant_new_string (tty));
+    session_set_tty (SESSION (session), tty);
     g_free (tty);
-    session_set_console_kit_parameter (SESSION (session), "is-local", g_variant_new_boolean (TRUE));
 
     return SESSION (session);
 }
index d61acc14d9c55900f6dc38758f3176099741dc82..252f0cc4fe5456a7242e78d9caa6d174234d9239 100644 (file)
@@ -53,8 +53,7 @@ seat_xremote_create_session (Seat *seat, Display *display)
     xserver = XSERVER_REMOTE (display_get_display_server (display));
 
     session = xsession_new (XSERVER (xserver));
-    session_set_console_kit_parameter (SESSION (session), "remote-host-name", g_variant_new_string (xserver_get_hostname (XSERVER (xserver))));
-    session_set_console_kit_parameter (SESSION (session), "is-local", g_variant_new_boolean (FALSE));
+    session_set_remote_host_name (SESSION (session), xserver_get_hostname (XSERVER (xserver)));
 
     return SESSION (session);
 }
index de3574ae47ac58b6a954f54ffe101989a4916827..eb150c31da46c944720bfdb5c38b23b35b42f290 100644 (file)
@@ -73,9 +73,8 @@ seat_xvnc_create_session (Seat *seat, Display *display)
     session = xsession_new (XSERVER (xserver));
     address = G_INET_SOCKET_ADDRESS (g_socket_get_remote_address (SEAT_XVNC (seat)->priv->connection, NULL));
     hostname = g_inet_address_to_string (g_inet_socket_address_get_address (address));
-    session_set_console_kit_parameter (SESSION (session), "remote-host-name", g_variant_new_string (hostname));
+    session_set_remote_host_name (SESSION (session), hostname);
     g_free (hostname);
-    session_set_console_kit_parameter (SESSION (session), "is-local", g_variant_new_boolean (FALSE));
 
     return SESSION (session);
 }
index 42b92b23906e430563e214af4cfd988a34acda2f..369aa885c57c9f9a6537e7f6392d4f11c09d4c62 100644 (file)
@@ -383,7 +383,7 @@ session_stopped_cb (Session *session, Seat *seat)
     if (script)
         run_script (seat, display, script, session_get_user (session));
 
-    if (seat->priv->guest_username && strcmp (user_get_name (session_get_user (session)), seat->priv->guest_username) == 0)
+    if (seat->priv->guest_username && strcmp (session_get_username (session), seat->priv->guest_username) == 0)
     {
         guest_account_cleanup (seat->priv->guest_username);
         g_free (seat->priv->guest_username);
@@ -429,7 +429,6 @@ display_stopped_cb (Display *display, Seat *seat)
     g_object_unref (display);
 
     check_stopped (seat);
-
 }
 
 static gboolean
index c525450514746802657a55567639dca8b1c08037..4b4a8d1e729e23609efb46d08e85bb17249ac183 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <glib-object.h>
 #include "display.h"
+#include "process.h"
 
 G_BEGIN_DECLS
 
diff --git a/src/session-child.c b/src/session-child.c
new file mode 100644 (file)
index 0000000..9ea86de
--- /dev/null
@@ -0,0 +1,534 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+#include <glib.h>
+#include <security/pam_appl.h>
+
+#include "session-child.h"
+#include "session.h"
+#include "console-kit.h"
+#include "privileges.h"
+#include "xauthority.h"
+
+/* Child process being run */
+static GPid child_pid = 0;
+
+/* Pipe to communicate with daemon */
+static int from_daemon_output = 0;
+static int to_daemon_input = 0;
+
+static gboolean is_interactive;
+static gboolean do_authenticate;
+static pam_handle_t *pam_handle;
+
+/* Maximum length of a string to pass between daemon and session */
+#define MAX_STRING_LENGTH 65535
+
+static void
+write_data (const void *buf, size_t count)
+{
+    if (write (to_daemon_input, buf, count) != count)
+        g_printerr ("Error writing to daemon: %s\n", strerror (errno));
+}
+
+static void
+write_string (const char *value)
+{
+    int length;
+
+    length = value ? strlen (value) : -1;
+    write_data (&length, sizeof (length));
+    if (value)
+        write_data (value, sizeof (char) * length);
+}
+
+static ssize_t
+read_data (void *buf, size_t count)
+{
+    ssize_t n_read;
+
+    n_read = read (from_daemon_output, buf, count);
+    if (n_read < 0)
+        g_printerr ("Error reading from daemon: %s\n", strerror (errno));
+  
+    return n_read;
+}
+
+static gchar *
+read_string ()
+{
+    int length;
+    char *value;
+
+    if (read_data (&length, sizeof (length)) <= 0)
+        return NULL;
+    if (length < 0)
+        return NULL;
+    if (length > MAX_STRING_LENGTH)
+    {
+        g_printerr ("Invalid string length %d from daemon\n", length);
+        return NULL;
+    }
+  
+    value = g_malloc (sizeof (char) * (length + 1));
+    read_data (value, length);
+    value[length] = '\0';      
+
+    return value;
+}
+
+static int
+pam_conv_cb (int msg_length, const struct pam_message **msg, struct pam_response **resp, void *app_data)
+{
+    int i, error;
+    gboolean auth_complete = FALSE;
+    gchar *username = NULL;
+
+    /* Cancel authentication if requiring input */
+    if (!is_interactive)
+    {
+        for (i = 0; i < msg_length; i++)
+        {
+            if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON || msg[i]->msg_style == PAM_PROMPT_ECHO_OFF)
+            {
+                g_printerr ("Stopping PAM conversation, interaction requested but not supported\n");
+                return PAM_CONV_ERR;
+            }
+        }
+    }
+
+    /* Check if we changed user */
+    pam_get_item (pam_handle, PAM_USER, (const void **) &username);
+
+    /* Notify the daemon */
+    write_string (username);
+    write_data (&auth_complete, sizeof (auth_complete));
+    write_data (&msg_length, sizeof (msg_length));
+    for (i = 0; i < msg_length; i++)
+    {
+        const struct pam_message *m = msg[i];
+        write_data (&m->msg_style, sizeof (m->msg_style));
+        write_string (m->msg);
+    }
+
+    /* Get response */
+    *resp = calloc (msg_length, sizeof (struct pam_response));
+    read_data (&error, sizeof (error));
+    if (error != PAM_SUCCESS)
+        return error;
+    for (i = 0; i < msg_length; i++)
+    {
+        struct pam_response *r = resp[i];
+        r->resp = read_string ();
+        read_data (&r->resp_retcode, sizeof (r->resp_retcode));
+    }
+
+    return PAM_SUCCESS;
+}
+
+static void
+signal_cb (int signum)
+{
+    /* Pass on signal to child, otherwise just quit */
+    if (child_pid > 0)
+        kill (child_pid, signum);
+    else
+        exit (EXIT_SUCCESS);
+}
+
+int
+session_child_run (int argc, char **argv)
+{
+    struct pam_conv conversation = { pam_conv_cb, NULL };
+    int i, version, fd, result;
+    gboolean auth_complete = TRUE;
+    User *user = NULL;
+    gchar *log_filename;
+    gsize env_length;
+    gsize command_argc;
+    gchar **command_argv;
+    GVariantBuilder ck_parameters;
+    int return_code;
+    int authentication_result;
+    gchar *authentication_result_string;
+    gchar *service;
+    gchar *username;
+    gchar *class;
+    gchar *tty;
+    gchar *remote_host_name;
+    gchar *xdisplay;
+    gchar *xauth_name;
+    XAuthority *xauthority = NULL;
+    gchar *xauth_filename;
+    GDBusConnection *bus;
+    gchar *console_kit_cookie;
+    GError *error = NULL;
+  
+    g_type_init ();
+
+    /* Make input non-blocking */
+    fd = open ("/dev/null", O_RDONLY);
+    dup2 (fd, STDIN_FILENO);
+    close (fd);
+
+    /* Close stdout */
+    fd = open ("/dev/null", O_WRONLY);
+    dup2 (fd, STDOUT_FILENO);
+    close (fd);
+
+    /* Get the pipe from the daemon */
+    if (argc != 4)
+    {
+        g_printerr ("Usage: lightdm --session-child INPUTFD OUTPUTFD\n");
+        return EXIT_FAILURE;
+    }
+    from_daemon_output = atoi (argv[2]);
+    to_daemon_input = atoi (argv[3]);
+    if (from_daemon_output == 0 || to_daemon_input == 0)
+    {
+        g_printerr ("Invalid LIGHTDM_DAEMON_PIPE\n");
+        return EXIT_FAILURE;
+    }
+    g_unsetenv ("LIGHTDM_DAEMON_PIPE");
+
+    /* Read a version number so we can handle upgrades (i.e. a newer version of session child is run for an old daemon */
+    read_data (&version, sizeof (version));
+
+    service = read_string ();
+    username = read_string ();
+    read_data (&do_authenticate, sizeof (do_authenticate));
+    read_data (&is_interactive, sizeof (is_interactive));
+    class = read_string ();
+    tty = read_string ();
+    remote_host_name = read_string ();
+    xdisplay = read_string ();
+    xauth_name = read_string ();
+    if (xauth_name)
+    {
+        guint16 xauth_family;
+        guint8 *xauth_address;
+        gsize xauth_address_length;
+        gchar *xauth_number;
+        guint8 *xauth_data;
+        gsize xauth_data_length;
+
+        read_data (&xauth_family, sizeof (xauth_family));
+        read_data (&xauth_address_length, sizeof (xauth_address_length));
+        xauth_address = g_malloc (xauth_address_length);
+        read_data (xauth_address, xauth_address_length);
+        xauth_number = read_string ();
+        read_data (&xauth_data_length, sizeof (xauth_data_length));
+        xauth_data = g_malloc (xauth_data_length);
+        read_data (xauth_data, xauth_data_length);
+
+        xauthority = xauth_new (xauth_family, xauth_address, xauth_address_length, xauth_number, xauth_name, xauth_data, xauth_data_length);
+    }
+
+    /* Setup PAM */
+    result = pam_start (service, username, &conversation, &pam_handle);
+    if (result != PAM_SUCCESS)
+    {
+        g_printerr ("Failed to start PAM: %s", pam_strerror (NULL, result));
+        return EXIT_FAILURE;
+    }
+    if (xdisplay)
+    {
+        pam_set_item (pam_handle, PAM_XDISPLAY, xdisplay);
+        pam_set_item (pam_handle, PAM_TTY, xdisplay);
+    }
+    else if (tty)
+        pam_set_item (pam_handle, PAM_TTY, tty);    
+    if (xauthority)
+    {
+        struct pam_xauth_data value;
+
+        value.name = (char *) xauth_get_authorization_name (xauthority);
+        value.namelen = strlen (xauth_get_authorization_name (xauthority));
+        value.data = (char *) xauth_get_authorization_data (xauthority);
+        value.datalen = xauth_get_authorization_data_length (xauthority);
+        pam_set_item (pam_handle, PAM_XAUTHDATA, &value);
+    }
+
+    /* Authenticate */
+    if (do_authenticate)
+    {
+        const gchar *new_username;
+
+        authentication_result = pam_authenticate (pam_handle, 0);
+
+        /* See what user we ended up as */
+        if (pam_get_item (pam_handle, PAM_USER, (const void **) &new_username) != PAM_SUCCESS)
+            return EXIT_FAILURE;
+        g_free (username);
+        username = g_strdup (new_username);
+
+        /* Check account is valid */
+        if (authentication_result == PAM_SUCCESS)
+            authentication_result = pam_acct_mgmt (pam_handle, 0);
+        if (authentication_result == PAM_NEW_AUTHTOK_REQD)
+            authentication_result = pam_chauthtok (pam_handle, 0);
+    }
+    else
+        authentication_result = PAM_SUCCESS;
+
+    if (authentication_result == PAM_SUCCESS)
+    {
+        /* Fail authentication if user doesn't actually exist */
+        user = accounts_get_user_by_name (username);
+        if (!user)
+        {
+            g_printerr ("Failed to get information on user %s: %s\n", username, strerror (errno));
+            authentication_result = PAM_USER_UNKNOWN;
+        }
+        else
+        {
+            /* Set POSIX variables */
+            pam_putenv (pam_handle, "PATH=/usr/local/bin:/usr/bin:/bin");
+            pam_putenv (pam_handle, g_strdup_printf ("USER=%s", username));
+            pam_putenv (pam_handle, g_strdup_printf ("LOGNAME=%s", username));
+            pam_putenv (pam_handle, g_strdup_printf ("HOME=%s", user_get_home_directory (user)));
+            pam_putenv (pam_handle, g_strdup_printf ("SHELL=%s", user_get_shell (user)));
+        }
+    }
+
+    authentication_result_string = g_strdup (pam_strerror (pam_handle, authentication_result));
+
+    /* Report authentication result */
+    write_string (username);
+    write_data (&auth_complete, sizeof (auth_complete));
+    write_data (&authentication_result, sizeof (authentication_result));
+    write_string (authentication_result_string);
+
+    /* Check we got a valid user */
+    if (!username)
+    {
+        g_printerr ("No user selected during authentication\n");
+        return EXIT_FAILURE;
+    }
+
+    /* Stop if we didn't authenticated */
+    if (authentication_result != PAM_SUCCESS)
+        return EXIT_FAILURE;
+
+    /* Get the command to run (blocks) */
+    log_filename = read_string ();
+    xauth_filename = read_string ();
+    read_data (&env_length, sizeof (env_length));
+    for (i = 0; i < env_length; i++)
+        pam_putenv (pam_handle, read_string ());
+    read_data (&command_argc, sizeof (command_argc));
+    command_argv = g_malloc (sizeof (gchar *) * (command_argc + 1));
+    for (i = 0; i < command_argc; i++)
+        command_argv[i] = read_string ();
+    command_argv[i] = NULL;
+
+    /* Redirect stderr to a log file */
+    if (!log_filename)
+    {
+        fd = open ("/dev/null", O_WRONLY);   
+        dup2 (fd, STDERR_FILENO);
+        close (fd);
+    }
+    else if (g_path_is_absolute (log_filename))
+    {
+        fd = open (log_filename, O_WRONLY | O_CREAT, 0600);
+        dup2 (fd, STDERR_FILENO);
+        close (fd);
+    }   
+
+    /* Set credentials */
+    result = pam_setcred (pam_handle, PAM_ESTABLISH_CRED);
+      
+    /* Open a the session */
+    result = pam_open_session (pam_handle, 0);
+    if (result != PAM_SUCCESS)
+    {
+        g_printerr ("Failed to open PAM session: %s\n", pam_strerror (pam_handle, result));
+        return EXIT_FAILURE;
+    }
+
+    /* Open a connection to the system bus for ConsoleKit - we must keep it open or CK will close the session */
+    bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+    if (error)
+        g_printerr ("Unable to contact system bus: %s", error->message);
+    if (!bus)
+        return EXIT_FAILURE;
+
+    /* Open a Console Kit session */
+    g_variant_builder_init (&ck_parameters, G_VARIANT_TYPE ("(a(sv))"));
+    g_variant_builder_open (&ck_parameters, G_VARIANT_TYPE ("a(sv)"));
+    g_variant_builder_add (&ck_parameters, "(sv)", "unix-user", g_variant_new_int32 (user_get_uid (user)));
+    if (g_strcmp0 (class, XDG_SESSION_CLASS_GREETER) == 0)
+        g_variant_builder_add (&ck_parameters, "(sv)", "session-type", g_variant_new_string ("LoginWindow"));
+    if (xdisplay)
+    {
+        g_variant_builder_add (&ck_parameters, "(sv)", "x11-display", g_variant_new_string (xdisplay));
+        if (tty)
+            g_variant_builder_add (&ck_parameters, "(sv)", "x11-display-device", g_variant_new_string (tty));
+    }
+    if (remote_host_name)
+    {
+        g_variant_builder_add (&ck_parameters, "(sv)", "is-local", g_variant_new_boolean (FALSE));
+        g_variant_builder_add (&ck_parameters, "(sv)", "remote-host-name", g_variant_new_string (remote_host_name));
+    }
+    else
+        g_variant_builder_add (&ck_parameters, "(sv)", "is-local", g_variant_new_boolean (TRUE));
+    console_kit_cookie = ck_open_session (&ck_parameters);
+    write_string (console_kit_cookie);
+    if (console_kit_cookie)
+    {
+        gchar *value;
+        value = g_strdup_printf ("XDG_SESSION_COOKIE=%s", console_kit_cookie);
+        pam_putenv (pam_handle, value);
+        g_free (value);
+    }
+
+    /* Write X authority */
+    if (xauthority)
+    {
+        GFile *file;
+        gboolean drop_privileges, result;
+        gchar *value;
+        GError *error = NULL;
+
+        file = g_file_new_for_path (xauth_filename);
+
+        drop_privileges = geteuid () == 0;
+        if (drop_privileges)
+            privileges_drop (user);
+        result = xauth_write (xauthority, XAUTH_WRITE_MODE_REPLACE, file, &error);
+        if (drop_privileges)
+            privileges_reclaim ();
+
+        g_object_unref (file);
+        if (error)
+            g_printerr ("Error writing X authority: %s\n", error->message);
+        g_clear_error (&error);
+        if (!result)
+            return EXIT_FAILURE;
+
+        value = g_strdup_printf ("XAUTHORITY=%s", xauth_filename);
+        pam_putenv (pam_handle, value);
+        g_free (value);
+    }
+
+    /* Catch terminate signal and pass it to the child */
+    signal (SIGTERM, signal_cb);
+
+    /* Run the command as the authenticated user */
+    child_pid = fork (); 
+    if (child_pid == 0)
+    {
+        // FIXME: This is not thread safe (particularly the printfs)
+
+        /* Make this process its own session */
+        if (setsid () < 0)
+            g_printerr ("Failed to make process a new session: %s\n", strerror (errno));
+
+        /* Change to this user */
+        if (getuid () == 0)
+        {
+            if (initgroups (username, user_get_gid (user)) < 0)
+            {
+                g_printerr ("Failed to initialize supplementary groups for %s: %s\n", username, strerror (errno));
+                _exit (EXIT_FAILURE);
+            }
+
+            if (setgid (user_get_gid (user)) != 0)
+            {
+                g_printerr ("Failed to set group ID to %d: %s\n", user_get_gid (user), strerror (errno));
+                _exit (EXIT_FAILURE);
+            }
+
+            if (setuid (user_get_uid (user)) != 0)
+            {
+                g_printerr ("Failed to set user ID to %d: %s\n", user_get_uid (user), strerror (errno));
+                _exit (EXIT_FAILURE);
+            }
+        }
+
+        /* Change working directory */
+        /* NOTE: This must be done after the permissions are changed because NFS filesystems can
+         * be setup so the local root user accesses the NFS files as 'nobody'.  If the home directories
+         * are not system readable then the chdir can fail */
+        if (chdir (user_get_home_directory (user)) != 0)
+        {
+            g_printerr ("Failed to change to home directory %s: %s\n", user_get_home_directory (user), strerror (errno));
+            _exit (EXIT_FAILURE);
+        }
+
+        /* Redirect stderr to a log file */
+        if (log_filename && !g_path_is_absolute (log_filename))
+        {
+            fd = open (log_filename, O_WRONLY | O_CREAT, 0600);
+            dup2 (fd, STDERR_FILENO);
+            close (fd);
+        }
+
+        /* Run the command */
+        execve (command_argv[0], command_argv, pam_getenvlist (pam_handle));
+        g_printerr ("Failed to run command: %s\n", strerror (errno));
+        _exit (EXIT_FAILURE);
+    }
+
+    /* Bail out if failed to fork */
+    if (child_pid < 0)
+    {
+        g_printerr ("Failed to fork session child process: %s\n", strerror (errno));
+        return_code = EXIT_FAILURE;
+    }
+
+    /* Wait for the command to complete (blocks) */
+    if (child_pid > 0)
+    {
+        waitpid (child_pid, &return_code, 0);
+        child_pid = 0;
+    }
+
+    /* Remove X authority */
+    if (xauthority)
+    {
+        GFile *file;
+        gboolean drop_privileges, result;
+        GError *error = NULL;
+
+        file = g_file_new_for_path (xauth_filename);
+
+        drop_privileges = geteuid () == 0;
+        if (drop_privileges)
+            privileges_drop (user);
+        result = xauth_write (xauthority, XAUTH_WRITE_MODE_REMOVE, file, &error);
+        if (drop_privileges)
+            privileges_reclaim ();
+
+        g_object_unref (file);
+        if (error)
+            g_printerr ("Error removing X authority: %s\n", error->message);
+        g_clear_error (&error);
+        if (!result)
+            _exit (EXIT_FAILURE);
+    }
+
+    /* Close the Console Kit session */
+    if (console_kit_cookie)
+        ck_close_session (console_kit_cookie);
+
+    /* Close the session */
+    pam_close_session (pam_handle, 0);
+
+    /* Remove credentials */
+    result = pam_setcred (pam_handle, PAM_DELETE_CRED);
+
+    pam_end (pam_handle, 0);
+    pam_handle = NULL;
+
+    /* Return result of session process to the daemon */
+    return return_code;
+}
diff --git a/src/session-child.h b/src/session-child.h
new file mode 100644 (file)
index 0000000..39f3250
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2010-2011 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ * 
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+#ifndef _SESSION_CHILD_H_
+#define _SESSION_CHILD_H_
+
+int session_child_run (int argc, char **argv);
+
+#endif /* _SESSION_CHILD_H_ */
index a4bfd2b7348ed9eba98ad9882c22464db74c3fdf..cb300d651a1037e852c5bfcd0ab8977690cd8929 100644 (file)
 #include <fcntl.h>
 #include <glib/gstdio.h>
 #include <grp.h>
+#include <pwd.h>
 
 #include "session.h"
+#include "configuration.h"
 #include "console-kit.h"
 
+enum {
+    GOT_MESSAGES,
+    AUTHENTICATION_COMPLETE,
+    STOPPED,
+    LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
 struct SessionPrivate
 {
-    /* File to log to */
-    gchar *log_file;
+    /* PID of child process */
+    GPid pid;
+
+    /* Pipes to talk to child */
+    int to_child_input;
+    int from_child_output;
+    GIOChannel *from_child_channel;
+    guint from_child_watch;
+    guint child_watch;
+
+    /* User to authenticate as */
+    gchar *username;
+
+    /* User object that matches the current username */
+    User *user;
 
-    /* TRUE if the log file should be owned by the user */
-    gboolean log_file_as_user;
+    /* Messages being requested by PAM */
+    int messages_length;
+    struct pam_message *messages;
 
-    /* Authentication for this session */
-    PAMSession *authentication;
+    /* Authentication result from PAM */
+    gboolean authentication_started;
+    gboolean authentication_complete;
+    int authentication_result;
+    gchar *authentication_result_string;
+  
+    /* File to log to */
+    gchar *log_filename;
+  
+    /* Seat class */
+    gchar *class;
 
-    /* Command to run for this session */
-    gchar *command;
+    /* tty this session is running on */
+    gchar *tty;
+  
+    /* X display connected to */
+    gchar *xdisplay;
+    XAuthority *xauthority;
+    gboolean xauth_use_system_location;
 
-    /* ConsoleKit parameters for this session */
-    GHashTable *console_kit_parameters;
+    /* Remote host this session is being controlled from */
+    gchar *remote_host_name;
 
-    /* ConsoleKit cookie for the session */
+    /* Console kit cookie */
     gchar *console_kit_cookie;
 
-    /* TRUE if this is a greeter session */
-    gboolean is_greeter;
+    /* Environment to set in child */
+    GList *env;
 };
 
-G_DEFINE_TYPE (Session, session, PROCESS_TYPE);
+/* Maximum length of a string to pass between daemon and session */
+#define MAX_STRING_LENGTH 65535
+
+G_DEFINE_TYPE (Session, session, G_TYPE_OBJECT);
 
 void
-session_set_log_file (Session *session, const gchar *filename, gboolean as_user)
+session_set_log_file (Session *session, const gchar *filename)
 {
     g_return_if_fail (session != NULL);
-    g_free (session->priv->log_file);
-    session->priv->log_file = g_strdup (filename);
-    session->priv->log_file_as_user = as_user;
-}
-
-const gchar *
-session_get_log_file (Session *session)
-{
-    g_return_val_if_fail (session != NULL, NULL);
-    return session->priv->log_file;
+    g_free (session->priv->log_filename);
+    session->priv->log_filename = g_strdup (filename);
 }
 
 void
-session_set_authentication (Session *session, PAMSession *authentication)
+session_set_class (Session *session, const gchar *class)
 {
     g_return_if_fail (session != NULL);
-    session->priv->authentication = g_object_ref (authentication);
+    g_free (session->priv->class);
+    session->priv->class = g_strdup (class);
 }
 
-PAMSession *
-session_get_authentication (Session *session)
+void
+session_set_tty (Session *session, const gchar *tty)
 {
-    g_return_val_if_fail (session != NULL, NULL);
-    return session->priv->authentication;
+    g_return_if_fail (session != NULL);
+    g_free (session->priv->tty);
+    session->priv->tty = g_strdup (tty);
 }
 
-User *
-session_get_user (Session *session)
+void
+session_set_xdisplay (Session *session, const gchar *xdisplay)
 {
-    g_return_val_if_fail (session != NULL, NULL);
-    return pam_session_get_user (session->priv->authentication);
+    g_return_if_fail (session != NULL);
+    g_free (session->priv->xdisplay);
+    session->priv->xdisplay = g_strdup (xdisplay);
 }
 
 void
-session_set_is_greeter (Session *session, gboolean is_greeter)
+session_set_xauthority (Session *session, XAuthority *authority, gboolean use_system_location)
 {
     g_return_if_fail (session != NULL);
-    session->priv->is_greeter = is_greeter;
+    if (session->priv->xauthority)
+        g_object_unref (session->priv->xauthority);
+    session->priv->xauthority = g_object_ref (authority);
+    session->priv->xauth_use_system_location = use_system_location;
 }
 
-gboolean
-session_get_is_greeter (Session *session)
+void
+session_set_remote_host_name (Session *session, const gchar *remote_host_name)
 {
-    g_return_val_if_fail (session != NULL, FALSE);
-    return session->priv->is_greeter;
+    g_return_if_fail (session != NULL);
+    g_free (session->priv->remote_host_name);
+    session->priv->remote_host_name = g_strdup (remote_host_name);
 }
 
 void
-session_set_command (Session *session, const gchar *command)
+session_set_env (Session *session, const gchar *name, const gchar *value)
 {
     g_return_if_fail (session != NULL);
-
-    g_free (session->priv->command);
-    session->priv->command = g_strdup (command);
+    session->priv->env = g_list_append (session->priv->env, g_strdup_printf ("%s=%s", name, value));
 }
 
-const gchar *
-session_get_command (Session *session)
+User *
+session_get_user (Session *session)
 {
     g_return_val_if_fail (session != NULL, NULL);
-    return session->priv->command;
-}
-
-static gchar *
-get_absolute_command (const gchar *command)
-{
-    gchar **tokens;
-    gchar *absolute_binary, *absolute_command = NULL;
-
-    tokens = g_strsplit (command, " ", 2);
 
-    absolute_binary = g_find_program_in_path (tokens[0]);
-    if (absolute_binary)
-    {
-        if (tokens[1])
-            absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
-        else
-            absolute_command = g_strdup (absolute_binary);
-    }
-    g_free (absolute_binary);
+    if (session->priv->username == NULL)
+        return NULL;
 
-    g_strfreev (tokens);
+    if (!session->priv->user)
+        session->priv->user = accounts_get_user_by_name (session->priv->username);
 
-    return absolute_command;
+    return session->priv->user;
 }
 
 static void
-set_env_from_authentication (Session *session, PAMSession *authentication)
+write_data (Session *session, const void *buf, size_t count)
 {
-    gchar **pam_env;
-
-    pam_env = pam_session_get_envlist (authentication);
-    if (pam_env)
-    {
-        gchar *env_string;      
-        int i;
-
-        env_string = g_strjoinv (" ", pam_env);
-        g_debug ("PAM returns environment '%s'", env_string);
-        g_free (env_string);
-
-        for (i = 0; pam_env[i]; i++)
-        {
-            gchar **pam_env_vars = g_strsplit (pam_env[i], "=", 2);
-            if (pam_env_vars && pam_env_vars[0] && pam_env_vars[1])
-                session_set_env (session, pam_env_vars[0], pam_env_vars[1]);
-            else
-                g_warning ("Can't parse PAM environment variable %s", pam_env[i]);
-            g_strfreev (pam_env_vars);
-        }
-        g_strfreev (pam_env);
-    }
+    if (write (session->priv->to_child_input, buf, count) != count)
+        g_warning ("Error writing to session: %s", strerror (errno));
 }
 
-void
-session_set_env (Session *session, const gchar *name, const gchar *value)
+static void
+write_string (Session *session, const char *value)
 {
-    g_return_if_fail (session != NULL);
-    g_return_if_fail (name != NULL);
-    process_set_env (PROCESS (session), name, value);
+    int length;
+
+    length = value ? strlen (value) : -1;
+    write_data (session, &length, sizeof (length));
+    if (value)
+        write_data (session, value, sizeof (char) * length);
 }
 
-const gchar *
-session_get_env (Session *session, const gchar *name)
+static ssize_t
+read_from_child (Session *session, void *buf, size_t count)
 {
-    g_return_val_if_fail (session != NULL, NULL);
-    g_return_val_if_fail (name != NULL, NULL);
-    return process_get_env (PROCESS (session), name);
+    ssize_t n_read;
+    n_read = read (session->priv->from_child_output, buf, count);
+    if (n_read < 0)
+        g_warning ("Error reading from session: %s", strerror (errno));
+    return n_read;
 }
 
-void
-session_set_console_kit_parameter (Session *session, const gchar *name, GVariant *value)
+static gchar *
+read_string_from_child (Session *session)
 {
-    g_return_if_fail (session != NULL);
-    g_return_if_fail (name != NULL);
+    int length;
+    char *value;
+
+    if (read_from_child (session, &length, sizeof (length)) <= 0)
+        return NULL;
+    if (length < 0)
+        return NULL;
+    if (length > MAX_STRING_LENGTH)
+    {
+        g_warning ("Invalid string length %d from child", length);
+        return NULL;
+    }
+  
+    value = g_malloc (sizeof (char) * (length + 1));
+    read_from_child (session, value, length);
+    value[length] = '\0';      
 
-    g_hash_table_insert (session->priv->console_kit_parameters,
-                         g_strdup (name), g_variant_ref_sink (value));
+    return value;
 }
 
-const gchar *
-session_get_console_kit_cookie (Session *session)
+static void
+session_watch_cb (GPid pid, gint status, gpointer data)
 {
-    g_return_val_if_fail (session != NULL, NULL);
-    return session->priv->console_kit_cookie;
+    Session *session = data;
+
+    session->priv->pid = 0;
+
+    if (WIFEXITED (status))
+        g_debug ("Session %d exited with return value %d", pid, WEXITSTATUS (status));
+    else if (WIFSIGNALED (status))
+        g_debug ("Session %d terminated with signal %d", pid, WTERMSIG (status));
+
+    /* If failed during authentication then report this as an authentication failure */
+    if (session->priv->authentication_started && !session->priv->authentication_complete)
+    {
+        g_debug ("Session %d failed during authentication", pid);
+        session->priv->authentication_complete = TRUE;
+        session->priv->authentication_result = PAM_CONV_ERR;
+        g_free (session->priv->authentication_result_string);
+        session->priv->authentication_result_string = g_strdup ("Authentication stopped before completion");
+        g_signal_emit (G_OBJECT (session), signals[AUTHENTICATION_COMPLETE], 0);      
+    } 
+
+    g_signal_emit (G_OBJECT (session), signals[STOPPED], 0);
 }
 
-/* Set the LANG variable based on the chosen locale.  This is not a great
- * solution, as it will override the locale set in PAM (which is where it
- * should be set).  In the case of Ubuntu these will be overridden by setting
- * these variables in ~/.profile */
-static void
-set_locale (Session *session)
+static gboolean
+from_child_cb (GIOChannel *source, GIOCondition condition, gpointer data)
 {
-    User *user;
-    const gchar *locale;
+    Session *session = data;
+    gchar *username;
+    ssize_t n_read;
+    gboolean auth_complete;
 
-    user = pam_session_get_user (session->priv->authentication);
-    locale = user_get_locale (user);
-    if (locale)
+    /* Remote end gone */
+    if (condition == G_IO_HUP)
     {
-        g_debug ("Using locale %s", locale);
-        session_set_env (session, "LANG", locale);
+        session->priv->from_child_watch = 0;
+        return FALSE;
     }
-}
 
-/* Insert our own utility directory to PATH
- * This is to provide gdmflexiserver which provides backwards compatibility
- * with GDM.
- * Must be done after set_env_from_authentication because PAM sets PATH.
- * This can be removed when this is no longer required.
- */
-static void
-insert_utility_path (Session *session)
-{
-    const gchar *orig_path;
+    /* Get the username currently being authenticated (may change during authentication) */
+    username = read_string_from_child (session);
+    if (g_strcmp0 (username, session->priv->username) != 0)
+    {
+        g_free (session->priv->username);
+        session->priv->username = username;
+        if (session->priv->user)
+            g_object_unref (session->priv->user);
+        session->priv->user = NULL;
+    }
+    else
+        g_free (username);
 
-    orig_path = session_get_env (session, "PATH");
-    if (orig_path)
+    /* Check if authentication completed */
+    n_read = read_from_child (session, &auth_complete, sizeof (auth_complete));
+    if (n_read < 0)
+        g_debug ("Error reading from child: %s", strerror (errno));
+    if (n_read <= 0)
     {
-        gchar *path = g_strdup_printf ("%s:%s", PKGLIBEXEC_DIR, orig_path);
-        session_set_env (session, "PATH", path);
-        g_free (path);
+        session->priv->from_child_watch = 0;
+        return FALSE;
     }
-}
 
-gboolean
-session_start (Session *session)
-{
-    User *user;
+    if (auth_complete)
+    {
+        session->priv->authentication_complete = TRUE;
+        read_from_child (session, &session->priv->authentication_result, sizeof (session->priv->authentication_result));
+        g_free (session->priv->authentication_result_string);
+        session->priv->authentication_result_string = read_string_from_child (session);
 
-    g_return_val_if_fail (session != NULL, FALSE);
-    g_return_val_if_fail (session->priv->authentication != NULL, FALSE);
-    g_return_val_if_fail (session->priv->command != NULL, FALSE);
+        g_debug ("Session %d authentication complete with return value %d: %s", session->priv->pid, session->priv->authentication_result, session->priv->authentication_result_string);
 
-    g_debug ("Launching session");
+        /* No longer expect any more messages */
+        session->priv->from_child_watch = 0;
 
-    user = pam_session_get_user (session->priv->authentication);
-  
-    /* Set POSIX variables */
-    session_set_env (session, "PATH", "/usr/local/bin:/usr/bin:/bin");
-    session_set_env (session, "USER", user_get_name (user));
-    session_set_env (session, "LOGNAME", user_get_name (user));
-    session_set_env (session, "HOME", user_get_home_directory (user));
-    session_set_env (session, "SHELL", user_get_shell (user));
+        g_signal_emit (G_OBJECT (session), signals[AUTHENTICATION_COMPLETE], 0);
+
+        return FALSE;
+    }
+    else
+    {
+        int i;
+
+        session->priv->messages_length = 0;
+        read_from_child (session, &session->priv->messages_length, sizeof (session->priv->messages_length));
+        session->priv->messages = calloc (session->priv->messages_length, sizeof (struct pam_message));
+        for (i = 0; i < session->priv->messages_length; i++)
+        {
+            struct pam_message *m = &session->priv->messages[i];
+            read_from_child (session, &m->msg_style, sizeof (m->msg_style));          
+            m->msg = read_string_from_child (session);
+        }
 
-    return SESSION_GET_CLASS (session)->start (session);
+        g_debug ("Session %d got %d message(s) from PAM", session->priv->pid, session->priv->messages_length);
+
+        g_signal_emit (G_OBJECT (session), signals[GOT_MESSAGES], 0);
+    }    
+
+    return TRUE;
 }
 
-static gboolean
-session_real_start (Session *session)
+gboolean
+session_start (Session *session, const gchar *service, const gchar *username, gboolean do_authenticate, gboolean is_interactive)
 {
-    gboolean result;
-    gchar *absolute_command;
+    int version;
+    int to_child_pipe[2], from_child_pipe[2];
+    int to_child_output, from_child_input;
+
+    g_return_val_if_fail (session != NULL, FALSE);
+    g_return_val_if_fail (service != NULL, FALSE);
+    g_return_val_if_fail (session->priv->pid == 0, FALSE);
 
-    absolute_command = get_absolute_command (session->priv->command);
-    if (!absolute_command)
+    /* Create pipes to talk to the child */
+    if (pipe (to_child_pipe) < 0 || pipe (from_child_pipe) < 0)
     {
-        g_debug ("Can't launch session %s, not found in path", session->priv->command);
+        g_warning ("Failed to create pipe to communicated with session process: %s", strerror (errno));
         return FALSE;
     }
-    process_set_command (PROCESS (session), absolute_command);
-    g_free (absolute_command);
-
-    pam_session_open (session->priv->authentication);
-
-    /* Open ConsoleKit session */
-    if (getuid () == 0)
+    to_child_output = to_child_pipe[0];
+    session->priv->to_child_input = to_child_pipe[1];
+    session->priv->from_child_output = from_child_pipe[0];
+    from_child_input = from_child_pipe[1];
+    session->priv->from_child_channel = g_io_channel_unix_new (session->priv->from_child_output);
+    session->priv->from_child_watch = g_io_add_watch (session->priv->from_child_channel, G_IO_IN | G_IO_HUP, from_child_cb, session);
+
+    /* Don't allow the daemon end of the pipes to be accessed in child processes */
+    fcntl (session->priv->to_child_input, F_SETFD, FD_CLOEXEC);
+    fcntl (session->priv->from_child_output, F_SETFD, FD_CLOEXEC);
+
+    /* Remember what username we started with - it will be updated by PAM during authentication */
+    session->priv->username = g_strdup (username);
+
+    /* Run the child */
+    session->priv->pid = fork ();
+    if (session->priv->pid < 0)
     {
-        GVariantBuilder parameters;
-        User *user;
-        GHashTableIter iter;
-        gpointer key, value;
-
-        user = pam_session_get_user (session->priv->authentication);
-
-        g_variant_builder_init (&parameters, G_VARIANT_TYPE ("(a(sv))"));
-        g_variant_builder_open (&parameters, G_VARIANT_TYPE ("a(sv)"));
-        g_variant_builder_add (&parameters, "(sv)", "unix-user", g_variant_new_int32 (user_get_uid (user)));
-        if (session->priv->is_greeter)
-            g_variant_builder_add (&parameters, "(sv)", "session-type", g_variant_new_string ("LoginWindow"));
-        g_hash_table_iter_init (&iter, session->priv->console_kit_parameters);
-        while (g_hash_table_iter_next (&iter, &key, &value))
-            g_variant_builder_add (&parameters, "(sv)", (gchar *) key, (GVariant *) value);
-
-        g_free (session->priv->console_kit_cookie);
-        session->priv->console_kit_cookie = ck_open_session (&parameters);
+        g_debug ("Failed to fork session child process: %s", strerror (errno));
+        return FALSE;
     }
-    else
+    if (session->priv->pid == 0)
     {
-        g_free (session->priv->console_kit_cookie);
-        session->priv->console_kit_cookie = g_strdup (g_getenv ("XDG_SESSION_COOKIE"));
+        /* Run us again in session child mode */
+        execlp ("lightdm",
+                "lightdm",
+                "--session-child",
+                g_strdup_printf ("%d", to_child_output),
+                g_strdup_printf ("%d", from_child_input),
+                NULL);
+        _exit (EXIT_FAILURE);
     }
 
-    if (session->priv->console_kit_cookie)
-        session_set_env (session, "XDG_SESSION_COOKIE", session->priv->console_kit_cookie);
+    /* Listen for session termination */
+    session->priv->authentication_started = TRUE;
+    session->priv->child_watch = g_child_watch_add (session->priv->pid, session_watch_cb, session);
 
-    if (!SESSION_GET_CLASS (session)->setup (session))
-        return FALSE;
-
-    result = process_start (PROCESS (session));
+    /* Close the ends of the pipes we don't need */
+    close (to_child_output);
+    close (from_child_input);
   
-    if (!result)
+    /* Indicate what version of the protocol we are using */
+    version = 0;
+    write_data (session, &version, sizeof (version));
+
+    /* Send configuration */
+    write_string (session, service);
+    write_string (session, username);
+    write_data (session, &do_authenticate, sizeof (do_authenticate));
+    write_data (session, &is_interactive, sizeof (is_interactive));
+    write_string (session, session->priv->class);
+    write_string (session, session->priv->tty);
+    write_string (session, session->priv->remote_host_name);
+    write_string (session, session->priv->xdisplay);
+    if (session->priv->xauthority)
     {
-        pam_session_close (session->priv->authentication);
-        if (getuid () == 0 && session->priv->console_kit_cookie)
-            ck_close_session (session->priv->console_kit_cookie);
-    }  
+        guint16 family;
+        gsize length;
+
+        write_string (session, xauth_get_authorization_name (session->priv->xauthority));
+        family = xauth_get_family (session->priv->xauthority);
+        write_data (session, &family, sizeof (family));
+        length = xauth_get_address_length (session->priv->xauthority);
+        write_data (session, &length, sizeof (length));
+        write_data (session, xauth_get_address (session->priv->xauthority), length);
+        write_string (session, xauth_get_number (session->priv->xauthority));
+        length = xauth_get_authorization_data_length (session->priv->xauthority);
+        write_data (session, &length, sizeof (length));
+        write_data (session, xauth_get_authorization_data (session->priv->xauthority), length);
+    }
+    else
+        write_string (session, NULL);    
 
-    return result;
+    g_debug ("Started session %d with service '%s', username '%s'", session->priv->pid, service, username);
+
+    return TRUE;
 }
 
-void
-session_lock (Session *session)
-{    
-    g_return_if_fail (session != NULL);
-    if (getuid () == 0)
-        ck_lock_session (session->priv->console_kit_cookie);
+const gchar *
+session_get_username (Session *session)
+{
+    g_return_val_if_fail (session != NULL, NULL);
+    return session->priv->username;
 }
 
-void
-session_unlock (Session *session)
-{    
-    g_return_if_fail (session != NULL);
-    if (getuid () == 0)
-        ck_unlock_session (session->priv->console_kit_cookie);
+const gchar *
+session_get_console_kit_cookie (Session *session)
+{
+    g_return_val_if_fail (session != NULL, NULL);
+    return session->priv->console_kit_cookie;
 }
 
 void
-session_stop (Session *session)
+session_respond (Session *session, struct pam_response *response)
 {
+    int i;
+
     g_return_if_fail (session != NULL);
 
-    if (process_get_is_running (PROCESS (session)))
+    for (i = 0; i < session->priv->messages_length; i++)
     {
-        SESSION_GET_CLASS (session)->cleanup (session);
-        process_signal (PROCESS (session), SIGTERM);
+        int error = PAM_SUCCESS;
+        write_data (session, &error, sizeof (error));
+        write_string (session, response[i].resp);
+        write_data (session, &response[i].resp_retcode, sizeof (response[i].resp_retcode));
     }
+
+    /* Delete the old messages */
+    for (i = 0; i < session->priv->messages_length; i++)
+        g_free ((char *) session->priv->messages[i].msg);
+    g_free (session->priv->messages);
+    session->priv->messages = NULL;
+    session->priv->messages_length = 0;
 }
 
-gboolean
-session_get_is_stopped (Session *session)
+void
+session_respond_error (Session *session, int error)
 {
-    g_return_val_if_fail (session != NULL, TRUE);
-    return !process_get_is_running (PROCESS (session));
+    g_return_if_fail (session != NULL);
+    g_return_if_fail (error != PAM_SUCCESS);
+
+    write_data (session, &error, sizeof (error));  
 }
 
-static gboolean
-session_setup (Session *session)
+int
+session_get_messages_length (Session *session)
 {
-    return TRUE;
+    g_return_val_if_fail (session != NULL, 0);
+    return session->priv->messages_length;
 }
 
-static void
-session_cleanup (Session *session)
+const struct pam_message *
+session_get_messages (Session *session)
 {
+    g_return_val_if_fail (session != NULL, NULL);
+    return session->priv->messages;
 }
 
-static void
-setup_log_file (Session *session)
+gboolean
+session_get_is_authenticated (Session *session)
 {
-    int fd;
-
-    /* Redirect output to logfile */
-    if (!session->priv->log_file)
-        return;
-
-    fd = g_open (session->priv->log_file, O_WRONLY | O_CREAT | O_TRUNC, 0600);
-    if (fd < 0)
-        g_warning ("Failed to open log file %s: %s", session->priv->log_file, g_strerror (errno));
-    else
-    {
-        dup2 (fd, STDERR_FILENO);
-        close (fd);
-    }
+    g_return_val_if_fail (session != NULL, FALSE);
+    return session->priv->authentication_complete && session->priv->authentication_result == PAM_SUCCESS;
 }
 
-static void
-session_run (Process *process)
+int
+session_get_authentication_result (Session *session)
 {
-    Session *session = SESSION (process);
-    User *user;
-    int fd;
+    g_return_val_if_fail (session != NULL, 0);
+    return session->priv->authentication_result;
+}
 
-    /* No input and output */
-    fd = g_open ("/dev/null", O_RDONLY);
-    dup2 (fd, STDIN_FILENO);
-    dup2 (fd, STDOUT_FILENO);
-    close (fd);
+const gchar *
+session_get_authentication_result_string (Session *session)
+{
+    g_return_val_if_fail (session != NULL, NULL);
+    return session->priv->authentication_result_string;
+}
 
-    /* Redirect output to logfile */
-    if (!session->priv->log_file_as_user)
-        setup_log_file (session);
+void
+session_run (Session *session, gchar **argv)
+{
+    gsize i, argc;
+    gchar *command, *filename;
+    GList *link;
 
-    /* Make this process its own session */
-    if (setsid () < 0)
-        g_warning ("Failed to make process a new session: %s", strerror (errno));
+    g_return_if_fail (session != NULL);
+    g_return_if_fail (session_get_is_authenticated (session));
 
-    user = pam_session_get_user (session->priv->authentication);
+    command = g_strjoinv (" ", argv);
+    g_debug ("Session %d running command %s", session->priv->pid, command);
+    g_free (command);
 
-    /* Change to this user */
-    if (getuid () == 0)
+    /* Create authority location */
+    if (session->priv->xauth_use_system_location)
     {
-        if (initgroups (user_get_name (user), user_get_gid (user)) < 0)
-        {
-            g_warning ("Failed to initialize supplementary groups for %s: %s", user_get_name (user), strerror (errno));
-            _exit (EXIT_FAILURE);
-        }
+        gchar *run_dir, *dir;
 
-        if (setgid (user_get_gid (user)) != 0)
-        {
-            g_warning ("Failed to set group ID to %d: %s", user_get_gid (user), strerror (errno));
-            _exit (EXIT_FAILURE);
-        }
+        run_dir = config_get_string (config_get_instance (), "LightDM", "run-directory");          
+        dir = g_build_filename (run_dir, session->priv->username, NULL);
+        g_free (run_dir);
 
-        if (setuid (user_get_uid (user)) != 0)
+        g_mkdir_with_parents (dir, S_IRWXU);
+        if (getuid () == 0)
         {
-            g_warning ("Failed to set user ID to %d: %s", user_get_uid (user), strerror (errno));
-            _exit (EXIT_FAILURE);
+            if (chown (dir, user_get_uid (session_get_user (session)), user_get_gid (session_get_user (session))) < 0)
+                g_warning ("Failed to set ownership of user authority dir: %s", strerror (errno));
         }
-    }
 
-    /* Change working directory */
-    /* NOTE: This must be done after the permissions are changed because NFS filesystems can
-     * be setup so the local root user accesses the NFS files as 'nobody'.  If the home directories
-     * are not system readable then the chdir can fail */
-    if (chdir (user_get_home_directory (user)) != 0)
-    {
-        g_warning ("Failed to change to home directory %s: %s", user_get_home_directory (user), strerror (errno));
-        _exit (EXIT_FAILURE);
+        filename = g_build_filename (dir, "xauthority", NULL);
+        g_free (dir);
     }
+    else
+        filename = g_build_filename (user_get_home_directory (session_get_user (session)), ".Xauthority", NULL);
+
+    write_string (session, session->priv->log_filename);
+    write_string (session, filename);
+    g_free (filename);
+    argc = g_list_length (session->priv->env);
+    write_data (session, &argc, sizeof (argc));
+    for (link = session->priv->env; link; link = link->next)
+        write_string (session, (gchar *) link->data);
+    argc = g_strv_length (argv);
+    write_data (session, &argc, sizeof (argc));
+    for (i = 0; i < argc; i++)
+        write_string (session, argv[i]);
+
+    session->priv->console_kit_cookie = read_string_from_child (session);
+}
 
-    /* Redirect output to logfile */
-    if (session->priv->log_file_as_user)
-        setup_log_file (session);
-
-    /* Do PAM actions requiring session process */
-    pam_session_setup (session->priv->authentication);
-    set_env_from_authentication (session, session->priv->authentication);
-    set_locale (session);
-    insert_utility_path (session);
+void
+session_lock (Session *session)
+{    
+    g_return_if_fail (session != NULL);
+    if (getuid () == 0)
+        ck_lock_session (session->priv->console_kit_cookie);
+}
 
-    PROCESS_CLASS (session_parent_class)->run (process);
+void
+session_unlock (Session *session)
+{    
+    g_return_if_fail (session != NULL);
+    if (getuid () == 0)
+        ck_unlock_session (session->priv->console_kit_cookie);
 }
 
-static void
-session_stopped (Process *process)
+void
+session_stop (Session *session)
 {
-    Session *session = SESSION (process);
-
-    pam_session_close (session->priv->authentication);
-    if (getuid () == 0 && session->priv->console_kit_cookie)
-        ck_close_session (session->priv->console_kit_cookie);
+    g_return_if_fail (session != NULL);
+  
+    if (session->priv->pid > 0)
+    {
+        g_debug ("Session %d: Sending SIGTERM", session->priv->pid);
+        kill (session->priv->pid, SIGTERM);
+        // FIXME: Handle timeout
+    }
+}
 
-    PROCESS_CLASS (session_parent_class)->stopped (process);
+gboolean
+session_get_is_stopped (Session *session)
+{
+    g_return_val_if_fail (session != NULL, TRUE);
+    return session->priv->pid == 0;
 }
 
 static void
 session_init (Session *session)
 {
     session->priv = G_TYPE_INSTANCE_GET_PRIVATE (session, SESSION_TYPE, SessionPrivate);
-    session->priv->console_kit_parameters = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
-    process_set_clear_environment (PROCESS (session), TRUE);
 }
 
 static void
 session_finalize (GObject *object)
 {
-    Session *self;
-
-    self = SESSION (object);
-
-    g_free (self->priv->log_file);
-    if (self->priv->authentication)
-        g_object_unref (self->priv->authentication);
-    g_free (self->priv->command);
-    g_hash_table_unref (self->priv->console_kit_parameters);
+    Session *self = SESSION (object);
+    int i;
+  
+    if (self->priv->pid)
+        kill (self->priv->pid, SIGKILL);
+    if (self->priv->from_child_channel)
+        g_io_channel_unref (self->priv->from_child_channel);
+    if (self->priv->from_child_watch)
+        g_source_remove (self->priv->from_child_watch);
+    if (self->priv->child_watch)
+        g_source_remove (self->priv->child_watch);
+    g_free (self->priv->username);
+    if (self->priv->user)
+        g_object_unref (self->priv->user);
+    for (i = 0; i < self->priv->messages_length; i++)
+        g_free ((char *) self->priv->messages[i].msg);
+    g_free (self->priv->messages);
+    g_free (self->priv->authentication_result_string);
+    g_free (self->priv->log_filename);
+    g_free (self->priv->class);
+    g_free (self->priv->tty);
+    g_free (self->priv->xdisplay);
+    if (self->priv->xauthority)
+        g_object_unref (self->priv->xauthority);
+    g_free (self->priv->remote_host_name);
     g_free (self->priv->console_kit_cookie);
+    g_list_free_full (self->priv->env, g_free);
 
     G_OBJECT_CLASS (session_parent_class)->finalize (object);
 }
@@ -497,14 +619,35 @@ static void
 session_class_init (SessionClass *klass)
 {
     GObjectClass *object_class = G_OBJECT_CLASS (klass);
-    ProcessClass *process_class = PROCESS_CLASS (klass);
 
-    klass->start = session_real_start;
-    klass->setup = session_setup;
-    klass->cleanup = session_cleanup;
-    process_class->run = session_run;
-    process_class->stopped = session_stopped;
     object_class->finalize = session_finalize;
 
     g_type_class_add_private (klass, sizeof (SessionPrivate));
+
+    signals[GOT_MESSAGES] =
+        g_signal_new ("got-messages",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (SessionClass, got_messages),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0);
+
+    signals[AUTHENTICATION_COMPLETE] =
+        g_signal_new ("authentication-complete",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (SessionClass, authentication_complete),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0);
+
+    signals[STOPPED] =
+        g_signal_new ("stopped",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (SessionClass, stopped),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0);
 }
index b9e11e7d5210d8f0e72ef280dd544fc79518567d..a574726ad40a615289697837458cf01fd61a3a93 100644 (file)
 #ifndef _SESSION_H_
 #define _SESSION_H_
 
-#include "process.h"
+#include <glib-object.h>
+
+#include <security/pam_appl.h>
+
 #include "accounts.h"
-#include "pam-session.h"
+#include "xauthority.h"
 
 G_BEGIN_DECLS
 
@@ -27,48 +30,63 @@ typedef struct SessionPrivate SessionPrivate;
 
 typedef struct
 {
-    Process         parent_instance;
+    GObject         parent_instance;
     SessionPrivate *priv;
 } Session;
 
 typedef struct
 {
-    ProcessClass parent_class;
+    GObjectClass parent_class;
 
-    gboolean (*start)(Session *session);
-    gboolean (*setup)(Session *session);
-    void     (*cleanup)(Session *session);
+    void (*got_messages)(Session *session);
+    void (*authentication_complete)(Session *session);
+    void (*stopped)(Session *session);
 } SessionClass;
 
+#define XDG_SESSION_CLASS_USER        "user"
+#define XDG_SESSION_CLASS_GREETER     "greeter"
+#define XDG_SESSION_CLASS_LOCK_SCREEN "lock-screen"
+
 GType session_get_type (void);
 
-void session_set_log_file (Session *session, const gchar *filename, gboolean as_user);
+void session_set_log_file (Session *session, const gchar *filename);
+
+void session_set_class (Session *session, const gchar *class);
+
+void session_set_tty (Session *session, const gchar *tty);
 
-const gchar *session_get_log_file (Session *session);
+void session_set_xdisplay (Session *session, const gchar *xdisplay);
 
-void session_set_authentication (Session *session, PAMSession *authentication);
+void session_set_xauthority (Session *session, XAuthority *authority, gboolean use_system_location);
 
-PAMSession *session_get_authentication (Session *session);
+void session_set_remote_host_name (Session *session, const gchar *remote_host_name);
 
+void session_set_env (Session *session, const gchar *name, const gchar *value);
+
+// FIXME: Remove
 User *session_get_user (Session *session);
 
-void session_set_is_greeter (Session *session, gboolean is_greeter);
+gboolean session_start (Session *session, const gchar *service, const gchar *username, gboolean do_authenticate, gboolean is_interactive);
 
-gboolean session_get_is_greeter (Session *session);
+const gchar *session_get_username (Session *session);
 
-void session_set_command (Session *session, const gchar *command);
+const gchar *session_get_console_kit_cookie (Session *session);
 
-const gchar *session_get_command (Session *session);
+void session_respond (Session *session, struct pam_response *response);
 
-void session_set_env (Session *process, const gchar *name, const gchar *value);
+void session_respond_error (Session *session, int error);
 
-const gchar *session_get_env (Session *session, const gchar *name);
+int session_get_messages_length (Session *session);
 
-void session_set_console_kit_parameter (Session *session, const gchar *name, GVariant *value);
+const struct pam_message *session_get_messages (Session *session);
 
-const gchar *session_get_console_kit_cookie (Session *session);
+gboolean session_get_is_authenticated (Session *session);
+
+int session_get_authentication_result (Session *session);
+
+const gchar *session_get_authentication_result_string (Session *session);
 
-gboolean session_start (Session *session);
+void session_run (Session *session, gchar **argv);
 
 void session_lock (Session *session);
 
index 1b61e4a2446fd133c5f2ea111f69eae073df9028..0c0b99dfd42ecd4539e7ea2d7b903311695bac27 100644 (file)
@@ -22,11 +22,6 @@ struct XSessionPrivate
 {
     /* X server connected to */
     XServer *xserver;
-
-    /* X Authority */
-    gboolean authority_in_system_dir;
-    XAuthority *authority;
-    GFile *authority_file;
 };
 
 G_DEFINE_TYPE (XSession, xsession, SESSION_TYPE);
@@ -34,128 +29,21 @@ G_DEFINE_TYPE (XSession, xsession, SESSION_TYPE);
 XSession *
 xsession_new (XServer *xserver)
 {
-    XSession *session = g_object_new (XSESSION_TYPE, NULL);
+    XSession *session;
+    XAuthority *authority;
 
+    session = g_object_new (XSESSION_TYPE, NULL);
     session->priv->xserver = g_object_ref (xserver);
 
-    return session;
-}
-
-static gboolean
-xsession_start (Session *session)
-{
-    XSession *xsession = XSESSION (session);
-    PAMSession *authentication;
-    gchar *hostname;
-
-    authentication = session_get_authentication (session);
-    pam_session_set_item (authentication, PAM_TTY, xserver_get_address (xsession->priv->xserver));
-
-    session_set_console_kit_parameter (session, "x11-display", g_variant_new_string (xserver_get_address (xsession->priv->xserver)));
-    hostname = xserver_get_hostname (xsession->priv->xserver);
-    if (hostname)
-    {
-        session_set_console_kit_parameter (session, "remote-host-name", g_variant_new_string (hostname));
-        session_set_console_kit_parameter (session, "is-local", g_variant_new_boolean (FALSE));
-    }
-
-    session_set_env (session, "DISPLAY", xserver_get_address (xsession->priv->xserver));
-
-    return SESSION_CLASS (xsession_parent_class)->start (session);
-}
-
-static gboolean
-xsession_setup (Session *session)
-{
-    XSession *xsession = XSESSION (session);
-
-    if (xserver_get_authority (xsession->priv->xserver))
-    {
-        gchar *path;
-        gboolean drop_privileges, result;
-        GError *error = NULL;
-
-        xsession->priv->authority = g_object_ref (xserver_get_authority (xsession->priv->xserver));
-      
-        xsession->priv->authority_in_system_dir = config_get_boolean (config_get_instance (), "LightDM", "user-authority-in-system-dir");
-        if (xsession->priv->authority_in_system_dir)
-        {
-            gchar *run_dir, *dir;
-
-            run_dir = config_get_string (config_get_instance (), "LightDM", "run-directory");          
-            dir = g_build_filename (run_dir, user_get_name (session_get_user (session)), NULL);
-            g_free (run_dir);
-
-            g_mkdir_with_parents (dir, S_IRWXU);
-            if (getuid () == 0)
-            {
-                if (chown (dir, user_get_uid (session_get_user (session)), user_get_gid (session_get_user (session))) < 0)
-                    g_warning ("Failed to set ownership of user authority dir: %s", strerror (errno));
-            }
-
-            path = g_build_filename (dir, "xauthority", NULL);
-            g_free (dir);
-        }
-        else
-            path = g_build_filename (user_get_home_directory (session_get_user (session)), ".Xauthority", NULL);
-
-        session_set_env (session, "XAUTHORITY", path);
-        xsession->priv->authority_file = g_file_new_for_path (path);
-
-        drop_privileges = geteuid () == 0;
-        if (drop_privileges)
-            privileges_drop (session_get_user (SESSION (session)));
-        g_debug ("Adding session authority to %s", path);
-        result = xauth_write (xsession->priv->authority, XAUTH_WRITE_MODE_REPLACE, xsession->priv->authority_file, &error);
-        if (drop_privileges)
-            privileges_reclaim ();
-        if (error)
-            g_warning ("Failed to write authority: %s", error->message);
-        g_clear_error (&error); 
-        g_free (path);
-
-        if (!result)
-            return FALSE;
-    }
-
-    return SESSION_CLASS (xsession_parent_class)->setup (session);  
-}
+    session_set_env (SESSION (session), "DISPLAY", xserver_get_address (xserver));
+    session_set_tty (SESSION (session), xserver_get_address (xserver));
+    session_set_xdisplay (SESSION (session), xserver_get_address (xserver));
+    authority = xserver_get_authority (xserver);
+    if (authority)
+        session_set_xauthority (SESSION (session), authority, config_get_boolean (config_get_instance (), "LightDM", "user-authority-in-system-dir"));
+    session_set_log_file (SESSION (session), ".xsession-errors");
 
-static void
-xsession_remove_authority (XSession *session)
-{
-    if (session->priv->authority_file)
-    {
-        gboolean drop_privileges;
-        gchar *path;
-      
-        drop_privileges = geteuid () == 0;
-        if (drop_privileges)
-            privileges_drop (session_get_user (SESSION (session)));
-
-        path = g_file_get_path (session->priv->authority_file);
-        g_debug ("Removing session authority from %s", path);
-        g_free (path);
-        xauth_write (session->priv->authority, XAUTH_WRITE_MODE_REMOVE, session->priv->authority_file, NULL);
-
-        if (drop_privileges)
-            privileges_reclaim ();
-
-        g_object_unref (session->priv->authority_file);
-        session->priv->authority_file = NULL;
-    }
-    if (session->priv->authority)
-    {
-        g_object_unref (session->priv->authority);
-        session->priv->authority = NULL;
-    }
-}
-
-static void
-xsession_cleanup (Session *session)
-{
-    xsession_remove_authority (XSESSION (session));
-    SESSION_CLASS (xsession_parent_class)->cleanup (session);
+    return session;
 }
 
 static void
@@ -171,13 +59,8 @@ xsession_finalize (GObject *object)
 
     self = XSESSION (object);
 
-    xsession_remove_authority (self);
     if (self->priv->xserver)
         g_object_unref (self->priv->xserver);
-    if (self->priv->authority)
-        g_object_unref (self->priv->authority);
-    if (self->priv->authority_file)
-        g_object_unref (self->priv->authority_file);
 
     G_OBJECT_CLASS (xsession_parent_class)->finalize (object);
 }
@@ -186,11 +69,7 @@ static void
 xsession_class_init (XSessionClass *klass)
 {
     GObjectClass *object_class = G_OBJECT_CLASS (klass);
-    SessionClass *session_class = SESSION_CLASS (klass);
 
-    session_class->start = xsession_start;
-    session_class->setup = xsession_setup;
-    session_class->cleanup = xsession_cleanup;
     object_class->finalize = xsession_finalize;
 
     g_type_class_add_private (klass, sizeof (XSessionPrivate));
index 932aaa626f6e7a48a017527ddec4c4a4f4524cc9..e9a893e8938749f7c68b119ccfaa3c2530190381 100644 (file)
@@ -10,9 +10,11 @@ TESTS = \
        test-autologin \
        test-autologin-invalid-user \
        test-autologin-invalid-session \
+       test-autologin-crash-authenticate \
        test-autologin-xserver-crash \
        test-autologin-session-crash \
        test-autologin-password \
+       test-autologin-new-authtok \
        test-autologin-denied \
        test-autologin-expired \
        test-autologin-logout \
@@ -33,6 +35,7 @@ TESTS = \
        test-language \
        test-no-language \
        test-language-no-accounts-service \
+       test-login-crash-authenticate \
        test-login-gobject \
        test-login-gobject-manual \
        test-login-gobject-manual-previous-session \
@@ -90,11 +93,6 @@ TESTS = \
        test-console-kit \
        test-no-console-kit
 
-# FIXME: These fail, fixed in session-refactor3 branch
-#      test-autologin-crash-authenticate
-#      test-autologin-new-authtok
-#      test-login-crash-authenticate
-
 #      test-session-exit-error
 #      test-greeter-no-exit
 #   test-gobject-change-password