BSD chpass for System V

John F Haugh II jfh at rpp386.cactus.org
Fri Jan 11 02:07:31 AEST 1991


I finally got a copy of the freed BSD sources and was tinkering with
chpass.  It is a slightly more user-friendly version of the chfn and
chsh commands.  So, I ported it to SCO Xenix and this is what I got.
It should work just fine on any System V machine.  The chpass.1
manpage has not been modified at all and may not be usable.  Feel
free to fix it and mail the fixes back to me.

It requires that you have the "mkpasswd" command.  If you don't have
it, feel free to rip out that part of the code or pick up the version
which I posted here some time back from your nearest alt.sources
archive site.

I will be modifying this to work with the password libraries I've
been tinkering with and will add it to that collection of commands
at that time.  On thing I hope to fix is not having to rebuild the
entire password DBM file each time an entry is changed.  This will
greatly cut down on the amount of time the password file is locked.

As always, unshar and enjoy!
--
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	chpass.1
#	Makefile
#	chpass.c
#	chpass.h
#	field.c
#	pathnames.h
#	util.c
# This archive created: Thu Jan 10 09:00:12 1991
# By:	John F Haugh II (River Parishes Programming, Austin TX)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'chpass.1'" '(5439 characters)'
if test -f 'chpass.1'
then
	echo shar: "will not over-write existing file 'chpass.1'"
else
sed 's/^X//' << \SHAR_EOF > 'chpass.1'
X.\" Copyright (c) 1988, 1990 The Regents of the University of California.
X.\" All rights reserved.
X.\"
X.\" Redistribution and use in source and binary forms are permitted provided
X.\" that: (1) source distributions retain this entire copyright notice and
X.\" comment, and (2) distributions including binaries display the following
X.\" acknowledgement:  ``This product includes software developed by the
X.\" University of California, Berkeley and its contributors'' in the
X.\" documentation or other materials provided with the distribution and in
X.\" all advertising materials mentioning features or use of this software.
X.\" Neither the name of the University nor the names of its contributors may
X.\" be used to endorse or promote products derived from this software without
X.\" specific prior written permission.
X.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
X.\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
X.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X.\"
X.\"     @(#)chpass.1	5.9 (Berkeley) 7/24/90
X.\"
X.Dd July 24, 1990
X.Dt CHPASS 1
X.Os BSD 4.4
X.Sh NAME
X.Nm chpass
X.Nd add or change user database information
X.Sh SYNOPSIS
Xchpass
X.Op Fl a Ar list
X.Op Fl s Ar shell
X.Op user
X.Sh DESCRIPTION
X.Nm Chpass
Xallows editing of the user database information associated
Xwith
X.Ar user
Xor, by default, the current user.
XThe information is formatted and supplied to an editor for changes.
X.Pp
XOnly the information that the user is allowed to change is displayed.
X.Pp
XThe options are as follows:
X.Tw Ds
X.Tp Fl a
XThe super-user is allowed to directly supply a user database
Xentry, in the format specified by
X.Xr passwd 5 ,
Xas an argument.
XThis argument must be a colon (``:'') separated list of all the
Xuser database fields, although they may be empty.
X.Tp Fl s
XThe
X.Fl s
Xoption attempts to change the user's shell to
X.Ar newsh .
X.Tp
X.Pp
XPossible display items are as follows:
X.Pp
X.Dw Home\ Directory:
X.Dp Login:
Xuser's login name
X.Dp Password:
Xuser's encrypted password
X.Dp Uid:
Xuser's id
X.Dp Gid:
Xuser's login group id
X.Dp Change:
Xpassword change time
X.Dp Expire:
Xaccount expiration time
X.Dp Class:
Xuser's general classification
X.Dp Home Directory:
Xuser's home directory
X.Dp Shell:
Xuser's login shell
X.Dp Full Name:
Xuser's real name
X.Dp Location:
Xuser's normal location
X.Dp Home Phone:
Xuser's home phone
X.Dp Office Phone:
Xuser's office phone
X.Dp
X.Pp
XThe
X.Ar login
Xfield is the user name used to access the computer account.
X.Pp
XThe
X.Ar password
Xfield contains the encrypted form of the user's password.
X.Pp
XThe
X.Ar uid
Xfield is the number associated with the
X.Ar login
Xfield.
XBoth of these fields should be unique across the system (and often
Xacross a group of systems) as they control file access.
X.Pp
XWhile it is possible to have multiple entries with identical login names
Xand/or identical user id's, it is usually a mistake to do so.  Routines
Xthat manipulate these files will often return only one of the multiple
Xentries, and that one by random selection.
X.Pp
XThe
X.Ar group
Xfield is the group that the user will be placed in at login.
XSince this system supports multiple groups (see
X.Xr groups 1 )
Xthis field currently has little special meaning.
XThis field may be filled in with either a number or a group name (see
X.Xr group 5 ) .
X.Pp
XThe
X.Ar change
Xfield is the date by which the password must be changed.
X.Pp
XThe
X.Ar expire
Xfield is the date on which the account expires.
X.Pp
XBoth the
X.Ar change
Xand
X.Ar expire
Xfields should be entered in the form ``month day year'' where
X.Ar month
Xis the month name (the first three characters are sufficient),
X.Ar day
Xis the day of the month, and
X.Ar year
Xis the year.
X.bp
XThe
X.Ar class
Xfield is currently unused.  In the near future it will be a key to
Xa
X.Xr termcap 5
Xstyle database of user attributes.
X.Pp
XThe user's
X.Ar home directory
Xis the full UNIX path name where the user
Xwill be placed at login.
X.Pp
XThe
X.Ar shell
Xfield is the command interpreter the user prefers.
XIf the
X.Ar shell
Xfield is empty, the Bourne shell,
X.Pa /bin/sh ,
Xis assumed.
XWhen altering a login shell, and not the super-user, the user
Xmay not change from a non-standard shell or to a non-standard
Xshell.
XNon-standard is defined as a shell not found in
X.Pa /etc/shells .
X.Pp
XThe last four fields are for storing the user's
X.Ar full name , office location ,
Xand
X.Ar home
Xand
X.Ar work telephone
Xnumbers.
X.Pp
XOnce the information has been verified,
X.Nm chpass
Xuses
X.Xr mkpasswd 8
Xto update the user database.  This is run in the background, and,
Xat very large sites could take several minutes.  Until this update
Xis completed, the password file is unavailable for other updates
Xand the new information will not be available to programs.
X.Sh ENVIRONMENT
XThe
X.Xr vi 1
Xeditor will be used unless the environment variable EDITOR is set to
Xan alternate editor.
XWhen the editor terminates, the information is re-read and used to
Xupdate the user database itself.
XOnly the user, or the super-user, may edit the information associated
Xwith the user.
X.Sh FILES
X.Dw /etc/master.passwd
X.Di L
X.Dp Pa /etc/master.passwd
XThe user database
X.Dp Pa /etc/shells
XThe list of approved shells
X.Dp
X.Sh SEE ALSO
X.Xr login 1 ,
X.Xr finger 1 ,
X.Xr getusershell 3 ,
X.Xr passwd 5 ,
X.Xr mkpasswd 8 ,
X.Xr vipw 8
X.Pp
XRobert Morris and Ken Thompson,
X.Ar UNIX Password security
X.Sh HISTORY
XFirst release 4.3 Reno BSD.
X.Sh BUGS
XUser information should (and eventually will) be stored elsewhere.
SHAR_EOF
if test 5439 -ne "`wc -c < 'chpass.1'`"
then
	echo shar: "error transmitting 'chpass.1'" '(should have been 5439 characters)'
fi
fi
echo shar: "extracting 'Makefile'" '(302 characters)'
if test -f 'Makefile'
then
	echo shar: "will not over-write existing file 'Makefile'"
else
sed 's/^X//' << \SHAR_EOF > 'Makefile'
X#	@(#)Makefile	5.3 (Berkeley) 5/11/90
X
XPROG=	chpass
XSRCS=	chpass.c field.c util.c
XBINOWN=	root
XBINMODE=4555
XLINKS=	${BINDIR}/chpass ${BINDIR}/chfn ${BINDIR}/chpass ${BINDIR}/chsh
XMLINKS=	chpass.1 chfn.1 chpass.1 chsh.1
XCFLAGS=	-g
X
Xall:	$(PROG)
X
X$(PROG): $(SRCS:.c=.o)
X	cc $(CFLAGS) -o $@ $(SRCS:.c=.o)
SHAR_EOF
if test 302 -ne "`wc -c < 'Makefile'`"
then
	echo shar: "error transmitting 'Makefile'" '(should have been 302 characters)'
fi
fi
echo shar: "extracting 'chpass.c'" '(11797 characters)'
if test -f 'chpass.c'
then
	echo shar: "will not over-write existing file 'chpass.c'"
else
sed 's/^X//' << \SHAR_EOF > 'chpass.c'
X/*-
X * Copyright (c) 1988 The Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted provided
X * that: (1) source distributions retain this entire copyright notice and
X * comment, and (2) distributions including binaries display the following
X * acknowledgement:  ``This product includes software developed by the
X * University of California, Berkeley and its contributors'' in the
X * documentation or other materials provided with the distribution and in
X * all advertising materials mentioning features or use of this software.
X * Neither the name of the University nor the names of its contributors may
X * be used to endorse or promote products derived from this software without
X * specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
X * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X */
X
X/*
X * Modified 1/10/91, John F Haugh II
X *
X * Made changes needed to get chpass to run on System V.
X */
X
X#ifndef lint
Xchar copyright[] =
X"@(#) Copyright (c) 1988 The Regents of the University of California.\n\
X All rights reserved.\n";
X#endif /* not lint */
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)chpass.c	5.15 (Berkeley) 6/29/90";
X#endif /* not lint */
X
X#include <sys/types.h>
X#include <sys/param.h>
X#include <sys/file.h>
X#include <sys/stat.h>
X#include <sys/signal.h>
X#include <fcntl.h>
X#include <time.h>
X#include <pwd.h>
X#include <errno.h>
X#include <stdio.h>
X#include <ctype.h>
X#include <string.h>
X#include "chpass.h"
X#include "pathnames.h"
X
Xchar e1[] = ": ";
Xchar e2[] = ":,";
X
Xint p_gecos(), p_gid(), p_hdir();
Xint p_login(), p_passwd(), p_shell(), p_uid();
X
Xstruct entry list[] = {
X	{ "Login",		p_login,  1,   5, e1,   },
X	{ "Password",		p_passwd, 1,   8, e1,   },
X	{ "Uid",		p_uid,    1,   3, e1,   },
X	{ "Gid",		p_gid,    1,   3, e1,   },
X#define	E_NAME		4
X	{ "Full Name",		p_gecos,  0,   9, e2,   },
X#define	E_BPHONE	5
X	{ "Office Phone",	p_gecos,  0,  12, e2,   },
X#define	E_HPHONE	6
X	{ "Home Phone",		p_gecos,  0,  10, e2,   },
X#define	E_LOCATE	7
X	{ "Location",		p_gecos,  0,   8, e2,   },
X	{ "Home directory",	p_hdir,   1,  14, e1,   },
X#define	E_SHELL		9
X	{ "Shell",		p_shell,  0,   5, e1,   },
X	{ NULL, 0, },
X};
X
Xuid_t uid;
X
Xstruct flock lock = { F_WRLCK, 0, 0, 0, 0, 0 };
Xextern	char	*sys_errlist[];
X
Xmain(argc, argv)
X	int argc;
X	char **argv;
X{
X	extern int errno, optind;
X	extern char *optarg;
X	register char *p;
X	struct passwd lpw, *pw;
X	FILE *temp_fp;
X	int aflag, ch, fd;
X	char *fend, *newsh, *passwd, *temp, *tend;
X	char from[BUFSIZ], to[BUFSIZ];
X	char *getusershell();
X
X	uid = getuid();
X	aflag = 0;
X	newsh = NULL;
X	while ((ch = getopt(argc, argv, "a:s:")) != EOF)
X		switch(ch) {
X		case 'a':
X			if (uid)
X				baduser();
X			loadpw(optarg, pw = &lpw);
X			aflag = 1;
X			break;
X		case 's':
X			newsh = optarg;
X			/* protect p_field -- it thinks NULL is /bin/sh */
X			if (!*newsh)
X				usage();
X			break;
X		case '?':
X		default:
X			usage();
X		}
X	argc -= optind;
X	argv += optind;
X
X	if (!aflag)
X		switch(argc) {
X		case 0:
X			if (!(pw = getpwuid(uid))) {
X				(void)fprintf(stderr,
X				    "chpass: unknown user: uid %u\n", uid);
X				exit(1);
X			}
X			break;
X		case 1:
X			if (!(pw = getpwnam(*argv))) {
X				(void)fprintf(stderr,
X				    "chpass: unknown user %s.\n", *argv);
X				exit(1);
X			}
X			if (uid && uid != pw->pw_uid)
X				baduser();
X			break;
X		default:
X			usage();
X		}
X
X	(void)signal(SIGHUP, SIG_IGN);
X	(void)signal(SIGINT, SIG_IGN);
X	(void)signal(SIGQUIT, SIG_IGN);
X
X	(void)ulimit(2, (1L<<20));
X	(void)umask(0);
X
X	temp = _PATH_PTMP;
X	if ((fd = open(temp, O_WRONLY|O_CREAT|O_EXCL, 0644)) < 0) {
X		if (errno == EEXIST) {
X			(void)fprintf(stderr,
X			    "chpass: password file busy -- try again later.\n");
X			exit(1);
X		}
X		(void)fprintf(stderr, "chpass: %s: %s; ",
X		    temp, sys_errlist[errno]);
X		goto bad;
X	}
X	if (!(temp_fp = fdopen(fd, "w"))) {
X		(void)fprintf(stderr, "chpass: can't write %s; ", temp);
X		goto bad;
X	}
X
X	if (newsh) {
X		if (p_shell(newsh, pw, (struct entry *)NULL))
X			goto bad;
X	}
X	else if (!aflag && !info(pw))
X		goto bad;
X
X	/* root should have a 0 uid and a reasonable shell */
X	if (!strcmp(pw->pw_name, "root")) {
X		if (pw->pw_uid) {
X			(void)fprintf(stderr, "chpass: root uid should be 0.");
X			exit(1);
X		}
X		setusershell();
X		for (;;)
X			if (!(p = getusershell())) {
X				(void)fprintf(stderr,
X				    "chpass: warning, unknown root shell.");
X				break;
X			}
X			else if (!strcmp(pw->pw_shell, p))
X				break;
X	}
X
X	passwd = _PATH_PASSWD;
X	if (!freopen(passwd, "r", stdin)) {
X		(void)fprintf(stderr, "chpass: can't read %s; ", passwd);
X		goto bad;
X	}
X	if (!copy(pw, temp_fp))
X		goto bad;
X
X	(void)fclose(temp_fp);
X	(void)fclose(stdin);
X
X	switch(fork()) {
X	case 0:
X		break;
X	case -1:
X		(void)fprintf(stderr, "chpass: can't fork; ");
X		goto bad;
X		/* NOTREACHED */
X	default:
X		exit(0);
X		/* NOTREACHED */
X	}
X
X	if (makedb(temp)) {
X		(void)fprintf(stderr, "chpass: mkpasswd failed; ");
Xbad:		(void)fprintf(stderr, "%s unchanged.\n", _PATH_PASSWD);
X		(void)unlink(temp);
X		exit(1);
X	}
X
X	/*
X	 * possible race; have to rename four files, and someone could slip
X	 * in between them.  LOCK_EX and rename the ``passwd.dir'' file first
X	 * so that getpwent(3) can't slip in; the lock should never fail and
X	 * it's unclear what to do if it does.  Rename ``ptmp'' last so that
X	 * passwd/vipw/chpass can't slip in.
X	 */
X	(void)nice(-19);
X	fend = strcpy(from, temp) + strlen(temp);
X	tend = strcpy(to, _PATH_PASSWD) + strlen(_PATH_PASSWD);
X	memcpy(fend, ".dir", 5);
X	memcpy(tend, ".dir", 5);
X	if ((fd = open(from, O_RDONLY, 0)) >= 0)
X		fcntl (fd, F_SETLK, &lock);
X
X	/* here we go... */
X	(void)rename(from, to);
X	memcpy(fend, ".pag", 5);
X	memcpy(tend, ".pag", 5);
X	(void)rename(from, to);
X	memcpy(fend, ".orig", 6);
X	(void)rename(from, _PATH_PASSWD);
X	(void)rename(temp, passwd);
X	/* done! */
X	exit(0);
X}
X
Xrename (from, to)
Xchar *from;
Xchar *to;
X{
X	struct stat s1, s2;
X
X	if (stat (from, &s1))
X		return -1;
X
X	if (stat (to, &s2)) {
X		if (link (from, to))
X			return -1;
X
X		return unlink(from);
X	}
X	if (s1.st_dev != s2.st_dev) {
X		errno = EXDEV;
X		return -1;
X	}
X	if (unlink (to))
X		return -1;
X
X	if (link (from, to))
X		return -1;
X
X	return unlink (from);
X}
X
Xinfo(pw)
X	struct passwd *pw;
X{
X	struct stat begin, end;
X	FILE *fp;
X	int fd, rval;
X	char *tfile;
X
X	tfile = _PATH_TMP;
X	if ((fd = open(mktemp(tfile), O_RDWR|O_CREAT, 0600)) == -1
X			|| !(fp = fdopen(fd, "w+"))) {
X		(void)fprintf(stderr, "chpass: no temporary file");
X		return(0);
X	}
X
X	/*
X	 * if print doesn't print out a shell field, make it restricted.
X	 * Not particularly pretty, but print is the routine that checks
X	 * to see if the user can change their shell.
X	 */
X	if (!print(fp, pw))
X		list[E_SHELL].restricted = 1;
X	(void)fflush(fp);
X
X	/*
X	 * give the file to the real user; setuid permissions
X	 * are discarded in edit()
X	 */
X	(void)chown(tfile, getuid(), getgid());
X
X	for (rval = 0;;) {
X		(void)fstat(fd, &begin);
X		if (edit(tfile)) {
X			(void)fprintf(stderr, "chpass: edit failed; ");
X			break;
X		}
X		(void)fstat(fd, &end);
X		if (begin.st_mtime == end.st_mtime) {
X			(void)fprintf(stderr, "chpass: no changes made; ");
X			break;
X		}
X		(void)rewind(fp);
X		if (check(fp, pw)) {
X			rval = 1;
X			break;
X		}
X		(void)fflush(stderr);
X		if (prompt())
X			break;
X	}
X	(void)fclose(fp);
X	(void)unlink(tfile);
X	return(rval);
X}
X
Xcheck(fp, pw)
X	FILE *fp;
X	struct passwd *pw;
X{
X	register struct entry *ep;
X	register char *p;
X	static char buf[1024];
X
X	while (fgets(buf, sizeof(buf), fp)) {
X		if (!buf[0] || buf[0] == '#')
X			continue;
X		if (!(p = strchr(buf, '\n'))) {
X			(void)fprintf(stderr, "chpass: line too long.\n");
X			return(0);
X		}
X		*p = '\0';
X		for (ep = list;; ++ep) {
X			if (!ep->prompt) {
X				(void)fprintf(stderr,
X				    "chpass: unrecognized field.\n");
X				return(0);
X			}
X			if (!strncmp(buf, ep->prompt, ep->len)) {
Xprintf ("found %s\n", buf);
X				if (ep->restricted && uid) {
X					(void)fprintf(stderr,
X					    "chpass: you may not change the %s field.\n",
X					    ep->prompt);
X					return(0);
X				}
X				if (!(p = strchr(buf, ':'))) {
X					(void)fprintf(stderr,
X					    "chpass: line corrupted.\n");
X					return(0);
X				}
X				while (isspace(*++p));
X				if (ep->except && strpbrk(p, ep->except)) {
X					(void)fprintf(stderr,
X					    "chpass: illegal character in the \"%s\" field.\n",
X					    ep->prompt);
X					return(0);
X				}
X				if ((*ep->func)(p, pw, ep))
X					return(0);
X				break;
X			}
X		}
X	}
X	/*
X	 * special checks...
X	 *
X	 * there has to be a limit on the size of the gecos fields,
X	 * otherwise getpwent(3) can choke.
X	 * ``if I swallow anything evil, put your fingers down my throat...''
X	 *	-- The Who
X	 */
X	if (strlen(list[E_NAME].save) + strlen(list[E_BPHONE].save) +
X	    strlen(list[E_HPHONE].save) + strlen(list[E_LOCATE].save)
X	    > 512) {
X		(void)fprintf(stderr, "chpass: gecos field too large.\n");
X		exit(1);
X	}
X	(void)sprintf(pw->pw_gecos = buf, "%s,%s,%s,%s",
X	    list[E_NAME].save, list[E_LOCATE].save, list[E_BPHONE].save,
X	    list[E_HPHONE].save);
X	return(1);
X}
X
Xcopy(pw, fp)
X	struct passwd *pw;
X	FILE *fp;
X{
X	register int done;
X	register char *p;
X	char buf[1024];
X
X	for (done = 0; fgets(buf, sizeof(buf), stdin);) {
X		/* skip lines that are too big */
X		if (!strchr(buf, '\n')) {
X			(void)fprintf(stderr, "chpass: line too long; ");
X			return(0);
X		}
X		if (done) {
X			(void)fprintf(fp, "%s", buf);
X			continue;
X		}
X		if (!(p = strchr(buf, ':'))) {
X			(void)fprintf(stderr, "chpass: corrupted entry; ");
X			return(0);
X		}
X		*p = '\0';
X		if (strcmp(buf, pw->pw_name)) {
X			*p = ':';
X			(void)fprintf(fp, "%s", buf);
X			continue;
X		}
X		(void)fprintf(fp, "%s:%s:%d:%d:%s:%s:%s\n",
X		    pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
X		    pw->pw_gecos, pw->pw_dir, pw->pw_shell);
X		done = 1;
X	}
X	if (!done)
X		(void)fprintf(fp, "%s:%s:%d:%d:%s:%s:%s\n",
X		    pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
X		    pw->pw_gecos, pw->pw_dir, pw->pw_shell);
X	return(1);
X}
X
Xmakedb(file)
X	char *file;
X{
X	int status, pid, w;
X
X	if (!(pid = fork())) {
X		execl(_PATH_MKPASSWD, "mkpasswd", "-p", file, NULL);
X		(void)fprintf(stderr, "chpass: can't find \"mkpasswd\".\n");
X		_exit(127);
X	}
X	while ((w = wait(&status)) != pid && w != -1);
X	return(w == -1 || status);
X}
X
Xedit(file)
X	char *file;
X{
X	int status, pid, w;
X	char *p, *editor, *getenv();
X
X	if (editor = getenv("EDITOR")) {
X		if (p = strrchr(editor, '/'))
X			++p;
X		else
X			p = editor;
X	}
X	else
X		p = editor = "vi";
X	if (!(pid = fork())) {
X		(void)setgid(getgid());
X		(void)setuid(getuid());
X		execlp(editor, p, file, NULL);
X		(void)fprintf(stderr, "chpass: can't find \"%s\".\n", editor);
X		_exit(127);
X	}
X	while ((w = wait(&status)) != pid && w != -1);
X	return(w == -1 || status);
X}
X
Xloadpw(arg, pw)
X	char *arg;
X	register struct passwd *pw;
X{
X	register char *cp;
X	char *bp = arg;
X	long atol();
X	char *strtok();
X
X	pw->pw_name = strtok(bp, ":");
X	pw->pw_passwd = strtok((char *) 0, ":");
X	if (!(cp = strtok((char *) 0, ":")))
X		goto bad;
X	pw->pw_uid = atoi(cp);
X	if (!(cp = strtok((char *) 0, ":")))
X		goto bad;
X	pw->pw_gid = atoi(cp);
X	pw->pw_gecos = strtok((char *) 0, ":");
X	pw->pw_dir = strtok((char *) 0, ":");
X	pw->pw_shell = strtok((char *) 0, ":");
X	if (!pw->pw_shell || strtok((char *) 0, ":")) {
Xbad:		(void)fprintf(stderr, "chpass: bad password list.\n");
X		exit(1);
X	}
X}
X
Xprompt()
X{
X	register int c;
X
X	for (;;) {
X		(void)printf("re-edit the password file? [y]: ");
X		(void)fflush(stdout);
X		c = getchar();
X		if (c != EOF && c != (int)'\n')
X			while (getchar() != (int)'\n');
X		return(c == (int)'n');
X	}
X	/* NOTREACHED */
X}
X
Xbaduser()
X{
X	(void)fprintf(stderr, "chpass: %s\n", sys_errlist[EACCES]);
X	exit(1);
X}
X
Xusage()
X{
X	(void)fprintf(stderr, "usage: chpass [-a list] [-s shell] [user]\n");
X	exit(1);
X}
SHAR_EOF
if test 11797 -ne "`wc -c < 'chpass.c'`"
then
	echo shar: "error transmitting 'chpass.c'" '(should have been 11797 characters)'
fi
fi
echo shar: "extracting 'chpass.h'" '(1156 characters)'
if test -f 'chpass.h'
then
	echo shar: "will not over-write existing file 'chpass.h'"
else
sed 's/^X//' << \SHAR_EOF > 'chpass.h'
X/*
X * Copyright (c) 1988 The Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that: (1) source distributions retain this entire copyright
X * notice and comment, and (2) distributions including binaries display
X * the following acknowledgement:  ``This product includes software
X * developed by the University of California, Berkeley and its contributors''
X * in the documentation or other materials provided with the distribution
X * and in all advertising materials mentioning features or use of this
X * software. Neither the name of the University nor the names of its
X * contributors may be used to endorse or promote products derived
X * from this software without specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X *
X *	@(#)chpass.h	5.2 (Berkeley) 6/1/90
X */
X
Xstruct entry {
X	char *prompt;
X	int (*func)(), restricted, len;
X	char *except, *save;
X};
X
Xextern uid_t uid;
SHAR_EOF
if test 1156 -ne "`wc -c < 'chpass.h'`"
then
	echo shar: "error transmitting 'chpass.h'" '(should have been 1156 characters)'
fi
fi
echo shar: "extracting 'field.c'" '(4654 characters)'
if test -f 'field.c'
then
	echo shar: "will not over-write existing file 'field.c'"
else
sed 's/^X//' << \SHAR_EOF > 'field.c'
X/*
X * Copyright (c) 1988 The Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that: (1) source distributions retain this entire copyright
X * notice and comment, and (2) distributions including binaries display
X * the following acknowledgement:  ``This product includes software
X * developed by the University of California, Berkeley and its contributors''
X * in the documentation or other materials provided with the distribution
X * and in all advertising materials mentioning features or use of this
X * software. Neither the name of the University nor the names of its
X * contributors may be used to endorse or promote products derived
X * from this software without specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X */
X
X/*
X * Modified 1/10/91, John F Haugh II
X *
X * Made changes needed to get chpass to run on System V.
X */
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)field.c	5.12 (Berkeley) 6/1/90";
X#endif /* not lint */
X
X#include <sys/types.h>
X#include <sys/param.h>
X#include <pwd.h>
X#include <grp.h>
X#include <string.h>
X#include <stdio.h>
X#include <ctype.h>
X#include "chpass.h"
X#include "pathnames.h"
X
X#ifndef	USHRT_MAX
X#define	USHRT_MAX	(0xffff)
X#endif
X
X/* ARGSUSED */
Xp_login(p, pw, ep)
X	char *p;
X	struct passwd *pw;
X	struct entry *ep;
X{
X	if (!*p) {
X		(void)fprintf(stderr, "chpass: empty login field.\n");
X		return(1);
X	}
X	if (*p == '-') {
X		(void)fprintf(stderr,
X		    "chpass: login names may not begin with a hyphen.\n");
X		return(1);
X	}
X	if (!(pw->pw_name = strdup(p))) {
X		(void)fprintf(stderr, "chpass: can't save entry.\n");
X		return(1);
X	}
X	if (strchr(p, '.'))
X		(void)fprintf(stderr,
X		    "chpass: \'.\' is dangerous in a login name.\n");
X	for (; *p; ++p)
X		if (isupper(*p)) {
X			(void)fprintf(stderr,
X			    "chpass: upper-case letters are dangerous in a login name.\n");
X			break;
X		}
X	return(0);
X}
X
X/* ARGSUSED */
Xp_passwd(p, pw, ep)
X	char *p;
X	struct passwd *pw;
X	struct entry *ep;
X{
X	if (!*p)
X		pw->pw_passwd = "";	/* "NOLOGIN"; */
X	else if (!(pw->pw_passwd = strdup(p))) {
X		(void)fprintf(stderr, "chpass: can't save password entry.\n");
X		return(1);
X	}
X	
X	return(0);
X}
X
X/* ARGSUSED */
Xp_uid(p, pw, ep)
X	register char *p;
X	struct passwd *pw;
X	struct entry *ep;
X{
X	int id;
X
X	if (!*p) {
X		(void)fprintf(stderr, "chpass: empty uid field.\n");
X		return(1);
X	}
X	if (!isdigit(*p)) {
X		(void)fprintf(stderr, "chpass: illegal uid.\n");
X		return(1);
X	}
X	id = atoi(p);
X	if ((unsigned int)id > USHRT_MAX) {
X		(void)fprintf(stderr, "chpass: %d > max uid value (%d).\n",
X		    id, USHRT_MAX);
X		return(1);
X	}
X	pw->pw_uid = id;
X	return(0);
X}
X
X/* ARGSUSED */
Xp_gid(p, pw, ep)
X	register char *p;
X	struct passwd *pw;
X	struct entry *ep;
X{
X	struct group *gr;
X	int id;
X
X	if (!*p) {
X		(void)fprintf(stderr, "chpass: empty gid field.\n");
X		return(1);
X	}
X	if (!isdigit(*p)) {
X		if (!(gr = getgrnam(p))) {
X			(void)fprintf(stderr,
X			    "chpass: unknown group %s.\n", p);
X			return(1);
X		}
X		pw->pw_gid = gr->gr_gid;
X		return(0);
X	}
X	id = atoi(p);
X	if ((unsigned int)id > USHRT_MAX) {
X		(void)fprintf(stderr, "chpass: %d > max gid value (%d).\n",
X		    id, USHRT_MAX);
X		return(1);
X	}
X	pw->pw_gid = id;
X	return(0);
X}
X
X/* ARGSUSED */
Xp_gecos(p, pw, ep)
X	char *p;
X	struct passwd *pw;
X	struct entry *ep;
X{
Xprintf ("adding %s to %s\n", p, ep->prompt);
X	if (!*p)
X		ep->save = "";
X	else if (!(ep->save = strdup(p))) {
X		(void)fprintf(stderr, "chpass: can't save entry.\n");
X		return(1);
X	}
X	return(0);
X}
X
X/* ARGSUSED */
Xp_hdir(p, pw, ep)
X	char *p;
X	struct passwd *pw;
X	struct entry *ep;
X{
X	if (!*p) {
X		(void)fprintf(stderr, "chpass: empty home directory field.\n");
X		return(1);
X	}
X	if (!(pw->pw_dir = strdup(p))) {
X		(void)fprintf(stderr, "chpass: can't save entry.\n");
X		return(1);
X	}
X	return(0);
X}
X
X/* ARGSUSED */
Xp_shell(p, pw, ep)
X	register char *p;
X	struct passwd *pw;
X	struct entry *ep;
X{
X	char *t, *ok_shell();
X
X	if (!*p) {
X		pw->pw_shell = _PATH_BSHELL;
X		return(0);
X	}
X	/* only admin can change from or to "restricted" shells */
X	if (uid && pw->pw_shell && !ok_shell(pw->pw_shell)) {
X		(void)fprintf(stderr,
X		    "chpass: %s: current shell non-standard.\n", pw->pw_shell);
X		return(1);
X	}
X	if (!(t = ok_shell(p))) {
X		if (uid) {
X			(void)fprintf(stderr,
X			    "chpass: %s: non-standard shell.\n", p);
X			return(1);
X		}
X	}
X	else
X		p = t;
X	if (!(pw->pw_shell = strdup(p))) {
X		(void)fprintf(stderr, "chpass: can't save entry.\n");
X		return(1);
X	}
X	return(0);
X}
SHAR_EOF
if test 4654 -ne "`wc -c < 'field.c'`"
then
	echo shar: "error transmitting 'field.c'" '(should have been 4654 characters)'
fi
fi
echo shar: "extracting 'pathnames.h'" '(1367 characters)'
if test -f 'pathnames.h'
then
	echo shar: "will not over-write existing file 'pathnames.h'"
else
sed 's/^X//' << \SHAR_EOF > 'pathnames.h'
X/*
X * Copyright (c) 1989 The Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that: (1) source distributions retain this entire copyright
X * notice and comment, and (2) distributions including binaries display
X * the following acknowledgement:  ``This product includes software
X * developed by the University of California, Berkeley and its contributors''
X * in the documentation or other materials provided with the distribution
X * and in all advertising materials mentioning features or use of this
X * software. Neither the name of the University nor the names of its
X * contributors may be used to endorse or promote products derived
X * from this software without specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X *
X *	@(#)pathnames.h	5.3 (Berkeley) 6/1/90
X */
X
X/*
X * Modified 1/10/91, John F Haugh II
X *
X * Made changes needed to get chpass to run on System V.
X */
X
X#define	_PATH_TMP	"/tmp/passwd.XXXXXX"
X#define	_PATH_PTMP	"/etc/ptmp"
X#define	_PATH_PASSWD	"/etc/passwd"
X#define	_PATH_MKPASSWD	"/etc/mkpasswd"
X#define	_PATH_BSHELL	"/bin/sh"
X#define	_PATH_SHELLS	"/etc/shells"
SHAR_EOF
if test 1367 -ne "`wc -c < 'pathnames.h'`"
then
	echo shar: "error transmitting 'pathnames.h'" '(should have been 1367 characters)'
fi
fi
echo shar: "extracting 'util.c'" '(3810 characters)'
if test -f 'util.c'
then
	echo shar: "will not over-write existing file 'util.c'"
else
sed 's/^X//' << \SHAR_EOF > 'util.c'
X/*-
X * Copyright (c) 1988 The Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted provided
X * that: (1) source distributions retain this entire copyright notice and
X * comment, and (2) distributions including binaries display the following
X * acknowledgement:  ``This product includes software developed by the
X * University of California, Berkeley and its contributors'' in the
X * documentation or other materials provided with the distribution and in
X * all advertising materials mentioning features or use of this software.
X * Neither the name of the University nor the names of its contributors may
X * be used to endorse or promote products derived from this software without
X * specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
X * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X */
X
X/*
X * Modified 1/10/91, John F Haugh II
X *
X * Made changes needed to get chpass to run on System V.
X */
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)util.c	5.13 (Berkeley) 6/29/90";
X#endif /* not lint */
X
X#include <sys/types.h>
X#include <time.h>
X#include <pwd.h>
X#include <stdio.h>
X#include <string.h>
X#include <ctype.h>
X#include "chpass.h"
X#include "pathnames.h"
X
Xstatic int dmsize[] =
X	{ -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
Xstatic char *months[] =
X	{ "January", "February", "March", "April", "May", "June",
X	  "July", "August", "September", "October", "November",
X	  "December", NULL };
X
X#define	TM_YEAR_BASE	(1900)
X#define	EPOCH_YEAR	(1969)
X#define	DAYSPERLYEAR	(366)
X#define	DAYSPERNYEAR	(365)
X#define	HOURSPERDAY	(24)
X#define	MINSPERHOUR	(60)
X#define	SECSPERMIN	(60)
X
Xstatic FILE *shell_fp;
X
Xint
Xsetusershell ()
X{
X	if (shell_fp) {
X		rewind (shell_fp);
X		return 0;
X	}
X	return (shell_fp = fopen (_PATH_SHELLS, "r")) == 0 ? -1:0;
X}
X
Xchar *
Xgetusershell ()
X{
X	static char shell[BUFSIZ];
X	char *cp;
X
X	if (! shell_fp)
X		return 0;
X
X	if (fgets (shell, sizeof shell, shell_fp) == 0)
X		return 0;
X
X	if (cp = strrchr (shell, '\n'))
X		*cp = '\0';
X
X	return shell;
X}
X
X/*
X * print --
X *	print out the file for the user to edit; strange side-effect:
X *	return if the user is allowed to modify their shell.
X */
Xprint(fp, pw)
X	FILE *fp;
X	struct passwd *pw;
X{
X	register char *p;
X	int shellval;
X	char *bp;
X	char *getusershell(), *ok_shell(), *ttoa();
X
X	shellval = 1;
X	(void)fprintf(fp, "#Changing user database information for %s.\n",
X	    pw->pw_name);
X	if (!uid) {
X		(void)fprintf(fp, "Login: %s\n", pw->pw_name);
X		(void)fprintf(fp, "Password: %s\n", pw->pw_passwd);
X		(void)fprintf(fp, "Uid [#]: %d\n", pw->pw_uid);
X		(void)fprintf(fp, "Gid [# or name]: %d\n", pw->pw_gid);
X		(void)fprintf(fp, "Home directory: %s\n", pw->pw_dir);
X		(void)fprintf(fp, "Shell: %s\n",
X		    *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL);
X	}
X	/* only admin can change "restricted" shells */
X	else if (ok_shell(pw->pw_shell))
X		(void)fprintf(fp, "Shell: %s\n",
X		    *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL);
X	else
X		shellval = 0;
X	bp = pw->pw_gecos;
X	p = strtok(bp, ",");
X	(void)fprintf(fp, "Full Name: %s\n", p ? p : "");
X	p = strtok((char *) 0, ",");
X	(void)fprintf(fp, "Location: %s\n", p ? p : "");
X	p = strtok((char *) 0, ",");
X	(void)fprintf(fp, "Office Phone: %s\n", p ? p : "");
X	p = strtok((char *) 0, ",");
X	(void)fprintf(fp, "Home Phone: %s\n", p ? p : "");
X	return(shellval);
X}
X
Xchar *
Xok_shell(name)
X	register char *name;
X{
X	register char *p, *sh;
X	char *getusershell();
X
X	setusershell();
X	while (sh = getusershell()) {
X		if (!strcmp(name, sh))
X			return(name);
X		/* allow just shell name, but use "real" path */
X		if ((p = strrchr(sh, '/')) && !strcmp(name, p + 1))
X			return(sh);
X	}
X	return(NULL);
X}
SHAR_EOF
if test 3810 -ne "`wc -c < 'util.c'`"
then
	echo shar: "error transmitting 'util.c'" '(should have been 3810 characters)'
fi
fi
exit 0
#	End of shell archive
-- 
John F. Haugh II                             UUCP: ...!cs.utexas.edu!rpp386!jfh
Ma Bell: (512) 832-8832                           Domain: jfh at rpp386.cactus.org
"While you are here, your wives and girlfriends are dating handsome American
 movie and TV stars. Stars like Tom Selleck, Bruce Willis, and Bart Simpson."



More information about the Alt.sources mailing list