summaryrefslogtreecommitdiff
path: root/capplets
diff options
context:
space:
mode:
Diffstat (limited to 'capplets')
-rw-r--r--capplets/about-me/mate-about-me-password.c237
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"),