src/greeter.c | 13 +++++++ src/session-child.c | 86 +++++++++++++++++++++++++++-------------- src/session.c | 14 +++++++ src/session.h | 2 + tests/Makefile.am | 2 + tests/scripts/language-pam.conf | 46 ++++++++++++++++++++++ tests/src/libsystem.c | 7 ++++ tests/src/test-runner.c | 2 + tests/test-language-pam | 2 + 9 files changed, 146 insertions(+), 28 deletions(-) diff --git a/src/greeter.c b/src/greeter.c index 75fe9ff6..e8083616 100644 --- a/src/greeter.c +++ b/src/greeter.c @@ -57,6 +57,9 @@ typedef struct /* Currently selected user */ gchar *active_username; + /* Currently selected language */ + gchar *language; + /* PAM session being constructed by the greeter */ Session *authentication_session; @@ -548,6 +551,10 @@ handle_authenticate (Greeter *greeter, guint32 sequence_number, const gchar *use is_interactive = TRUE; } + // Set the language here in order to run the PAM session + // with the selected locale: + session_set_language (priv->authentication_session, priv->language); + /* Run the session process */ session_set_pam_service (priv->authentication_session, service); session_set_username (priv->authentication_session, username); @@ -759,6 +766,11 @@ handle_set_language (Greeter *greeter, const gchar *language) { GreeterPrivate *priv = greeter_get_instance_private (greeter); + // Save the selected language string in order to pass it to the + // next authentication session: + g_free (priv->language); + priv->language = g_strdup (language); + if (!priv->guest_account_authenticated && !session_get_is_authenticated (priv->authentication_session)) { g_debug ("Ignoring set language request, user is not authorized"); @@ -1092,6 +1104,7 @@ greeter_finalize (GObject *object) g_hash_table_unref (priv->hints); g_clear_pointer (&priv->remote_session, g_free); g_clear_pointer (&priv->active_username, g_free); + g_clear_pointer (&priv->language, g_free); if (priv->authentication_session) { g_signal_handlers_disconnect_matched (priv->authentication_session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self); diff --git a/src/session-child.c b/src/session-child.c index 112daabe..b6752f21 100644 --- a/src/session-child.c +++ b/src/session-child.c @@ -16,6 +16,7 @@ #include #include #include +#include #if HAVE_LIBAUDIT #include @@ -239,6 +240,48 @@ audit_event (int type, const gchar *username, uid_t uid, const gchar *remote_hos } #endif +static void +apply_locale (const gchar *locale, GError **error) +{ + if (setlocale (LC_ALL, locale)) + { + static const gchar * const locale_var_names[] = { + "LC_PAPER", + "LC_NAME", + "LC_ADDRESS", + "LC_TELEPHONE", + "LC_MEASUREMENT", + "LC_IDENTIFICATION", + "LC_COLLATE", + "LC_CTYPE", + "LC_MONETARY", + "LC_NUMERIC", + "LC_TIME", + "LC_MESSAGES", + "LC_ALL", + "LANG", + NULL + }; + for (int i = 0; locale_var_names[i] != NULL; i++) + { + setenv (locale_var_names[i], locale, 1); + } + } + else + { + g_set_error_literal (error, 1, errno, strerror (errno)); + } +} + +static void +set_locale (const gchar *language, GError **error) +{ + if (language && strlen (language) > 0) + { + apply_locale (language, error); + } +} + int session_child_run (int argc, char **argv) { @@ -293,6 +336,15 @@ session_child_run (int argc, char **argv) g_autofree gchar *remote_host_name = read_string (); g_autofree gchar *xdisplay = read_string (); g_autoptr(XAuthority) x_authority = read_xauth (); + g_autofree gchar *language = read_string (); + + g_autoptr(GError) locale_err = NULL; + set_locale (language, &locale_err); + if (locale_err) + { + g_printerr ("Unable to read and set locale: %s", locale_err->message); + return EXIT_FAILURE; + } /* Setup PAM */ struct pam_conv conversation = { pam_conv_cb, NULL }; @@ -399,34 +451,12 @@ session_child_run (int argc, char **argv) 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))); - - /* Let the greeter and user session inherit the system default locale */ - static const gchar * const locale_var_names[] = { - "LC_PAPER", - "LC_NAME", - "LC_ADDRESS", - "LC_TELEPHONE", - "LC_MEASUREMENT", - "LC_IDENTIFICATION", - "LC_COLLATE", - "LC_CTYPE", - "LC_MONETARY", - "LC_NUMERIC", - "LC_TIME", - "LC_MESSAGES", - "LC_ALL", - "LANG", - NULL - }; - for (int i = 0; locale_var_names[i] != NULL; i++) - { - const gchar *locale_value; - if ((locale_value = g_getenv (locale_var_names[i])) != NULL) - { - g_autofree gchar *locale_var = g_strdup_printf ("%s=%s", locale_var_names[i], locale_value); - pam_putenv (pam_handle, locale_var); - } - } + /* Let the greeter and user session inherit the selected + or the system default locale. However, don't set + anything except the LANG var --- let's leave the other + on behalf of the environment, that is copyed from the + session configuration below. */ + pam_putenv (pam_handle, g_strdup_printf ("LANG=%s", g_getenv ("LANG"))); } } diff --git a/src/session.c b/src/session.c index fce12a0b..486a16a4 100644 --- a/src/session.c +++ b/src/session.c @@ -96,6 +96,9 @@ typedef struct XAuthority *x_authority; gboolean x_authority_use_system_location; + /* The language currently selected in the greeter */ + gchar *language; + /* Socket to allow greeters to connect to (if allowed) */ GreeterSocket *greeter_socket; @@ -278,6 +281,15 @@ session_set_x_authority (Session *session, XAuthority *authority, gboolean use_s priv->x_authority_use_system_location = use_system_location; } +void +session_set_language (Session *session, const gchar *language) +{ + SessionPrivate *priv = session_get_instance_private (session); + g_return_if_fail (session != NULL); + g_free (priv->language); + priv->language = g_strdup (language); +} + void session_set_remote_host_name (Session *session, const gchar *remote_host_name) { @@ -657,6 +669,7 @@ session_real_start (Session *session) write_string (session, priv->remote_host_name); write_string (session, priv->xdisplay); write_xauth (session, priv->x_authority); + write_string (session, priv->language); l_debug (session, "Started with service '%s', username '%s'", priv->pam_service, priv->username); @@ -1015,6 +1028,7 @@ session_finalize (GObject *object) g_clear_pointer (&priv->tty, g_free); g_clear_pointer (&priv->xdisplay, g_free); g_clear_object (&priv->x_authority); + g_clear_pointer (&priv->language, g_free); g_clear_pointer (&priv->remote_host_name, g_free); g_clear_pointer (&priv->login1_session_id, g_free); g_clear_pointer (&priv->console_kit_cookie, g_free); diff --git a/src/session.h b/src/session.h index e1130e83..2ad6aee3 100644 --- a/src/session.h +++ b/src/session.h @@ -99,6 +99,8 @@ void session_set_xdisplay (Session *session, const gchar *xdisplay); void session_set_x_authority (Session *session, XAuthority *authority, gboolean use_system_location); +void session_set_language (Session *session, const gchar *language); + 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); diff --git a/tests/Makefile.am b/tests/Makefile.am index da6305f0..9649cf2b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -88,6 +88,7 @@ TESTS = \ test-language \ test-language-no-accounts-service \ test-language-greeter \ + test-language-pam \ test-login-crash-authenticate \ test-login-invalid-greeter \ test-login-gobject \ @@ -420,6 +421,7 @@ EXTRA_DIST = \ scripts/language-env.conf \ scripts/language-no-accounts-service.conf \ scripts/language-greeter.conf \ + scripts/language-pam.conf \ scripts/lock-seat.conf \ scripts/lock-seat-after-vt-switch.conf \ scripts/lock-seat-console-kit.conf \ diff --git a/tests/scripts/language-pam.conf b/tests/scripts/language-pam.conf new file mode 100644 index 00000000..6fc79e1c --- /dev/null +++ b/tests/scripts/language-pam.conf @@ -0,0 +1,46 @@ +# +# Checks the language is changed in PAM +# + +#?*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 + +# Greeter starts +#?GREETER-X-0 START XDG_SEAT=seat0 XDG_VTNR=7 XDG_SESSION_CLASS=greeter +#?LOGIN1 ACTIVATE-SESSION SESSION=c0 +#?XSERVER-0 ACCEPT-CONNECT +#?GREETER-X-0 CONNECT-XSERVER +#?GREETER-X-0 CONNECT-TO-DAEMON +#?GREETER-X-0 CONNECTED-TO-DAEMON + +# Start authentication +#?*GREETER-X-0 AUTHENTICATE USERNAME=show-locale +#?GREETER-X-0 SHOW-MESSAGE TEXT="" +#?GREETER-X-0 SHOW-PROMPT TEXT="Password:" + +# Cancel authentication +#?*GREETER-X-0 CANCEL-AUTHENTICATION +#?GREETER-X-0 AUTHENTICATION-COMPLETE USERNAME=show-locale AUTHENTICATED=FALSE + +# Select language +#?*GREETER-X-0 SET-LANGUAGE VALUE=ru_RU.UTF-8 +#?GREETER-X-0 SET-LANGUAGE OK + +# Start new authentication +#?*GREETER-X-0 AUTHENTICATE USERNAME=show-locale +#?GREETER-X-0 SHOW-MESSAGE TEXT="ru_RU.UTF-8" +#?GREETER-X-0 SHOW-PROMPT TEXT="Password:" + +# Cleanup +#?*STOP-DAEMON +#?GREETER-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 1c68dc10..564b07c1 100644 --- a/tests/src/libsystem.c +++ b/tests/src/libsystem.c @@ -1034,6 +1034,13 @@ pam_authenticate (pam_handle_t *pamh, int flags) msg[n_messages]->msg = "Favorite Color:"; n_messages++; } + if (strcmp (pamh->user, "show-locale") == 0) + { + msg[n_messages] = malloc (sizeof (struct pam_message)); + msg[n_messages]->msg_style = PAM_TEXT_INFO; + msg[n_messages]->msg = getenv ("LANG"); + n_messages++; + } msg[n_messages] = malloc (sizeof (struct pam_message)); msg[n_messages]->msg_style = PAM_PROMPT_ECHO_OFF; msg[n_messages]->msg = "Password:"; diff --git a/tests/src/test-runner.c b/tests/src/test-runner.c index 1b961961..2eaa172e 100644 --- a/tests/src/test-runner.c +++ b/tests/src/test-runner.c @@ -2644,6 +2644,8 @@ main (int argc, char **argv) {"corrupt-xauth", "password", "Corrupt Xauthority", 1032}, /* User to test properties */ {"prop-user", "", "TEST", 1033}, + /* This account shows current locale */ + {"show-locale", "password", "Show Locale", 1034}, {NULL, NULL, NULL, 0} }; g_autoptr(GString) passwd_data = g_string_new (""); diff --git a/tests/test-language-pam b/tests/test-language-pam new file mode 100755 index 00000000..024e5df5 --- /dev/null +++ b/tests/test-language-pam @@ -0,0 +1,2 @@ +#!/bin/sh +./src/dbus-env ./src/test-runner language-pam test-gobject-greeter