From 32e47bb06d8e8aa02e8643d872103b7ff998e1aa Mon Sep 17 00:00:00 2001 From: Guido Berhoerster Date: Thu, 21 Sep 2023 11:37:47 +0200 Subject: [PATCH 1/3] Add support for changing the home directory in PAM modules PAM modules such as pam_mklocaluser may change or even create the home directory. Currently, LightDM assumes that the home directory will not change when opening the PAM session, the user's home directory is obtained via getpwent() after authentication but before opening the session. Fix this by trying to update the user's home directory from the HOME environment variable from PAM after opening the session. Furthermore, if the Xauthority file is not stored in a system directory, the daemon hardcodes its path to the user's home directory and passes it as an absolute path to the session child. Fix this by passing it as a relative path so that the actual path can be constructed after the PAM session has been opened and the home directory has potentially been updated. --- src/session-child.c | 16 ++++++++++++++-- src/session.c | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/session-child.c b/src/session-child.c index 112daabe..67bcb2b8 100644 --- a/src/session-child.c +++ b/src/session-child.c @@ -538,6 +538,12 @@ session_child_run (int argc, char **argv) return EXIT_FAILURE; } + /* try to get HOME from PAM since it might have been changed */ + const gchar *home_directory = pam_getenv (pam_handle, "HOME"); + if (!home_directory) { + home_directory = user_get_home_directory (user); + } + /* Open a connection to the system bus for ConsoleKit - we must keep it open or CK will close the session */ g_autoptr(GError) error = NULL; g_autoptr(GDBusConnection) bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); @@ -602,6 +608,11 @@ session_child_run (int argc, char **argv) /* Write X authority */ if (x_authority) { + if (!g_path_is_absolute (x_authority_filename)) { + gchar *x_authority_filename_new = g_build_filename (home_directory, x_authority_filename, NULL); + g_free (x_authority_filename); + x_authority_filename = x_authority_filename_new; + } gboolean drop_privileges = geteuid () == 0; if (drop_privileges) privileges_drop (user_get_uid (user), user_get_gid (user)); @@ -629,7 +640,6 @@ session_child_run (int argc, char **argv) /* Run the command as the authenticated user */ uid_t uid = user_get_uid (user); gid_t gid = user_get_gid (user); - const gchar *home_directory = user_get_home_directory (user); child_pid = fork (); if (child_pid == 0) { @@ -651,8 +661,10 @@ session_child_run (int argc, char **argv) /* 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 (home_directory) != 0) + if (chdir (home_directory) != 0) { + g_printerr ("chdir: %s\n", strerror (errno)); _exit (errno); + } if (log_filename) { diff --git a/src/session.c b/src/session.c index fce12a0b..40bed8dd 100644 --- a/src/session.c +++ b/src/session.c @@ -812,7 +812,7 @@ session_real_run (Session *session) x_authority_filename = g_build_filename (dir, "xauthority", NULL); } else - x_authority_filename = g_build_filename (user_get_home_directory (session_get_user (session)), ".Xauthority", NULL); + x_authority_filename = g_strdup (".Xauthority"); /* Make sure shared user directory for this user exists */ if (!priv->remote_host_name) From ded06e8c75554a49999c6d698408c047faf1424d Mon Sep 17 00:00:00 2001 From: Guido Berhoerster Date: Mon, 9 Oct 2023 08:51:16 +0200 Subject: [PATCH 2/3] Return and use the potentially changed home directory from the session child Return the home directory from the session child to the daemon which may have been changed by PAM. Use the returned home directory in the daemon when running script hooks. --- src/seat.c | 12 ++++++------ src/session-child.c | 2 ++ src/session.c | 22 +++++++++++++++++++++- src/session.h | 2 ++ 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/seat.c b/src/seat.c index 998acdbb..8e09026c 100644 --- a/src/seat.c +++ b/src/seat.c @@ -365,7 +365,7 @@ seat_get_allow_guest (Seat *seat) } static gboolean -run_script (Seat *seat, DisplayServer *display_server, const gchar *script_name, User *user) +run_script (Seat *seat, DisplayServer *display_server, const gchar *script_name, User *user, const gchar *home_directory) { g_autoptr(Process) script = process_new (NULL, NULL); @@ -392,7 +392,7 @@ run_script (Seat *seat, DisplayServer *display_server, const gchar *script_name, { process_set_env (script, "USER", user_get_name (user)); process_set_env (script, "LOGNAME", user_get_name (user)); - process_set_env (script, "HOME", user_get_home_directory (user)); + process_set_env (script, "HOME", home_directory ? home_directory : user_get_home_directory (user)); } else process_set_env (script, "HOME", "/"); @@ -457,7 +457,7 @@ display_server_stopped_cb (DisplayServer *display_server, Seat *seat) /* Run a script right after stopping the display server */ const gchar *script = seat_get_string_property (seat, "display-stopped-script"); if (script) - run_script (seat, NULL, script, NULL); + run_script (seat, NULL, script, NULL, NULL); g_signal_handlers_disconnect_matched (display_server, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, seat); priv->display_servers = g_list_remove (priv->display_servers, display_server); @@ -654,7 +654,7 @@ run_session (Seat *seat, Session *session) script = seat_get_string_property (seat, "greeter-setup-script"); else script = seat_get_string_property (seat, "session-setup-script"); - if (script && !run_script (seat, session_get_display_server (session), script, session_get_user (session))) + if (script && !run_script (seat, session_get_display_server (session), script, session_get_user (session), session_get_home_directory (session))) { l_debug (seat, "Switching to greeter due to failed setup script"); switch_to_greeter_from_failed_session (seat, session); @@ -778,7 +778,7 @@ session_stopped_cb (Session *session, Seat *seat) { const gchar *script = seat_get_string_property (seat, "session-cleanup-script"); if (script) - run_script (seat, display_server, script, session_get_user (session)); + run_script (seat, display_server, script, session_get_user (session), session_get_home_directory (session)); } if (priv->stopping) @@ -1324,7 +1324,7 @@ display_server_ready_cb (DisplayServer *display_server, Seat *seat) { /* Run setup script */ const gchar *script = seat_get_string_property (seat, "display-setup-script"); - if (script && !run_script (seat, display_server, script, NULL)) + if (script && !run_script (seat, display_server, script, NULL, NULL)) { l_debug (seat, "Stopping display server due to failed setup script"); display_server_stop (display_server); diff --git a/src/session-child.c b/src/session-child.c index 67bcb2b8..c15473bc 100644 --- a/src/session-child.c +++ b/src/session-child.c @@ -543,6 +543,8 @@ session_child_run (int argc, char **argv) if (!home_directory) { home_directory = user_get_home_directory (user); } + if (version >= 4) + write_string (home_directory); /* Open a connection to the system bus for ConsoleKit - we must keep it open or CK will close the session */ g_autoptr(GError) error = NULL; diff --git a/src/session.c b/src/session.c index 40bed8dd..d0d8a235 100644 --- a/src/session.c +++ b/src/session.c @@ -59,6 +59,9 @@ typedef struct /* User to authenticate as */ gchar *username; + /* Home directory of the authenticating user */ + gchar *home_directory; + /* TRUE if is a guest account */ gboolean is_guest; @@ -644,7 +647,7 @@ session_real_start (Session *session) close (from_child_input); /* Indicate what version of the protocol we are using */ - int version = 3; + int version = 4; write_data (session, &version, sizeof (version)); /* Send configuration */ @@ -671,6 +674,14 @@ session_get_username (Session *session) return priv->username; } +const gchar * +session_get_home_directory (Session *session) +{ + SessionPrivate *priv = session_get_instance_private (session); + g_return_val_if_fail (session != NULL, NULL); + return priv->home_directory; +} + const gchar * session_get_login1_session_id (Session *session) { @@ -863,6 +874,14 @@ session_real_run (Session *session) for (gsize i = 0; i < argc; i++) write_string (session, priv->argv[i]); + /* Get the home directory of the user currently being authenticated (may change after opening PAM session) */ + g_autofree gchar *home_directory = read_string_from_child (session); + if (g_strcmp0 (home_directory, priv->home_directory) != 0) + { + g_free (priv->home_directory); + priv->home_directory = g_steal_pointer (&home_directory); + } + priv->login1_session_id = read_string_from_child (session); priv->console_kit_cookie = read_string_from_child (session); } @@ -1005,6 +1024,7 @@ session_finalize (GObject *object) if (priv->child_watch) g_source_remove (priv->child_watch); g_clear_pointer (&priv->username, g_free); + g_clear_pointer (&priv->home_directory, g_free); g_clear_object (&priv->user); g_clear_pointer (&priv->pam_service, g_free); for (size_t i = 0; i < priv->messages_length; i++) diff --git a/src/session.h b/src/session.h index e1130e83..ed9bacf4 100644 --- a/src/session.h +++ b/src/session.h @@ -118,6 +118,8 @@ gboolean session_get_is_started (Session *session); const gchar *session_get_username (Session *session); +const gchar *session_get_home_directory (Session *session); + const gchar *session_get_login1_session_id (Session *session); const gchar *session_get_console_kit_cookie (Session *session); From 5ec1cf22bcf24e3a86d39f43721f46c0f1204d5e Mon Sep 17 00:00:00 2001 From: Guido Berhoerster Date: Mon, 9 Oct 2023 13:02:09 +0200 Subject: [PATCH 3/3] Add test case for users who have their home directory changed by PAM --- tests/Makefile.am | 4 ++- tests/scripts/change-home-dir-on-session.conf | 34 +++++++++++++++++++ tests/src/libsystem.c | 21 ++++++++++++ tests/src/test-runner.c | 4 ++- tests/test-change-home-dir-on-session | 2 ++ 5 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 tests/scripts/change-home-dir-on-session.conf create mode 100755 tests/test-change-home-dir-on-session diff --git a/tests/Makefile.am b/tests/Makefile.am index 98a3acce..ac43aa1c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -256,7 +256,8 @@ TESTS = \ test-wayland-greeter \ test-wayland-session \ test-invalid-seat \ - test-seatdefaults-still-supported + test-seatdefaults-still-supported \ + test-change-home-dir-on-session # test-switch-to-greeter-return-session-repeat # test-session-exit-error @@ -389,6 +390,7 @@ EXTRA_DIST = \ scripts/autologin-timeout-logout.conf \ scripts/autologin-xserver-crash.conf \ scripts/change-authentication.conf \ + scripts/change-home-dir-on-session.conf \ scripts/cancel-authentication.conf \ scripts/console-kit.conf \ scripts/console-kit-no-xdg-runtime.conf \ diff --git a/tests/scripts/change-home-dir-on-session.conf b/tests/scripts/change-home-dir-on-session.conf new file mode 100644 index 00000000..c1e469ba --- /dev/null +++ b/tests/scripts/change-home-dir-on-session.conf @@ -0,0 +1,34 @@ +# +# Check home directory is set correctly in session after having been changed by PAM +# + +[Seat:*] +autologin-user=change-home-dir +user-session=default + +#?*START-DAEMON +#?RUNNER DAEMON-START + +# X server starts +#?XSERVER-0 START VT=7 SEAT=seat0 + +# Daemon connects when X server is ready +#?*XSERVER-0 INDICATE-READY +#?XSERVER-0 INDICATE-READY +#?XSERVER-0 ACCEPT-CONNECT + +# Session starts +#?SESSION-X-0 START XDG_SEAT=seat0 XDG_VTNR=7 XDG_GREETER_DATA_DIR=.*/change-home-dir XDG_SESSION_TYPE=x11 XDG_SESSION_DESKTOP=default USER=change-home-dir +#?LOGIN1 ACTIVATE-SESSION SESSION=c0 +#?XSERVER-0 ACCEPT-CONNECT +#?SESSION-X-0 CONNECT-XSERVER + +# Check environment variables +#?*SESSION-X-0 READ-ENV NAME=HOME +#?SESSION-X-0 READ-ENV NAME=HOME VALUE=.*/users/change-home-dir + +# Cleanup +#?*STOP-DAEMON +#?SESSION-X-0 TERMINATE SIGNAL=15 +#?XSERVER-0 TERMINATE SIGNAL=15 +#?RUNNER DAEMON-EXIT STATUS=0 diff --git a/tests/src/libsystem.c b/tests/src/libsystem.c index a15e7df5..4007ab49 100644 --- a/tests/src/libsystem.c +++ b/tests/src/libsystem.c @@ -1285,6 +1285,27 @@ pam_open_session (pam_handle_t *pamh, int flags) g_mkdir_with_parents (entry->pw_dir, 0755); } + if (strcmp (pamh->user, "change-home-dir") == 0) + { + struct passwd *entry = getpwnam (pamh->user); + + /* Actual home dir is changed by PAM, differing from passwd, strip off + trailing /home/ and replace with /users/ */ + const char *endp = pamh->user; + int slashes = 0; + while (*endp++ != '\0'); + while (slashes < 2 && endp > pamh->user) { + if (*endp-- == '/') + slashes++; + } + g_autofree gchar *changed_home = g_strdup_printf("%.*s/users/%s\n", (int)(endp - entry->pw_dir), entry->pw_dir, pamh->user); + + g_mkdir_with_parents (changed_home, 0755); + + g_autofree gchar *e = g_strdup_printf ("HOME=%s", changed_home); + pam_putenv (pamh, e); + } + /* Open logind session */ g_autoptr(GError) error = NULL; g_autoptr(GVariant) result = g_dbus_connection_call_sync (g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL), diff --git a/tests/src/test-runner.c b/tests/src/test-runner.c index 7de9694c..bfda38ef 100644 --- a/tests/src/test-runner.c +++ b/tests/src/test-runner.c @@ -2772,13 +2772,15 @@ main (int argc, char **argv) {"corrupt-xauth", "password", "Corrupt Xauthority", 1032}, /* User to test properties */ {"prop-user", "", "TEST", 1033}, + /* This account has the home directory changed by PAM during authentication */ + {"change-home-dir", "", "Change Home Dir User", 1034}, {NULL, NULL, NULL, 0} }; g_autoptr(GString) passwd_data = g_string_new (""); g_autoptr(GString) group_data = g_string_new (""); for (int i = 0; users[i].user_name; i++) { - if (strcmp (users[i].user_name, "mount-home-dir") != 0 && strcmp (users[i].user_name, "make-home-dir") != 0) + if (strcmp (users[i].user_name, "mount-home-dir") != 0 && strcmp (users[i].user_name, "make-home-dir") != 0 && strcmp (users[i].user_name, "change-home-dir") != 0) { g_autofree gchar *path = g_build_filename (home_dir, users[i].user_name, NULL); g_mkdir_with_parents (path, 0755); diff --git a/tests/test-change-home-dir-on-session b/tests/test-change-home-dir-on-session new file mode 100755 index 00000000..38d5a9db --- /dev/null +++ b/tests/test-change-home-dir-on-session @@ -0,0 +1,2 @@ +#!/bin/sh +./src/dbus-env ./src/test-runner change-home-dir-on-session test-gobject-greeter