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