From bf9c247bfa87cbddb6f6c036b031b56703a6e9c2 Mon Sep 17 00:00:00 2001 From: antonios-f <79258415+antonios-f@users.noreply.github.com> Date: Tue, 12 Oct 2021 22:16:37 +0300 Subject: Change capplets/about-me/mate-about-me-password.c for correct interract with passwd in rhel-based distros Process stdout and stderr separatly in spawn_passwd(). Both are handled by the io_watch_stdout() Increase BUFSIZE up to 128, because the message from libpwquality doesn't fit into previous buffer. In io_watch_stdout() change order of switching PASSDLG_STATE_*, according to current version of passwd with enabled pam_pwquality in rhel-based distros. --- capplets/about-me/mate-about-me-password.c | 163 +++++++++++++++++------------ 1 file changed, 98 insertions(+), 65 deletions(-) (limited to 'capplets/about-me') diff --git a/capplets/about-me/mate-about-me-password.c b/capplets/about-me/mate-about-me-password.c index 31f92b93..fd7eb2d9 100644 --- a/capplets/about-me/mate-about-me-password.c +++ b/capplets/about-me/mate-about-me-password.c @@ -63,6 +63,9 @@ typedef struct { GtkImage *dialog_image; GtkLabel *status_label; + /* We need to save message from libpwquality globaly */ + gchar *ext_msg; + /* Whether we have authenticated */ gboolean authenticated; @@ -71,20 +74,22 @@ typedef struct { GIOChannel *backend_stdin; GIOChannel *backend_stdout; + GIOChannel *backend_stderr; GQueue *backend_stdin_queue; /* Write queue to backend_stdin */ /* GMainLoop IDs */ guint backend_child_watch_id; /* g_child_watch_add (PID) */ guint backend_stdout_watch_id; /* g_io_add_watch (stdout) */ + guint backend_stderr_watch_id; /* g_io_add_watch (stderr) */ /* State of the passwd program */ PasswdState backend_state; } PasswordDialog; -/* Buffer size for backend output */ -#define BUFSIZE 64 +/* Buffer size for backend output (64 is too small to store message from libpwquality)*/ +#define BUFSIZE 128 /* * Error handling {{ @@ -177,26 +182,26 @@ spawn_passwd (PasswordDialog *pdialog, GError **error) argv[0] = "/usr/bin/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, - * will the childs environment be empty, and the - * locales set to the C default? From the manual: - * "If envp is NULL, the child inherits its - * parent'senvironment." - * If I'm wrong here, we somehow have to set - * 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 */ + envp[0] = NULL; /* If we pass an empty array as the environment, + * will the childs environment be empty, and the + * locales set to the C default? From the manual: + * "If envp is NULL, the child inherits its + * parent'senvironment." + * If I'm wrong here, we somehow have to set + * 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); @@ -204,31 +209,22 @@ spawn_passwd (PasswordDialog *pdialog, GError **error) return FALSE; } - /* 2>&1 */ - if (dup2 (my_stderr, my_stdout) == -1) { - /* Failed! */ - g_set_error (error, - PASSDLG_ERROR, - PASSDLG_ERROR_BACKEND, - "%s", - strerror (errno)); - - /* Clean up */ - stop_passwd (pdialog); - - 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_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); @@ -238,11 +234,15 @@ spawn_passwd (PasswordDialog *pdialog, GError **error) /* 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); + 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); /* Add child watcher */ pdialog->backend_child_watch_id = g_child_watch_add (pdialog->backend_pid, (GChildWatchFunc) child_watch_cb, pdialog); @@ -317,6 +317,19 @@ free_passwd_resources (PasswordDialog *pdialog) pdialog->backend_stdout = NULL; } + if (pdialog->backend_stderr != NULL) { + + if (g_io_channel_shutdown (pdialog->backend_stderr, TRUE, &error) != G_IO_STATUS_NORMAL) { + g_warning (_("Could not shutdown backend_stderr IO channel: %s"), error->message); + g_error_free (error); + error = NULL; + } + + g_io_channel_unref (pdialog->backend_stderr); + + pdialog->backend_stderr = NULL; + } + /* Remove IO watcher */ if (pdialog->backend_stdout_watch_id != 0) { @@ -325,6 +338,14 @@ free_passwd_resources (PasswordDialog *pdialog) pdialog->backend_stdout_watch_id = 0; } + if (pdialog->backend_stderr_watch_id != 0) { + + g_source_remove (pdialog->backend_stderr_watch_id); + + pdialog->backend_stderr_watch_id = 0; + } + + /* Close PID */ if (pdialog->backend_pid != -1) { @@ -356,7 +377,6 @@ io_queue_pop (GQueue *queue, GIOChannel *channel) buf = g_queue_pop_head (queue); if (buf != NULL) { - if (g_io_channel_write_chars (channel, buf, -1, &bytes_written, &error) != G_IO_STATUS_NORMAL) { g_warning ("Could not write queue element \"%s\" to channel: %s", buf, error->message); g_error_free (error); @@ -394,7 +414,7 @@ is_string_complete (gchar *str, ...) /* Authentication attempt succeeded. Update the GUI accordingly. */ static void -authenticated_user (PasswordDialog *pdialog) +authenticated_user (PasswordDialog *pdialog, gboolean retry) { pdialog->backend_state = PASSWD_STATE_NEW; @@ -406,7 +426,9 @@ authenticated_user (PasswordDialog *pdialog) /* Update UI state */ passdlg_set_auth_state (pdialog, TRUE); - passdlg_set_status (pdialog, _("Authenticated!")); + if (!retry) { + passdlg_set_status (pdialog, _("Authenticated!")); + } /* Check to see if the passwords are valid * (They might be non-empty if the user had to re-authenticate, @@ -421,7 +443,7 @@ authenticated_user (PasswordDialog *pdialog) static gboolean io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswordDialog *pdialog) { - static GString *str = NULL; /* Persistent buffer */ + static GString *str = NULL; /* Persistent buffer */ gchar buf[BUFSIZE]; /* Temporary buffer */ gsize bytes_read; @@ -446,24 +468,21 @@ io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswordDialog *pdi } str = g_string_append_len (str, buf, bytes_read); - /* In which state is the backend? */ switch (pdialog->backend_state) { case PASSWD_STATE_AUTH: /* Passwd is asking for our current password */ - if (is_string_complete (str->str, "assword: ", "failure", "wrong", "error", NULL)) { /* Which response did we get? */ passdlg_set_busy (pdialog, FALSE); - if (g_strrstr (str->str, "assword: ") != NULL) { + if (g_strrstr (str->str, "New password: ") != NULL) { /* Authentication successful */ - authenticated_user (pdialog); + authenticated_user (pdialog, FALSE); } else { /* Authentication failed */ - if (pdialog->authenticated) { /* This is a re-auth, and it failed. * The password must have been changed in the meantime! @@ -485,20 +504,20 @@ io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswordDialog *pdi break; case PASSWD_STATE_NEW: /* Passwd is asking for our new password */ - - if (is_string_complete (str->str, "assword: ", NULL)) { - /* Advance to next state */ - pdialog->backend_state = PASSWD_STATE_RETYPE; - + if (is_string_complete (str->str, " new password: ", NULL)) { /* Pop retyped password from queue and into IO channel */ io_queue_pop (pdialog->backend_stdin_queue, pdialog->backend_stdin); - + reinit = TRUE; + + } else if (is_string_complete (str->str, "New password: ", NULL)) { + authenticated_user(pdialog, TRUE); reinit = TRUE; } + /* Advance to next state */ + pdialog->backend_state = PASSWD_STATE_RETYPE; break; case PASSWD_STATE_RETYPE: /* Passwd is asking for our retyped new password */ - if (is_string_complete (str->str, "successfully", "short", "longer", @@ -515,6 +534,9 @@ io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswordDialog *pdi "match", "1 numeric or special", "failure", + "rotated", + "error", + "BAD PASSWORD", NULL)) { /* What response did we get? */ @@ -559,26 +581,35 @@ io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswordDialog *pdi msg = g_strdup (_("Your password has been changed since you initially authenticated! Please re-authenticate.")); passdlg_set_auth_state (pdialog, FALSE); + } 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]); + stop_passwd(pdialog); } } - - reinit = TRUE; - + /* child_watch_cb should clean up for us now */ + } + reinit = TRUE; if (msg != NULL) { /* An error occurred! */ + passdlg_clear(pdialog); passdlg_set_status (pdialog, msg); - g_free (msg); /* At this point, passwd might have exited, in which case * child_watch_cb should clean up for us and remove this watcher. * On some error conditions though, passwd just re-prompts us * for our new password. */ - + if (pdialog->ext_msg != NULL) { + passdlg_clear(pdialog); + passdlg_set_status (pdialog, g_strconcat(msg, "\n", pdialog->ext_msg, NULL)); + g_free (pdialog->ext_msg); + } pdialog->backend_state = PASSWD_STATE_ERR; + /* Clean backen_stdin_queue if error occured */ + g_queue_clear_full(pdialog->backend_stdin_queue, g_free); + g_free (msg); } - - /* child_watch_cb should clean up for us now */ - } break; case PASSWD_STATE_NONE: /* Passwd is not asking for anything yet */ @@ -593,7 +624,7 @@ io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswordDialog *pdi pdialog->backend_state = PASSWD_STATE_NEW; passdlg_set_busy (pdialog, FALSE); - authenticated_user (pdialog); + authenticated_user (pdialog, FALSE); /* since passwd didn't ask for our old password * in this case, simply remove it from the queue */ @@ -1041,6 +1072,7 @@ passdlg_init (PasswordDialog *pdialog, GtkWindow *parent) /* Initialize IO Channels */ pdialog->backend_stdin = NULL; pdialog->backend_stdout = NULL; + pdialog->backend_stderr = NULL; /* Initialize write queue */ pdialog->backend_stdin_queue = g_queue_new (); @@ -1048,6 +1080,7 @@ passdlg_init (PasswordDialog *pdialog, GtkWindow *parent) /* Initialize watchers */ pdialog->backend_child_watch_id = 0; pdialog->backend_stdout_watch_id = 0; + pdialog->backend_stderr_watch_id = 0; /* Initialize backend state */ pdialog->backend_state = PASSWD_STATE_NONE; -- cgit v1.2.1