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