home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Source Code 1992 March
/
Source_Code_CD-ROM_Walnut_Creek_March_1992.iso
/
usenet
/
altsrcs
/
2
/
2288
/
chage.c
next >
Wrap
C/C++ Source or Header
|
1990-12-28
|
10KB
|
535 lines
/*
* 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>
#include <time.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.3 08:59:07 11/5/90";
#endif
char *myname;
time_t today;
char name[BUFSIZ];
char newage[10];
int lflg;
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 [ -l ] [ -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");
}
/*
* list_fields - display the current values of the expiration fields
*
* display the mindays, maxdays, and lastday values.
*/
list_fields ()
{
struct tm *tp;
char *cp;
long changed;
long expires;
changed = lastday * (24L*60L*60L);
expires = maxdays * (24L*60L*60L) + changed;
printf ("Minimum:\t%d\n", mindays);
printf ("Maximum:\t%d\n", maxdays);
printf ("Last Change:\t");
if (changed == 0) {
printf ("Never\n");
} else {
tp = localtime (&changed);
cp = asctime (tp);
printf ("%6.6s, %4.4s\n", cp + 4, cp + 20);
}
printf ("Expires:\t");
if (expires == 0 || maxdays == 10000) {
printf ("Never\n");
} else {
tp = localtime (&expires);
cp = asctime (tp);
printf ("%6.6s, %4.4s\n", cp + 4, cp + 20);
}
}
#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, "lm:M:d:w:")) != EOF) {
switch (flag) {
case 'l':
lflg++;
break;
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);
}
if (lflg && (mflg || Mflg || dflg)) {
fprintf (stderr, "%s: do not include \"l\" with other flags\n",
myname);
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;
}
/*
* Print out the expiration fields if the user has
* requested the list option.
*/
if (lflg) {
list_fields ();
exit (0);
}
/*
* 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*/
}