diff options
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/display.c | 736 | ||||
-rw-r--r-- | src/greeter.c | 233 | ||||
-rw-r--r-- | src/greeter.h | 11 | ||||
-rw-r--r-- | src/lightdm.c | 16 | ||||
-rw-r--r-- | src/pam-session.c | 472 | ||||
-rw-r--r-- | src/pam-session.h | 88 | ||||
-rw-r--r-- | src/seat-xdmcp-session.c | 4 | ||||
-rw-r--r-- | src/seat-xlocal.c | 3 | ||||
-rw-r--r-- | src/seat-xremote.c | 3 | ||||
-rw-r--r-- | src/seat-xvnc.c | 3 | ||||
-rw-r--r-- | src/seat.c | 3 | ||||
-rw-r--r-- | src/seat.h | 1 | ||||
-rw-r--r-- | src/session-child.c | 534 | ||||
-rw-r--r-- | src/session-child.h | 17 | ||||
-rw-r--r-- | src/session.c | 789 | ||||
-rw-r--r-- | src/session.h | 58 | ||||
-rw-r--r-- | src/xsession.c | 143 | ||||
-rw-r--r-- | tests/Makefile.am | 8 |
20 files changed, 1562 insertions, 1566 deletions
@@ -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 diff --git a/src/Makefile.am b/src/Makefile.am index b66f197e..4cf2863a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/display.c b/src/display.c index f572f3ba..da6949dd 100644 --- a/src/display.c +++ b/src/display.c @@ -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); diff --git a/src/greeter.c b/src/greeter.c index 10fc31fb..434a6bd3 100644 --- a/src/greeter.c +++ b/src/greeter.c @@ -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; @@ -601,54 +630,27 @@ read_cb (GIOChannel *source, GIOCondition condition, gpointer data) } 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) { g_return_val_if_fail (greeter != NULL, FALSE); 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", diff --git a/src/greeter.h b/src/greeter.h index 4694698e..56eea002 100644 --- a/src/greeter.h +++ b/src/greeter.h @@ -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 diff --git a/src/lightdm.c b/src/lightdm.c index c853650a..4bc5c0c1 100644 --- a/src/lightdm.c +++ b/src/lightdm.c @@ -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 index 9dead913..00000000 --- a/src/pam-session.c +++ /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 index 2f583696..00000000 --- a/src/pam-session.h +++ /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_ */ diff --git a/src/seat-xdmcp-session.c b/src/seat-xdmcp-session.c index 65a95e16..720a3704 100644 --- a/src/seat-xdmcp-session.c +++ b/src/seat-xdmcp-session.c @@ -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); } diff --git a/src/seat-xlocal.c b/src/seat-xlocal.c index 15607ea5..a1fb2e01 100644 --- a/src/seat-xlocal.c +++ b/src/seat-xlocal.c @@ -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); } diff --git a/src/seat-xremote.c b/src/seat-xremote.c index d61acc14..252f0cc4 100644 --- a/src/seat-xremote.c +++ b/src/seat-xremote.c @@ -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); } diff --git a/src/seat-xvnc.c b/src/seat-xvnc.c index de3574ae..eb150c31 100644 --- a/src/seat-xvnc.c +++ b/src/seat-xvnc.c @@ -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); } @@ -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 @@ -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 index 00000000..9ea86deb --- /dev/null +++ b/src/session-child.c @@ -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 index 00000000..39f32509 --- /dev/null +++ b/src/session-child.h @@ -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_ */ diff --git a/src/session.c b/src/session.c index a4bfd2b7..cb300d65 100644 --- a/src/session.c +++ b/src/session.c @@ -17,478 +17,600 @@ #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 (¶meters, G_VARIANT_TYPE ("(a(sv))")); - g_variant_builder_open (¶meters, G_VARIANT_TYPE ("a(sv)")); - g_variant_builder_add (¶meters, "(sv)", "unix-user", g_variant_new_int32 (user_get_uid (user))); - if (session->priv->is_greeter) - g_variant_builder_add (¶meters, "(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 (¶meters, "(sv)", (gchar *) key, (GVariant *) value); - - g_free (session->priv->console_kit_cookie); - session->priv->console_kit_cookie = ck_open_session (¶meters); + 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); } diff --git a/src/session.h b/src/session.h index b9e11e7d..a574726a 100644 --- a/src/session.h +++ b/src/session.h @@ -12,9 +12,12 @@ #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); diff --git a/src/xsession.c b/src/xsession.c index 1b61e4a2..0c0b99df 100644 --- a/src/xsession.c +++ b/src/xsession.c @@ -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)); diff --git a/tests/Makefile.am b/tests/Makefile.am index 932aaa62..e9a893e8 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 |