home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright 1989, 1990, John F. Haugh II
- * All rights reserved.
- *
- * Use, duplication, and disclosure prohibited without
- * the express written permission of the author.
- */
-
- #include <sys/types.h>
- #include <stdio.h>
- #include <pwd.h>
- #include <fcntl.h>
- #include <signal.h>
- #include <errno.h>
- #include <ctype.h>
- #ifndef BSD
- #include <string.h>
- #include <memory.h>
- #else
- #include <strings.h>
- #define strchr index
- #define strrchr rindex
- #endif
- #include "config.h"
-
- #ifdef SHADOWPWD
- #include "shadow.h"
- #endif
- #ifdef DBM
- #include <dbm.h>
- #endif
-
- #ifndef lint
- static char _sccsid[] = "@(#)chage.c 2.2 09:20:57 8/23/90";
- #endif
-
- char *myname;
-
- time_t today;
- char name[BUFSIZ];
- char newage[10];
- int mflg;
- int Mflg;
- int dflg;
-
- struct passwd pwent;
- #ifdef SHADOWPWD
- struct spwd spwd;
- #endif
- int mindays;
- int maxdays;
- long lastday;
-
- extern int errno;
-
- char Usage[] =
- "Usage: %s [ -m min_days ] [ -M max_days ] [ -w week | -d day ] user\n";
-
- /*
- * usage - print command line syntax and exit
- */
-
- void
- usage ()
- {
- fprintf (stderr, Usage, myname);
- exit (1);
- }
-
- /*
- * change_field - change a single field if a new value is given.
- *
- * prompt the user with the name of the field being changed and the
- * current value.
- */
-
- void
- change_field (val, prompt)
- int *val;
- char *prompt;
- {
- int newval;
- char new[BUFSIZ];
- char *cp;
-
- while (1) {
- if (*val == -1)
- printf ("\t%s []: ", prompt);
- else
- printf ("\t%s [%d]: ", prompt, *val);
-
- fgets (new, BUFSIZ, stdin);
-
- if (cp = strchr (new, '\n'))
- *cp = '\0';
- else
- return;
-
- if (new[0] == '\0')
- return;
-
- newval = strtol (new, &cp, 10);
- if (cp != new && newval >= -1 && newval <= 10000) {
- *val = newval;
- return;
- }
- fprintf (stderr, "%s: illegal value: %s\n", myname, new);
- }
- }
-
- /*
- * new_fields - change the user's password aging information interactively.
- *
- * prompt the user for the two password age values. set the fields
- * from the user's response, or leave alone if nothing was entered.
- */
-
- new_fields ()
- {
- printf ("Enter the new value, or press return for the default\n\n");
-
- change_field (&mindays, "Minimum Password Age");
- change_field (&maxdays, "Maximum Password Age");
- change_field (&lastday, "Last Password Change");
- }
-
- #ifdef DBM
- /*
- * update_dbm
- *
- * Updates the DBM password files, if they exist.
- */
-
- update_dbm (pw)
- struct passwd *pw;
- {
- datum key;
- datum content;
- char data[BUFSIZ];
- int len;
-
- strcpy (data, PWDFILE);
- strcat (data, ".pag");
- if (access (data, 0))
- return;
-
- len = pw_pack (pw, data);
- content.dsize = len;
- content.dptr = data;
-
- key.dsize = strlen (pw->pw_name);
- key.dptr = pw->pw_name;
- store (key, content);
-
- key.dsize = sizeof pw->pw_uid;
- key.dptr = (char *) &pw->pw_uid;
- store (key, content);
- }
- #endif
-
- int
- main (argc, argv)
- int argc;
- char **argv;
- {
- extern int optind;
- extern char *optarg;
- void die ();
- char *cp;
- char *getlogin ();
- int amroot;
- int lockfd = -1;
- int flag;
- struct passwd *pw;
- #ifdef SHADOWPWD
- struct spwd *sp;
- #endif
- struct passwd *getpwuid ();
- struct passwd *getpwnam ();
- struct passwd *sgetpwent ();
- FILE *npwd;
- FILE *pwd;
- char buf[BUFSIZ];
- char tmp[BUFSIZ];
-
- if (myname = strchr (argv[0], '/'))
- myname++;
- else
- myname = argv[0];
-
- if (getuid () != 0) {
- fprintf (stderr, "%s: permission denied\n", myname);
- exit (1);
- }
- while ((flag = getopt (argc, argv, "m:M:d:w:")) != EOF) {
- switch (flag) {
- case 'm':
- mflg++;
- mindays = strtol (optarg, 0, 10);
- break;
- case 'M':
- Mflg++;
- maxdays = strtol (optarg, 0, 10);
- break;
- case 'd':
- dflg++;
- lastday = strtol (optarg, 0, 10);
- break;
- case 'w':
- dflg++;
- lastday = strtol (optarg, 0, 10) * 7;
- break;
- default:
- usage ();
- }
- }
- if (argc != optind + 1)
- usage ();
-
- if (! (pw = getpwnam (argv[optind]))) {
- fprintf (stderr, "%s: unknown user: %s\n",
- myname, argv[optind]);
- exit (1);
- }
- strcpy (name, pw->pw_name);
- #ifdef SHADOWPWD
- if (sp = getspnam (name)) {
- spwd = *sp;
- spwd.sp_namp = strdup (sp->sp_namp);
- spwd.sp_pwdp = strdup (sp->sp_pwdp);
- }
- #endif
- pwent = *pw;
- pwent.pw_name = strdup (pw->pw_name);
- pwent.pw_passwd = strdup (pw->pw_passwd);
- pwent.pw_age = strdup (pw->pw_age);
- pwent.pw_gecos = strdup (pw->pw_gecos);
- pwent.pw_dir = strdup (pw->pw_dir);
- pwent.pw_shell = strdup (pw->pw_shell);
-
- /*
- * Set the fields that aren't being set from the command line
- * from the password file.
- */
-
- #ifdef SHADOWPWD
- if (sp) {
- if (! Mflg)
- maxdays = spwd.sp_max;
- if (! mflg)
- mindays = spwd.sp_min;
- if (! dflg)
- lastday = spwd.sp_lstchg;
- } else
- #endif
- if (pwent.pw_age && strlen (pwent.pw_age) >= 2) {
- if (! Mflg)
- maxdays = c64i (pwent.pw_age[0]) * 7;
- if (! mflg)
- mindays = c64i (pwent.pw_age[1]) * 7;
- if (! dflg && strlen (pwent.pw_age) == 4)
- lastday = a64l (&pwent.pw_age[2]) * 7;
- }
-
- /*
- * If none of the fields were changed from the command line,
- * let the user interactively change them.
- */
-
- if (! mflg && ! Mflg && ! dflg) {
- printf ("Changing the aging information for %s\n", name);
- new_fields ();
- }
-
- /*
- * Output the new password files.
- */
-
- signal (SIGHUP, SIG_IGN);
- signal (SIGINT, SIG_IGN);
- signal (SIGQUIT, SIG_IGN);
- signal (SIGTERM, SIG_IGN);
-
- ulimit (30000); /* prevent any funny business */
- umask (0); /* get new files modes correct */
-
- #ifndef NDEBUG
- if ((lockfd = open (".pwdlock", O_RDONLY|O_CREAT|O_EXCL), 0444) == -1)
- #else
- if ((lockfd = open (PWDLOCK, O_RDONLY|O_CREAT|O_EXCL), 0444) == -1)
- #endif /* NDEBUG */
- {
- puts ("Can't get lock");
- exit (1);
- }
- umask (077); /* close security holes to come ... */
-
- #ifdef SHADOWPWD
- if (sp) {
- spwd.sp_min = mindays;
- spwd.sp_max = maxdays;
- spwd.sp_lstchg = lastday;
-
- if (access (NSHADOW, 0) == 0 && unlink (NSHADOW) == -1)
- goto failure;
-
- if ((npwd = fopen (NSHADOW, "w")) == (FILE *) 0)
- goto failure;
-
- if (chmod (NSHADOW, 0400) || chown (NSHADOW, 0, 0))
- goto failure;
-
- setspent ();
-
- while (sp = getspent ()) {
- if (strcmp (sp->sp_namp, name) == 0)
- break;
-
- if (putspent (sp, npwd))
- goto failure;
- }
- (void) putspent (&spwd, npwd); /* add the new entry */
-
- while (sp = getspent ()) /* finish the other ones off */
- (void) putspent (sp, npwd);
-
- endspent ();
-
- if (ferror (npwd)) {
- perror (NSHADOW);
- if (unlink (NPWDFILE) || unlink (PWDLOCK))
- fputs ("Help!\n", stderr);
-
- exit (1);
- }
- fflush (npwd);
- fclose (npwd);
-
- if (access (OSHADOW, 0) == 0) {
- if (unlink (OSHADOW)) {
- puts ("Can't remove backup file");
- goto unlock;
- }
- }
- if (link (SHADOW, OSHADOW) || unlink (SHADOW)) {
- puts ("Can't save backup file");
- goto unlock;
- }
- #ifndef BSD
- if (link (NSHADOW, SHADOW) || unlink (NSHADOW))
- #else
- if (rename (NSHADOW, SHADOW))
- #endif
- {
- (void) unlink (OSHADOW);
- puts ("Can't rename new file");
- goto unlock;
- }
- #ifndef NDEBUG
- (void) unlink (".pwdlock");
- #else
- (void) unlink (PWDLOCK);
- #endif
- exit (0);
- /*NOTREACHED*/
- }
- #endif
- if (maxdays == -1 || mindays == -1 || lastday == -1) {
- pwent.pw_age = "";
- } else {
- if (maxdays > (63*7))
- maxdays = 63*7;
- if (mindays > (63*7))
- mindays = 63*7;
-
- newage[0] = i64c (maxdays / 7);
- newage[1] = i64c (mindays / 7);
- strcpy (&newage[2], l64a (lastday / 7));
- pwent.pw_age = newage;
- }
- #ifdef DBM
- update_dbm (&pwent);
- #endif
- if (access (NPWDFILE, 0) == 0 && unlink (NPWDFILE) == -1) {
- perror (NPWDFILE);
- exit (1);
- }
- #ifndef NDEBUG
- if ((npwd = fopen ("npasswd", "w")) == (FILE *) 0)
- #else
- umask (077); /* no permissions for non-roots */
-
- if ((npwd = fopen (NPWDFILE, "w")) == (FILE *) 0)
- #endif /* NDEBUG */
- {
- perror (NPWDFILE);
- exit (1);
- }
- #ifndef NDEBUG
- chmod (NPWDFILE, 0444); /* lets have some security here ... */
- chown (NPWDFILE, 0, 0); /* ... and keep the bad guy away */
- #endif /* NDEBUG */
- if ((pwd = fopen (PWDFILE, "r")) == (FILE *) 0) {
- perror (NPWDFILE);
- exit (1);
- }
- while (fgets (buf, BUFSIZ, pwd) != (char *) 0) {
- if (buf[0] == '#' || ! (pw = sgetpwent (buf))) {
- fputs (buf, npwd);
- } else if (strcmp (pw->pw_name, pwent.pw_name) != 0)
- fputs (buf, npwd);
- else
- break;
- }
- (void) fprintf (npwd, "%s:", pw->pw_name);
- if (pwent.pw_age && pwent.pw_age[0])
- (void) fprintf (npwd, "%s,%s:", pwent.pw_passwd, pwent.pw_age);
- else
- (void) fprintf (npwd, "%s:", pwent.pw_passwd);
-
- (void) fprintf (npwd, "%d:%d:%s:%s:%s\n",
- pwent.pw_uid, pwent.pw_gid, pwent.pw_gecos, pwent.pw_dir,
- pwent.pw_shell);
-
- while (fgets (buf, BUFSIZ, pwd) != (char *) 0)
- fputs (buf, npwd);
-
- if (ferror (npwd)) {
- perror (NPWDFILE);
- if (unlink (NPWDFILE) || unlink (PWDLOCK))
- fputs ("Help!\n", stderr);
-
- exit (1);
- }
- fflush (npwd);
- fclose (npwd);
- #ifdef NDEBUG
- chmod (NPWDFILE, 0644);
- if (unlink (OPWDFILE) == -1) {
- if (errno != ENOENT) {
- puts ("Can't unlink backup file");
- goto unlock;
- }
- }
- if (link (PWDFILE, OPWDFILE) || unlink (PWDFILE)) {
- puts ("Can't save backup file");
- goto unlock;
- }
- #ifndef BSD
- if (link (NPWDFILE, PWDFILE) || unlink (NPWDFILE))
- #else
- if (rename (NPWDFILE, PWDFILE))
- #endif
- {
- puts ("Can't rename new file");
- goto unlock;
- }
- #endif /* NDEBUG */
- #ifndef NDEBUG
- (void) unlink (".pwdlock");
- #else
- (void) unlink (PWDLOCK);
- #endif
- exit (0);
- /*NOTREACHED*/
-
- failure:
- puts ("Permission denied.");
- unlock:
- if (lockfd >= 0)
- (void) unlink (PWDLOCK);
-
- (void) unlink (NPWDFILE);
- exit (1);
- /*NOTREACHED*/
- }
-