]> granicus.if.org Git - shadow/blob - src/passwd.c
* NEWS, src/passwd.c, man/passwd.1.xml: Add --root option.
[shadow] / src / passwd.c
1 /*
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 - 2011, Nicolas François
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
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.
19  *
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.
31  */
32
33 #include <config.h>
34
35 #ident "$Id$"
36
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <getopt.h>
40 #include <pwd.h>
41 #include <signal.h>
42 #include <stdio.h>
43 #include <sys/types.h>
44 #ifdef WITH_SELINUX
45 #include <selinux/selinux.h>
46 #include <selinux/flask.h>
47 #include <selinux/av_permissions.h>
48 #include <selinux/context.h>
49 #endif                          /* WITH_SELINUX */
50 #include <time.h>
51 #include "defines.h"
52 #include "getdef.h"
53 #include "nscd.h"
54 #include "prototypes.h"
55 #include "pwauth.h"
56 #include "pwio.h"
57 #include "shadowio.h"
58
59 /*
60  * exit status values
61  */
62 /*@-exitarg@*/
63 #define E_SUCCESS       0       /* success */
64 #define E_NOPERM        1       /* permission denied */
65 #define E_USAGE         2       /* invalid combination of options */
66 #define E_FAILURE       3       /* unexpected failure, nothing done */
67 #define E_MISSING       4       /* unexpected failure, passwd file missing */
68 #define E_PWDBUSY       5       /* passwd file busy, try again later */
69 #define E_BAD_ARG       6       /* invalid argument to option */
70 /*
71  * Global variables
72  */
73 const char *Prog;               /* Program name */
74
75 static char *name;              /* The name of user whose password is being changed */
76 static char *myname;            /* The current user's name */
77 static bool amroot;             /* The caller's real UID was 0 */
78
79 static bool
80     aflg = false,                       /* -a - show status for all users */
81     dflg = false,                       /* -d - delete password */
82     eflg = false,                       /* -e - force password change */
83     iflg = false,                       /* -i - set inactive days */
84     kflg = false,                       /* -k - change only if expired */
85     lflg = false,                       /* -l - lock the user's password */
86     nflg = false,                       /* -n - set minimum days */
87     qflg = false,                       /* -q - quiet mode */
88     Sflg = false,                       /* -S - show password status */
89     uflg = false,                       /* -u - unlock the user's password */
90     wflg = false,                       /* -w - set warning days */
91     xflg = false;                       /* -x - set maximum days */
92
93 /*
94  * set to 1 if there are any flags which require root privileges,
95  * and require username to be specified
96  */
97 static bool anyflag = false;
98
99 static long age_min = 0;        /* Minimum days before change   */
100 static long age_max = 0;        /* Maximum days until change     */
101 static long warn = 0;           /* Warning days before change   */
102 static long inact = 0;          /* Days without change before locked */
103
104 #ifndef USE_PAM
105 static bool do_update_age = false;
106 #endif                          /* ! USE_PAM */
107
108 static bool pw_locked = false;
109 static bool spw_locked = false;
110
111 #ifndef USE_PAM
112 /*
113  * Size of the biggest passwd:
114  *   $6$        3
115  *   rounds=    7
116  *   999999999  9
117  *   $          1
118  *   salt       16
119  *   $          1
120  *   SHA512     123
121  *   nul        1
122  *
123  *   total      161
124  */
125 static char crypt_passwd[256];
126 static bool do_update_pwd = false;
127 #endif                          /* !USE_PAM */
128
129 /*
130  * External identifiers
131  */
132
133 /* local function prototypes */
134 static /*@noreturn@*/void usage (int);
135
136 #ifndef USE_PAM
137 static bool reuse (const char *, const struct passwd *);
138 static int new_password (const struct passwd *);
139
140 static void check_password (const struct passwd *, const struct spwd *);
141 #endif                          /* !USE_PAM */
142 static /*@observer@*/const char *date_to_str (time_t);
143 static /*@observer@*/const char *pw_status (const char *);
144 static void print_status (const struct passwd *);
145 static /*@noreturn@*/void fail_exit (int);
146 static /*@noreturn@*/void oom (void);
147 static char *update_crypt_pw (char *);
148 static void update_noshadow (void);
149
150 static void update_shadow (void);
151 #ifdef WITH_SELINUX
152 static int check_selinux_access (const char *changed_user,
153                                  uid_t changed_uid,
154                                  access_vector_t requested_access);
155 #endif                          /* WITH_SELINUX */
156
157 /*
158  * usage - print command usage and exit
159  */
160 static /*@noreturn@*/void usage (int status)
161 {
162         FILE *usageout = (E_SUCCESS != status) ? stderr : stdout;
163         (void) fprintf (usageout,
164                         _("Usage: %s [options] [LOGIN]\n"
165                           "\n"
166                           "Options:\n"),
167                         Prog);
168         (void) fputs (_("  -a, --all                     report password status on all accounts\n"), usageout);
169         (void) fputs (_("  -d, --delete                  delete the password for the named account\n"), usageout);
170         (void) fputs (_("  -e, --expire                  force expire the password for the named account\n"), usageout);
171         (void) fputs (_("  -h, --help                    display this help message and exit\n"), usageout);
172         (void) fputs (_("  -k, --keep-tokens             change password only if expired\n"), usageout);
173         (void) fputs (_("  -i, --inactive INACTIVE       set password inactive after expiration\n"
174                         "                                to INACTIVE\n"), usageout);
175         (void) fputs (_("  -l, --lock                    lock the password of the named account\n"), usageout);
176         (void) fputs (_("  -n, --mindays MIN_DAYS        set minimum number of days before password\n"
177                         "                                change to MIN_DAYS\n"), usageout);
178         (void) fputs (_("  -q, --quiet                   quiet mode\n"), usageout);
179         (void) fputs (_("  -r, --repository REPOSITORY   change password in REPOSITORY repository\n"), usageout);
180         (void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), usageout);
181         (void) fputs (_("  -S, --status                  report password status on the named account\n"), usageout);
182         (void) fputs (_("  -u, --unlock                  unlock the password of the named account\n"), usageout);
183         (void) fputs (_("  -w, --warndays WARN_DAYS      set expiration warning days to WARN_DAYS\n"), usageout);
184         (void) fputs (_("  -x, --maxdays MAX_DAYS        set maximum number of days before password\n"
185                         "                                change to MAX_DAYS\n"), usageout);
186         (void) fputs ("\n", usageout);
187         exit (status);
188 }
189
190 #ifndef USE_PAM
191 static bool reuse (const char *pass, const struct passwd *pw)
192 {
193 #ifdef HAVE_LIBCRACK_HIST
194         const char *reason;
195
196 #ifdef HAVE_LIBCRACK_PW
197         const char *FascistHistoryPw (const char *, const struct passwd *);
198
199         reason = FascistHistory (pass, pw);
200 #else                           /* !HAVE_LIBCRACK_PW */
201         const char *FascistHistory (const char *, int);
202
203         reason = FascistHistory (pass, pw->pw_uid);
204 #endif                          /* !HAVE_LIBCRACK_PW */
205         if (NULL != reason) {
206                 (void) printf (_("Bad password: %s.  "), reason);
207                 return true;
208         }
209 #endif                          /* HAVE_LIBCRACK_HIST */
210         return false;
211 }
212
213 /*
214  * new_password - validate old password and replace with new (both old and
215  * new in global "char crypt_passwd[128]")
216  */
217 static int new_password (const struct passwd *pw)
218 {
219         char *clear;            /* Pointer to clear text */
220         char *cipher;           /* Pointer to cipher text */
221         char *cp;               /* Pointer to getpass() response */
222         char orig[200];         /* Original password */
223         char pass[200];         /* New password */
224         int i;                  /* Counter for retries */
225         bool warned;
226         int pass_max_len = -1;
227         const char *method;
228
229 #ifdef HAVE_LIBCRACK_HIST
230         int HistUpdate (const char *, const char *);
231 #endif                          /* HAVE_LIBCRACK_HIST */
232
233         /*
234          * Authenticate the user. The user will be prompted for their own
235          * password.
236          */
237
238         if (!amroot && ('\0' != crypt_passwd[0])) {
239                 clear = getpass (_("Old password: "));
240                 if (NULL == clear) {
241                         return -1;
242                 }
243
244                 cipher = pw_encrypt (clear, crypt_passwd);
245                 if (strcmp (cipher, crypt_passwd) != 0) {
246                         strzero (clear);
247                         strzero (cipher);
248                         SYSLOG ((LOG_WARN, "incorrect password for %s",
249                                  pw->pw_name));
250                         (void) sleep (1);
251                         (void) fprintf (stderr,
252                                         _("Incorrect password for %s.\n"),
253                                         pw->pw_name);
254                         return -1;
255                 }
256                 STRFCPY (orig, clear);
257                 strzero (clear);
258                 strzero (cipher);
259         } else {
260                 orig[0] = '\0';
261         }
262
263         /*
264          * Get the new password. The user is prompted for the new password
265          * and has five tries to get it right. The password will be tested
266          * for strength, unless it is the root user. This provides an escape
267          * for initial login passwords.
268          */
269         method = getdef_str ("ENCRYPT_METHOD");
270         if (NULL == method) {
271                 if (!getdef_bool ("MD5_CRYPT_ENAB")) {
272                         pass_max_len = getdef_num ("PASS_MAX_LEN", 8);
273                 }
274         } else {
275                 if (   (strcmp (method, "MD5")    == 0)
276 #ifdef USE_SHA_CRYPT
277                     || (strcmp (method, "SHA256") == 0)
278                     || (strcmp (method, "SHA512") == 0)
279 #endif                          /* USE_SHA_CRYPT */
280                     ) {
281                         pass_max_len = -1;
282                 } else {
283                         pass_max_len = getdef_num ("PASS_MAX_LEN", 8);
284                 }
285         }
286         if (!qflg) {
287                 if (pass_max_len == -1) {
288                         (void) printf (_(
289 "Enter the new password (minimum of %d characters)\n"
290 "Please use a combination of upper and lower case letters and numbers.\n"),
291                                 getdef_num ("PASS_MIN_LEN", 5));
292                 } else {
293                         (void) printf (_(
294 "Enter the new password (minimum of %d, maximum of %d characters)\n"
295 "Please use a combination of upper and lower case letters and numbers.\n"),
296                                 getdef_num ("PASS_MIN_LEN", 5), pass_max_len);
297                 }
298         }
299
300         warned = false;
301         for (i = getdef_num ("PASS_CHANGE_TRIES", 5); i > 0; i--) {
302                 cp = getpass (_("New password: "));
303                 if (NULL == cp) {
304                         memzero (orig, sizeof orig);
305                         return -1;
306                 }
307                 if (warned && (strcmp (pass, cp) != 0)) {
308                         warned = false;
309                 }
310                 STRFCPY (pass, cp);
311                 strzero (cp);
312
313                 if (!amroot && (!obscure (orig, pass, pw) || reuse (pass, pw))) {
314                         (void) puts (_("Try again."));
315                         continue;
316                 }
317
318                 /*
319                  * If enabled, warn about weak passwords even if you are
320                  * root (enter this password again to use it anyway). 
321                  * --marekm
322                  */
323                 if (amroot && !warned && getdef_bool ("PASS_ALWAYS_WARN")
324                     && (!obscure (orig, pass, pw) || reuse (pass, pw))) {
325                         (void) puts (_("\nWarning: weak password (enter it again to use it anyway)."));
326                         warned = true;
327                         continue;
328                 }
329                 cp = getpass (_("Re-enter new password: "));
330                 if (NULL == cp) {
331                         memzero (orig, sizeof orig);
332                         return -1;
333                 }
334                 if (strcmp (cp, pass) != 0) {
335                         (void) fputs (_("They don't match; try again.\n"), stderr);
336                 } else {
337                         strzero (cp);
338                         break;
339                 }
340         }
341         memzero (orig, sizeof orig);
342
343         if (i == 0) {
344                 memzero (pass, sizeof pass);
345                 return -1;
346         }
347
348         /*
349          * Encrypt the password, then wipe the cleartext password.
350          */
351         cp = pw_encrypt (pass, crypt_make_salt (NULL, NULL));
352         memzero (pass, sizeof pass);
353
354 #ifdef HAVE_LIBCRACK_HIST
355         HistUpdate (pw->pw_name, crypt_passwd);
356 #endif                          /* HAVE_LIBCRACK_HIST */
357         STRFCPY (crypt_passwd, cp);
358         return 0;
359 }
360
361 /*
362  * check_password - test a password to see if it can be changed
363  *
364  *      check_password() sees if the invoker has permission to change the
365  *      password for the given user.
366  */
367 static void check_password (const struct passwd *pw, const struct spwd *sp)
368 {
369         time_t now;
370         int exp_status;
371
372         exp_status = isexpired (pw, sp);
373
374         /*
375          * If not expired and the "change only if expired" option (idea from
376          * PAM) was specified, do nothing. --marekm
377          */
378         if (kflg && (0 == exp_status)) {
379                 exit (E_SUCCESS);
380         }
381
382         /*
383          * Root can change any password any time.
384          */
385         if (amroot) {
386                 return;
387         }
388
389         (void) time (&now);
390
391         /*
392          * Expired accounts cannot be changed ever. Passwords which are
393          * locked may not be changed. Passwords where min > max may not be
394          * changed. Passwords which have been inactive too long cannot be
395          * changed.
396          */
397         if (   (sp->sp_pwdp[0] == '!')
398             || (exp_status > 1)
399             || (   (sp->sp_max >= 0)
400                 && (sp->sp_min > sp->sp_max))) {
401                 (void) fprintf (stderr,
402                                 _("The password for %s cannot be changed.\n"),
403                                 sp->sp_namp);
404                 SYSLOG ((LOG_WARN, "password locked for '%s'", sp->sp_namp));
405                 closelog ();
406                 exit (E_NOPERM);
407         }
408
409         /*
410          * Passwords may only be changed after sp_min time is up.
411          */
412         if (sp->sp_lstchg > 0) {
413                 time_t last, ok;
414                 last = sp->sp_lstchg * SCALE;
415                 ok = last + (sp->sp_min > 0 ? sp->sp_min * SCALE : 0);
416
417                 if (now < ok) {
418                         (void) fprintf (stderr,
419                                         _("The password for %s cannot be changed yet.\n"),
420                                         pw->pw_name);
421                         SYSLOG ((LOG_WARN, "now < minimum age for '%s'", pw->pw_name));
422                         closelog ();
423                         exit (E_NOPERM);
424                 }
425         }
426 }
427 #endif                          /* !USE_PAM */
428
429 static /*@observer@*/const char *date_to_str (time_t t)
430 {
431         static char buf[80];
432         struct tm *tm;
433
434         tm = gmtime (&t);
435 #ifdef HAVE_STRFTIME
436         (void) strftime (buf, sizeof buf, "%m/%d/%Y", tm);
437 #else                           /* !HAVE_STRFTIME */
438         (void) snprintf (buf, sizeof buf, "%02d/%02d/%04d",
439                          tm->tm_mon + 1, tm->tm_mday, tm->tm_year + 1900);
440 #endif                          /* !HAVE_STRFTIME */
441         return buf;
442 }
443
444 static /*@observer@*/const char *pw_status (const char *pass)
445 {
446         if (*pass == '*' || *pass == '!') {
447                 return "L";
448         }
449         if (*pass == '\0') {
450                 return "NP";
451         }
452         return "P";
453 }
454
455 /*
456  * print_status - print current password status
457  */
458 static void print_status (const struct passwd *pw)
459 {
460         struct spwd *sp;
461
462         sp = getspnam (pw->pw_name); /* local, no need for xgetspnam */
463         if (NULL != sp) {
464                 (void) printf ("%s %s %s %lld %lld %lld %lld\n",
465                                pw->pw_name,
466                                pw_status (sp->sp_pwdp),
467                                date_to_str (sp->sp_lstchg * SCALE),
468                                ((long long)sp->sp_min * SCALE) / DAY,
469                                ((long long)sp->sp_max * SCALE) / DAY,
470                                ((long long)sp->sp_warn * SCALE) / DAY,
471                                ((long long)sp->sp_inact * SCALE) / DAY);
472         } else {
473                 (void) printf ("%s %s\n",
474                                pw->pw_name, pw_status (pw->pw_passwd));
475         }
476 }
477
478
479 static /*@noreturn@*/void fail_exit (int status)
480 {
481         if (pw_locked) {
482                 if (pw_unlock () == 0) {
483                         (void) fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
484                         SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
485                         /* continue */
486                 }
487         }
488
489         if (spw_locked) {
490                 if (spw_unlock () == 0) {
491                         (void) fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
492                         SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
493                         /* continue */
494                 }
495         }
496
497         exit (status);
498 }
499
500 static /*@noreturn@*/void oom (void)
501 {
502         (void) fprintf (stderr, _("%s: out of memory\n"), Prog);
503         fail_exit (E_FAILURE);
504 }
505
506 static char *update_crypt_pw (char *cp)
507 {
508 #ifndef USE_PAM
509         if (do_update_pwd) {
510                 cp = xstrdup (crypt_passwd);
511         }
512 #endif                          /* !USE_PAM */
513
514         if (dflg) {
515                 *cp = '\0';
516         }
517
518         if (uflg && *cp == '!') {
519                 if (cp[1] == '\0') {
520                         (void) fprintf (stderr,
521                                         _("%s: unlocking the password would result in a passwordless account.\n"
522                                           "You should set a password with usermod -p to unlock the password of this account.\n"),
523                                         Prog);
524                         fail_exit (E_FAILURE);
525                 } else {
526                         cp++;
527                 }
528         }
529
530         if (lflg && *cp != '!') {
531                 char *newpw = xmalloc (strlen (cp) + 2);
532
533                 strcpy (newpw, "!");
534                 strcat (newpw, cp);
535                 cp = newpw;
536         }
537         return cp;
538 }
539
540
541 static void update_noshadow (void)
542 {
543         const struct passwd *pw;
544         struct passwd *npw;
545
546         if (pw_lock () == 0) {
547                 (void) fprintf (stderr,
548                                 _("%s: cannot lock %s; try again later.\n"),
549                                 Prog, pw_dbname ());
550                 exit (E_PWDBUSY);
551         }
552         pw_locked = true;
553         if (pw_open (O_RDWR) == 0) {
554                 (void) fprintf (stderr,
555                                 _("%s: cannot open %s\n"),
556                                 Prog, pw_dbname ());
557                 SYSLOG ((LOG_WARN, "cannot open %s", pw_dbname ()));
558                 fail_exit (E_MISSING);
559         }
560         pw = pw_locate (name);
561         if (NULL == pw) {
562                 (void) fprintf (stderr,
563                                 _("%s: user '%s' does not exist in %s\n"),
564                                 Prog, name, pw_dbname ());
565                 fail_exit (E_NOPERM);
566         }
567         npw = __pw_dup (pw);
568         if (NULL == npw) {
569                 oom ();
570         }
571         npw->pw_passwd = update_crypt_pw (npw->pw_passwd);
572         if (pw_update (npw) == 0) {
573                 (void) fprintf (stderr,
574                                 _("%s: failed to prepare the new %s entry '%s'\n"),
575                                 Prog, pw_dbname (), npw->pw_name);
576                 fail_exit (E_FAILURE);
577         }
578         if (pw_close () == 0) {
579                 (void) fprintf (stderr,
580                                 _("%s: failure while writing changes to %s\n"),
581                                 Prog, pw_dbname ());
582                 SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ()));
583                 fail_exit (E_FAILURE);
584         }
585         if (pw_unlock () == 0) {
586                 (void) fprintf (stderr,
587                                 _("%s: failed to unlock %s\n"),
588                                 Prog, pw_dbname ());
589                 SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
590                 /* continue */
591         }
592         pw_locked = false;
593 }
594
595 static void update_shadow (void)
596 {
597         const struct spwd *sp;
598         struct spwd *nsp;
599
600         if (spw_lock () == 0) {
601                 (void) fprintf (stderr,
602                                 _("%s: cannot lock %s; try again later.\n"),
603                                 Prog, spw_dbname ());
604                 exit (E_PWDBUSY);
605         }
606         spw_locked = true;
607         if (spw_open (O_RDWR) == 0) {
608                 (void) fprintf (stderr,
609                                 _("%s: cannot open %s\n"),
610                                 Prog, spw_dbname ());
611                 SYSLOG ((LOG_WARN, "cannot open %s", spw_dbname ()));
612                 fail_exit (E_FAILURE);
613         }
614         sp = spw_locate (name);
615         if (NULL == sp) {
616                 /* Try to update the password in /etc/passwd instead. */
617                 (void) spw_close ();
618                 update_noshadow ();
619                 if (spw_unlock () == 0) {
620                         (void) fprintf (stderr,
621                                         _("%s: failed to unlock %s\n"),
622                                         Prog, spw_dbname ());
623                         SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
624                         /* continue */
625                 }
626                 spw_locked = false;
627                 return;
628         }
629         nsp = __spw_dup (sp);
630         if (NULL == nsp) {
631                 oom ();
632         }
633         nsp->sp_pwdp = update_crypt_pw (nsp->sp_pwdp);
634         if (xflg) {
635                 nsp->sp_max = (age_max * DAY) / SCALE;
636         }
637         if (nflg) {
638                 nsp->sp_min = (age_min * DAY) / SCALE;
639         }
640         if (wflg) {
641                 nsp->sp_warn = (warn * DAY) / SCALE;
642         }
643         if (iflg) {
644                 nsp->sp_inact = (inact * DAY) / SCALE;
645         }
646 #ifndef USE_PAM
647         if (do_update_age) {
648                 nsp->sp_lstchg = (long) time ((time_t *) 0) / SCALE;
649                 if (0 == nsp->sp_lstchg) {
650                         /* Better disable aging than requiring a password
651                          * change */
652                         nsp->sp_lstchg = -1;
653                 }
654         }
655 #endif                          /* !USE_PAM */
656
657         /*
658          * Force change on next login, like SunOS 4.x passwd -e or Solaris
659          * 2.x passwd -f. Solaris 2.x seems to do the same thing (set
660          * sp_lstchg to 0).
661          */
662         if (eflg) {
663                 nsp->sp_lstchg = 0;
664         }
665
666         if (spw_update (nsp) == 0) {
667                 (void) fprintf (stderr,
668                                 _("%s: failed to prepare the new %s entry '%s'\n"),
669                                 Prog, spw_dbname (), nsp->sp_namp);
670                 fail_exit (E_FAILURE);
671         }
672         if (spw_close () == 0) {
673                 (void) fprintf (stderr,
674                                 _("%s: failure while writing changes to %s\n"),
675                                 Prog, spw_dbname ());
676                 SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ()));
677                 fail_exit (E_FAILURE);
678         }
679         if (spw_unlock () == 0) {
680                 (void) fprintf (stderr,
681                                 _("%s: failed to unlock %s\n"),
682                                 Prog, spw_dbname ());
683                 SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
684                 /* continue */
685         }
686         spw_locked = false;
687 }
688
689 #ifdef WITH_SELINUX
690 static int check_selinux_access (const char *changed_user,
691                                  uid_t changed_uid,
692                                  access_vector_t requested_access)
693 {
694         int status = -1;
695         security_context_t user_context;
696         context_t c;
697         const char *user;
698
699         /* if in permissive mode then allow the operation */
700         if (security_getenforce() == 0) {
701                 return 0;
702         }
703
704         /* get the context of the process which executed passwd */
705         if (getprevcon(&user_context) != 0) {
706                 return -1;
707         }
708
709         /* get the "user" portion of the context (the part before the first
710            colon) */
711         c = context_new(user_context);
712         user = context_user_get(c);
713
714         /* if changing a password for an account with UID==0 or for an account
715            where the identity matches then return success */
716         if (changed_uid != 0 && strcmp(changed_user, user) == 0) {
717                 status = 0;
718         } else {
719                 struct av_decision avd;
720                 int retval;
721                 retval = security_compute_av(user_context,
722                                              user_context,
723                                              SECCLASS_PASSWD,
724                                              requested_access,
725                                              &avd);
726                 if ((retval == 0) &&
727                     ((requested_access & avd.allowed) == requested_access)) {
728                         status = 0;
729                 }
730         }
731         context_free(c);
732         freecon(user_context);
733         return status;
734 }
735
736 #endif                          /* WITH_SELINUX */
737
738 /*
739  * passwd - change a user's password file information
740  *
741  *      This command controls the password file and commands which are used
742  *      to modify it.
743  *
744  *      The valid options are
745  *
746  *      -d      delete the password for the named account (*)
747  *      -e      expire the password for the named account (*)
748  *      -f      execute chfn command to interpret flags
749  *      -g      execute gpasswd command to interpret flags
750  *      -i #    set sp_inact to # days (*)
751  *      -k      change password only if expired
752  *      -l      lock the password of the named account (*)
753  *      -n #    set sp_min to # days (*)
754  *      -r #    change password in # repository
755  *      -s      execute chsh command to interpret flags
756  *      -S      show password status of named account
757  *      -u      unlock the password of the named account (*)
758  *      -w #    set sp_warn to # days (*)
759  *      -x #    set sp_max to # days (*)
760  *
761  *      (*) requires root permission to execute.
762  *
763  *      All of the time fields are entered in days and converted to the
764  *      appropriate internal format. For finer resolute the chage
765  *      command must be used.
766  */
767 int main (int argc, char **argv)
768 {
769         const struct passwd *pw;        /* Password file entry for user      */
770
771 #ifndef USE_PAM
772         char *cp;               /* Miscellaneous character pointing  */
773
774         const struct spwd *sp;  /* Shadow file entry for user   */
775 #endif                          /* !USE_PAM */
776
777         sanitize_env ();
778
779         /*
780          * Get the program name. The program name is used as a prefix to
781          * most error messages.
782          */
783         Prog = Basename (argv[0]);
784
785         (void) setlocale (LC_ALL, "");
786         (void) bindtextdomain (PACKAGE, LOCALEDIR);
787         (void) textdomain (PACKAGE);
788
789         process_root_flag ("-R", argc, argv);
790
791         /*
792          * The program behaves differently when executed by root than when
793          * executed by a normal user.
794          */
795         amroot = (getuid () == 0);
796
797         OPENLOG ("passwd");
798
799         {
800                 /*
801                  * Parse the command line options.
802                  */
803                 int c;
804                 static struct option long_options[] = {
805                         {"all", no_argument, NULL, 'a'},
806                         {"delete", no_argument, NULL, 'd'},
807                         {"expire", no_argument, NULL, 'e'},
808                         {"help", no_argument, NULL, 'h'},
809                         {"inactive", required_argument, NULL, 'i'},
810                         {"keep-tokens", no_argument, NULL, 'k'},
811                         {"lock", no_argument, NULL, 'l'},
812                         {"mindays", required_argument, NULL, 'n'},
813                         {"quiet", no_argument, NULL, 'q'},
814                         {"repository", required_argument, NULL, 'r'},
815                         {"root", required_argument, NULL, 'R'},
816                         {"status", no_argument, NULL, 'S'},
817                         {"unlock", no_argument, NULL, 'u'},
818                         {"warndays", required_argument, NULL, 'w'},
819                         {"maxdays", required_argument, NULL, 'x'},
820                         {NULL, 0, NULL, '\0'}
821                 };
822
823                 while ((c = getopt_long (argc, argv, "adehi:kln:qr:R:Suw:x:",
824                                          long_options, NULL)) != -1) {
825                         switch (c) {
826                         case 'a':
827                                 aflg = true;
828                                 break;
829                         case 'd':
830                                 dflg = true;
831                                 anyflag = true;
832                                 break;
833                         case 'e':
834                                 eflg = true;
835                                 anyflag = true;
836                                 break;
837                         case 'i':
838                                 if (   (getlong (optarg, &inact) == 0)
839                                     || (inact < -1)) {
840                                         fprintf (stderr,
841                                                  _("%s: invalid numeric argument '%s'\n"),
842                                                  Prog, optarg);
843                                         usage (E_BAD_ARG);
844                                 }
845                                 iflg = true;
846                                 anyflag = true;
847                                 break;
848                         case 'k':
849                                 /* change only if expired, like Linux-PAM passwd -k. */
850                                 kflg = true;    /* ok for users */
851                                 break;
852                         case 'l':
853                                 lflg = true;
854                                 anyflag = true;
855                                 break;
856                         case 'n':
857                                 if (   (getlong (optarg, &age_min) == 0)
858                                     || (age_min < -1)) {
859                                         fprintf (stderr,
860                                                  _("%s: invalid numeric argument '%s'\n"),
861                                                  Prog, optarg);
862                                         usage (E_BAD_ARG);
863                                 }
864                                 nflg = true;
865                                 anyflag = true;
866                                 break;
867                         case 'q':
868                                 qflg = true;    /* ok for users */
869                                 break;
870                         case 'r':
871                                 /* -r repository (files|nis|nisplus) */
872                                 /* only "files" supported for now */
873                                 if (strcmp (optarg, "files") != 0) {
874                                         fprintf (stderr,
875                                                  _("%s: repository %s not supported\n"),
876                                                  Prog, optarg);
877                                         exit (E_BAD_ARG);
878                                 }
879                                 break;
880                         case 'R': /* no-op, handled in process_root_flag () */
881                                 break;
882                         case 'S':
883                                 Sflg = true;    /* ok for users */
884                                 break;
885                         case 'u':
886                                 uflg = true;
887                                 anyflag = true;
888                                 break;
889                         case 'w':
890                                 if (   (getlong (optarg, &warn) == 0)
891                                     || (warn < -1)) {
892                                         (void) fprintf (stderr,
893                                                         _("%s: invalid numeric argument '%s'\n"),
894                                                         Prog, optarg);
895                                         usage (E_BAD_ARG);
896                                 }
897                                 wflg = true;
898                                 anyflag = true;
899                                 break;
900                         case 'x':
901                                 if (   (getlong (optarg, &age_max) == 0)
902                                     || (age_max < -1)) {
903                                         (void) fprintf (stderr,
904                                                         _("%s: invalid numeric argument '%s'\n"),
905                                                         Prog, optarg);
906                                         usage (E_BAD_ARG);
907                                 }
908                                 xflg = true;
909                                 anyflag = true;
910                                 break;
911                         case 'h':
912                                 usage (E_SUCCESS);
913                                 /*@notreached@*/break;
914                         default:
915                                 usage (E_BAD_ARG);
916                         }
917                 }
918         }
919
920         /*
921          * Now I have to get the user name. The name will be gotten from the
922          * command line if possible. Otherwise it is figured out from the
923          * environment.
924          */
925         pw = get_my_pwent ();
926         if (NULL == pw) {
927                 (void) fprintf (stderr,
928                                 _("%s: Cannot determine your user name.\n"),
929                                 Prog);
930                 SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)",
931                          (unsigned long) getuid ()));
932                 exit (E_NOPERM);
933         }
934         myname = xstrdup (pw->pw_name);
935         if (optind < argc) {
936                 name = argv[optind];
937         } else {
938                 name = myname;
939         }
940
941         /*
942          * Make sure that at most one username was specified.
943          */
944         if (argc > (optind+1)) {
945                 usage (E_USAGE);
946         }
947
948         /*
949          * The -a flag requires -S, no other flags, no username, and
950          * you must be root.  --marekm
951          */
952         if (aflg) {
953                 if (anyflag || !Sflg || (optind < argc)) {
954                         usage (E_USAGE);
955                 }
956                 if (!amroot) {
957                         (void) fprintf (stderr,
958                                         _("%s: Permission denied.\n"),
959                                         Prog);
960                         exit (E_NOPERM);
961                 }
962                 setpwent ();
963                 while ( (pw = getpwent ()) != NULL ) {
964                         print_status (pw);
965                 }
966                 endpwent ();
967                 exit (E_SUCCESS);
968         }
969 #if 0
970         /*
971          * Allow certain users (administrators) to change passwords of
972          * certain users. Not implemented yet. --marekm
973          */
974         if (may_change_passwd (myname, name))
975                 amroot = 1;
976 #endif
977
978         /*
979          * If any of the flags were given, a user name must be supplied on
980          * the command line. Only an unadorned command line doesn't require
981          * the user's name be given. Also, -x, -n, -w, -i, -e, -d,
982          * -l, -u may appear with each other. -S, -k must appear alone.
983          */
984
985         /*
986          * -S now ok for normal users (check status of my own account), and
987          * doesn't require username.  --marekm
988          */
989         if (anyflag && optind >= argc) {
990                 usage (E_USAGE);
991         }
992
993         if (   (Sflg && kflg)
994             || (anyflag && (Sflg || kflg))) {
995                 usage (E_USAGE);
996         }
997
998         if (anyflag && !amroot) {
999                 (void) fprintf (stderr, _("%s: Permission denied.\n"), Prog);
1000                 exit (E_NOPERM);
1001         }
1002
1003         pw = xgetpwnam (name);
1004         if (NULL == pw) {
1005                 (void) fprintf (stderr,
1006                                 _("%s: user '%s' does not exist\n"),
1007                                 Prog, name);
1008                 exit (E_NOPERM);
1009         }
1010 #ifdef WITH_SELINUX
1011         /* only do this check when getuid()==0 because it's a pre-condition for
1012            changing a password without entering the old one */
1013         if ((is_selinux_enabled() > 0) && (getuid() == 0) &&
1014             (check_selinux_access (name, pw->pw_uid, PASSWD__PASSWD) != 0)) {
1015                 security_context_t user_context = NULL;
1016                 const char *user = "Unknown user context";
1017                 if (getprevcon (&user_context) == 0) {
1018                         user = user_context; /* FIXME: use context_user_get? */
1019                 }
1020                 SYSLOG ((LOG_ALERT,
1021                          "%s is not authorized to change the password of %s",
1022                          user, name));
1023                 (void) fprintf(stderr,
1024                                _("%s: %s is not authorized to change the password of %s\n"),
1025                                Prog, user, name);
1026                 if (NULL != user_context) {
1027                         freecon (user_context);
1028                 }
1029                 exit (E_NOPERM);
1030         }
1031 #endif                          /* WITH_SELINUX */
1032
1033         /*
1034          * If the UID of the user does not match the current real UID,
1035          * check if I'm root.
1036          */
1037         if (!amroot && (pw->pw_uid != getuid ())) {
1038                 (void) fprintf (stderr,
1039                                 _("%s: You may not view or modify password information for %s.\n"),
1040                                 Prog, name);
1041                 SYSLOG ((LOG_WARN,
1042                          "%s: can't view or modify password information for %s",
1043                          Prog, name));
1044                 closelog ();
1045                 exit (E_NOPERM);
1046         }
1047
1048         if (Sflg) {
1049                 print_status (pw);
1050                 exit (E_SUCCESS);
1051         }
1052 #ifndef USE_PAM
1053         /*
1054          * The user name is valid, so let's get the shadow file entry.
1055          */
1056         sp = getspnam (name); /* !USE_PAM, no need for xgetspnam */
1057         if (NULL == sp) {
1058                 sp = pwd_to_spwd (pw);
1059         }
1060
1061         cp = sp->sp_pwdp;
1062
1063         /*
1064          * If there are no other flags, just change the password.
1065          */
1066         if (!anyflag) {
1067                 STRFCPY (crypt_passwd, cp);
1068
1069                 /*
1070                  * See if the user is permitted to change the password. 
1071                  * Otherwise, go ahead and set a new password.
1072                  */
1073                 check_password (pw, sp);
1074
1075                 /*
1076                  * Let the user know whose password is being changed.
1077                  */
1078                 if (!qflg) {
1079                         (void) printf (_("Changing password for %s\n"), name);
1080                 }
1081
1082                 if (new_password (pw) != 0) {
1083                         (void) fprintf (stderr,
1084                                         _("The password for %s is unchanged.\n"),
1085                                         name);
1086                         closelog ();
1087                         exit (E_NOPERM);
1088                 }
1089                 do_update_pwd = true;
1090                 do_update_age = true;
1091         }
1092 #endif                          /* !USE_PAM */
1093         /*
1094          * Before going any further, raise the ulimit to prevent colliding
1095          * into a lowered ulimit, and set the real UID to root to protect
1096          * against unexpected signals. Any keyboard signals are set to be
1097          * ignored.
1098          */
1099         pwd_init ();
1100
1101 #ifdef USE_PAM
1102         /*
1103          * Don't set the real UID for PAM...
1104          */
1105         if (!anyflag) {
1106                 do_pam_passwd (name, qflg, kflg);
1107                 exit (E_SUCCESS);
1108         }
1109 #endif                          /* USE_PAM */
1110         if (setuid (0) != 0) {
1111                 (void) fputs (_("Cannot change ID to root.\n"), stderr);
1112                 SYSLOG ((LOG_ERR, "can't setuid(0)"));
1113                 closelog ();
1114                 exit (E_NOPERM);
1115         }
1116         if (spw_file_present ()) {
1117                 update_shadow ();
1118         } else {
1119                 update_noshadow ();
1120         }
1121
1122         nscd_flush_cache ("passwd");
1123         nscd_flush_cache ("group");
1124
1125         SYSLOG ((LOG_INFO, "password for '%s' changed by '%s'", name, myname));
1126         closelog ();
1127         if (!qflg) {
1128                 if (!anyflag) {
1129 #ifndef USE_PAM
1130                         (void) printf (_("%s: password changed.\n"), Prog);
1131 #endif                          /* USE_PAM */
1132                 } else {
1133                         (void) printf (_("%s: password expiry information changed.\n"), Prog);
1134                 }
1135         }
1136
1137         return E_SUCCESS;
1138 }
1139