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