~kroq-gar78/ubuntu/precise/gnome-control-center/fix-885947

« back to all changes in this revision

Viewing changes to panels/user-accounts/run-passwd.c

  • Committer: Bazaar Package Importer
  • Author(s): Rodrigo Moya
  • Date: 2011-05-17 10:47:27 UTC
  • mfrom: (0.1.11 experimental) (1.1.45 upstream)
  • Revision ID: james.westby@ubuntu.com-20110517104727-lqel6m8vhfw5jby1
Tags: 1:3.0.1.1-1ubuntu1
* Rebase on Debian, remaining Ubuntu changes:
* debian/control:
  - Build-Depend on hardening-wrapper, dpkg-dev and dh-autoreconf
  - Add dependency on ubuntu-system-service
  - Remove dependency on gnome-icon-theme-symbolic
  - Move dependency on apg, gnome-icon-theme-symbolic and accountsservice to
    be a Recommends: until we get them in main
* debian/rules:
  - Use autoreconf
  - Add binary-post-install rule for gnome-control-center-data
  - Run dh-autoreconf
* debian/gnome-control-center.dirs:
* debian/gnome-control-center.links:
  - Add a link to the control center shell for indicators
* debian/patches/00_disable-nm.patch:
  - Temporary patch to disable building with NetworkManager until we get
    the new one in the archive
* debian/patches/01_git_remove_gettext_calls.patch:
  - Remove calls to AM_GNU_GETTEXT, IT_PROG_INTLTOOL should be enough
* debian/patches/01_git_kill_warning.patch:
  - Kill warning
* debian/patches/50_ubuntu_systemwide_prefs.patch:
  - Ubuntu specific proxy preferences
* debian/patches/51_ubuntu_system_keyboard.patch:
  - Implement the global keyboard spec at https://wiki.ubuntu.com/DefaultKeyboardSettings

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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, write to the Free Software
 
25
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 
26
 * 02111-1307, USA.
 
27
 *
 
28
 * Most of this code originally comes from gnome-about-me-password.c,
 
29
 * from gnome-control-center.
 
30
 */
 
31
 
 
32
#include <config.h>
 
33
#include <glib/gi18n.h>
 
34
 
 
35
#include <unistd.h>
 
36
#include <errno.h>
 
37
#include <string.h>
 
38
#include <sys/wait.h>
 
39
 
 
40
#if __sun
 
41
#include <sys/types.h>
 
42
#include <signal.h>
 
43
#endif
 
44
 
 
45
#include "run-passwd.h"
 
46
 
 
47
/* Passwd states */
 
48
typedef enum {
 
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 */
 
55
} PasswdState;
 
56
 
 
57
struct PasswdHandler {
 
58
        const char *current_password;
 
59
        const char *new_password;
 
60
 
 
61
        /* Communication with the passwd program */
 
62
        GPid backend_pid;
 
63
 
 
64
        GIOChannel *backend_stdin;
 
65
        GIOChannel *backend_stdout;
 
66
 
 
67
        GQueue *backend_stdin_queue;            /* Write queue to backend_stdin */
 
68
 
 
69
        /* GMainLoop IDs */
 
70
        guint backend_child_watch_id;           /* g_child_watch_add (PID) */
 
71
        guint backend_stdout_watch_id;          /* g_io_add_watch (stdout) */
 
72
 
 
73
        /* State of the passwd program */
 
74
        PasswdState backend_state;
 
75
        gboolean    changing_password;
 
76
 
 
77
        PasswdCallback auth_cb;
 
78
        gpointer       auth_cb_data;
 
79
 
 
80
        PasswdCallback chpasswd_cb;
 
81
        gpointer       chpasswd_cb_data;
 
82
};
 
83
 
 
84
/* Buffer size for backend output */
 
85
#define BUFSIZE 64
 
86
 
 
87
 
 
88
static GQuark
 
89
passwd_error_quark (void)
 
90
{
 
91
        static GQuark q = 0;
 
92
 
 
93
        if (q == 0) {
 
94
                q = g_quark_from_static_string("passwd_error");
 
95
        }
 
96
 
 
97
        return q;
 
98
}
 
99
 
 
100
/* Error handling */
 
101
#define PASSWD_ERROR (passwd_error_quark ())
 
102
 
 
103
 
 
104
static void
 
105
stop_passwd (PasswdHandler *passwd_handler);
 
106
 
 
107
static void
 
108
free_passwd_resources (PasswdHandler *passwd_handler);
 
109
 
 
110
static gboolean
 
111
io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswdHandler *passwd_handler);
 
112
 
 
113
 
 
114
/*
 
115
 * Spawning and closing of backend {{
 
116
 */
 
117
 
 
118
/* Child watcher */
 
119
static void
 
120
child_watch_cb (GPid pid, gint status, PasswdHandler *passwd_handler)
 
121
{
 
122
        if (WIFEXITED (status)) {
 
123
                if (WEXITSTATUS (status) >= 255) {
 
124
                        g_warning ("Child exited unexpectedly");
 
125
                }
 
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,
 
131
                                                                             NULL,
 
132
                                                                             passwd_handler->auth_cb_data);
 
133
                        }
 
134
                }
 
135
        }
 
136
 
 
137
        free_passwd_resources (passwd_handler);
 
138
}
 
139
 
 
140
static void
 
141
ignore_sigpipe (gpointer data)
 
142
{
 
143
        signal (SIGPIPE, SIG_IGN);
 
144
}
 
145
 
 
146
/* Spawn passwd backend
 
147
 * Returns: TRUE on success, FALSE otherwise and sets error appropriately */
 
148
static gboolean
 
149
spawn_passwd (PasswdHandler *passwd_handler, GError **error)
 
150
{
 
151
        gchar   *argv[2];
 
152
        gchar   *envp[1];
 
153
        gint    my_stdin, my_stdout, my_stderr;
 
154
 
 
155
        argv[0] = "/usr/bin/passwd";    /* Is it safe to rely on a hard-coded path? */
 
156
        argv[1] = NULL;
 
157
 
 
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
 
164
                                         * the locales here.
 
165
                                         */
 
166
 
 
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 */
 
178
 
 
179
                /* An error occured */
 
180
                free_passwd_resources (passwd_handler);
 
181
 
 
182
                return FALSE;
 
183
        }
 
184
 
 
185
        /* 2>&1 */
 
186
        if (dup2 (my_stderr, my_stdout) == -1) {
 
187
                /* Failed! */
 
188
                g_set_error_literal (error,
 
189
                                     PASSWD_ERROR,
 
190
                                     PASSWD_ERROR_BACKEND,
 
191
                                     strerror (errno));
 
192
 
 
193
                /* Clean up */
 
194
                stop_passwd (passwd_handler);
 
195
 
 
196
                return FALSE;
 
197
        }
 
198
 
 
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);
 
202
 
 
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 ) {
 
209
 
 
210
                /* Clean up */
 
211
                stop_passwd (passwd_handler);
 
212
                return FALSE;
 
213
        }
 
214
 
 
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);
 
218
 
 
219
        /* Add IO Channel watcher */
 
220
        passwd_handler->backend_stdout_watch_id = g_io_add_watch (passwd_handler->backend_stdout,
 
221
                                                                  G_IO_IN | G_IO_PRI,
 
222
                                                                  (GIOFunc) io_watch_stdout, passwd_handler);
 
223
 
 
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);
 
226
 
 
227
        /* Success! */
 
228
 
 
229
        return TRUE;
 
230
}
 
231
 
 
232
/* Stop passwd backend */
 
233
static void
 
234
stop_passwd (PasswdHandler *passwd_handler)
 
235
{
 
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
 
238
         * its task.
 
239
         */
 
240
 
 
241
        if (passwd_handler->backend_pid != -1) {
 
242
                kill (passwd_handler->backend_pid, 9);
 
243
        }
 
244
 
 
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.
 
248
         *
 
249
         * If we had more than a single thread we'd need to remove
 
250
         * the child watch before trying to kill the child.
 
251
         */
 
252
        free_passwd_resources (passwd_handler);
 
253
}
 
254
 
 
255
/* Clean up passwd resources */
 
256
static void
 
257
free_passwd_resources (PasswdHandler *passwd_handler)
 
258
{
 
259
        GError  *error = NULL;
 
260
 
 
261
        /* Remove the child watcher */
 
262
        if (passwd_handler->backend_child_watch_id != 0) {
 
263
 
 
264
                g_source_remove (passwd_handler->backend_child_watch_id);
 
265
 
 
266
                passwd_handler->backend_child_watch_id = 0;
 
267
        }
 
268
 
 
269
 
 
270
        /* Close IO channels (internal file descriptors are automatically closed) */
 
271
        if (passwd_handler->backend_stdin != NULL) {
 
272
 
 
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);
 
276
                        error = NULL;
 
277
                }
 
278
 
 
279
                g_io_channel_unref (passwd_handler->backend_stdin);
 
280
                passwd_handler->backend_stdin = NULL;
 
281
        }
 
282
 
 
283
        if (passwd_handler->backend_stdout != NULL) {
 
284
 
 
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);
 
288
                        error = NULL;
 
289
                }
 
290
 
 
291
                g_io_channel_unref (passwd_handler->backend_stdout);
 
292
 
 
293
                passwd_handler->backend_stdout = NULL;
 
294
        }
 
295
 
 
296
        /* Remove IO watcher */
 
297
        if (passwd_handler->backend_stdout_watch_id != 0) {
 
298
 
 
299
                g_source_remove (passwd_handler->backend_stdout_watch_id);
 
300
 
 
301
                passwd_handler->backend_stdout_watch_id = 0;
 
302
        }
 
303
 
 
304
        /* Close PID */
 
305
        if (passwd_handler->backend_pid != -1) {
 
306
 
 
307
                g_spawn_close_pid (passwd_handler->backend_pid);
 
308
 
 
309
                passwd_handler->backend_pid = -1;
 
310
        }
 
311
 
 
312
        /* Clear backend state */
 
313
        passwd_handler->backend_state = PASSWD_STATE_NONE;
 
314
}
 
315
 
 
316
/*
 
317
 * }} Spawning and closing of backend
 
318
 */
 
319
 
 
320
/*
 
321
 * Backend communication code {{
 
322
 */
 
323
 
 
324
/* Write the first element of queue through channel */
 
325
static void
 
326
io_queue_pop (GQueue *queue, GIOChannel *channel)
 
327
{
 
328
        gchar   *buf;
 
329
        gsize   bytes_written;
 
330
        GError  *error = NULL;
 
331
 
 
332
        buf = g_queue_pop_head (queue);
 
333
 
 
334
        if (buf != NULL) {
 
335
 
 
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);
 
339
                }
 
340
 
 
341
                /* Ensure passwords are cleared from memory */
 
342
                memset (buf, 0, strlen (buf));
 
343
                g_free (buf);
 
344
        }
 
345
}
 
346
 
 
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 */
 
349
static gboolean
 
350
is_string_complete (gchar *str, ...)
 
351
{
 
352
        va_list ap;
 
353
        gchar   *arg;
 
354
 
 
355
        if (strlen (str) == 0) {
 
356
                return FALSE;
 
357
        }
 
358
 
 
359
        va_start (ap, str);
 
360
 
 
361
        while ((arg = va_arg (ap, char *)) != NULL) {
 
362
                if (strstr (str, arg) != NULL) {
 
363
                        va_end (ap);
 
364
                        return TRUE;
 
365
                }
 
366
        }
 
367
 
 
368
        va_end (ap);
 
369
 
 
370
        return FALSE;
 
371
}
 
372
 
 
373
/*
 
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.
 
376
 */
 
377
static gboolean
 
378
io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswdHandler *passwd_handler)
 
379
{
 
380
        static GString *str = NULL;     /* Persistent buffer */
 
381
 
 
382
        gchar           buf[BUFSIZE];           /* Temporary buffer */
 
383
        gsize           bytes_read;
 
384
        GError          *gio_error = NULL;      /* Error returned by functions */
 
385
        GError          *error = NULL;          /* Error sent to callbacks */
 
386
 
 
387
        gboolean        reinit = FALSE;
 
388
 
 
389
        /* Initialize buffer */
 
390
        if (str == NULL) {
 
391
                str = g_string_new ("");
 
392
        }
 
393
 
 
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);
 
398
 
 
399
                return TRUE;
 
400
        }
 
401
 
 
402
        str = g_string_append_len (str, buf, bytes_read);
 
403
 
 
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 */
 
408
 
 
409
                        if (is_string_complete (str->str, "assword: ", "failure", "wrong", "error", NULL)) {
 
410
 
 
411
                                if (strstr (str->str, "assword: ") != NULL) {
 
412
                                        /* Authentication successful */
 
413
 
 
414
                                        passwd_handler->backend_state = PASSWD_STATE_NEW;
 
415
 
 
416
                                        /* Trigger callback to update authentication status */
 
417
                                        if (passwd_handler->auth_cb)
 
418
                                                passwd_handler->auth_cb (passwd_handler,
 
419
                                                                         NULL,
 
420
                                                                         passwd_handler->auth_cb_data);
 
421
 
 
422
                                } else {
 
423
                                        /* Authentication failed */
 
424
 
 
425
                                        error = g_error_new_literal (PASSWD_ERROR, PASSWD_ERROR_AUTH_FAILED,
 
426
                                                                     _("Authentication failed"));
 
427
 
 
428
                                        passwd_handler->changing_password = FALSE;
 
429
 
 
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,
 
434
                                                                             error,
 
435
                                                                             passwd_handler->auth_cb_data);
 
436
                                        else if (passwd_handler->auth_cb)
 
437
                                                passwd_handler->auth_cb (passwd_handler,
 
438
                                                                         error,
 
439
                                                                         passwd_handler->auth_cb_data);
 
440
 
 
441
                                        g_error_free (error);
 
442
                                }
 
443
 
 
444
                                reinit = TRUE;
 
445
                        }
 
446
                        break;
 
447
                case PASSWD_STATE_NEW:
 
448
                        /* Passwd is asking for our new password */
 
449
 
 
450
                        if (is_string_complete (str->str, "assword: ", NULL)) {
 
451
                                /* Advance to next state */
 
452
                                passwd_handler->backend_state = PASSWD_STATE_RETYPE;
 
453
 
 
454
                                /* Pop retyped password from queue and into IO channel */
 
455
                                io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
 
456
 
 
457
                                reinit = TRUE;
 
458
                        }
 
459
                        break;
 
460
                case PASSWD_STATE_RETYPE:
 
461
                        /* Passwd is asking for our retyped new password */
 
462
 
 
463
                        if (is_string_complete (str->str,
 
464
                                                "successfully",
 
465
                                                "short",
 
466
                                                "longer",
 
467
                                                "palindrome",
 
468
                                                "dictionary",
 
469
                                                "simple",
 
470
                                                "simplistic",
 
471
                                                "similar",
 
472
                                                "case",
 
473
                                                "different",
 
474
                                                "wrapped",
 
475
                                                "recovered",
 
476
                                                "recent",
 
477
                                                "unchanged",
 
478
                                                "match",
 
479
                                                "1 numeric or special",
 
480
                                                "failure",
 
481
                                                "DIFFERENT",
 
482
                                                "BAD PASSWORD",
 
483
                                                NULL)) {
 
484
 
 
485
                                if (strstr (str->str, "successfully") != NULL) {
 
486
                                        /* Hooray! */
 
487
 
 
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,
 
492
                                                                             NULL,
 
493
                                                                             passwd_handler->chpasswd_cb_data);
 
494
                                }
 
495
                                else {
 
496
                                        /* Ohnoes! */
 
497
 
 
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,
 
502
                                                                             str->str);
 
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!"));
 
533
                                        }
 
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"));
 
537
                                        }
 
538
                                        else {
 
539
                                                error = g_error_new (PASSWD_ERROR, PASSWD_ERROR_UNKNOWN,
 
540
                                                                     _("Unknown error"));
 
541
                                        }
 
542
 
 
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;
 
548
 
 
549
                                        passwd_handler->changing_password = FALSE;
 
550
 
 
551
                                        /* Trigger callback to update status */
 
552
                                        if (passwd_handler->chpasswd_cb)
 
553
                                                passwd_handler->chpasswd_cb (passwd_handler,
 
554
                                                                             error,
 
555
                                                                             passwd_handler->chpasswd_cb_data);
 
556
 
 
557
                                        g_error_free (error);
 
558
 
 
559
                                }
 
560
 
 
561
                                reinit = TRUE;
 
562
 
 
563
                                /* child_watch_cb should clean up for us now */
 
564
                        }
 
565
                        break;
 
566
                case PASSWD_STATE_NONE:
 
567
                        /* Passwd is not asking for anything yet */
 
568
                        if (is_string_complete (str->str, "assword: ", NULL)) {
 
569
 
 
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)) {
 
574
                                        gchar *pw;
 
575
 
 
576
                                        passwd_handler->backend_state = PASSWD_STATE_NEW;
 
577
 
 
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);
 
581
                                        g_free (pw);
 
582
 
 
583
                                        /* Pop the IO queue, i.e. send new password */
 
584
                                        io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
 
585
 
 
586
                                } else {
 
587
 
 
588
                                        passwd_handler->backend_state = PASSWD_STATE_AUTH;
 
589
 
 
590
                                        /* Pop the IO queue, i.e. send current password */
 
591
                                        io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
 
592
                                }
 
593
 
 
594
                                reinit = TRUE;
 
595
                        }
 
596
                        break;
 
597
                default:
 
598
                        /* Passwd has returned an error */
 
599
                        reinit = TRUE;
 
600
                        break;
 
601
        }
 
602
 
 
603
        if (reinit) {
 
604
                g_string_free (str, TRUE);
 
605
                str = NULL;
 
606
        }
 
607
 
 
608
        /* Continue calling us */
 
609
        return TRUE;
 
610
}
 
611
 
 
612
/*
 
613
 * }} Backend communication code
 
614
 */
 
615
 
 
616
/* Adds the current password to the IO queue */
 
617
static void
 
618
authenticate (PasswdHandler *passwd_handler)
 
619
{
 
620
        gchar   *s;
 
621
 
 
622
        s = g_strdup_printf ("%s\n", passwd_handler->current_password);
 
623
 
 
624
        g_queue_push_tail (passwd_handler->backend_stdin_queue, s);
 
625
}
 
626
 
 
627
/* Adds the new password twice to the IO queue */
 
628
static void
 
629
update_password (PasswdHandler *passwd_handler)
 
630
{
 
631
        gchar   *s;
 
632
 
 
633
        s = g_strdup_printf ("%s\n", passwd_handler->new_password);
 
634
 
 
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));
 
639
}
 
640
 
 
641
 
 
642
PasswdHandler *
 
643
passwd_init (void)
 
644
{
 
645
        PasswdHandler *passwd_handler;
 
646
 
 
647
        passwd_handler = g_new0 (PasswdHandler, 1);
 
648
 
 
649
        /* Initialize backend_pid. -1 means the backend is not running */
 
650
        passwd_handler->backend_pid = -1;
 
651
 
 
652
        /* Initialize IO Channels */
 
653
        passwd_handler->backend_stdin = NULL;
 
654
        passwd_handler->backend_stdout = NULL;
 
655
 
 
656
        /* Initialize write queue */
 
657
        passwd_handler->backend_stdin_queue = g_queue_new ();
 
658
 
 
659
        /* Initialize watchers */
 
660
        passwd_handler->backend_child_watch_id = 0;
 
661
        passwd_handler->backend_stdout_watch_id = 0;
 
662
 
 
663
        /* Initialize backend state */
 
664
        passwd_handler->backend_state = PASSWD_STATE_NONE;
 
665
        passwd_handler->changing_password = FALSE;
 
666
 
 
667
        return passwd_handler;
 
668
}
 
669
 
 
670
void
 
671
passwd_destroy (PasswdHandler *passwd_handler)
 
672
{
 
673
        g_queue_free (passwd_handler->backend_stdin_queue);
 
674
        stop_passwd (passwd_handler);
 
675
        g_free (passwd_handler);
 
676
}
 
677
 
 
678
void
 
679
passwd_authenticate (PasswdHandler *passwd_handler,
 
680
                     const char    *current_password,
 
681
                     PasswdCallback cb,
 
682
                     const gpointer user_data)
 
683
{
 
684
        GError *error = NULL;
 
685
 
 
686
        /* Don't stop if we've already started chaging password */
 
687
        if (passwd_handler->changing_password)
 
688
                return;
 
689
 
 
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);
 
696
 
 
697
        passwd_handler->current_password = current_password;
 
698
        passwd_handler->auth_cb = cb;
 
699
        passwd_handler->auth_cb_data = user_data;
 
700
 
 
701
        /* Spawn backend */
 
702
        stop_passwd (passwd_handler);
 
703
 
 
704
        if (!spawn_passwd (passwd_handler, &error)) {
 
705
                g_warning ("%s", error->message);
 
706
                g_error_free (error);
 
707
 
 
708
                return;
 
709
        }
 
710
 
 
711
        authenticate (passwd_handler);
 
712
 
 
713
        /* Our IO watcher should now handle the rest */
 
714
}
 
715
 
 
716
gboolean
 
717
passwd_change_password (PasswdHandler *passwd_handler,
 
718
                        const char    *new_password,
 
719
                        PasswdCallback cb,
 
720
                        const gpointer user_data)
 
721
{
 
722
        GError *error = NULL;
 
723
 
 
724
        passwd_handler->changing_password = TRUE;
 
725
 
 
726
        passwd_handler->new_password = new_password;
 
727
        passwd_handler->chpasswd_cb = cb;
 
728
        passwd_handler->chpasswd_cb_data = user_data;
 
729
 
 
730
        /* Stop passwd if an error occured and it is still running */
 
731
        if (passwd_handler->backend_state == PASSWD_STATE_ERR) {
 
732
 
 
733
                /* Stop passwd, free resources */
 
734
                stop_passwd (passwd_handler);
 
735
        }
 
736
 
 
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 */
 
741
 
 
742
                /* Spawn backend */
 
743
                stop_passwd (passwd_handler);
 
744
 
 
745
                if (!spawn_passwd (passwd_handler, &error)) {
 
746
                        g_warning ("%s", error->message);
 
747
                        g_error_free (error);
 
748
 
 
749
                        return FALSE;
 
750
                }
 
751
 
 
752
                /* Add current and new passwords to queue */
 
753
                authenticate (passwd_handler);
 
754
                update_password (passwd_handler);
 
755
        } else {
 
756
                /* Only add new passwords to queue */
 
757
                update_password (passwd_handler);
 
758
        }
 
759
 
 
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.
 
765
         */
 
766
        if (passwd_handler->current_password)
 
767
                io_queue_pop (passwd_handler->backend_stdin_queue, passwd_handler->backend_stdin);
 
768
 
 
769
        /* Our IO watcher should now handle the rest */
 
770
 
 
771
        return TRUE;
 
772
}