1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* run-passwd.c: this file is part of users-admin, a gnome-system-tools frontend
3 * for user administration.
4 *
5 * Copyright (C) 2002 Diego Gonzalez
6 * Copyright (C) 2006 Johannes H. Jensen
7 * Copyright (C) 2010 Milan Bouchet-Valat
8 *
9 * Written by: Diego Gonzalez <diego@pemas.net>
10 * Modified by: Johannes H. Jensen <joh@deworks.net>,
11 * Milan Bouchet-Valat <nalimilan@club.fr>.
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2, or (at your option)
16 * any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, see <http://www.gnu.org/licenses/>.
25 *
26 * Most of this code originally comes from gnome-about-me-password.c,
27 * from gnome-control-center.
28 */
29
30 #include <config.h>
31 #include <glib/gi18n.h>
32
33 #include <unistd.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <sys/wait.h>
37
38 #if __sun
39 #include <sys/types.h>
40 #include <signal.h>
41 #endif
42
43 #include "run-passwd.h"
44
45 /* Passwd states */
46 typedef enum {
47 PASSWD_STATE_NONE, /* Passwd is not asking for anything */
48 PASSWD_STATE_AUTH, /* Passwd is asking for our current password */
49 PASSWD_STATE_NEW, /* Passwd is asking for our new password */
50 PASSWD_STATE_RETYPE, /* Passwd is asking for our retyped new password */
51 PASSWD_STATE_DONE, /* Passwd succeeded but has not yet exited */
52 PASSWD_STATE_ERR /* Passwd reported an error but has not yet exited */
53 } PasswdState;
54
55 struct PasswdHandler {
56 const char *current_password;
57 const char *new_password;
58
59 /* Communication with the passwd program */
60 GPid backend_pid;
61
62 GIOChannel *backend_stdin;
63 GIOChannel *backend_stdout;
64
65 GQueue *backend_stdin_queue; /* Write queue to backend_stdin */
66
67 /* GMainLoop IDs */
68 guint backend_child_watch_id; /* g_child_watch_add (PID) */
69 guint backend_stdout_watch_id; /* g_io_add_watch (stdout) */
70
71 /* State of the passwd program */
72 PasswdState backend_state;
73 gboolean changing_password;
74
75 PasswdCallback auth_cb;
76 gpointer auth_cb_data;
77
78 PasswdCallback chpasswd_cb;
79 gpointer chpasswd_cb_data;
80 };
81
82 /* Buffer size for backend output */
83 #define BUFSIZE 64
84
85
86 static GQuark
passwd_error_quark(void)87 passwd_error_quark (void)
88 {
89 static GQuark q = 0;
90
91 if (q == 0) {
92 q = g_quark_from_static_string("passwd_error");
93 }
94
95 return q;
96 }
97
98 /* Error handling */
99 #define PASSWD_ERROR (passwd_error_quark ())
100
101
102 static void
103 stop_passwd (PasswdHandler *passwd_handler);
104
105 static void
106 free_passwd_resources (PasswdHandler *passwd_handler);
107
108 static gboolean
109 io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswdHandler *passwd_handler);
110
111
112 /*
113 * Spawning and closing of backend {{
114 */
115
116 /* Child watcher */
117 static void
child_watch_cb(GPid pid,gint status,PasswdHandler * passwd_handler)118 child_watch_cb (GPid pid, gint status, PasswdHandler *passwd_handler)
119 {
120 if (WIFEXITED (status)) {
121 if (WEXITSTATUS (status) >= 255) {
122 g_warning ("Child exited unexpectedly");
123 }
124 if (WEXITSTATUS (status) == 0) {
125 if (passwd_handler->backend_state == PASSWD_STATE_RETYPE) {
126 passwd_handler->backend_state = PASSWD_STATE_DONE;
127 if (passwd_handler->chpasswd_cb)
128 passwd_handler->chpasswd_cb (passwd_handler,
129 NULL,
130 passwd_handler->chpasswd_cb_data);
131 }
132 }
133 }
134
135 free_passwd_resources (passwd_handler);
136 }
137
138 static void
ignore_sigpipe(gpointer data)139 ignore_sigpipe (gpointer data)
140 {
141 signal (SIGPIPE, SIG_IGN);
142 }
143
144 /* Spawn passwd backend
145 * Returns: TRUE on success, FALSE otherwise and sets error appropriately */
146 static gboolean
spawn_passwd(PasswdHandler * passwd_handler,GError ** error)147 spawn_passwd (PasswdHandler *passwd_handler, GError **error)
148 {
149 gchar *argv[2];
150 gchar **envp;
151 gint my_stdin, my_stdout, my_stderr;
152
153 argv[0] = "/usr/bin/passwd"; /* Is it safe to rely on a hard-coded path? */
154 argv[1] = NULL;
155
156 envp = g_get_environ ();
157 envp = g_environ_setenv (envp, "LC_ALL", "C", TRUE);
158
159 if (!g_spawn_async_with_pipes (NULL, /* Working directory */
160 argv, /* Argument vector */
161 envp, /* Environment */
162 G_SPAWN_DO_NOT_REAP_CHILD, /* Flags */
163 ignore_sigpipe, /* Child setup */
164 NULL, /* Data to child setup */
165 &passwd_handler->backend_pid, /* PID */
166 &my_stdin, /* Stdin */
167 &my_stdout, /* Stdout */
168 &my_stderr, /* Stderr */
169 error)) { /* GError */
170
171 /* An error occurred */
172 free_passwd_resources (passwd_handler);
173
174 g_strfreev (envp);
175
176 return FALSE;
177 }
178
179 g_strfreev (envp);
180
181 /* 2>&1 */
182 if (dup2 (my_stderr, my_stdout) == -1) {
183 /* Failed! */
184 g_set_error_literal (error,
185 PASSWD_ERROR,
186 PASSWD_ERROR_BACKEND,
187 strerror (errno));
188
189 /* Clean up */
190 stop_passwd (passwd_handler);
191
192 return FALSE;
193 }
194
195 /* Open IO Channels */
196 passwd_handler->backend_stdin = g_io_channel_unix_new (my_stdin);
197 passwd_handler->backend_stdout = g_io_channel_unix_new (my_stdout);
198
199 /* Set raw encoding */
200 /* Set nonblocking mode */
201 if (g_io_channel_set_encoding (passwd_handler->backend_stdin, NULL, error) != G_IO_STATUS_NORMAL ||
202 g_io_channel_set_encoding (passwd_handler->backend_stdout, NULL, error) != G_IO_STATUS_NORMAL ||
203 g_io_channel_set_flags (passwd_handler->backend_stdin, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ||
204 g_io_channel_set_flags (passwd_handler->backend_stdout, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ) {
205
206 /* Clean up */
207 stop_passwd (passwd_handler);
208 return FALSE;
209 }
210
211 /* Turn off buffering */
212 g_io_channel_set_buffered (passwd_handler->backend_stdin, FALSE);
213 g_io_channel_set_buffered (passwd_handler->backend_stdout, FALSE);
214
215 /* Add IO Channel watcher */
216 passwd_handler->backend_stdout_watch_id = g_io_add_watch (passwd_handler->backend_stdout,
217 G_IO_IN | G_IO_PRI,
218 (GIOFunc) io_watch_stdout, passwd_handler);
219
220 /* Add child watcher */
221 passwd_handler->backend_child_watch_id = g_child_watch_add (passwd_handler->backend_pid, (GChildWatchFunc) child_watch_cb, passwd_handler);
222
223 /* Success! */
224
225 return TRUE;
226 }
227
228 /* Stop passwd backend */
229 static void
stop_passwd(PasswdHandler * passwd_handler)230 stop_passwd (PasswdHandler *passwd_handler)
231 {
232 /* This is the standard way of returning from the dialog with passwd.
233 * If we return this way we can safely kill passwd as it has completed
234 * its task.
235 */
236
237 if (passwd_handler->backend_pid != -1) {
238 kill (passwd_handler->backend_pid, 9);
239 }
240
241 /* We must run free_passwd_resources here and not let our child
242 * watcher do it, since it will access invalid memory after the
243 * dialog has been closed and cleaned up.
244 *
245 * If we had more than a single thread we'd need to remove
246 * the child watch before trying to kill the child.
247 */
248 free_passwd_resources (passwd_handler);
249 }
250
251 /* Clean up passwd resources */
252 static void
free_passwd_resources(PasswdHandler * passwd_handler)253 free_passwd_resources (PasswdHandler *passwd_handler)
254 {
255 /* Remove the child watcher */
256 if (passwd_handler->backend_child_watch_id != 0) {
257
258 g_source_remove (passwd_handler->backend_child_watch_id);
259
260 passwd_handler->backend_child_watch_id = 0;
261 }
262
263
264 /* Close IO channels (internal file descriptors are automatically closed) */
265 if (passwd_handler->backend_stdin != NULL) {
266 g_autoptr(GError) error = NULL;
267
268 if (g_io_channel_shutdown (passwd_handler->backend_stdin, TRUE, &error) != G_IO_STATUS_NORMAL) {
269 g_warning ("Could not shutdown backend_stdin IO channel: %s", error->message);
270 }
271
272 g_clear_pointer (&passwd_handler->backend_stdin, g_io_channel_unref);
273 }
274
275 if (passwd_handler->backend_stdout != NULL) {
276 g_autoptr(GError) error = NULL;
277
278 if (g_io_channel_shutdown (passwd_handler->backend_stdout, TRUE, &error) != G_IO_STATUS_NORMAL) {
279 g_warning ("Could not shutdown backend_stdout IO channel: %s", error->message);
280 }
281
282 g_clear_pointer (&passwd_handler->backend_stdout, g_io_channel_unref);
283 }
284
285 /* Remove IO watcher */
286 if (passwd_handler->backend_stdout_watch_id != 0) {
287
288 g_source_remove (passwd_handler->backend_stdout_watch_id);
289
290 passwd_handler->backend_stdout_watch_id = 0;
291 }
292
293 /* Close PID */
294 if (passwd_handler->backend_pid != -1) {
295
296 g_spawn_close_pid (passwd_handler->backend_pid);
297
298 passwd_handler->backend_pid = -1;
299 }
300
301 /* Clear backend state */
302 passwd_handler->backend_state = PASSWD_STATE_NONE;
303 }
304
305 /*
306 * }} Spawning and closing of backend
307 */
308
309 /*
310 * Backend communication code {{
311 */
312
313 /* Write the first element of queue through channel */
314 static void
io_queue_pop(GQueue * queue,GIOChannel * channel)315 io_queue_pop (GQueue *queue, GIOChannel *channel)
316 {
317 g_autofree gchar *buf = NULL;
318 gsize bytes_written;
319 g_autoptr(GError) error = NULL;
320
321 buf = g_queue_pop_head (queue);
322
323 if (buf != NULL) {
324
325 if (g_io_channel_write_chars (channel, buf, -1, &bytes_written, &error) != G_IO_STATUS_NORMAL) {
326 g_warning ("Could not write queue element \"%s\" to channel: %s", buf, error->message);
327 }
328
329 /* Ensure passwords are cleared from memory */
330 memset (buf, 0, strlen (buf));
331 }
332 }
333
334 /* Goes through the argument list, checking if one of them occurs in str
335 * Returns: TRUE as soon as an element is found to match, FALSE otherwise */
336 static gboolean
is_string_complete(gchar * str,...)337 is_string_complete (gchar *str, ...)
338 {
339 va_list ap;
340 gchar *arg;
341
342 if (strlen (str) == 0) {
343 return FALSE;
344 }
345
346 va_start (ap, str);
347
348 while ((arg = va_arg (ap, char *)) != NULL) {
349 if (strstr (str, arg) != NULL) {
350 va_end (ap);
351 return TRUE;
352 }
353 }
354
355 va_end (ap);
356
357 return FALSE;
358 }
359
360 /*
361 * IO watcher for stdout, called whenever there is data to read from the backend.
362 * This is where most of the actual IO handling happens.
363 */
364 static gboolean
io_watch_stdout(GIOChannel * source,GIOCondition condition,PasswdHandler * passwd_handler)365 io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswdHandler *passwd_handler)
366 {
367 static GString *str = NULL; /* Persistent buffer */
368
369 gchar buf[BUFSIZE]; /* Temporary buffer */
370 gsize bytes_read;
371 g_autoptr(GError) gio_error = NULL;
372
373 gboolean reinit = FALSE;
374
375 /* Initialize buffer */
376 if (str == NULL) {
377 str = g_string_new ("");
378 }
379
380 if (g_io_channel_read_chars (source, buf, BUFSIZE, &bytes_read, &gio_error)
381 != G_IO_STATUS_NORMAL) {
382 g_warning ("IO Channel read error: %s", gio_error->message);
383 return TRUE;
384 }
385
386 str = g_string_append_len (str, buf, bytes_read);
387
388 /* In which state is the backend? */
389 switch (passwd_handler->backend_state) {
390 case PASSWD_STATE_AUTH:
391 /* Passwd is asking for our current password */
392
393 if (is_string_complete (str->str, "assword: ", "failure", "wrong", "error", NULL)) {
394
395 if (strstr (str->str, "assword: ") != NULL &&
396 strstr (str->str, "incorrect") == NULL &&
397 strstr (str->str, "urrent") == NULL) {
398 /* Authentication successful */
399
400 passwd_handler->backend_state = PASSWD_STATE_NEW;
401
402 /* Trigger callback to update authentication status */
403 if (passwd_handler->auth_cb)
404 passwd_handler->auth_cb (passwd_handler,
405 NULL,
406 passwd_handler->auth_cb_data);
407
408 } else {
409 /* Authentication failed */
410 g_autoptr(GError) error = NULL;
411
412 error = g_error_new_literal (PASSWD_ERROR, PASSWD_ERROR_AUTH_FAILED,
413 _("Authentication failed"));
414
415 passwd_handler->changing_password = FALSE;
416
417 /* This error can happen both while authenticating or while changing password:
418 * if chpasswd_cb is set, this means we're already changing password */
419 if (passwd_handler->chpasswd_cb)
420 passwd_handler->chpasswd_cb (passwd_handler,
421 error,
422 passwd_handler->chpasswd_cb_data);
423 else if (passwd_handler->auth_cb)
424 passwd_handler->auth_cb (passwd_handler,
425 error,
426 passwd_handler->auth_cb_data);
427 }
428
429 reinit = TRUE;
430 }
431 break;
432 case PASSWD_STATE_NEW:
433 /* Passwd is asking for our new password */
434
435 if (is_string_complete (str->str, "assword: ", NULL)) {
436 /* Advance to next state */
437 passwd_handler->backend_state = PASSWD_STATE_RETYPE;
438
439 /* Pop retyped password from queue and into IO channel */
440 io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
441
442 reinit = TRUE;
443 }
444 break;
445 case PASSWD_STATE_RETYPE:
446 /* Passwd is asking for our retyped new password */
447
448 if (is_string_complete (str->str,
449 "successfully",
450 "short",
451 "longer",
452 "palindrome",
453 "dictionary",
454 "simple",
455 "simplistic",
456 "similar",
457 "case",
458 "different",
459 "wrapped",
460 "recovered",
461 "recent",
462 "unchanged",
463 "match",
464 "1 numeric or special",
465 "failure",
466 "DIFFERENT",
467 "BAD PASSWORD",
468 NULL)) {
469
470 if (strstr (str->str, "successfully") != NULL) {
471 /* Hooray! */
472
473 passwd_handler->backend_state = PASSWD_STATE_DONE;
474 /* Trigger callback to update status */
475 if (passwd_handler->chpasswd_cb)
476 passwd_handler->chpasswd_cb (passwd_handler,
477 NULL,
478 passwd_handler->chpasswd_cb_data);
479 }
480 else {
481 /* Ohnoes! */
482 g_autoptr(GError) error = NULL;
483
484 if (strstr (str->str, "recovered") != NULL) {
485 /* What does this indicate?
486 * "Authentication information cannot be recovered?" from libpam? */
487 error = g_error_new_literal (PASSWD_ERROR, PASSWD_ERROR_UNKNOWN,
488 str->str);
489 } else if (strstr (str->str, "short") != NULL ||
490 strstr (str->str, "longer") != NULL) {
491 error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
492 _("The new password is too short"));
493 } else if (strstr (str->str, "palindrome") != NULL ||
494 strstr (str->str, "simple") != NULL ||
495 strstr (str->str, "simplistic") != NULL ||
496 strstr (str->str, "dictionary") != NULL) {
497 error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
498 _("The new password is too simple"));
499 } else if (strstr (str->str, "similar") != NULL ||
500 strstr (str->str, "different") != NULL ||
501 strstr (str->str, "case") != NULL ||
502 strstr (str->str, "wrapped") != NULL) {
503 error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
504 _("The old and new passwords are too similar"));
505 } else if (strstr (str->str, "recent") != NULL) {
506 error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
507 _("The new password has already been used recently."));
508 } else if (strstr (str->str, "1 numeric or special") != NULL) {
509 error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
510 _("The new password must contain numeric or special characters"));
511 } else if (strstr (str->str, "unchanged") != NULL ||
512 strstr (str->str, "match") != NULL) {
513 error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
514 _("The old and new passwords are the same"));
515 } else if (strstr (str->str, "failure") != NULL) {
516 /* Authentication failure */
517 error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_AUTH_FAILED,
518 _("Your password has been changed since you initially authenticated!"));
519 }
520 else if (strstr (str->str, "DIFFERENT")) {
521 error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
522 _("The new password does not contain enough different characters"));
523 }
524 else {
525 error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_UNKNOWN,
526 _("Unknown error"));
527 }
528
529 /* At this point, passwd might have exited, in which case
530 * child_watch_cb should clean up for us and remove this watcher.
531 * On some error conditions though, passwd just re-prompts us
532 * for our new password. */
533 passwd_handler->backend_state = PASSWD_STATE_ERR;
534
535 passwd_handler->changing_password = FALSE;
536
537 /* Trigger callback to update status */
538 if (passwd_handler->chpasswd_cb)
539 passwd_handler->chpasswd_cb (passwd_handler,
540 error,
541 passwd_handler->chpasswd_cb_data);
542 }
543
544 reinit = TRUE;
545
546 /* child_watch_cb should clean up for us now */
547 }
548 break;
549 case PASSWD_STATE_NONE:
550 /* Passwd is not asking for anything yet */
551 if (is_string_complete (str->str, "assword: ", NULL)) {
552
553 /* If the user does not have a password set,
554 * passwd will immediately ask for the new password,
555 * so skip the AUTH phase */
556 if (is_string_complete (str->str, "new", "New", NULL)) {
557 g_autofree gchar *pw = NULL;
558
559 passwd_handler->backend_state = PASSWD_STATE_NEW;
560
561 /* since passwd didn't ask for our old password
562 * in this case, simply remove it from the queue */
563 pw = g_queue_pop_head (passwd_handler->backend_stdin_queue);
564
565 /* Pop the IO queue, i.e. send new password */
566 io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
567
568 } else {
569
570 passwd_handler->backend_state = PASSWD_STATE_AUTH;
571
572 /* Pop the IO queue, i.e. send current password */
573 io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
574 }
575
576 reinit = TRUE;
577 }
578 break;
579 default:
580 /* Passwd has returned an error */
581 reinit = TRUE;
582 break;
583 }
584
585 if (reinit) {
586 g_string_free (str, TRUE);
587 str = NULL;
588 }
589
590 /* Continue calling us */
591 return TRUE;
592 }
593
594 /*
595 * }} Backend communication code
596 */
597
598 /* Adds the current password to the IO queue */
599 static void
authenticate(PasswdHandler * passwd_handler)600 authenticate (PasswdHandler *passwd_handler)
601 {
602 gchar *s;
603
604 s = g_strdup_printf ("%s\n", passwd_handler->current_password);
605
606 g_queue_push_tail (passwd_handler->backend_stdin_queue, s);
607 }
608
609 /* Adds the new password twice to the IO queue */
610 static void
update_password(PasswdHandler * passwd_handler)611 update_password (PasswdHandler *passwd_handler)
612 {
613 gchar *s;
614
615 s = g_strdup_printf ("%s\n", passwd_handler->new_password);
616
617 g_queue_push_tail (passwd_handler->backend_stdin_queue, s);
618 /* We need to allocate new space because io_queue_pop() g_free()s
619 * every element of the queue after it's done */
620 g_queue_push_tail (passwd_handler->backend_stdin_queue, g_strdup (s));
621 }
622
623
624 PasswdHandler *
passwd_init(void)625 passwd_init (void)
626 {
627 PasswdHandler *passwd_handler;
628
629 passwd_handler = g_new0 (PasswdHandler, 1);
630
631 /* Initialize backend_pid. -1 means the backend is not running */
632 passwd_handler->backend_pid = -1;
633
634 /* Initialize IO Channels */
635 passwd_handler->backend_stdin = NULL;
636 passwd_handler->backend_stdout = NULL;
637
638 /* Initialize write queue */
639 passwd_handler->backend_stdin_queue = g_queue_new ();
640
641 /* Initialize watchers */
642 passwd_handler->backend_child_watch_id = 0;
643 passwd_handler->backend_stdout_watch_id = 0;
644
645 /* Initialize backend state */
646 passwd_handler->backend_state = PASSWD_STATE_NONE;
647 passwd_handler->changing_password = FALSE;
648
649 return passwd_handler;
650 }
651
652 void
passwd_destroy(PasswdHandler * passwd_handler)653 passwd_destroy (PasswdHandler *passwd_handler)
654 {
655 g_queue_free (passwd_handler->backend_stdin_queue);
656 stop_passwd (passwd_handler);
657 g_free (passwd_handler);
658 }
659
660 void
passwd_authenticate(PasswdHandler * passwd_handler,const char * current_password,PasswdCallback cb,const gpointer user_data)661 passwd_authenticate (PasswdHandler *passwd_handler,
662 const char *current_password,
663 PasswdCallback cb,
664 const gpointer user_data)
665 {
666 g_autoptr(GError) error = NULL;
667
668 /* Don't stop if we've already started changing password */
669 if (passwd_handler->changing_password)
670 return;
671
672 /* Clear data from possible previous attempts to change password */
673 passwd_handler->new_password = NULL;
674 passwd_handler->chpasswd_cb = NULL;
675 passwd_handler->chpasswd_cb_data = NULL;
676 g_queue_foreach (passwd_handler->backend_stdin_queue, (GFunc) g_free, NULL);
677 g_queue_clear (passwd_handler->backend_stdin_queue);
678
679 passwd_handler->current_password = current_password;
680 passwd_handler->auth_cb = cb;
681 passwd_handler->auth_cb_data = user_data;
682
683 /* Spawn backend */
684 stop_passwd (passwd_handler);
685
686 if (!spawn_passwd (passwd_handler, &error)) {
687 g_warning ("%s", error->message);
688 return;
689 }
690
691 authenticate (passwd_handler);
692
693 /* Our IO watcher should now handle the rest */
694 }
695
696 gboolean
passwd_change_password(PasswdHandler * passwd_handler,const char * new_password,PasswdCallback cb,const gpointer user_data)697 passwd_change_password (PasswdHandler *passwd_handler,
698 const char *new_password,
699 PasswdCallback cb,
700 const gpointer user_data)
701 {
702 passwd_handler->changing_password = TRUE;
703
704 passwd_handler->new_password = new_password;
705 passwd_handler->chpasswd_cb = cb;
706 passwd_handler->chpasswd_cb_data = user_data;
707
708 /* Stop passwd if an error occurred and it is still running */
709 if (passwd_handler->backend_state == PASSWD_STATE_ERR) {
710
711 /* Stop passwd, free resources */
712 stop_passwd (passwd_handler);
713 }
714
715 /* Check that the backend is still running, or that an error
716 * has occurred but it has not yet exited */
717 if (passwd_handler->backend_pid == -1) {
718 /* If it is not, re-run authentication */
719 g_autoptr(GError) error = NULL;
720
721 /* Spawn backend */
722 stop_passwd (passwd_handler);
723
724 if (!spawn_passwd (passwd_handler, &error)) {
725 g_warning ("%s", error->message);
726 return FALSE;
727 }
728
729 /* Add current and new passwords to queue */
730 authenticate (passwd_handler);
731 update_password (passwd_handler);
732 } else {
733 /* Only add new passwords to queue */
734 update_password (passwd_handler);
735 }
736
737 /* Pop new password through the backend.
738 * If user has no password, popping the queue would output current
739 * password, while 'passwd' is waiting for the new one. So wait for
740 * io_watch_stdout() to remove current password from the queue,
741 * and output the new one for us.
742 */
743 if (passwd_handler->current_password)
744 io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
745
746 /* Our IO watcher should now handle the rest */
747
748 return TRUE;
749 }
750