~ubuntu-branches/ubuntu/raring/shadow/raring-proposed

« back to all changes in this revision

Viewing changes to src/passwd.c

  • Committer: Bazaar Package Importer
  • Author(s): Kees Cook
  • Date: 2009-05-05 09:45:21 UTC
  • mfrom: (1.1.6 upstream)
  • Revision ID: james.westby@ubuntu.com-20090505094521-wpk2wn3q7957tlah
Tags: 1:4.1.3.1-1ubuntu1
* Merge from debian unstable, remaining changes:
  - Ubuntu specific:
    + debian/login.defs: use SHA512 by default for password crypt routine.
  - debian/patches/stdout-encrypted-password.patch: chpasswd can report
    password hashes on stdout (debian bug 505640).
  - debian/login.pam: Enable SELinux support (debian bug 527106).
  - debian/securetty.linux: support Freescale MX-series (debian bug 527095).
* Add debian/patches/300_lastlog_failure: fixed upstream (debian bug 524873).
* Drop debian/patches/593_omit_lastchange_field_if_clock_is_misset: fixed
  upstream.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
 
 * Copyright 1989 - 1994, Julianne Frances Haugh
 
2
 * Copyright (c) 1989 - 1994, Julianne Frances Haugh
 
3
 * Copyright (c) 1996 - 2000, Marek Michałkiewicz
 
4
 * Copyright (c) 2001 - 2006, Tomasz Kłoczko
 
5
 * Copyright (c) 2007 - 2009, Nicolas François
3
6
 * All rights reserved.
4
7
 *
5
8
 * Redistribution and use in source and binary forms, with or without
10
13
 * 2. Redistributions in binary form must reproduce the above copyright
11
14
 *    notice, this list of conditions and the following disclaimer in the
12
15
 *    documentation and/or other materials provided with the distribution.
13
 
 * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
14
 
 *    may be used to endorse or promote products derived from this software
15
 
 *    without specific prior written permission.
 
16
 * 3. The name of the copyright holders or contributors may not be used to
 
17
 *    endorse or promote products derived from this software without
 
18
 *    specific prior written permission.
16
19
 *
17
 
 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
18
 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 
 * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
21
 
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 
 * SUCH DAMAGE.
 
20
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
21
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
22
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 
23
 * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
 
24
 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
25
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
26
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
27
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
28
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
29
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 
30
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
31
 */
29
32
 
30
33
#include <config.h>
31
34
 
32
 
#ident "$Id: passwd.c 1932 2008-03-26 22:00:50Z nekral-guest $"
 
35
#ident "$Id: passwd.c 2612 2009-04-10 22:34:23Z nekral-guest $"
33
36
 
34
37
#include <errno.h>
35
38
#include <fcntl.h>
65
68
/*
66
69
 * Global variables
67
70
 */
 
71
char *Prog;                     /* Program name */
 
72
 
68
73
static char *name;              /* The name of user whose password is being changed */
69
74
static char *myname;            /* The current user's name */
70
 
static char *Prog;              /* Program name */
71
 
static int amroot;              /* The real UID was 0 */
 
75
static bool amroot;             /* The caller's real UID was 0 */
72
76
 
73
 
static int
74
 
 aflg = 0,                      /* -a - show status for all users */
75
 
    dflg = 0,                   /* -d - delete password */
76
 
    eflg = 0,                   /* -e - force password change */
77
 
    iflg = 0,                   /* -i - set inactive days */
78
 
    kflg = 0,                   /* -k - change only if expired */
79
 
    lflg = 0,                   /* -l - lock account */
80
 
    nflg = 0,                   /* -n - set minimum days */
81
 
    qflg = 0,                   /* -q - quiet mode */
82
 
    Sflg = 0,                   /* -S - show password status */
83
 
    uflg = 0,                   /* -u - unlock account */
84
 
    wflg = 0,                   /* -w - set warning days */
85
 
    xflg = 0;                   /* -x - set maximum days */
 
77
static bool
 
78
    aflg = false,                       /* -a - show status for all users */
 
79
    dflg = false,                       /* -d - delete password */
 
80
    eflg = false,                       /* -e - force password change */
 
81
    iflg = false,                       /* -i - set inactive days */
 
82
    kflg = false,                       /* -k - change only if expired */
 
83
    lflg = false,                       /* -l - lock the user's password */
 
84
    nflg = false,                       /* -n - set minimum days */
 
85
    qflg = false,                       /* -q - quiet mode */
 
86
    Sflg = false,                       /* -S - show password status */
 
87
    uflg = false,                       /* -u - unlock the user's password */
 
88
    wflg = false,                       /* -w - set warning days */
 
89
    xflg = false;                       /* -x - set maximum days */
86
90
 
87
91
/*
88
92
 * set to 1 if there are any flags which require root privileges,
89
93
 * and require username to be specified
90
94
 */
91
 
static int anyflag = 0;
 
95
static bool anyflag = false;
92
96
 
93
97
static long age_min = 0;        /* Minimum days before change   */
94
98
static long age_max = 0;        /* Maximum days until change     */
95
99
static long warn = 0;           /* Warning days before change   */
96
100
static long inact = 0;          /* Days without change before locked */
97
101
 
98
 
static int do_update_age = 0;
 
102
#ifndef USE_PAM
 
103
static bool do_update_age = false;
 
104
#endif
 
105
 
 
106
static bool pw_locked = false;
 
107
static bool spw_locked = false;
99
108
 
100
109
#ifndef USE_PAM
101
110
/*
112
121
 *   total      161
113
122
 */
114
123
static char crypt_passwd[256];
115
 
static int do_update_pwd = 0;
 
124
static bool do_update_pwd = false;
116
125
#endif
117
126
 
118
127
/*
138
147
static void update_noshadow (void);
139
148
 
140
149
static void update_shadow (void);
141
 
static long getnumber (const char *);
 
150
#ifdef WITH_SELINUX
 
151
static int check_selinux_access (const char *changed_user,
 
152
                                 uid_t changed_uid,
 
153
                                 access_vector_t requested_access);
 
154
#endif
142
155
 
143
156
/*
144
157
 * usage - print command usage and exit
155
168
                 "  -k, --keep-tokens             change password only if expired\n"
156
169
                 "  -i, --inactive INACTIVE       set password inactive after expiration\n"
157
170
                 "                                to INACTIVE\n"
158
 
                 "  -l, --lock                    lock the named account\n"
 
171
                 "  -l, --lock                    lock the password of the named account\n"
159
172
                 "  -n, --mindays MIN_DAYS        set minimum number of days before password\n"
160
173
                 "                                change to MIN_DAYS\n"
161
174
                 "  -q, --quiet                   quiet mode\n"
162
175
                 "  -r, --repository REPOSITORY   change password in REPOSITORY repository\n"
163
176
                 "  -S, --status                  report password status on the named account\n"
164
 
                 "  -u, --unlock                  unlock the named account\n"
 
177
                 "  -u, --unlock                  unlock the password of the named account\n"
165
178
                 "  -w, --warndays WARN_DAYS      set expiration warning days to WARN_DAYS\n"
166
 
                 "  -x, --maxdays MAX_DAYS        set maximim number of days before password\n"
 
179
                 "  -x, --maxdays MAX_DAYS        set maximum number of days before password\n"
167
180
                 "                                change to MAX_DAYS\n"
168
181
                 "\n"), stderr);
169
182
        exit (status);
184
197
 
185
198
        reason = FascistHistory (pass, pw->pw_uid);
186
199
#endif
187
 
        if (reason) {
 
200
        if (NULL != reason) {
188
201
                printf (_("Bad password: %s.  "), reason);
189
202
                return 1;
190
203
        }
218
231
         */
219
232
 
220
233
        if (!amroot && crypt_passwd[0]) {
221
 
                if (!(clear = getpass (_("Old password: "))))
 
234
                clear = getpass (_("Old password: "));
 
235
                if (NULL == clear) {
222
236
                        return -1;
 
237
                }
223
238
 
224
239
                cipher = pw_encrypt (clear, crypt_passwd);
225
240
                if (strcmp (cipher, crypt_passwd) != 0) {
245
260
         * for initial login passwords.
246
261
         */
247
262
        if ((method = getdef_str ("ENCRYPT_METHOD")) == NULL) {
248
 
                if (!getdef_bool ("MD5_CRYPT_ENAB"))
 
263
                if (!getdef_bool ("MD5_CRYPT_ENAB")) {
249
264
                        pass_max_len = getdef_num ("PASS_MAX_LEN", 8);
 
265
                }
250
266
        } else {
251
 
                if (   !strcmp (method, "MD5")
 
267
                if (   (strcmp (method, "MD5")    == 0)
252
268
#ifdef USE_SHA_CRYPT
253
 
                    || !strcmp (method, "SHA256")
254
 
                    || !strcmp (method, "SHA512")
 
269
                    || (strcmp (method, "SHA256") == 0)
 
270
                    || (strcmp (method, "SHA512") == 0)
255
271
#endif
256
 
                    )
 
272
                    ) {
257
273
                        pass_max_len = -1;
258
 
                else
 
274
                } else {
259
275
                        pass_max_len = getdef_num ("PASS_MAX_LEN", 8);
 
276
                }
260
277
        }
261
278
        if (!qflg) {
262
279
                if (pass_max_len == -1) {
274
291
 
275
292
        warned = 0;
276
293
        for (i = getdef_num ("PASS_CHANGE_TRIES", 5); i > 0; i--) {
277
 
                if (!(cp = getpass (_("New password: ")))) {
 
294
                cp = getpass (_("New password: "));
 
295
                if (NULL == cp) {
278
296
                        memzero (orig, sizeof orig);
279
297
                        return -1;
280
298
                }
281
 
                if (warned && strcmp (pass, cp) != 0)
 
299
                if (warned && (strcmp (pass, cp) != 0)) {
282
300
                        warned = 0;
 
301
                }
283
302
                STRFCPY (pass, cp);
284
303
                strzero (cp);
285
304
 
299
318
                        warned++;
300
319
                        continue;
301
320
                }
302
 
                if (!(cp = getpass (_("Re-enter new password: ")))) {
 
321
                cp = getpass (_("Re-enter new password: "));
 
322
                if (NULL == cp) {
303
323
                        memzero (orig, sizeof orig);
304
324
                        return -1;
305
325
                }
306
 
                if (strcmp (cp, pass))
 
326
                if (strcmp (cp, pass) != 0) {
307
327
                        fputs (_("They don't match; try again.\n"), stderr);
308
 
                else {
 
328
                } else {
309
329
                        strzero (cp);
310
330
                        break;
311
331
                }
347
367
         * If not expired and the "change only if expired" option (idea from
348
368
         * PAM) was specified, do nothing. --marekm
349
369
         */
350
 
        if (kflg && exp_status == 0)
 
370
        if (kflg && (0 == exp_status)) {
351
371
                exit (E_SUCCESS);
 
372
        }
352
373
 
353
374
        /*
354
375
         * Root can change any password any time.
355
376
         */
356
 
        if (amroot)
 
377
        if (amroot) {
357
378
                return;
 
379
        }
358
380
 
359
 
        time (&now);
 
381
        (void) time (&now);
360
382
 
361
383
        /*
362
384
         * Expired accounts cannot be changed ever. Passwords which are
364
386
         * changed. Passwords which have been inactive too long cannot be
365
387
         * changed.
366
388
         */
367
 
        if (sp->sp_pwdp[0] == '!' || exp_status > 1 ||
368
 
            (sp->sp_max >= 0 && sp->sp_min > sp->sp_max)) {
 
389
        if (   (sp->sp_pwdp[0] == '!')
 
390
            || (exp_status > 1)
 
391
            || (   (sp->sp_max >= 0)
 
392
                && (sp->sp_min > sp->sp_max))) {
369
393
                fprintf (stderr,
370
 
                         _("The password for %s cannot be changed.\n"),
371
 
                         sp->sp_namp);
372
 
                SYSLOG ((LOG_WARN, "password locked for `%s'", sp->sp_namp));
 
394
                         _("The password for %s cannot be changed.\n"),
 
395
                         sp->sp_namp);
 
396
                SYSLOG ((LOG_WARN, "password locked for '%s'", sp->sp_namp));
373
397
                closelog ();
374
398
                exit (E_NOPERM);
375
399
        }
377
401
        /*
378
402
         * Passwords may only be changed after sp_min time is up.
379
403
         */
380
 
        last = sp->sp_lstchg * SCALE;
381
 
        ok = last + (sp->sp_min > 0 ? sp->sp_min * SCALE : 0);
 
404
        if (sp->sp_lstchg > 0) {
 
405
                last = sp->sp_lstchg * SCALE;
 
406
                ok = last + (sp->sp_min > 0 ? sp->sp_min * SCALE : 0);
382
407
 
383
 
        if (now < ok) {
384
 
                fprintf (stderr,
385
 
                         _
386
 
                         ("The password for %s cannot be changed yet.\n"),
387
 
                         pw->pw_name);
388
 
                SYSLOG ((LOG_WARN, "now < minimum age for `%s'", pw->pw_name));
389
 
                closelog ();
390
 
                exit (E_NOPERM);
 
408
                if (now < ok) {
 
409
                        fprintf (stderr,
 
410
                                 _("The password for %s cannot be changed yet.\n"),
 
411
                                 pw->pw_name);
 
412
                        SYSLOG ((LOG_WARN, "now < minimum age for '%s'", pw->pw_name));
 
413
                        closelog ();
 
414
                        exit (E_NOPERM);
 
415
                }
391
416
        }
392
417
}
393
418
 
418
443
 
419
444
static const char *pw_status (const char *pass)
420
445
{
421
 
        if (*pass == '*' || *pass == '!')
 
446
        if (*pass == '*' || *pass == '!') {
422
447
                return "L";
423
 
        if (*pass == '\0')
 
448
        }
 
449
        if (*pass == '\0') {
424
450
                return "NP";
 
451
        }
425
452
        return "P";
426
453
}
427
454
 
433
460
        struct spwd *sp;
434
461
 
435
462
        sp = getspnam (pw->pw_name); /* local, no need for xgetspnam */
436
 
        if (sp) {
 
463
        if (NULL != sp) {
437
464
                printf ("%s %s %s %ld %ld %ld %ld\n",
438
465
                        pw->pw_name,
439
466
                        pw_status (sp->sp_pwdp),
450
477
 
451
478
static void fail_exit (int status)
452
479
{
453
 
        pw_unlock ();
454
 
        spw_unlock ();
 
480
        if (pw_locked) {
 
481
                if (pw_unlock () == 0) {
 
482
                        fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
 
483
                        SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
 
484
                        /* continue */
 
485
                }
 
486
        }
 
487
 
 
488
        if (spw_locked) {
 
489
                if (spw_unlock () == 0) {
 
490
                        fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
 
491
                        SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
 
492
                        /* continue */
 
493
                }
 
494
        }
 
495
 
455
496
        exit (status);
456
497
}
457
498
 
464
505
static char *update_crypt_pw (char *cp)
465
506
{
466
507
#ifndef USE_PAM
467
 
        if (do_update_pwd)
 
508
        if (do_update_pwd) {
468
509
                cp = insert_crypt_passwd (cp, crypt_passwd);
 
510
        }
469
511
#endif
470
512
 
471
 
        if (dflg)
 
513
        if (dflg) {
472
514
                *cp = '\0';
 
515
        }
473
516
 
474
517
        if (uflg && *cp == '!') {
475
518
                if (cp[1] == '\0') {
476
519
                        fprintf (stderr,
477
 
                                 _("%s: unlocking the user would result in a passwordless account.\n"
478
 
                                   "You should set a password with usermod -p to unlock this user account.\n"),
 
520
                                 _("%s: unlocking the password would result in a passwordless account.\n"
 
521
                                   "You should set a password with usermod -p to unlock the password of this account.\n"),
479
522
                                 Prog);
480
523
                } else {
481
524
                        cp++;
498
541
        const struct passwd *pw;
499
542
        struct passwd *npw;
500
543
 
501
 
        if (!pw_lock ()) {
502
 
                fputs (_("Cannot lock the password file; try again later.\n"),
503
 
                       stderr);
504
 
                SYSLOG ((LOG_WARN, "can't lock password file"));
 
544
        if (pw_lock () == 0) {
 
545
                fprintf (stderr,
 
546
                         _("%s: cannot lock %s; try again later.\n"),
 
547
                         Prog, pw_dbname ());
505
548
                exit (E_PWDBUSY);
506
549
        }
507
 
        if (!pw_open (O_RDWR)) {
508
 
                fputs (_("Cannot open the password file.\n"), stderr);
509
 
                SYSLOG ((LOG_ERR, "can't open password file"));
 
550
        pw_locked = true;
 
551
        if (pw_open (O_RDWR) == 0) {
 
552
                fprintf (stderr,
 
553
                         _("%s: cannot open %s\n"),
 
554
                         Prog, pw_dbname ());
 
555
                SYSLOG ((LOG_WARN, "cannot open %s", pw_dbname ()));
510
556
                fail_exit (E_MISSING);
511
557
        }
512
558
        pw = pw_locate (name);
513
 
        if (!pw) {
514
 
                fprintf (stderr, _("%s: %s not found in /etc/passwd\n"),
515
 
                         Prog, name);
 
559
        if (NULL == pw) {
 
560
                fprintf (stderr,
 
561
                         _("%s: user '%s' does not exist in %s\n"),
 
562
                         Prog, name, pw_dbname ());
516
563
                fail_exit (E_NOPERM);
517
564
        }
518
565
        npw = __pw_dup (pw);
519
 
        if (!npw)
 
566
        if (NULL == npw) {
520
567
                oom ();
 
568
        }
521
569
        npw->pw_passwd = update_crypt_pw (npw->pw_passwd);
522
 
        if (!pw_update (npw)) {
523
 
                fputs (_("Error updating the password entry.\n"), stderr);
524
 
                SYSLOG ((LOG_ERR, "error updating password entry"));
525
 
                fail_exit (E_FAILURE);
526
 
        }
527
 
        if (!pw_close ()) {
528
 
                fputs (_("Cannot commit password file changes.\n"), stderr);
529
 
                SYSLOG ((LOG_ERR, "can't rewrite password file"));
530
 
                fail_exit (E_FAILURE);
531
 
        }
532
 
        pw_unlock ();
 
570
        if (pw_update (npw) == 0) {
 
571
                fprintf (stderr,
 
572
                         _("%s: failed to prepare the new %s entry '%s'\n"),
 
573
                         Prog, pw_dbname (), npw->pw_name);
 
574
                fail_exit (E_FAILURE);
 
575
        }
 
576
        if (pw_close () == 0) {
 
577
                fprintf (stderr,
 
578
                         _("%s: failure while writing changes to %s\n"),
 
579
                         Prog, pw_dbname ());
 
580
                SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ()));
 
581
                fail_exit (E_FAILURE);
 
582
        }
 
583
        if (pw_unlock () == 0) {
 
584
                fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
 
585
                SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
 
586
                /* continue */
 
587
        }
 
588
        pw_locked = false;
533
589
}
534
590
 
535
591
static void update_shadow (void)
537
593
        const struct spwd *sp;
538
594
        struct spwd *nsp;
539
595
 
540
 
        if (!spw_lock ()) {
541
 
                fputs (_("Cannot lock the password file; try again later.\n"),
542
 
                       stderr);
543
 
                SYSLOG ((LOG_WARN, "can't lock password file"));
 
596
        if (spw_lock () == 0) {
 
597
                fprintf (stderr,
 
598
                         _("%s: cannot lock %s; try again later.\n"),
 
599
                         Prog, spw_dbname ());
544
600
                exit (E_PWDBUSY);
545
601
        }
546
 
        if (!spw_open (O_RDWR)) {
547
 
                fputs (_("Cannot open the password file.\n"), stderr);
548
 
                SYSLOG ((LOG_ERR, "can't open password file"));
 
602
        spw_locked = true;
 
603
        if (spw_open (O_RDWR) == 0) {
 
604
                fprintf (stderr, _("%s: cannot open %s\n"), Prog, spw_dbname ());
 
605
                SYSLOG ((LOG_WARN, "cannot open %s", spw_dbname ()));
549
606
                fail_exit (E_FAILURE);
550
607
        }
551
608
        sp = spw_locate (name);
552
 
        if (!sp) {
 
609
        if (NULL == sp) {
553
610
                /* Try to update the password in /etc/passwd instead. */
554
 
                spw_close ();
 
611
                (void) spw_close ();
555
612
                update_noshadow ();
556
 
                spw_unlock ();
 
613
                if (spw_unlock () == 0) {
 
614
                        fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
 
615
                        SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
 
616
                        /* continue */
 
617
                }
 
618
                spw_locked = false;
557
619
                return;
558
620
        }
559
621
        nsp = __spw_dup (sp);
560
 
        if (!nsp)
 
622
        if (NULL == nsp) {
561
623
                oom ();
 
624
        }
562
625
        nsp->sp_pwdp = update_crypt_pw (nsp->sp_pwdp);
563
 
        if (xflg)
 
626
        if (xflg) {
564
627
                nsp->sp_max = (age_max * DAY) / SCALE;
565
 
        if (nflg)
 
628
        }
 
629
        if (nflg) {
566
630
                nsp->sp_min = (age_min * DAY) / SCALE;
567
 
        if (wflg)
 
631
        }
 
632
        if (wflg) {
568
633
                nsp->sp_warn = (warn * DAY) / SCALE;
569
 
        if (iflg)
 
634
        }
 
635
        if (iflg) {
570
636
                nsp->sp_inact = (inact * DAY) / SCALE;
571
 
        if (do_update_age)
572
 
                nsp->sp_lstchg = time ((time_t *) 0) / SCALE;
573
 
        if (lflg) {
574
 
                /* Set the account expiry field to 1.
575
 
                 * Some PAM implementation consider zero as a non expired
576
 
                 * account.
577
 
                 */
578
 
                nsp->sp_expire = 1;
579
 
        }
580
 
        if (uflg)
581
 
                nsp->sp_expire = -1;
 
637
        }
 
638
#ifndef USE_PAM
 
639
        if (do_update_age) {
 
640
                nsp->sp_lstchg = (long) time ((time_t *) 0) / SCALE;
 
641
                if (0 == nsp->sp_lstchg) {
 
642
                        /* Better disable aging than requiring a password
 
643
                         * change */
 
644
                        nsp->sp_lstchg = -1;
 
645
                }
 
646
        }
 
647
#endif
582
648
 
583
649
        /*
584
650
         * Force change on next login, like SunOS 4.x passwd -e or Solaris
585
651
         * 2.x passwd -f. Solaris 2.x seems to do the same thing (set
586
652
         * sp_lstchg to 0).
587
653
         */
588
 
        if (eflg)
 
654
        if (eflg) {
589
655
                nsp->sp_lstchg = 0;
590
 
 
591
 
        if (!spw_update (nsp)) {
592
 
                fputs (_("Error updating the password entry.\n"), stderr);
593
 
                SYSLOG ((LOG_ERR, "error updating password entry"));
594
 
                fail_exit (E_FAILURE);
595
 
        }
596
 
        if (!spw_close ()) {
597
 
                fputs (_("Cannot commit password file changes.\n"), stderr);
598
 
                SYSLOG ((LOG_ERR, "can't rewrite password file"));
599
 
                fail_exit (E_FAILURE);
600
 
        }
601
 
        spw_unlock ();
602
 
}
603
 
 
604
 
static long getnumber (const char *numstr)
605
 
{
606
 
        long val;
607
 
        char *errptr;
608
 
 
609
 
        val = strtol (numstr, &errptr, 10);
610
 
        if (*errptr || errno == ERANGE) {
611
 
                fprintf (stderr, _("%s: invalid numeric argument '%s'\n"), Prog,
612
 
                         numstr);
613
 
                exit (E_BAD_ARG);
614
 
        }
615
 
        return val;
 
656
        }
 
657
 
 
658
        if (spw_update (nsp) == 0) {
 
659
                fprintf (stderr,
 
660
                         _("%s: failed to prepare the new %s entry '%s'\n"),
 
661
                         Prog, spw_dbname (), nsp->sp_namp);
 
662
                fail_exit (E_FAILURE);
 
663
        }
 
664
        if (spw_close () == 0) {
 
665
                fprintf (stderr,
 
666
                         _("%s: failure while writing changes to %s\n"),
 
667
                         Prog, spw_dbname ());
 
668
                SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ()));
 
669
                fail_exit (E_FAILURE);
 
670
        }
 
671
        if (spw_unlock () == 0) {
 
672
                fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
 
673
                SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
 
674
                /* continue */
 
675
        }
 
676
        spw_locked = false;
616
677
}
617
678
 
618
679
#ifdef WITH_SELINUX
619
 
int
620
 
check_selinux_access(const char *change_user, int change_uid, unsigned int access)
 
680
static int check_selinux_access (const char *changed_user,
 
681
                                 uid_t changed_uid,
 
682
                                 access_vector_t requested_access)
621
683
{
622
684
        int status = -1;
623
685
        security_context_t user_context;
 
686
        context_t c;
624
687
        const char *user;
625
688
 
626
689
        /* if in permissive mode then allow the operation */
627
 
        if (security_getenforce() == 0)
 
690
        if (security_getenforce() == 0) {
628
691
                return 0;
 
692
        }
629
693
 
630
694
        /* get the context of the process which executed passwd */
631
 
        if (getprevcon(&user_context))
 
695
        if (getprevcon(&user_context) != 0) {
632
696
                return -1;
 
697
        }
633
698
 
634
699
        /* get the "user" portion of the context (the part before the first
635
700
           colon) */
636
 
        context_t c;
637
701
        c = context_new(user_context);
638
702
        user = context_user_get(c);
639
703
 
640
704
        /* if changing a password for an account with UID==0 or for an account
641
705
           where the identity matches then return success */
642
 
        if (change_uid != 0 && strcmp(change_user, user) == 0) {
 
706
        if (changed_uid != 0 && strcmp(changed_user, user) == 0) {
643
707
                status = 0;
644
708
        } else {
645
709
                struct av_decision avd;
646
710
                int retval;
647
 
                retval = security_compute_av(user_context, user_context,
648
 
                                SECCLASS_PASSWD, access, &avd);
 
711
                retval = security_compute_av(user_context,
 
712
                                             user_context,
 
713
                                             SECCLASS_PASSWD,
 
714
                                             requested_access,
 
715
                                             &avd);
649
716
                if ((retval == 0) &&
650
 
                        ((access & avd.allowed) == access)) {
 
717
                    ((requested_access & avd.allowed) == requested_access)) {
651
718
                        status = 0;
652
719
                }
653
720
        }
672
739
 *      -g      execute gpasswd command to interpret flags
673
740
 *      -i #    set sp_inact to # days (*)
674
741
 *      -k      change password only if expired
675
 
 *      -l      lock the named account (*)
 
742
 *      -l      lock the password of the named account (*)
676
743
 *      -n #    set sp_min to # days (*)
677
744
 *      -r #    change password in # repository
678
745
 *      -s      execute chsh command to interpret flags
679
746
 *      -S      show password status of named account
680
 
 *      -u      unlock the named account (*)
 
747
 *      -u      unlock the password of the named account (*)
681
748
 *      -w #    set sp_warn to # days (*)
682
749
 *      -x #    set sp_max to # days (*)
683
750
 *
697
764
        const struct spwd *sp;  /* Shadow file entry for user   */
698
765
#endif
699
766
 
700
 
        setlocale (LC_ALL, "");
701
 
        bindtextdomain (PACKAGE, LOCALEDIR);
702
 
        textdomain (PACKAGE);
 
767
        (void) setlocale (LC_ALL, "");
 
768
        (void) bindtextdomain (PACKAGE, LOCALEDIR);
 
769
        (void) textdomain (PACKAGE);
703
770
 
704
771
        /*
705
772
         * The program behaves differently when executed by root than when
746
813
                                     long_options, &option_index)) != -1) {
747
814
                        switch (c) {
748
815
                        case 'a':
749
 
                                aflg++;
 
816
                                aflg = true;
750
817
                                break;
751
818
                        case 'd':
752
 
                                dflg++;
753
 
                                anyflag = 1;
 
819
                                dflg = true;
 
820
                                anyflag = true;
754
821
                                break;
755
822
                        case 'e':
756
 
                                eflg++;
757
 
                                anyflag = 1;
 
823
                                eflg = true;
 
824
                                anyflag = true;
758
825
                                break;
759
826
                        case 'i':
760
 
                                inact = getnumber (optarg);
761
 
                                if (inact >= -1)
762
 
                                        iflg++;
763
 
                                anyflag = 1;
 
827
                                if (   (getlong (optarg, &inact) == 0)
 
828
                                    || (inact < -1)) {
 
829
                                        fprintf (stderr,
 
830
                                                 _("%s: invalid numeric argument '%s'\n"),
 
831
                                                 Prog, optarg);
 
832
                                        usage (E_BAD_ARG);
 
833
                                }
 
834
                                iflg = true;
 
835
                                anyflag = true;
764
836
                                break;
765
837
                        case 'k':
766
838
                                /* change only if expired, like Linux-PAM passwd -k. */
767
 
                                kflg++; /* ok for users */
 
839
                                kflg = true;    /* ok for users */
768
840
                                break;
769
841
                        case 'l':
770
 
                                lflg++;
771
 
                                anyflag = 1;
 
842
                                lflg = true;
 
843
                                anyflag = true;
772
844
                                break;
773
845
                        case 'n':
774
 
                                age_min = getnumber (optarg);
775
 
                                nflg++;
776
 
                                anyflag = 1;
 
846
                                if (   (getlong (optarg, &age_min) == 0)
 
847
                                    || (age_min < -1)) {
 
848
                                        fprintf (stderr,
 
849
                                                 _("%s: invalid numeric argument '%s'\n"),
 
850
                                                 Prog, optarg);
 
851
                                        usage (E_BAD_ARG);
 
852
                                }
 
853
                                nflg = true;
 
854
                                anyflag = true;
777
855
                                break;
778
856
                        case 'q':
779
 
                                qflg++; /* ok for users */
 
857
                                qflg = true;    /* ok for users */
780
858
                                break;
781
859
                        case 'r':
782
860
                                /* -r repository (files|nis|nisplus) */
783
861
                                /* only "files" supported for now */
784
862
                                if (strcmp (optarg, "files") != 0) {
785
863
                                        fprintf (stderr,
786
 
                                                 _
787
 
                                                 ("%s: repository %s not supported\n"),
 
864
                                                 _("%s: repository %s not supported\n"),
788
865
                                                 Prog, optarg);
789
866
                                        exit (E_BAD_ARG);
790
867
                                }
791
868
                                break;
792
869
                        case 'S':
793
 
                                Sflg++; /* ok for users */
 
870
                                Sflg = true;    /* ok for users */
794
871
                                break;
795
872
                        case 'u':
796
 
                                uflg++;
797
 
                                anyflag = 1;
 
873
                                uflg = true;
 
874
                                anyflag = true;
798
875
                                break;
799
876
                        case 'w':
800
 
                                warn = getnumber (optarg);
801
 
                                if (warn >= -1)
802
 
                                        wflg++;
803
 
                                anyflag = 1;
 
877
                                if (   (getlong (optarg, &warn) == 0)
 
878
                                    || (warn < -1)) {
 
879
                                        fprintf (stderr,
 
880
                                                 _("%s: invalid numeric argument '%s'\n"),
 
881
                                                 Prog, optarg);
 
882
                                        usage (E_BAD_ARG);
 
883
                                }
 
884
                                wflg = true;
 
885
                                anyflag = true;
804
886
                                break;
805
887
                        case 'x':
806
 
                                age_max = getnumber (optarg);
807
 
                                xflg++;
808
 
                                anyflag = 1;
 
888
                                if (   (getlong (optarg, &age_max) == 0)
 
889
                                    || (age_max < -1)) {
 
890
                                        fprintf (stderr,
 
891
                                                 _("%s: invalid numeric argument '%s'\n"),
 
892
                                                 Prog, optarg);
 
893
                                        usage (E_BAD_ARG);
 
894
                                }
 
895
                                xflg = true;
 
896
                                anyflag = true;
809
897
                                break;
810
898
                        default:
811
899
                                usage (E_BAD_ARG);
819
907
         * environment.
820
908
         */
821
909
        pw = get_my_pwent ();
822
 
        if (!pw) {
 
910
        if (NULL == pw) {
823
911
                fprintf (stderr,
824
 
                         _("%s: Cannot determine your user name.\n"), Prog);
 
912
                         _("%s: Cannot determine your user name.\n"), Prog);
 
913
                SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)",
 
914
                         (unsigned long) getuid ()));
825
915
                exit (E_NOPERM);
826
916
        }
827
917
        myname = xstrdup (pw->pw_name);
828
 
        if (optind < argc)
 
918
        if (optind < argc) {
829
919
                name = argv[optind];
830
 
        else
 
920
        } else {
831
921
                name = myname;
 
922
        }
832
923
 
833
924
        /*
834
925
         * Make sure that at most one username was specified.
835
926
         */
836
 
        if (argc > optind+1)
 
927
        if (argc > (optind+1)) {
837
928
                usage (E_USAGE);
 
929
        }
838
930
 
839
931
        /*
840
932
         * The -a flag requires -S, no other flags, no username, and
841
933
         * you must be root.  --marekm
842
934
         */
843
935
        if (aflg) {
844
 
                if (anyflag || !Sflg || (optind < argc))
 
936
                if (anyflag || !Sflg || (optind < argc)) {
845
937
                        usage (E_USAGE);
 
938
                }
846
939
                if (!amroot) {
847
940
                        fprintf (stderr, _("%s: Permission denied.\n"), Prog);
848
941
                        exit (E_NOPERM);
849
942
                }
850
943
                setpwent ();
851
 
                while ((pw = getpwent ()))
 
944
                while ( (pw = getpwent ()) != NULL ) {
852
945
                        print_status (pw);
 
946
                }
 
947
                endpwent ();
853
948
                exit (E_SUCCESS);
854
949
        }
855
950
#if 0
872
967
         * -S now ok for normal users (check status of my own account), and
873
968
         * doesn't require username.  --marekm
874
969
         */
875
 
        if (anyflag && optind >= argc)
 
970
        if (anyflag && optind >= argc) {
876
971
                usage (E_USAGE);
 
972
        }
877
973
 
878
 
        if (anyflag + Sflg + kflg > 1)
 
974
        if (   (Sflg && kflg)
 
975
            || (anyflag && (Sflg || kflg))) {
879
976
                usage (E_USAGE);
 
977
        }
880
978
 
881
979
        if (anyflag && !amroot) {
882
980
                fprintf (stderr, _("%s: Permission denied.\n"), Prog);
884
982
        }
885
983
 
886
984
        pw = xgetpwnam (name);
887
 
        if (!pw) {
888
 
                fprintf (stderr, _("%s: unknown user %s\n"), Prog, name);
 
985
        if (NULL == pw) {
 
986
                fprintf (stderr, _("%s: user '%s' does not exist\n"), Prog, name);
889
987
                exit (E_NOPERM);
890
988
        }
891
989
#ifdef WITH_SELINUX
892
990
        /* only do this check when getuid()==0 because it's a pre-condition for
893
991
           changing a password without entering the old one */
894
992
        if ((is_selinux_enabled() > 0) && (getuid() == 0) &&
895
 
          (check_selinux_access(name, pw->pw_uid, PASSWD__PASSWD) != 0))
896
 
        {
 
993
            (check_selinux_access (name, pw->pw_uid, PASSWD__PASSWD) != 0)) {
897
994
                security_context_t user_context;
898
995
                if (getprevcon(&user_context) < 0) {
899
996
                        user_context = strdup("Unknown user context");
900
997
                }
901
998
                syslog(LOG_ALERT,
902
 
                "%s is not authorized to change the password of %s",
903
 
                user_context, name);
904
 
                fprintf(stderr, _("%s: %s is not authorized to change the "
905
 
                        "password of %s\n"),
906
 
                Prog, user_context, name);
 
999
                       "%s is not authorized to change the password of %s",
 
1000
                       user_context, name);
 
1001
                fprintf(stderr,
 
1002
                        _("%s: %s is not authorized to change the password of %s\n"),
 
1003
                        Prog, user_context, name);
907
1004
                freecon(user_context);
908
1005
                exit(1);
909
1006
        }
910
 
 
911
 
#endif
 
1007
#endif /* WITH_SELINUX */
912
1008
 
913
1009
        /*
914
1010
         * If the UID of the user does not match the current real UID,
915
1011
         * check if I'm root.
916
1012
         */
917
 
        if (!amroot && pw->pw_uid != getuid ()) {
 
1013
        if (!amroot && (pw->pw_uid != getuid ())) {
918
1014
                fprintf (stderr,
919
 
                         _
920
 
                         ("%s: You may not view or modify password information for %s.\n"),
921
 
                         Prog, name);
 
1015
                         _("%s: You may not view or modify password information for %s.\n"),
 
1016
                         Prog, name);
922
1017
                SYSLOG ((LOG_WARN,
923
1018
                         "%s: can't view or modify password information for %s",
924
1019
                         Prog, name));
935
1030
         * The user name is valid, so let's get the shadow file entry.
936
1031
         */
937
1032
        sp = getspnam (name); /* !USE_PAM, no need for xgetspnam */
938
 
        if (!sp)
 
1033
        if (NULL == sp) {
939
1034
                sp = pwd_to_spwd (pw);
 
1035
        }
940
1036
 
941
1037
        cp = sp->sp_pwdp;
942
1038
 
955
1051
                /*
956
1052
                 * Let the user know whose password is being changed.
957
1053
                 */
958
 
                if (!qflg)
 
1054
                if (!qflg) {
959
1055
                        printf (_("Changing password for %s\n"), name);
 
1056
                }
960
1057
 
961
1058
                if (new_password (pw)) {
962
1059
                        fprintf (stderr,
965
1062
                        closelog ();
966
1063
                        exit (E_NOPERM);
967
1064
                }
968
 
                do_update_pwd = 1;
969
 
                do_update_age = 1;
 
1065
                do_update_pwd = true;
 
1066
                do_update_age = true;
970
1067
        }
971
1068
#endif                          /* !USE_PAM */
972
1069
        /*
986
1083
                exit (E_SUCCESS);
987
1084
        }
988
1085
#endif                          /* USE_PAM */
989
 
        if (setuid (0)) {
 
1086
        if (setuid (0) != 0) {
990
1087
                fputs (_("Cannot change ID to root.\n"), stderr);
991
1088
                SYSLOG ((LOG_ERR, "can't setuid(0)"));
992
1089
                closelog ();
993
1090
                exit (E_NOPERM);
994
1091
        }
995
 
        if (spw_file_present ())
 
1092
        if (spw_file_present ()) {
996
1093
                update_shadow ();
997
 
        else
 
1094
        } else {
998
1095
                update_noshadow ();
 
1096
        }
999
1097
 
1000
1098
        nscd_flush_cache ("passwd");
1001
1099
        nscd_flush_cache ("group");
1002
1100
 
1003
 
        SYSLOG ((LOG_INFO, "password for `%s' changed by `%s'", name, myname));
 
1101
        SYSLOG ((LOG_INFO, "password for '%s' changed by '%s'", name, myname));
1004
1102
        closelog ();
1005
1103
        if (!qflg) {
1006
 
                if (!eflg)
 
1104
                if (!eflg) {
1007
1105
                        puts (_("Password changed."));
1008
 
                else
 
1106
                } else {
1009
1107
                        puts (_("Password set to expire."));
 
1108
                }
1010
1109
        }
1011
1110
        exit (E_SUCCESS);
1012
1111
        /* NOT REACHED */
1013
1112
}
 
1113