diff options
Diffstat (limited to 'capplets')
| -rw-r--r-- | capplets/about-me/mate-about-me-password.c | 237 |
1 files changed, 184 insertions, 53 deletions
diff --git a/capplets/about-me/mate-about-me-password.c b/capplets/about-me/mate-about-me-password.c index 6d739f6f..bf7785a8 100644 --- a/capplets/about-me/mate-about-me-password.c +++ b/capplets/about-me/mate-about-me-password.c @@ -39,9 +39,17 @@ #ifdef __sun #include <sys/types.h> #include <signal.h> +#include <fcntl.h> #endif #include "capplet-util.h" + +#define PASSWD "/usr/bin/passwd" + +#ifdef __sun +/* /dev/pts/N */ +#define PTY_MAX_NAME 20 +#endif #include "mate-about-me-password.h" /* Passwd states */ @@ -157,6 +165,28 @@ passdlg_refresh_password_state (PasswordDialog *pdialog); * Spawning and closing of backend {{ */ +#ifdef __sun +/* Child setup callback for PTY-based passwd interaction on illumos. + * Runs in the child process after fork(), before exec(). + * Only async-signal-safe functions may be called here. */ +static void +child_setup_pty (gpointer user_data) +{ + const char *slave_name = (const char *) user_data; + int pty_s; + + setsid (); + pty_s = open (slave_name, O_RDWR); + if (pty_s < 0) + _exit (1); + dup2 (pty_s, 0); + dup2 (pty_s, 1); + dup2 (pty_s, 2); + if (pty_s > 2) + close (pty_s); +} +#endif + /* Child watcher */ static void child_watch_cb (GPid pid, gint status, PasswordDialog *pdialog) @@ -177,9 +207,8 @@ spawn_passwd (PasswordDialog *pdialog, GError **error) { gchar *argv[2]; gchar *envp[1]; - gint my_stdin, my_stdout, my_stderr; - argv[0] = "/usr/bin/passwd"; /* Is it safe to rely on a hard-coded path? */ + argv[0] = PASSWD; /* Is it safe to rely on a hard-coded path? */ argv[1] = NULL; envp[0] = NULL; /* If we pass an empty array as the environment, @@ -191,58 +220,146 @@ spawn_passwd (PasswordDialog *pdialog, GError **error) * the locales here. */ - if (!g_spawn_async_with_pipes (NULL, /* Working directory */ - argv, /* Argument vector */ - envp, /* Environment */ - G_SPAWN_DO_NOT_REAP_CHILD, /* Flags */ - NULL, /* Child setup */ - NULL, /* Data to child setup */ - &pdialog->backend_pid, /* PID */ - &my_stdin, /* Stdin */ - &my_stdout, /* Stdout */ - &my_stderr, /* Stderr */ - error)) { /* GError */ - - /* An error occurred */ - free_passwd_resources (pdialog); +#ifdef __sun + /* On illumos/Solaris, passwd requires a real terminal (PTY) for + * interactive password input. Use g_spawn_async with a child_setup + * callback that attaches the child to a pseudo-terminal. */ + { + int pty_m; + char slave_name[PTY_MAX_NAME]; + char *name; + + pty_m = posix_openpt (O_RDWR | O_NOCTTY); + if (pty_m < 0) { + g_set_error (error, PASSDLG_ERROR, PASSDLG_ERROR_BACKEND, + "%s", strerror (errno)); + return FALSE; + } - return FALSE; - } + if (grantpt (pty_m) < 0) { + g_set_error (error, PASSDLG_ERROR, PASSDLG_ERROR_BACKEND, + "%s", strerror (errno)); + close (pty_m); + return FALSE; + } - /* Initialize ext_msg with NULL */ - pdialog->ext_msg = NULL; - - /* Open IO Channels */ - pdialog->backend_stdin = g_io_channel_unix_new (my_stdin); - pdialog->backend_stdout = g_io_channel_unix_new (my_stdout); - pdialog->backend_stderr = g_io_channel_unix_new (my_stderr); - - /* Set raw encoding */ - /* Set nonblocking mode */ - if (g_io_channel_set_encoding (pdialog->backend_stdin, NULL, error) != G_IO_STATUS_NORMAL || - g_io_channel_set_encoding (pdialog->backend_stdout, NULL, error) != G_IO_STATUS_NORMAL || - g_io_channel_set_encoding (pdialog->backend_stderr, NULL, error) != G_IO_STATUS_NORMAL || - g_io_channel_set_flags (pdialog->backend_stdin, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL || - g_io_channel_set_flags (pdialog->backend_stdout, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL || - g_io_channel_set_flags (pdialog->backend_stderr, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL) { - - /* Clean up */ - stop_passwd (pdialog); - return FALSE; + if (unlockpt (pty_m) < 0) { + g_set_error (error, PASSDLG_ERROR, PASSDLG_ERROR_BACKEND, + "%s", strerror (errno)); + close (pty_m); + return FALSE; + } + + name = ptsname (pty_m); + if (name == NULL || strlen (name) >= PTY_MAX_NAME) { + g_set_error (error, PASSDLG_ERROR, PASSDLG_ERROR_BACKEND, + "%s", _("Failed to get pseudo-terminal slave name")); + close (pty_m); + return FALSE; + } + strncpy (slave_name, name, PTY_MAX_NAME - 1); + slave_name[PTY_MAX_NAME - 1] = '\0'; + + if (!g_spawn_async (NULL, + argv, + envp, + G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_CHILD_INHERITS_STDIN, + child_setup_pty, + slave_name, + &pdialog->backend_pid, + error)) { + close (pty_m); + return FALSE; + } + + /* Initialize ext_msg with NULL */ + pdialog->ext_msg = NULL; + + /* PTY master serves as both stdin and stdout. + * Use dup() for stdout so that g_io_channel_shutdown on one + * channel does not invalidate the other's file descriptor. */ + pdialog->backend_stdin = g_io_channel_unix_new (pty_m); + pdialog->backend_stdout = g_io_channel_unix_new (dup (pty_m)); + + /* Set raw encoding */ + /* Set nonblocking mode */ + if (g_io_channel_set_encoding (pdialog->backend_stdin, NULL, error) != G_IO_STATUS_NORMAL || + g_io_channel_set_encoding (pdialog->backend_stdout, NULL, error) != G_IO_STATUS_NORMAL || + g_io_channel_set_flags (pdialog->backend_stdin, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL || + g_io_channel_set_flags (pdialog->backend_stdout, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL) { + + /* Clean up */ + stop_passwd (pdialog); + return FALSE; + } + + /* Turn off buffering */ + g_io_channel_set_buffered (pdialog->backend_stdin, FALSE); + g_io_channel_set_buffered (pdialog->backend_stdout, FALSE); + + /* Add IO Channel watcher */ + pdialog->backend_stdout_watch_id = g_io_add_watch (pdialog->backend_stdout, + G_IO_IN | G_IO_PRI, + (GIOFunc) io_watch_stdout, pdialog); } +#else + { + gint my_stdin, my_stdout, my_stderr; + + if (!g_spawn_async_with_pipes (NULL, /* Working directory */ + argv, /* Argument vector */ + envp, /* Environment */ + G_SPAWN_DO_NOT_REAP_CHILD, /* Flags */ + NULL, /* Child setup */ + NULL, /* Data to child setup */ + &pdialog->backend_pid, /* PID */ + &my_stdin, /* Stdin */ + &my_stdout, /* Stdout */ + &my_stderr, /* Stderr */ + error)) { /* GError */ + + /* An error occurred */ + free_passwd_resources (pdialog); + + return FALSE; + } + + /* Initialize ext_msg with NULL */ + pdialog->ext_msg = NULL; - /* Turn off buffering */ - g_io_channel_set_buffered (pdialog->backend_stdin, FALSE); - g_io_channel_set_buffered (pdialog->backend_stdout, FALSE); - g_io_channel_set_buffered (pdialog->backend_stderr, FALSE); + /* Open IO Channels */ + pdialog->backend_stdin = g_io_channel_unix_new (my_stdin); + pdialog->backend_stdout = g_io_channel_unix_new (my_stdout); + pdialog->backend_stderr = g_io_channel_unix_new (my_stderr); - /* Add IO Channel watcher */ - pdialog->backend_stdout_watch_id = g_io_add_watch (pdialog->backend_stdout, - G_IO_IN | G_IO_PRI, - (GIOFunc) io_watch_stdout, pdialog); - pdialog->backend_stderr_watch_id = g_io_add_watch (pdialog->backend_stderr, - G_IO_IN | G_IO_PRI, - (GIOFunc) io_watch_stdout, pdialog); + /* Set raw encoding */ + /* Set nonblocking mode */ + if (g_io_channel_set_encoding (pdialog->backend_stdin, NULL, error) != G_IO_STATUS_NORMAL || + g_io_channel_set_encoding (pdialog->backend_stdout, NULL, error) != G_IO_STATUS_NORMAL || + g_io_channel_set_encoding (pdialog->backend_stderr, NULL, error) != G_IO_STATUS_NORMAL || + g_io_channel_set_flags (pdialog->backend_stdin, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL || + g_io_channel_set_flags (pdialog->backend_stdout, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL || + g_io_channel_set_flags (pdialog->backend_stderr, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL) { + + /* Clean up */ + stop_passwd (pdialog); + return FALSE; + } + + /* Turn off buffering */ + g_io_channel_set_buffered (pdialog->backend_stdin, FALSE); + g_io_channel_set_buffered (pdialog->backend_stdout, FALSE); + g_io_channel_set_buffered (pdialog->backend_stderr, FALSE); + + /* Add IO Channel watcher */ + pdialog->backend_stdout_watch_id = g_io_add_watch (pdialog->backend_stdout, + G_IO_IN | G_IO_PRI, + (GIOFunc) io_watch_stdout, pdialog); + pdialog->backend_stderr_watch_id = g_io_add_watch (pdialog->backend_stderr, + G_IO_IN | G_IO_PRI, + (GIOFunc) io_watch_stdout, pdialog); + } +#endif /* Add child watcher */ pdialog->backend_child_watch_id = g_child_watch_add (pdialog->backend_pid, (GChildWatchFunc) child_watch_cb, pdialog); @@ -532,6 +649,11 @@ io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswordDialog *pdi "match", "1 numeric or special", "failure", + "must contain", + "must differ", + "are not allowed", + "repeating characters", + "history", "rotated", "error", "BAD PASSWORD", @@ -559,12 +681,16 @@ io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswordDialog *pdi g_strrstr (str->str, "longer") != NULL) { msg = g_strdup (_("The password is too short.")); } else if (g_strrstr (str->str, "palindrome") != NULL || + g_strrstr (str->str, "must contain") != NULL || + g_strrstr (str->str, "are not allowed") != NULL || + g_strrstr (str->str, "repeating characters") != NULL || g_strrstr (str->str, "simpl") != NULL || g_strrstr (str->str, "dictionary") != NULL) { msg = g_strdup (_("The password is too simple.")); } else if (g_strrstr (str->str, "similar") != NULL || g_strrstr (str->str, "different") != NULL || g_strrstr (str->str, "case") != NULL || + g_strrstr (str->str, "must differ") != NULL || g_strrstr (str->str, "wrapped") != NULL) { msg = g_strdup (_("The old and new passwords are too similar.")); } else if (g_strrstr (str->str, "1 numeric or special") != NULL) { @@ -572,7 +698,8 @@ io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswordDialog *pdi } else if (g_strrstr (str->str, "unchanged") != NULL || g_strrstr (str->str, "match") != NULL) { msg = g_strdup (_("The old and new passwords are the same.")); - } else if (g_strrstr (str->str, "recent") != NULL) { + } else if (g_strrstr (str->str, "recent") != NULL || + g_strrstr (str->str, "history") != NULL) { msg = g_strdup (_("The new password has already been used recently.")); } else if (g_strrstr (str->str, "failure") != NULL) { /* Authentication failure */ @@ -582,7 +709,9 @@ io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswordDialog *pdi } else if (g_strrstr (str->str, "BAD PASSWORD") != NULL) { /* Actual error description from libpwquality is located in the first string */ msg = g_strdup("Password hasn't changed!"); - pdialog->ext_msg = g_strdup(g_strsplit(str->str, "\n", 2)[0]); + gchar **_parts = g_strsplit(str->str, "\n", 2); + pdialog->ext_msg = g_strdup(_parts[0]); + g_strfreev(_parts); stop_passwd(pdialog); } } @@ -600,7 +729,9 @@ io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswordDialog *pdi * for our new password. */ if (pdialog->ext_msg != NULL) { passdlg_clear(pdialog); - passdlg_set_status (pdialog, g_strconcat(msg, "\n", pdialog->ext_msg, NULL)); + gchar *_combined = g_strconcat(msg, "\n", pdialog->ext_msg, NULL); + passdlg_set_status (pdialog, _combined); + g_free (_combined); g_free (pdialog->ext_msg); } pdialog->backend_state = PASSWD_STATE_ERR; @@ -860,7 +991,7 @@ passdlg_spawn_passwd (PasswordDialog *pdialog) /* translators: Unable to launch <program>: <error message> */ details = g_strdup_printf (_("Unable to launch %s: %s"), - "/usr/bin/passwd", error->message); + PASSWD, error->message); passdlg_error_dialog (GTK_WINDOW (parent), _("Unable to launch backend"), |
