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.
5
* Copyright (C) 2002 Diego Gonzalez
6
* Copyright (C) 2006 Johannes H. Jensen
7
* Copyright (C) 2010 Milan Bouchet-Valat
9
* Written by: Diego Gonzalez <diego@pemas.net>
10
* Modified by: Johannes H. Jensen <joh@deworks.net>,
11
* Milan Bouchet-Valat <nalimilan@club.fr>.
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)
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.
23
* You should have received a copy of the GNU General Public License
24
* along with this program; if not, write to the Free Software
25
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
28
* Most of this code originally comes from gnome-about-me-password.c,
29
* from gnome-control-center.
33
#include <glib/gi18n.h>
41
#include <sys/types.h>
45
#include "run-passwd.h"
49
PASSWD_STATE_NONE, /* Passwd is not asking for anything */
50
PASSWD_STATE_AUTH, /* Passwd is asking for our current password */
51
PASSWD_STATE_NEW, /* Passwd is asking for our new password */
52
PASSWD_STATE_RETYPE, /* Passwd is asking for our retyped new password */
53
PASSWD_STATE_DONE, /* Passwd succeeded but has not yet exited */
54
PASSWD_STATE_ERR /* Passwd reported an error but has not yet exited */
57
struct PasswdHandler {
58
const char *current_password;
59
const char *new_password;
61
/* Communication with the passwd program */
64
GIOChannel *backend_stdin;
65
GIOChannel *backend_stdout;
67
GQueue *backend_stdin_queue; /* Write queue to backend_stdin */
70
guint backend_child_watch_id; /* g_child_watch_add (PID) */
71
guint backend_stdout_watch_id; /* g_io_add_watch (stdout) */
73
/* State of the passwd program */
74
PasswdState backend_state;
75
gboolean changing_password;
77
PasswdCallback auth_cb;
78
gpointer auth_cb_data;
80
PasswdCallback chpasswd_cb;
81
gpointer chpasswd_cb_data;
84
/* Buffer size for backend output */
89
passwd_error_quark (void)
94
q = g_quark_from_static_string("passwd_error");
101
#define PASSWD_ERROR (passwd_error_quark ())
105
stop_passwd (PasswdHandler *passwd_handler);
108
free_passwd_resources (PasswdHandler *passwd_handler);
111
io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswdHandler *passwd_handler);
115
* Spawning and closing of backend {{
120
child_watch_cb (GPid pid, gint status, PasswdHandler *passwd_handler)
122
if (WIFEXITED (status)) {
123
if (WEXITSTATUS (status) >= 255) {
124
g_warning ("Child exited unexpectedly");
126
if (WEXITSTATUS (status) == 0) {
127
if (passwd_handler->backend_state == PASSWD_STATE_RETYPE) {
128
passwd_handler->backend_state = PASSWD_STATE_DONE;
129
if (passwd_handler->chpasswd_cb)
130
passwd_handler->chpasswd_cb (passwd_handler,
132
passwd_handler->auth_cb_data);
137
free_passwd_resources (passwd_handler);
141
ignore_sigpipe (gpointer data)
143
signal (SIGPIPE, SIG_IGN);
146
/* Spawn passwd backend
147
* Returns: TRUE on success, FALSE otherwise and sets error appropriately */
149
spawn_passwd (PasswdHandler *passwd_handler, GError **error)
153
gint my_stdin, my_stdout, my_stderr;
155
argv[0] = "/usr/bin/passwd"; /* Is it safe to rely on a hard-coded path? */
158
envp[0] = NULL; /* If we pass an empty array as the environment,
159
* will the childs environment be empty, and the
160
* locales set to the C default? From the manual:
161
* "If envp is NULL, the child inherits its
162
* parent'senvironment."
163
* If I'm wrong here, we somehow have to set
167
if (!g_spawn_async_with_pipes (NULL, /* Working directory */
168
argv, /* Argument vector */
169
envp, /* Environment */
170
G_SPAWN_DO_NOT_REAP_CHILD, /* Flags */
171
ignore_sigpipe, /* Child setup */
172
NULL, /* Data to child setup */
173
&passwd_handler->backend_pid, /* PID */
174
&my_stdin, /* Stdin */
175
&my_stdout, /* Stdout */
176
&my_stderr, /* Stderr */
177
error)) { /* GError */
179
/* An error occured */
180
free_passwd_resources (passwd_handler);
186
if (dup2 (my_stderr, my_stdout) == -1) {
188
g_set_error_literal (error,
190
PASSWD_ERROR_BACKEND,
194
stop_passwd (passwd_handler);
199
/* Open IO Channels */
200
passwd_handler->backend_stdin = g_io_channel_unix_new (my_stdin);
201
passwd_handler->backend_stdout = g_io_channel_unix_new (my_stdout);
203
/* Set raw encoding */
204
/* Set nonblocking mode */
205
if (g_io_channel_set_encoding (passwd_handler->backend_stdin, NULL, error) != G_IO_STATUS_NORMAL ||
206
g_io_channel_set_encoding (passwd_handler->backend_stdout, NULL, error) != G_IO_STATUS_NORMAL ||
207
g_io_channel_set_flags (passwd_handler->backend_stdin, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ||
208
g_io_channel_set_flags (passwd_handler->backend_stdout, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ) {
211
stop_passwd (passwd_handler);
215
/* Turn off buffering */
216
g_io_channel_set_buffered (passwd_handler->backend_stdin, FALSE);
217
g_io_channel_set_buffered (passwd_handler->backend_stdout, FALSE);
219
/* Add IO Channel watcher */
220
passwd_handler->backend_stdout_watch_id = g_io_add_watch (passwd_handler->backend_stdout,
222
(GIOFunc) io_watch_stdout, passwd_handler);
224
/* Add child watcher */
225
passwd_handler->backend_child_watch_id = g_child_watch_add (passwd_handler->backend_pid, (GChildWatchFunc) child_watch_cb, passwd_handler);
232
/* Stop passwd backend */
234
stop_passwd (PasswdHandler *passwd_handler)
236
/* This is the standard way of returning from the dialog with passwd.
237
* If we return this way we can safely kill passwd as it has completed
241
if (passwd_handler->backend_pid != -1) {
242
kill (passwd_handler->backend_pid, 9);
245
/* We must run free_passwd_resources here and not let our child
246
* watcher do it, since it will access invalid memory after the
247
* dialog has been closed and cleaned up.
249
* If we had more than a single thread we'd need to remove
250
* the child watch before trying to kill the child.
252
free_passwd_resources (passwd_handler);
255
/* Clean up passwd resources */
257
free_passwd_resources (PasswdHandler *passwd_handler)
259
GError *error = NULL;
261
/* Remove the child watcher */
262
if (passwd_handler->backend_child_watch_id != 0) {
264
g_source_remove (passwd_handler->backend_child_watch_id);
266
passwd_handler->backend_child_watch_id = 0;
270
/* Close IO channels (internal file descriptors are automatically closed) */
271
if (passwd_handler->backend_stdin != NULL) {
273
if (g_io_channel_shutdown (passwd_handler->backend_stdin, TRUE, &error) != G_IO_STATUS_NORMAL) {
274
g_warning ("Could not shutdown backend_stdin IO channel: %s", error->message);
275
g_error_free (error);
279
g_io_channel_unref (passwd_handler->backend_stdin);
280
passwd_handler->backend_stdin = NULL;
283
if (passwd_handler->backend_stdout != NULL) {
285
if (g_io_channel_shutdown (passwd_handler->backend_stdout, TRUE, &error) != G_IO_STATUS_NORMAL) {
286
g_warning ("Could not shutdown backend_stdout IO channel: %s", error->message);
287
g_error_free (error);
291
g_io_channel_unref (passwd_handler->backend_stdout);
293
passwd_handler->backend_stdout = NULL;
296
/* Remove IO watcher */
297
if (passwd_handler->backend_stdout_watch_id != 0) {
299
g_source_remove (passwd_handler->backend_stdout_watch_id);
301
passwd_handler->backend_stdout_watch_id = 0;
305
if (passwd_handler->backend_pid != -1) {
307
g_spawn_close_pid (passwd_handler->backend_pid);
309
passwd_handler->backend_pid = -1;
312
/* Clear backend state */
313
passwd_handler->backend_state = PASSWD_STATE_NONE;
317
* }} Spawning and closing of backend
321
* Backend communication code {{
324
/* Write the first element of queue through channel */
326
io_queue_pop (GQueue *queue, GIOChannel *channel)
330
GError *error = NULL;
332
buf = g_queue_pop_head (queue);
336
if (g_io_channel_write_chars (channel, buf, -1, &bytes_written, &error) != G_IO_STATUS_NORMAL) {
337
g_warning ("Could not write queue element \"%s\" to channel: %s", buf, error->message);
338
g_error_free (error);
341
/* Ensure passwords are cleared from memory */
342
memset (buf, 0, strlen (buf));
347
/* Goes through the argument list, checking if one of them occurs in str
348
* Returns: TRUE as soon as an element is found to match, FALSE otherwise */
350
is_string_complete (gchar *str, ...)
355
if (strlen (str) == 0) {
361
while ((arg = va_arg (ap, char *)) != NULL) {
362
if (strstr (str, arg) != NULL) {
374
* IO watcher for stdout, called whenever there is data to read from the backend.
375
* This is where most of the actual IO handling happens.
378
io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswdHandler *passwd_handler)
380
static GString *str = NULL; /* Persistent buffer */
382
gchar buf[BUFSIZE]; /* Temporary buffer */
384
GError *gio_error = NULL; /* Error returned by functions */
385
GError *error = NULL; /* Error sent to callbacks */
387
gboolean reinit = FALSE;
389
/* Initialize buffer */
391
str = g_string_new ("");
394
if (g_io_channel_read_chars (source, buf, BUFSIZE, &bytes_read, &gio_error)
395
!= G_IO_STATUS_NORMAL) {
396
g_warning ("IO Channel read error: %s", gio_error->message);
397
g_error_free (gio_error);
402
str = g_string_append_len (str, buf, bytes_read);
404
/* In which state is the backend? */
405
switch (passwd_handler->backend_state) {
406
case PASSWD_STATE_AUTH:
407
/* Passwd is asking for our current password */
409
if (is_string_complete (str->str, "assword: ", "failure", "wrong", "error", NULL)) {
411
if (strstr (str->str, "assword: ") != NULL) {
412
/* Authentication successful */
414
passwd_handler->backend_state = PASSWD_STATE_NEW;
416
/* Trigger callback to update authentication status */
417
if (passwd_handler->auth_cb)
418
passwd_handler->auth_cb (passwd_handler,
420
passwd_handler->auth_cb_data);
423
/* Authentication failed */
425
error = g_error_new_literal (PASSWD_ERROR, PASSWD_ERROR_AUTH_FAILED,
426
_("Authentication failed"));
428
passwd_handler->changing_password = FALSE;
430
/* This error can happen both while authenticating or while changing password:
431
* if chpasswd_cb is set, this means we're already changing password */
432
if (passwd_handler->chpasswd_cb)
433
passwd_handler->chpasswd_cb (passwd_handler,
435
passwd_handler->auth_cb_data);
436
else if (passwd_handler->auth_cb)
437
passwd_handler->auth_cb (passwd_handler,
439
passwd_handler->auth_cb_data);
441
g_error_free (error);
447
case PASSWD_STATE_NEW:
448
/* Passwd is asking for our new password */
450
if (is_string_complete (str->str, "assword: ", NULL)) {
451
/* Advance to next state */
452
passwd_handler->backend_state = PASSWD_STATE_RETYPE;
454
/* Pop retyped password from queue and into IO channel */
455
io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
460
case PASSWD_STATE_RETYPE:
461
/* Passwd is asking for our retyped new password */
463
if (is_string_complete (str->str,
479
"1 numeric or special",
485
if (strstr (str->str, "successfully") != NULL) {
488
passwd_handler->backend_state = PASSWD_STATE_DONE;
489
/* Trigger callback to update status */
490
if (passwd_handler->chpasswd_cb)
491
passwd_handler->chpasswd_cb (passwd_handler,
493
passwd_handler->chpasswd_cb_data);
498
if (strstr (str->str, "recovered") != NULL) {
499
/* What does this indicate?
500
* "Authentication information cannot be recovered?" from libpam? */
501
error = g_error_new_literal (PASSWD_ERROR, PASSWD_ERROR_UNKNOWN,
503
} else if (strstr (str->str, "short") != NULL ||
504
strstr (str->str, "longer") != NULL) {
505
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
506
_("The new password is too short"));
507
} else if (strstr (str->str, "palindrome") != NULL ||
508
strstr (str->str, "simple") != NULL ||
509
strstr (str->str, "simplistic") != NULL ||
510
strstr (str->str, "dictionary") != NULL) {
511
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
512
_("The new password is too simple"));
513
} else if (strstr (str->str, "similar") != NULL ||
514
strstr (str->str, "different") != NULL ||
515
strstr (str->str, "case") != NULL ||
516
strstr (str->str, "wrapped") != NULL) {
517
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
518
_("The old and new passwords are too similar"));
519
} else if (strstr (str->str, "recent") != NULL) {
520
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
521
_("The new password has already been used recently."));
522
} else if (strstr (str->str, "1 numeric or special") != NULL) {
523
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
524
_("The new password must contain numeric or special characters"));
525
} else if (strstr (str->str, "unchanged") != NULL ||
526
strstr (str->str, "match") != NULL) {
527
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
528
_("The old and new passwords are the same"));
529
} else if (strstr (str->str, "failure") != NULL) {
530
/* Authentication failure */
531
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_AUTH_FAILED,
532
_("Your password has been changed since you initially authenticated!"));
534
else if (strstr (str->str, "DIFFERENT")) {
535
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_REJECTED,
536
_("The new password does not contain enough different characters"));
539
error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_UNKNOWN,
543
/* At this point, passwd might have exited, in which case
544
* child_watch_cb should clean up for us and remove this watcher.
545
* On some error conditions though, passwd just re-prompts us
546
* for our new password. */
547
passwd_handler->backend_state = PASSWD_STATE_ERR;
549
passwd_handler->changing_password = FALSE;
551
/* Trigger callback to update status */
552
if (passwd_handler->chpasswd_cb)
553
passwd_handler->chpasswd_cb (passwd_handler,
555
passwd_handler->chpasswd_cb_data);
557
g_error_free (error);
563
/* child_watch_cb should clean up for us now */
566
case PASSWD_STATE_NONE:
567
/* Passwd is not asking for anything yet */
568
if (is_string_complete (str->str, "assword: ", NULL)) {
570
/* If the user does not have a password set,
571
* passwd will immediately ask for the new password,
572
* so skip the AUTH phase */
573
if (is_string_complete (str->str, "new", "New", NULL)) {
576
passwd_handler->backend_state = PASSWD_STATE_NEW;
578
/* since passwd didn't ask for our old password
579
* in this case, simply remove it from the queue */
580
pw = g_queue_pop_head (passwd_handler->backend_stdin_queue);
583
/* Pop the IO queue, i.e. send new password */
584
io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
588
passwd_handler->backend_state = PASSWD_STATE_AUTH;
590
/* Pop the IO queue, i.e. send current password */
591
io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
598
/* Passwd has returned an error */
604
g_string_free (str, TRUE);
608
/* Continue calling us */
613
* }} Backend communication code
616
/* Adds the current password to the IO queue */
618
authenticate (PasswdHandler *passwd_handler)
622
s = g_strdup_printf ("%s\n", passwd_handler->current_password);
624
g_queue_push_tail (passwd_handler->backend_stdin_queue, s);
627
/* Adds the new password twice to the IO queue */
629
update_password (PasswdHandler *passwd_handler)
633
s = g_strdup_printf ("%s\n", passwd_handler->new_password);
635
g_queue_push_tail (passwd_handler->backend_stdin_queue, s);
636
/* We need to allocate new space because io_queue_pop() g_free()s
637
* every element of the queue after it's done */
638
g_queue_push_tail (passwd_handler->backend_stdin_queue, g_strdup (s));
645
PasswdHandler *passwd_handler;
647
passwd_handler = g_new0 (PasswdHandler, 1);
649
/* Initialize backend_pid. -1 means the backend is not running */
650
passwd_handler->backend_pid = -1;
652
/* Initialize IO Channels */
653
passwd_handler->backend_stdin = NULL;
654
passwd_handler->backend_stdout = NULL;
656
/* Initialize write queue */
657
passwd_handler->backend_stdin_queue = g_queue_new ();
659
/* Initialize watchers */
660
passwd_handler->backend_child_watch_id = 0;
661
passwd_handler->backend_stdout_watch_id = 0;
663
/* Initialize backend state */
664
passwd_handler->backend_state = PASSWD_STATE_NONE;
665
passwd_handler->changing_password = FALSE;
667
return passwd_handler;
671
passwd_destroy (PasswdHandler *passwd_handler)
673
g_queue_free (passwd_handler->backend_stdin_queue);
674
stop_passwd (passwd_handler);
675
g_free (passwd_handler);
679
passwd_authenticate (PasswdHandler *passwd_handler,
680
const char *current_password,
682
const gpointer user_data)
684
GError *error = NULL;
686
/* Don't stop if we've already started chaging password */
687
if (passwd_handler->changing_password)
690
/* Clear data from possible previous attempts to change password */
691
passwd_handler->new_password = NULL;
692
passwd_handler->chpasswd_cb = NULL;
693
passwd_handler->chpasswd_cb_data = NULL;
694
g_queue_foreach (passwd_handler->backend_stdin_queue, (GFunc) g_free, NULL);
695
g_queue_clear (passwd_handler->backend_stdin_queue);
697
passwd_handler->current_password = current_password;
698
passwd_handler->auth_cb = cb;
699
passwd_handler->auth_cb_data = user_data;
702
stop_passwd (passwd_handler);
704
if (!spawn_passwd (passwd_handler, &error)) {
705
g_warning ("%s", error->message);
706
g_error_free (error);
711
authenticate (passwd_handler);
713
/* Our IO watcher should now handle the rest */
717
passwd_change_password (PasswdHandler *passwd_handler,
718
const char *new_password,
720
const gpointer user_data)
722
GError *error = NULL;
724
passwd_handler->changing_password = TRUE;
726
passwd_handler->new_password = new_password;
727
passwd_handler->chpasswd_cb = cb;
728
passwd_handler->chpasswd_cb_data = user_data;
730
/* Stop passwd if an error occured and it is still running */
731
if (passwd_handler->backend_state == PASSWD_STATE_ERR) {
733
/* Stop passwd, free resources */
734
stop_passwd (passwd_handler);
737
/* Check that the backend is still running, or that an error
738
* has occured but it has not yet exited */
739
if (passwd_handler->backend_pid == -1) {
740
/* If it is not, re-run authentication */
743
stop_passwd (passwd_handler);
745
if (!spawn_passwd (passwd_handler, &error)) {
746
g_warning ("%s", error->message);
747
g_error_free (error);
752
/* Add current and new passwords to queue */
753
authenticate (passwd_handler);
754
update_password (passwd_handler);
756
/* Only add new passwords to queue */
757
update_password (passwd_handler);
760
/* Pop new password through the backend.
761
* If user has no password, popping the queue would output current
762
* password, while 'passwd' is waiting for the new one. So wait for
763
* io_watch_stdout() to remove current password from the queue,
764
* and output the new one for us.
766
if (passwd_handler->current_password)
767
io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
769
/* Our IO watcher should now handle the rest */