]> granicus.if.org Git - shadow/blob - src/passwd.c
[svn-upgrade] Integrating new upstream version, shadow (4.0.5)
[shadow] / src / passwd.c
1 /*
2  * Copyright 1989 - 1994, Julianne Frances Haugh
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <config.h>
31
32 #include "rcsid.h"
33 RCSID (PKG_VER "$Id: passwd.c,v 1.30 2003/12/17 09:43:30 kloczek Exp $")
34 #include "prototypes.h"
35 #include "defines.h"
36 #include <sys/types.h>
37 #include <time.h>
38 #include <stdio.h>
39 #include <fcntl.h>
40 #include <signal.h>
41 #include <errno.h>
42 #ifndef GPASSWD_PROGRAM
43 #define GPASSWD_PROGRAM "gpasswd"
44 #endif
45 #ifndef CHFN_PROGRAM
46 #define CHFN_PROGRAM "chfn"
47 #endif
48 #ifndef CHSH_PROGRAM
49 #define CHSH_PROGRAM "chsh"
50 #endif
51 #include <pwd.h>
52 #include "pwauth.h"
53 #ifdef SHADOWPWD
54 #include "shadowio.h"
55 #endif
56 #include "pwio.h"
57 #include "nscd.h"
58 #include "getdef.h"
59 /*
60  * exit status values
61  */
62 #define E_SUCCESS       0       /* success */
63 #define E_NOPERM        1       /* permission denied */
64 #define E_USAGE         2       /* invalid combination of options */
65 #define E_FAILURE       3       /* unexpected failure, nothing done */
66 #define E_MISSING       4       /* unexpected failure, passwd file missing */
67 #define E_PWDBUSY       5       /* passwd file busy, try again later */
68 #define E_BAD_ARG       6       /* invalid argument to option */
69 /*
70  * Global variables
71  */
72 static char *name;              /* The name of user whose password is being changed */
73 static char *myname;            /* The current user's name */
74 static char *Prog;              /* Program name */
75 static int amroot;              /* The real UID was 0 */
76
77 static int
78 #ifdef SHADOWPWD
79  eflg = 0,                      /* -e - force password change */
80  iflg = 0,                      /* -i - set inactive days */
81  kflg = 0,                      /* -k - change only if expired */
82  nflg = 0,                      /* -n - set minimum days */
83  wflg = 0,                      /* -w - set warning days */
84  xflg = 0,                      /* -x - set maximum days */
85 #endif
86  aflg = 0,                      /* -a - show status for all users */
87  dflg = 0,                      /* -d - delete password */
88  lflg = 0,                      /* -l - lock account */
89  qflg = 0,                      /* -q - quiet mode */
90  Sflg = 0,                      /* -S - show password status */
91  uflg = 0;                      /* -u - unlock account */
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 int anyflag = 0;
98
99 #ifdef SHADOWPWD
100 static long age_min = 0;        /* Minimum days before change   */
101 static long age_max = 0;        /* Maximum days until change     */
102 static long warn = 0;           /* Warning days before change   */
103 static long inact = 0;          /* Days without change before locked */
104 #endif
105
106 static int do_update_age = 0;
107
108 #ifndef USE_PAM
109 static char crypt_passwd[128];  /* The "old-style" password, if present */
110 static int do_update_pwd = 0;
111 #endif
112
113 /*
114  * External identifiers
115  */
116
117 #ifdef  NDBM
118 extern int sp_dbm_mode;
119 extern int pw_dbm_mode;
120 #endif
121
122 /* local function prototypes */
123 static void usage (int);
124
125 #ifndef USE_PAM
126 static int reuse (const char *, const struct passwd *);
127 static int new_password (const struct passwd *);
128
129 #ifdef SHADOWPWD
130 static void check_password (const struct passwd *, const struct spwd *);
131 #else                           /* !SHADOWPWD */
132 static void check_password (const struct passwd *);
133 #endif                          /* !SHADOWPWD */
134 static char *insert_crypt_passwd (const char *, const char *);
135 #endif                          /* !USE_PAM */
136 static char *date_to_str (time_t);
137 static const char *pw_status (const char *);
138 static void print_status (const struct passwd *);
139 static void fail_exit (int);
140 static void oom (void);
141 static char *update_crypt_pw (char *);
142 static void update_noshadow (void);
143
144 #ifdef SHADOWPWD
145 static void update_shadow (void);
146 #endif
147 static long getnumber (const char *);
148
149 /*
150  * usage - print command usage and exit
151  */
152
153 static void usage (int status)
154 {
155         fprintf (stderr, _("Usage: %s [-f|-s] [name]\n"), Prog);
156         if (amroot) {
157                 fprintf (stderr,
158                          _
159                          ("       %s [-x max] [-n min] [-w warn] [-i inact] name\n"),
160                          Prog);
161                 fprintf (stderr, _("       %s {-l|-u|-d|-S|-e} name\n"),
162                          Prog);
163         }
164         exit (status);
165 }
166
167 #ifndef USE_PAM
168 static int reuse (const char *pass, const struct passwd *pw)
169 {
170 #ifdef HAVE_LIBCRACK_HIST
171         const char *reason;
172
173 #ifdef HAVE_LIBCRACK_PW
174         const char *FascistHistoryPw (const char *, const struct passwd *);
175
176         reason = FascistHistory (pass, pw);
177 #else
178         const char *FascistHistory (const char *, int);
179
180         reason = FascistHistory (pass, pw->pw_uid);
181 #endif
182         if (reason) {
183                 printf (_("Bad password: %s.  "), reason);
184                 return 1;
185         }
186 #endif
187         return 0;
188 }
189
190 /*
191  * new_password - validate old password and replace with new (both old and
192  * new in global "char crypt_passwd[128]")
193  */
194
195  /*ARGSUSED*/ static int new_password (const struct passwd *pw)
196 {
197         char *clear;            /* Pointer to clear text */
198         char *cipher;           /* Pointer to cipher text */
199         char *cp;               /* Pointer to getpass() response */
200         char orig[200];         /* Original password */
201         char pass[200];         /* New password */
202         int i;                  /* Counter for retries */
203         int warned;
204         int pass_max_len;
205
206 #ifdef HAVE_LIBCRACK_HIST
207         int HistUpdate (const char *, const char *);
208 #endif
209
210         /*
211          * Authenticate the user. The user will be prompted for their own
212          * password.
213          */
214
215         if (!amroot && crypt_passwd[0]) {
216                 if (!(clear = getpass (_("Old password: "))))
217                         return -1;
218
219                 cipher = pw_encrypt (clear, crypt_passwd);
220                 if (strcmp (cipher, crypt_passwd) != 0) {
221                         SYSLOG ((LOG_WARN, "incorrect password for `%s'",
222                                  pw->pw_name));
223                         sleep (1);
224                         fprintf (stderr,
225                                  _("Incorrect password for `%s'\n"),
226                                  pw->pw_name);
227                         return -1;
228                 }
229                 STRFCPY (orig, clear);
230                 strzero (clear);
231                 strzero (cipher);
232         } else {
233                 orig[0] = '\0';
234         }
235
236         /*
237          * Get the new password. The user is prompted for the new password
238          * and has five tries to get it right. The password will be tested
239          * for strength, unless it is the root user. This provides an escape
240          * for initial login passwords.
241          */
242
243         if (getdef_bool ("MD5_CRYPT_ENAB"))
244                 pass_max_len = 127;
245         else
246                 pass_max_len = getdef_num ("PASS_MAX_LEN", 8);
247
248         if (!qflg)
249                 printf (_("\
250 Enter the new password (minimum of %d, maximum of %d characters)\n\
251 Please use a combination of upper and lower case letters and numbers.\n"), getdef_num ("PASS_MIN_LEN", 5), pass_max_len);
252
253         warned = 0;
254         for (i = getdef_num ("PASS_CHANGE_TRIES", 5); i > 0; i--) {
255                 if (!(cp = getpass (_("New password: ")))) {
256                         memzero (orig, sizeof orig);
257                         return -1;
258                 }
259                 if (warned && strcmp (pass, cp) != 0)
260                         warned = 0;
261                 STRFCPY (pass, cp);
262                 strzero (cp);
263
264                 if (!amroot
265                     && (!obscure (orig, pass, pw) || reuse (pass, pw))) {
266                         printf (_("Try again.\n"));
267                         continue;
268                 }
269                 /*
270                  * If enabled, warn about weak passwords even if you are
271                  * root (enter this password again to use it anyway). 
272                  * --marekm
273                  */
274                 if (amroot && !warned && getdef_bool ("PASS_ALWAYS_WARN")
275                     && (!obscure (orig, pass, pw) || reuse (pass, pw))) {
276                         printf (_
277                                 ("\nWarning: weak password (enter it again to use it anyway).\n"));
278                         warned++;
279                         continue;
280                 }
281                 if (!(cp = getpass (_("Re-enter new password: ")))) {
282                         memzero (orig, sizeof orig);
283                         return -1;
284                 }
285                 if (strcmp (cp, pass))
286                         fprintf (stderr,
287                                  _("They don't match; try again.\n"));
288                 else {
289                         strzero (cp);
290                         break;
291                 }
292         }
293         memzero (orig, sizeof orig);
294
295         if (i == 0) {
296                 memzero (pass, sizeof pass);
297                 return -1;
298         }
299
300         /*
301          * Encrypt the password, then wipe the cleartext password.
302          */
303
304         cp = pw_encrypt (pass, crypt_make_salt ());
305         memzero (pass, sizeof pass);
306
307 #ifdef HAVE_LIBCRACK_HIST
308         HistUpdate (pw->pw_name, crypt_passwd);
309 #endif
310         STRFCPY (crypt_passwd, cp);
311         return 0;
312 }
313
314 /*
315  * check_password - test a password to see if it can be changed
316  *
317  *      check_password() sees if the invoker has permission to change the
318  *      password for the given user.
319  */
320
321 #ifdef SHADOWPWD
322 static void check_password (const struct passwd *pw, const struct spwd *sp)
323 {
324 #else
325 static void check_password (const struct passwd *pw)
326 {
327 #endif
328         time_t now, last, ok;
329         int exp_status;
330
331 #ifdef SHADOWPWD
332         exp_status = isexpired (pw, sp);
333 #else
334         exp_status = isexpired (pw);
335 #endif
336
337         /*
338          * If not expired and the "change only if expired" option (idea from
339          * PAM) was specified, do nothing. --marekm
340          */
341         if (kflg && exp_status == 0)
342                 exit (E_SUCCESS);
343
344         /*
345          * Root can change any password any time.
346          */
347
348         if (amroot)
349                 return;
350
351         time (&now);
352
353 #ifdef SHADOWPWD
354         /*
355          * Expired accounts cannot be changed ever. Passwords which are
356          * locked may not be changed. Passwords where min > max may not be
357          * changed. Passwords which have been inactive too long cannot be
358          * changed.
359          */
360
361         if (sp->sp_pwdp[0] == '!' || exp_status > 1 ||
362             (sp->sp_max >= 0 && sp->sp_min > sp->sp_max)) {
363                 fprintf (stderr,
364                          _("The password for %s cannot be changed.\n"),
365                          sp->sp_namp);
366                 SYSLOG ((LOG_WARN, "password locked for `%s'",
367                          sp->sp_namp));
368                 closelog ();
369                 exit (E_NOPERM);
370         }
371
372         /*
373          * Passwords may only be changed after sp_min time is up.
374          */
375
376         last = sp->sp_lstchg * SCALE;
377         ok = last + (sp->sp_min > 0 ? sp->sp_min * SCALE : 0);
378
379 #else                           /* !SHADOWPWD */
380         if (pw->pw_passwd[0] == '!' || exp_status > 1) {
381                 fprintf (stderr,
382                          _("The password for %s cannot be changed.\n"),
383                          pw->pw_name);
384                 SYSLOG ((LOG_WARN, "password locked for `%s'",
385                          pw->pw_name));
386                 closelog ();
387                 exit (E_NOPERM);
388         }
389
390         last = 0;
391         ok = 0;
392 #endif                          /* !SHADOWPWD */
393         if (now < ok) {
394                 fprintf (stderr,
395                          _
396                          ("Sorry, the password for %s cannot be changed yet.\n"),
397                          pw->pw_name);
398                 SYSLOG ((LOG_WARN, "now < minimum age for `%s'",
399                          pw->pw_name));
400                 closelog ();
401                 exit (E_NOPERM);
402         }
403 }
404
405 /*
406  * insert_crypt_passwd - add an "old-style" password to authentication
407  * string result now malloced to avoid overflow, just in case.  --marekm
408  */
409 static char *insert_crypt_passwd (const char *string, const char *passwd)
410 {
411         return xstrdup (passwd);
412 }
413 #endif                          /* !USE_PAM */
414
415 static char *date_to_str (time_t t)
416 {
417         static char buf[80];
418         struct tm *tm;
419
420         tm = gmtime (&t);
421 #ifdef HAVE_STRFTIME
422         strftime (buf, sizeof buf, "%m/%d/%Y", tm);
423 #else
424         snprintf (buf, sizeof buf, "%02d/%02d/%04d",
425                   tm->tm_mon + 1, tm->tm_mday, tm->tm_year + 1900);
426 #endif
427         return buf;
428 }
429
430 static const char *pw_status (const char *pass)
431 {
432         if (*pass == '*' || *pass == '!')
433                 return "L";
434         if (*pass == '\0')
435                 return "NP";
436         return "P";
437 }
438
439 /*
440  * print_status - print current password status
441  */
442
443 static void print_status (const struct passwd *pw)
444 {
445 #ifdef SHADOWPWD
446         struct spwd *sp;
447 #endif
448
449 #ifdef SHADOWPWD
450         sp = getspnam (pw->pw_name);
451         if (sp) {
452                 printf ("%s %s %s %ld %ld %ld %ld\n",
453                         pw->pw_name,
454                         pw_status (sp->sp_pwdp),
455                         date_to_str (sp->sp_lstchg * SCALE),
456                         (sp->sp_min * SCALE) / DAY,
457                         (sp->sp_max * SCALE) / DAY,
458                         (sp->sp_warn * SCALE) / DAY,
459                         (sp->sp_inact * SCALE) / DAY);
460         } else
461 #endif
462         {
463                 printf ("%s %s\n", pw->pw_name, pw_status (pw->pw_passwd));
464         }
465 }
466
467
468 static void fail_exit (int status)
469 {
470         pw_unlock ();
471 #ifdef SHADOWPWD
472         spw_unlock ();
473 #endif
474         exit (status);
475 }
476
477 static void oom (void)
478 {
479         fprintf (stderr, _("%s: out of memory\n"), Prog);
480         fail_exit (E_FAILURE);
481 }
482
483 static char *update_crypt_pw (char *cp)
484 {
485 #ifndef USE_PAM
486         if (do_update_pwd)
487                 cp = insert_crypt_passwd (cp, crypt_passwd);
488 #endif
489
490         if (dflg)
491                 cp = "";        /* XXX warning: const */
492
493         if (uflg && *cp == '!')
494                 cp++;
495
496         if (lflg && *cp != '!') {
497                 char *newpw = xmalloc (strlen (cp) + 2);
498
499                 strcpy (newpw, "!");
500                 strcat (newpw, cp);
501                 cp = newpw;
502         }
503         return cp;
504 }
505
506
507 static void update_noshadow (void)
508 {
509         const struct passwd *pw;
510         struct passwd *npw;
511
512         if (!pw_lock ()) {
513                 fprintf (stderr,
514                          _
515                          ("Cannot lock the password file; try again later.\n"));
516                 SYSLOG ((LOG_WARN, "can't lock password file"));
517                 exit (E_PWDBUSY);
518         }
519         if (!pw_open (O_RDWR)) {
520                 fprintf (stderr, _("Cannot open the password file.\n"));
521                 SYSLOG ((LOG_ERR, "can't open password file"));
522                 fail_exit (E_MISSING);
523         }
524         pw = pw_locate (name);
525         if (!pw) {
526                 fprintf (stderr, _("%s: %s not found in /etc/passwd\n"),
527                          Prog, name);
528                 fail_exit (E_NOPERM);
529         }
530         npw = __pw_dup (pw);
531         if (!npw)
532                 oom ();
533         npw->pw_passwd = update_crypt_pw (npw->pw_passwd);
534         if (!pw_update (npw)) {
535                 fprintf (stderr,
536                          _("Error updating the password entry.\n"));
537                 SYSLOG ((LOG_ERR, "error updating password entry"));
538                 fail_exit (E_FAILURE);
539         }
540 #ifdef NDBM
541         if (pw_dbm_present () && !pw_dbm_update (npw)) {
542                 fprintf (stderr,
543                          _("Error updating the DBM password entry.\n"));
544                 SYSLOG ((LOG_ERR, "error updaring dbm password entry"));
545                 fail_exit (E_FAILURE);
546         }
547         endpwent ();
548 #endif
549         if (!pw_close ()) {
550                 fprintf (stderr,
551                          _("Cannot commit password file changes.\n"));
552                 SYSLOG ((LOG_ERR, "can't rewrite password file"));
553                 fail_exit (E_FAILURE);
554         }
555         pw_unlock ();
556 }
557
558 #ifdef SHADOWPWD
559 static void update_shadow (void)
560 {
561         const struct spwd *sp;
562         struct spwd *nsp;
563
564         if (!spw_lock ()) {
565                 fprintf (stderr,
566                          _
567                          ("Cannot lock the password file; try again later.\n"));
568                 SYSLOG ((LOG_WARN, "can't lock password file"));
569                 exit (E_PWDBUSY);
570         }
571         if (!spw_open (O_RDWR)) {
572                 fprintf (stderr, _("Cannot open the password file.\n"));
573                 SYSLOG ((LOG_ERR, "can't open password file"));
574                 fail_exit (E_FAILURE);
575         }
576         sp = spw_locate (name);
577         if (!sp) {
578                 /* Try to update the password in /etc/passwd instead. */
579                 spw_close ();
580                 update_noshadow ();
581                 spw_unlock ();
582                 return;
583         }
584         nsp = __spw_dup (sp);
585         if (!nsp)
586                 oom ();
587         nsp->sp_pwdp = update_crypt_pw (nsp->sp_pwdp);
588         if (xflg)
589                 nsp->sp_max = (age_max * DAY) / SCALE;
590         if (nflg)
591                 nsp->sp_min = (age_min * DAY) / SCALE;
592         if (wflg)
593                 nsp->sp_warn = (warn * DAY) / SCALE;
594         if (iflg)
595                 nsp->sp_inact = (inact * DAY) / SCALE;
596         if (do_update_age)
597                 nsp->sp_lstchg = time ((time_t *) 0) / SCALE;
598         /*
599          * Force change on next login, like SunOS 4.x passwd -e or Solaris
600          * 2.x passwd -f. Solaris 2.x seems to do the same thing (set
601          * sp_lstchg to 0).
602          */
603         if (eflg)
604                 nsp->sp_lstchg = 0;
605
606         if (!spw_update (nsp)) {
607                 fprintf (stderr,
608                          _("Error updating the password entry.\n"));
609                 SYSLOG ((LOG_ERR, "error updating password entry"));
610                 fail_exit (E_FAILURE);
611         }
612 #ifdef NDBM
613         if (sp_dbm_present () && !sp_dbm_update (nsp)) {
614                 fprintf (stderr,
615                          _("Error updating the DBM password entry.\n"));
616                 SYSLOG ((LOG_ERR, "error updaring dbm password entry"));
617                 fail_exit (E_FAILURE);
618         }
619         endspent ();
620 #endif
621         if (!spw_close ()) {
622                 fprintf (stderr,
623                          _("Cannot commit password file changes.\n"));
624                 SYSLOG ((LOG_ERR, "can't rewrite password file"));
625                 fail_exit (E_FAILURE);
626         }
627         spw_unlock ();
628 }
629 #endif                          /* SHADOWPWD */
630
631 static long getnumber (const char *str)
632 {
633         long val;
634         char *cp;
635
636         val = strtol (str, &cp, 10);
637         if (*cp)
638                 usage (E_BAD_ARG);
639         return val;
640 }
641
642 /*
643  * passwd - change a user's password file information
644  *
645  *      This command controls the password file and commands which are used
646  *      to modify it.
647  *
648  *      The valid options are
649  *
650  *      -l      lock the named account (*)
651  *      -u      unlock the named account (*)
652  *      -d      delete the password for the named account (*)
653  *      -e      expire the password for the named account (*)
654  *      -x #    set sp_max to # days (*)
655  *      -n #    set sp_min to # days (*)
656  *      -w #    set sp_warn to # days (*)
657  *      -i #    set sp_inact to # days (*)
658  *      -S      show password status of named account
659  *      -g      execute gpasswd command to interpret flags
660  *      -f      execute chfn command to interpret flags
661  *      -s      execute chsh command to interpret flags
662  *      -k      change password only if expired
663  *
664  *      (*) requires root permission to execute.
665  *
666  *      All of the time fields are entered in days and converted to the
667  *      appropriate internal format. For finer resolute the chage
668  *      command must be used.
669  */
670
671 int main (int argc, char **argv)
672 {
673         int flag;               /* Current option to process     */
674         const struct passwd *pw;        /* Password file entry for user      */
675
676 #ifndef USE_PAM
677         char *cp;               /* Miscellaneous character pointing  */
678
679 #ifdef SHADOWPWD
680         const struct spwd *sp;  /* Shadow file entry for user   */
681 #endif
682 #endif
683
684         setlocale (LC_ALL, "");
685         bindtextdomain (PACKAGE, LOCALEDIR);
686         textdomain (PACKAGE);
687
688         /*
689          * The program behaves differently when executed by root than when
690          * executed by a normal user.
691          */
692         amroot = (getuid () == 0);
693
694         /*
695          * Get the program name. The program name is used as a prefix to
696          * most error messages.
697          */
698         Prog = Basename (argv[0]);
699
700         sanitize_env ();
701
702         OPENLOG ("passwd");
703
704         /*
705          * Start with the flags which cause another command to be executed.
706          * The effective UID will be set back to the real UID and the new
707          * command executed with the flags
708          *
709          * These flags are deprecated, may change in a future release.
710          * Please run these programs directly.  --marekm
711          */
712
713         if (argc > 1 && argv[1][0] == '-' && strchr ("gfs", argv[1][1])) {
714                 char buf[200];
715
716                 setuid (getuid ());
717                 switch (argv[1][1]) {
718                 case 'g':
719                         argv[1] = GPASSWD_PROGRAM;      /* XXX warning: const */
720                         break;
721                 case 'f':
722                         argv[1] = CHFN_PROGRAM; /* XXX warning: const */
723                         break;
724                 case 's':
725                         argv[1] = CHSH_PROGRAM; /* XXX warning: const */
726                         break;
727                 default:
728                         usage (E_BAD_ARG);
729                 }
730                 snprintf (buf, sizeof buf, _("%s: Cannot execute %s"),
731                           Prog, argv[1]);
732                 execvp (argv[1], &argv[1]);
733                 perror (buf);
734                 SYSLOG ((LOG_ERR, "cannot execute %s", argv[1]));
735                 closelog ();
736                 exit (E_FAILURE);
737         }
738
739         /* 
740          * The remaining arguments will be processed one by one and executed
741          * by this command. The name is the last argument if it does not
742          * begin with a "-", otherwise the name is determined from the
743          * environment and must agree with the real UID. Also, the UID will
744          * be checked for any commands which are restricted to root only.
745          */
746
747 #ifdef SHADOWPWD
748 #define FLAGS "adlqr:uSekn:x:i:w:"
749 #else
750 # define FLAGS "adlqr:uS"
751 #endif
752
753         while ((flag = getopt (argc, argv, FLAGS)) != EOF) {
754 #undef FLAGS
755                 switch (flag) {
756 #ifdef  SHADOWPWD
757                 case 'x':
758                         age_max = getnumber (optarg);
759                         xflg++;
760                         anyflag = 1;
761                         break;
762                 case 'n':
763                         age_min = getnumber (optarg);
764                         nflg++;
765                         anyflag = 1;
766                         break;
767                 case 'w':
768                         warn = getnumber (optarg);
769                         if (warn >= -1)
770                                 wflg++;
771                         anyflag = 1;
772                         break;
773                 case 'i':
774                         inact = getnumber (optarg);
775                         if (inact >= -1)
776                                 iflg++;
777                         anyflag = 1;
778                         break;
779                 case 'e':
780                         eflg++;
781                         anyflag = 1;
782                         break;
783                 case 'k':
784                         /* change only if expired, like Linux-PAM passwd -k. */
785                         kflg++; /* ok for users */
786                         break;
787 #endif                          /* SHADOWPWD */
788                 case 'a':
789                         aflg++;
790                         break;
791                 case 'q':
792                         qflg++; /* ok for users */
793                         break;
794                 case 'S':
795                         Sflg++; /* ok for users */
796                         break;
797                 case 'd':
798                         dflg++;
799                         anyflag = 1;
800                         break;
801                 case 'l':
802                         lflg++;
803                         anyflag = 1;
804                         break;
805                 case 'u':
806                         uflg++;
807                         anyflag = 1;
808                         break;
809                 case 'r':
810                         /* -r repository (files|nis|nisplus) */
811                         /* only "files" supported for now */
812                         if (strcmp (optarg, "files") != 0) {
813                                 fprintf (stderr,
814                                          _
815                                          ("%s: repository %s not supported\n"),
816                                          Prog, optarg);
817                                 exit (E_BAD_ARG);
818                         }
819                         break;
820                 default:
821                         usage (E_BAD_ARG);
822                 }
823         }
824
825         /*
826          * Now I have to get the user name. The name will be gotten from the
827          * command line if possible. Otherwise it is figured out from the
828          * environment.
829          */
830
831         pw = get_my_pwent ();
832         if (!pw) {
833                 fprintf (stderr,
834                          _("%s: Cannot determine your user name.\n"),
835                          Prog);
836                 exit (E_NOPERM);
837         }
838         myname = xstrdup (pw->pw_name);
839         if (optind < argc)
840                 name = argv[optind];
841         else
842                 name = myname;
843
844         /*
845          * The -a flag requires -S, no other flags, no username, and
846          * you must be root.  --marekm
847          */
848
849         if (aflg) {
850                 if (anyflag || !Sflg || (optind < argc))
851                         usage (E_USAGE);
852                 if (!amroot) {
853                         fprintf (stderr, _("%s: Permission denied.\n"),
854                                  Prog);
855                         exit (E_NOPERM);
856                 }
857                 setpwent ();
858                 while ((pw = getpwent ()))
859                         print_status (pw);
860                 exit (E_SUCCESS);
861         }
862 #if 0
863         /*
864          * Allow certain users (administrators) to change passwords of
865          * certain users. Not implemented yet. --marekm
866          */
867         if (may_change_passwd (myname, name))
868                 amroot = 1;
869 #endif
870
871         /*
872          * If any of the flags were given, a user name must be supplied on
873          * the command line. Only an unadorned command line doesn't require
874          * the user's name be given. Also, -x, -n, -w, -i, -e, -d,
875          * -l, -u may appear with each other. -S, -k must appear alone.
876          */
877
878         /*
879          * -S now ok for normal users (check status of my own account), and
880          * doesn't require username.  --marekm
881          */
882
883         if (anyflag && optind >= argc)
884                 usage (E_USAGE);
885
886         if (anyflag + Sflg + kflg > 1)
887                 usage (E_USAGE);
888
889         if (anyflag && !amroot) {
890                 fprintf (stderr, _("%s: Permission denied\n"), Prog);
891                 exit (E_NOPERM);
892         }
893 #ifdef NDBM
894         endpwent ();
895         pw_dbm_mode = O_RDWR;
896 #ifdef SHADOWPWD
897         sp_dbm_mode = O_RDWR;
898 #endif
899 #endif
900
901         pw = getpwnam (name);
902         if (!pw) {
903                 fprintf (stderr, _("%s: Unknown user %s\n"), Prog, name);
904                 exit (E_NOPERM);
905         }
906
907         /*
908          * Now I have a name, let's see if the UID for the name matches the
909          * current real UID.
910          */
911
912         if (!amroot && pw->pw_uid != getuid ()) {
913                 fprintf (stderr,
914                          _("You may not change the password for %s.\n"),
915                          name);
916                 SYSLOG ((LOG_WARN, "can't change pwd for `%s'", name));
917                 closelog ();
918                 exit (E_NOPERM);
919         }
920
921         if (Sflg) {
922                 print_status (pw);
923                 exit (E_SUCCESS);
924         }
925 #ifndef USE_PAM
926 #ifdef SHADOWPWD
927         /*
928          * The user name is valid, so let's get the shadow file entry.
929          */
930
931         sp = getspnam (name);
932         if (!sp)
933                 sp = pwd_to_spwd (pw);
934
935         cp = sp->sp_pwdp;
936 #else
937         cp = pw->pw_passwd;
938 #endif
939
940         /*
941          * If there are no other flags, just change the password.
942          */
943
944         if (!anyflag) {
945                 STRFCPY (crypt_passwd, cp);
946
947                 /*
948                  * See if the user is permitted to change the password. 
949                  * Otherwise, go ahead and set a new password.
950                  */
951
952 #ifdef SHADOWPWD
953                 check_password (pw, sp);
954 #else
955                 check_password (pw);
956 #endif
957
958                 /*
959                  * Let the user know whose password is being changed.
960                  */
961                 if (!qflg)
962                         printf (_("Changing password for %s\n"), name);
963
964                 if (new_password (pw)) {
965                         fprintf (stderr,
966                                  _("The password for %s is unchanged.\n"),
967                                  name);
968                         closelog ();
969                         exit (E_NOPERM);
970                 }
971                 do_update_pwd = 1;
972                 do_update_age = 1;
973         }
974 #endif                          /* !USE_PAM */
975         /*
976          * Before going any further, raise the ulimit to prevent colliding
977          * into a lowered ulimit, and set the real UID to root to protect
978          * against unexpected signals. Any keyboard signals are set to be
979          * ignored.
980          */
981
982         pwd_init ();
983
984         /*
985          * Don't set the real UID for PAM...
986          */
987 #ifdef USE_PAM
988         if (!anyflag) {
989                 do_pam_passwd (name, qflg, kflg);
990                 exit (E_SUCCESS);
991         }
992 #endif                          /* SHADOWPWD */
993         if (setuid (0)) {
994                 fprintf (stderr, _("Cannot change ID to root.\n"));
995                 SYSLOG ((LOG_ERR, "can't setuid(0)"));
996                 closelog ();
997                 exit (E_NOPERM);
998         }
999 #ifdef SHADOWPWD
1000         if (spw_file_present ())
1001                 update_shadow ();
1002         else
1003 #endif
1004                 update_noshadow ();
1005
1006         nscd_flush_cache ("passwd");
1007         nscd_flush_cache ("group");
1008 #ifdef SHADOWPWD
1009         nscd_flush_cache ("shadow");
1010 #endif
1011
1012         SYSLOG ((LOG_INFO, "password for `%s' changed by `%s'", name,
1013                  myname));
1014         closelog ();
1015         if (!qflg)
1016                 printf (_("Password changed.\n"));
1017         exit (E_SUCCESS);
1018         /* NOT REACHED */
1019 }