[alt.sources] Update passwords from a file

jfh@rpp386.cactus.org (John F. Haugh II) (11/17/90)

The next two postings are the code which I promised a few days
ago I would post.  There are two utilities.  chpasswd allows you
to change a group of passwords using a data file as input.  It
is really just a silly little example to how to use a collection
of password file routines I wrote.  newusers is much more amusing
and allows you to create a collection of users using something
like a password file as input.  It allocates the user and group
IDs and does other interesting stuff.  Neither is well documented,
in keeping with the USENET tradition of providing lots of code
and little documentation.

I'm going to ask that this code not be archived since I intend
to post this code on a semi-regular basis until the kinks are
all worked out.  I'd rather not have any confusion about what
is the current version.
-- 
John F. Haugh II                             UUCP: ...!cs.utexas.edu!rpp386!jfh
Ma Bell: (512) 832-8832                           Domain: jfh@rpp386.cactus.org
"SCCS, the source motel!  Programs check in and never check out!"
		-- Ken Thompson

jfh@rpp386.cactus.org (John F. Haugh II) (11/17/90)

#! /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:
#	README
#	Makefile
#	shadow.h
#	config.h.std
#	grent.c
#	chpasswd.c
#	shadowio.c
#	pwio.c
# This archive created: Fri Nov 16 22:38:02 1990
# By:	John F. Haugh II (River Parishes Programming, Austin TX)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'README'" '(2813 characters)'
if test -f 'README'
then
	echo shar: "will not over-write existing file 'README'"
else
sed 's/^X//' << \SHAR_EOF > 'README'
XThis little code drop contains two different utilities.  The first
Xoutlines how to modify password files in "batch" mode.  The second
Xis a utility for creating users in "batch" mode.
X
XThe first utility, "chpasswd", reads its standard input for colon
Xseparated pairs of user names and passwords.  The password for the
Xnamed user is set to the cleartext which follows.  A random salt
Xis generated and used as input to the crypt() subroutine.
X
XThe second utility, "newusers", reads its standard input for
Xrecords looking like entries in a password file.  The entries in
Xthis file are used to update the password file entries of existing
Xusers, or to create new entries if the user does not exist.  The
Xexpected values for the fields are -
X
X	name - a normal user name
X	password - the cleartext of the user's password.  it
X		will be encrypted with a random salt.  it
X		can't be blank just yet (or a value making
X		the account expired.  give me some time).
X	uid - blank, or an integer.  the uid is figured as
X		being the next highest uid.  this may have
X		some problems.
X	gid - blank, or a group name.  if blank, the gid will
X		be either the same as the uid, or the next
X		available gid.  if a name, the user will be
X		added as a member.  this isn't that well
X		thought out just yet ...
X	gecos - the new gecos field, or blank.
X	dir - the new home directory (which will be created
X		if it is non-blank and non-existent), or blank.
X	shell - the new login shell, or blank.
X
XEntries will be added to /etc/group as required, and if requested,
Xentries to /etc/shadow will be updated as well.  See the code if
Xyou have any questions.  Documentation will follow later.  Oh -
Xthe /etc/shadow file is in SVR4 format.  Sorry, but that is the
Xdirection this work is going.  If you want SVR3.2 format, run
Xwithout shadowing turned on, then use the pwconv utility to update
Xthe shadow file.  To get vanilla UNIX (no shadows of any kind)
Xuse "config.h.std" in place of "config.h"; it has the correct
Xdefines to get you boring, dull, uninteresting password files.
X
XAlong with this you get the first [?] release of a collection of
Xlibrary routines for dealing with password files in a more orderly
Xfashion.  There will be more documentation and features in the
Xfuture.  The interface that this code presents is intended to 
Xremain fixed forever, with only the implmentation changing as
Xneeded.
X
XSteve Simmons presented a talk on this work at the recent LISA
Xconference.  If you attended his talk, this is some of the code
Xthat is being worked on.
X
XPlease notice that this code is COPYRIGHTED and that I do have a
Xpolicy of enforcing copyrights.  This is to protect the integrity
Xof the work and not to restrict its use or normal distribution.
XIf you have any questions regarding commercial distribution,
Xplease feel free to contact me.
SHAR_EOF
if test 2813 -ne "`wc -c < 'README'`"
then
	echo shar: "error transmitting 'README'" '(should have been 2813 characters)'
fi
fi
echo shar: "extracting 'Makefile'" '(568 characters)'
if test -f 'Makefile'
then
	echo shar: "will not over-write existing file 'Makefile'"
else
sed 's/^X//' << \SHAR_EOF > 'Makefile'
XLIBS = -ldbm -lcrypt
XCFLAGS = -g
X
Xchpasswd: chpasswd.o libshadow.a
X	cc -o chpasswd -g chpasswd.o libshadow.a $(LIBS)
X
Xnewusers: newusers.o libshadow.a
X	cc -o newusers -g newusers.o libshadow.a $(LIBS)
X
Xchpasswd.o: config.h shadow.h
Xencrypt.o: config.h
Xpwent.o: config.h
Xshadow.o: shadow.h
Xshadowio.o: shadow.h
X
Xlibshadow.a: \
X	libshadow.a(encrypt.o) \
X	libshadow.a(grent.o) \
X	libshadow.a(groupio.o) \
X	libshadow.a(pwent.o) \
X	libshadow.a(pwio.o) \
X	libshadow.a(pwpack.o) \
X	libshadow.a(rad64.o) \
X	libshadow.a(shadow.o) \
X	libshadow.a(shadowio.o)
X	ranlib libshadow.a
SHAR_EOF
if test 568 -ne "`wc -c < 'Makefile'`"
then
	echo shar: "error transmitting 'Makefile'" '(should have been 568 characters)'
fi
fi
echo shar: "extracting 'shadow.h'" '(1223 characters)'
if test -f 'shadow.h'
then
	echo shar: "will not over-write existing file 'shadow.h'"
else
sed 's/^X//' << \SHAR_EOF > 'shadow.h'
X/*
X * Copyright 1988, 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X/*
X * This information is not derived from AT&T licensed sources.  Posted
X * to the USENET 11/88, and updated 11/90 with information from SVR4.
X *
X *	@(#)shadow.h	3.1	10:14:23	11/9/90
X */
X
X/*
X * Shadow password security file structure.
X */
X
Xstruct	spwd {
X	char	*sp_namp;	/* login name */
X	char	*sp_pwdp;	/* encrypted password */
X	long	sp_lstchg;	/* date of last change */
X	long	sp_min;		/* minimum number of days between changes */
X	long	sp_max;		/* maximum number of days between changes */
X	long	sp_warn;	/* number of days of warning before password
X				   expires */
X	long	sp_inact;	/* number of days after password expires
X				   until the account becomes unusable. */
X	long	sp_expire;	/* days since 1/1/70 until account expires */
X	unsigned long	sp_flag; /* reserved for future use */
X};
X
X/*
X * Shadow password security file functions.
X */
X
Xstruct	spwd	*getspent ();
Xstruct	spwd	*getspnam ();
Xstruct	spwd	*sgetspent ();
Xvoid	setspent ();
Xvoid	endspent ();
Xstruct	spwd	*fgetspent ();
Xint	putspent ();
X
X#define  SHADOW "/etc/shadow"
SHAR_EOF
if test 1223 -ne "`wc -c < 'shadow.h'`"
then
	echo shar: "error transmitting 'shadow.h'" '(should have been 1223 characters)'
fi
fi
echo shar: "extracting 'config.h.std'" '(1449 characters)'
if test -f 'config.h.std'
then
	echo shar: "will not over-write existing file 'config.h.std'"
else
sed 's/^X//' << \SHAR_EOF > 'config.h.std'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X/*
X * Configuration file for login. (Hacked on badly ...)
X *
X *	@(#)config.h	2.6	08:26:28	8/20/90
X */
X
X/*
X * Define SHADOWPWD to use shadow [ unreadable ] password file
X */
X
X#undef	SHADOWPWD
X
X/*
X * Define DOUBLESIZE to use 16 character passwords
X */
X
X#undef DOUBLESIZE
X
X/*
X * Define MAXDAYS to be the default maximum number of days a password
X * is valid for when converting to shadow passwords.  Define MINDAYS
X * to be the minimum number of days before a password may be changed.
X * See pwconv.c for more details.
X */
X
X#define	MAXDAYS	10000
X#define	MINDAYS	0
X
X/*
X * Define WARNAGE to be the number of days notice a user receives
X * of a soon to expire password.  Setting this to a value other than
X * -1 will force SVR4-style shadow password entries to be emitted.
X */
X
X#undef	WARNAGE	10
X
X/*
X * Pick your version of DBM.  Only DBM is presently supported, NDBM will
X * follow.  You must also define the GETPWENT macro below.
X */
X
X#define	DBM
X
X/*
X * Wierd stuff follows ...
X *
X *	The following macros exist solely to override stuff ...
X *	You will probably want to change their values to suit your
X *	fancy.
X */
X
X#define	UMASK		022
X
X#define	FGETPWENT	/* Define if library does not include FGETPWENT */
X#define	GETPWENT	/* Define if you want my GETPWENT(3) routines */
SHAR_EOF
if test 1449 -ne "`wc -c < 'config.h.std'`"
then
	echo shar: "error transmitting 'config.h.std'" '(should have been 1449 characters)'
fi
fi
echo shar: "extracting 'grent.c'" '(1893 characters)'
if test -f 'grent.c'
then
	echo shar: "will not over-write existing file 'grent.c'"
else
sed 's/^X//' << \SHAR_EOF > 'grent.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Non-commercial distribution permitted.  You must provide this source
X * code in any distribution.  This notice must remain intact.
X */
X
X#include <stdio.h>
X#include <grp.h>
X#include <string.h>
X#include "config.h"
X#ifdef	DBM
X#include <dbm.h>
X#endif
X
X#ifndef	lint
Xstatic	char	_sccsid[] = "@(#)grent.c	3.1	08:52:58	11/15/90";
X#endif
X
X#define	NFIELDS	4
X#define	MAXMEM	1024
X
Xstatic	char	grpbuf[4*BUFSIZ];
Xstatic	char	*grpfields[NFIELDS];
Xstatic	char	*members[MAXMEM+1];
X
Xstatic char **
Xlist (s)
Xchar	*s;
X{
X	int	nmembers = 0;
X
X	while (*s) {
X		members[nmembers++] = s;
X		if (s = strchr (s, ','))
X			*s++ = '\0';
X	}
X	members[nmembers] = (char *) 0;
X	return members;
X}
X
Xstruct	group	*sgetgrent (buf)
Xchar	*buf;
X{
X	int	i;
X	char	*cp;
X	static	struct	group	grent;
X
X	strncpy (grpbuf, buf, sizeof grpbuf);
X	grpbuf[sizeof grpbuf - 1] = '\0';
X	if (cp = strrchr (grpbuf, '\n'))
X		*cp = '\0';
X
X	for (cp = grpbuf, i = 0;i < NFIELDS && cp;i++) {
X		grpfields[i] = cp;
X		if (cp = strchr (cp, ':'))
X			*cp++ = 0;
X	}
X	if (i < (NFIELDS-1) || *grpfields[2] == '\0')
X		return ((struct group *) 0);
X
X	grent.gr_name = grpfields[0];
X	grent.gr_passwd = grpfields[1];
X	grent.gr_gid = atoi (grpfields[2]);
X	grent.gr_mem = list (grpfields[3]);
X
X	return (&grent);
X}
X
Xint
Xputgrent (g, f)
Xstruct	group	*g;
XFILE	*f;
X{
X	int	i;
X	char	*cp;
X
X	if (! g || ! f)
X		return -1;
X
X	sprintf (grpbuf, "%s:%s:%d:", g->gr_name, g->gr_passwd, g->gr_gid);
X	if (g->gr_mem) {
X		cp = strchr (grpbuf, '\0');
X		for (i = 0;g->gr_mem[i];i++) {
X			if (cp - grpbuf + strlen (g->gr_mem[i]) + 2
X					>= sizeof grpbuf)
X				return -1;
X
X			if (i > 0) {
X				strcpy (cp, ",");
X				cp = strchr (cp, '\0');
X			}
X			strcpy (cp, g->gr_mem[i]);
X			cp = strchr (cp, '\0');
X		}
X		strcat (cp, "\n");
X	} else
X		strcat (grpbuf, "\n");
X
X	if (fputs (grpbuf, f) == EOF || ferror (f))
X		return -1;
X
X	return 0;
X}
SHAR_EOF
if test 1893 -ne "`wc -c < 'grent.c'`"
then
	echo shar: "error transmitting 'grent.c'" '(should have been 1893 characters)'
fi
fi
echo shar: "extracting 'chpasswd.c'" '(4902 characters)'
if test -f 'chpasswd.c'
then
	echo shar: "will not over-write existing file 'chpasswd.c'"
else
sed 's/^X//' << \SHAR_EOF > 'chpasswd.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X *
X * chpass - update passwords in batch
X *
X *	chpass reads standard input for a list of colon separated
X *	user names and new passwords.  the appropriate password
X *	files are updated to reflect the changes.  because the
X *	changes are made in a batch fashion, the user must run
X *	the mkpasswd command after this command terminates since
X *	no password updates occur until the very end.
X */
X
X#include <stdio.h>
X#include <pwd.h>
X#include <fcntl.h>
X#include <string.h>
X#include "config.h"
X#ifdef	SHADOWPWD
X#include "shadow.h"
X#endif
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)chpasswd.c	3.1 01:18:53 11/14/90";
X#endif
X
Xchar	*Prog;
X
Xextern	char	*pw_encrypt();
X
X/* 
X * If it weren't for the different structures and differences in how
X * certain fields were manipulated, I could just use macros to replace
X * the function calls for the different file formats.  So I make the
X * best of things and just use macros to replace a few of the calls.
X */
X
X#ifdef	SHADOWPWD
X#define	pw_lock		spw_lock
X#define	pw_open		spw_open
X#define	pw_close	spw_close
X#define	pw_unlock	spw_unlock
X#endif
X
X/*
X * usage - display usage message and exit
X */
X
Xusage ()
X{
X	fprintf (stderr, "usage: %s\n", Prog);
X	exit (1);
X}
X
Xmain (argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	char	buf[BUFSIZ];
X	char	*name;
X	char	*newpwd;
X	char	*cp;
X#ifdef	SHADOWPWD
X	struct	spwd	*sp;
X	struct	spwd	newsp;
X	struct	spwd	*spw_locate();
X#else
X	struct	passwd	*pw;
X	struct	passwd	newpw;
X	struct	passwd	*pw_locate();
X	char	newage[5];
X#endif
X	int	errors = 0;
X	int	line = 0;
X	long	now = time ((long *) 0) / (24L*3600L);
X
X	if (Prog = strrchr (argv[0], '/'))
X		Prog++;
X	else
X		Prog = argv[0];
X
X	if (argc != 1)
X		usage ();
X
X	/*
X	 * Lock the password file and open it for reading.  This will
X	 * bring all of the entries into memory where they may be
X	 * updated.
X	 */
X
X	if (! pw_lock ()) {
X		fprintf (stderr, "%s: can't lock password file\n", Prog);
X		exit (1);
X	}
X	if (! pw_open (O_RDWR)) {
X		fprintf (stderr, "%s: can't open password file\n", Prog);
X		exit (1);
X	}
X
X	/*
X	 * Read each line, separating the user name from the password.
X	 * The password entry for each user will be looked up in the
X	 * appropriate file (shadow or passwd) and the password changed.
X	 * For shadow files the last change date is set directly, for
X	 * passwd files the last change date is set in the age only if
X	 * aging information is present.
X	 */
X
X	while (fgets (buf, sizeof buf, stdin) != (char *) 0) {
X		line++;
X		if (cp = strrchr (buf, '\n')) {
X			*cp = '\0';
X		} else {
X			fprintf (stderr, "%s: line %d: line too long\n",
X				Prog, line);
X			errors++;
X			continue;
X		}
X
X		/*
X		 * The username is the first field.  It is separated
X		 * from the password with a ":" character which is
X		 * replaced with a NUL to give the new password.  The
X		 * new password will then be encrypted in the normal
X		 * fashion with a new salt generated.
X		 */
X
X		name = buf;
X		if (cp = strchr (name, ':')) {
X			*cp++ = '\0';
X		} else {
X			fprintf (stderr, "%s: line %d: missing new password\n",
X				Prog, line);
X			errors++;
X			continue;
X		}
X		newpwd = cp;
X		cp = pw_encrypt (newpwd, (char *) 0);
X
X		/*
X		 * Get the password file entry for this user.  The user
X		 * must already exist.
X		 */
X
X#ifdef	SHADOWPWD
X		if (! (sp = spw_locate (name)))
X#else
X		if (! (pw = pw_locate (name)))
X#endif
X		{
X			fprintf (stderr, "%s: line %d: unknown user %s\n",
X				Prog, line, name);
X			errors++;
X			continue;
X		}
X
X		/*
X		 * The freshly encrypted new password is merged into
X		 * the user's password file entry and the last password
X		 * change date is set to the current date.
X		 */
X
X#ifdef	SHADOWPWD
X		newsp = *sp;
X		newsp.sp_pwdp = cp;
X		newsp.sp_lstchg = now;
X#else
X		newpw = *pw;
X		newpw.pw_passwd = cp;
X		if (newpw.pw_age[0]) {
X			strcpy (newage, newpw.pw_age);
X			strcpy (newage + 2, l64a (now / 7));
X			newpw.pw_age = newage;
X		}
X#endif
X
X		/* 
X		 * The updated password file entry is then put back
X		 * and will be written to the password file later, after
X		 * all the other entries have been updated as well.
X		 */
X
X#ifdef	SHADOWPWD
X		if (! spw_update (&newsp))
X#else
X		if (! pw_update (&newpw))
X#endif
X		{
X			fprintf (stderr, "%s: line %d: cannot update password entry\n",
X				Prog, line);
X			errors++;
X			continue;
X		}
X	}
X
X	/*
X	 * Any detected errors will cause the entire set of changes
X	 * to be aborted.  Unlocking the password file will cause
X	 * all of the changes to be ignored.  Otherwise the file is
X	 * closed, causing the changes to be written out all at
X	 * once, and then unlocked afterwards.
X	 */
X
X	if (errors) {
X		fprintf ("%s: error detected, changes ignored\n", Prog);
X		pw_unlock ();
X		exit (1);
X	}
X	if (! pw_close ()) {
X		fprintf ("%s: error updating password file\n", Prog);
X		exit (1);
X	}
X	(void) pw_unlock ();
X}
SHAR_EOF
if test 4902 -ne "`wc -c < 'chpasswd.c'`"
then
	echo shar: "error transmitting 'chpasswd.c'" '(should have been 4902 characters)'
fi
fi
echo shar: "extracting 'shadowio.c'" '(9862 characters)'
if test -f 'shadowio.c'
then
	echo shar: "will not over-write existing file 'shadowio.c'"
else
sed 's/^X//' << \SHAR_EOF > 'shadowio.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X *
X *	This file implements a transaction oriented password database
X *	library.  The password file is updated one entry at a time.
X *	After each transaction the file must be logically closed and
X *	transferred to the existing password file.  The sequence of
X *	events is
X *
X *	spw_lock			-- lock shadow file
X *	spw_open			-- logically open shadow file
X *	while transaction to process
X *		spw_(locate,update,remove) -- perform transaction
X *	done
X *	spw_close			-- commit transactions
X *	spw_unlock			-- remove shadow lock
X */
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)shadowio.c	3.1 00:51:22 11/14/90";
X#endif
X
X#include <sys/stat.h>
X#include <fcntl.h>
X#include <errno.h>
X#include <stdio.h>
X#include "shadow.h"
X
Xstatic	int	islocked;
Xstatic	int	isopen;
Xstatic	int	open_modes;
Xstatic	FILE	*spwfp;
X
Xstruct	spw_file_entry {
X	char	*spwf_line;
X	int	spwf_changed;
X	struct	spwd	*spwf_entry;
X	struct	spw_file_entry *spwf_next;
X};
X
Xstatic	struct	spw_file_entry	*spwf_head;
Xstatic	struct	spw_file_entry	*spwf_tail;
Xstatic	struct	spw_file_entry	*spwf_cursor;
Xstatic	int	sp_changed;
X
X#define	SPW_LOCK	"/etc/shadow.lock"
X#define	SPW_TEMP	"/etc/spwd.%d"
X#define	SHADOW		"/etc/shadow"
X#define	OSHADOW		"/etc/shadow-"
X
Xextern	char	*strdup();
Xextern	struct	spwd	*sgetspent();
X
X/*
X * spw_dup - duplicate a shadow file entry
X *
X *	spw_dup() accepts a pointer to a shadow file entry and
X *	returns a pointer to a shadow file entry in allocated
X *	memory.
X */
X
Xstatic struct spwd *
Xspw_dup (spwd)
Xstruct	spwd	*spwd;
X{
X	struct	spwd	*spw;
X
X	if (! (spw = (struct spwd *) malloc (sizeof *spw)))
X		return 0;
X
X	*spw = *spwd;
X	if ((spw->sp_namp = strdup (spwd->sp_namp)) == 0 ||
X			(spw->sp_pwdp = strdup (spwd->sp_pwdp)) == 0)
X		return 0;
X
X	return spw;
X}
X
X/*
X * spw_free - free a dynamically allocated shadow file entry
X *
X *	spw_free() frees up the memory which was allocated for the
X *	pointed to entry.
X */
X
Xstatic void
Xspw_free (spwd)
Xstruct	spwd	*spwd;
X{
X	free (spwd->sp_namp);
X	free (spwd->sp_pwdp);
X}
X
X/*
X * spw_lock - lock a password file
X *
X *	spw_lock() encapsulates the lock operation.  it returns
X *	TRUE or FALSE depending on the password file being
X *	properly locked.  the lock is set by creating a semaphore
X *	file, SPW_LOCK.
X */
X
Xint
Xspw_lock ()
X{
X	int	fd;
X	int	pid;
X	int	len;
X	char	file[BUFSIZ];
X	char	buf[32];
X	struct	stat	sb;
X
X	if (islocked)
X		return 1;
X
X	/*
X	 * Create a lock file which can be switched into place
X	 */
X
X	sprintf (file, SPW_TEMP, getpid ());
X	if ((fd = open (file, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1)
X		return 0;
X
X	sprintf (buf, "%d", getpid ());
X	if (write (fd, buf, strlen (buf) + 1) != strlen (buf) + 1) {
X		(void) close (fd);
X		(void) unlink (file);
X		return 0;
X	}
X	close (fd);
X
X	/*
X	 * Simple case first -
X	 *	Link fails (in a sane environment ...) if the target
X	 *	exists already.  So we try to switch in a new lock
X	 *	file.  If that succeeds, we assume we have the only
X	 *	valid lock.  Needs work for NFS where this assumption
X	 *	may not hold.  The simple hack is to check the link
X	 *	count on the source file, which should be 2 iff the
X	 *	link =really= worked.
X	 */
X
X	if (link (file, SPW_LOCK) == 0) {
X		if (stat (file, &sb) != 0)
X			return 0;
X
X		if (sb.st_nlink != 2)
X			return 0;
X
X		(void) unlink (file);
X		islocked = 1;
X		return 1;
X	}
X
X	/*
X	 * Invalid lock test -
X	 *	Open the lock file and see if the lock is valid.
X	 *	The PID of the lock file is checked, and if the PID
X	 *	is not valid, the lock file is removed.  If the unlink
X	 *	of the lock file fails, it should mean that someone
X	 *	else is executing this code.  They will get success,
X	 *	and we will fail.
X	 */
X
X	if ((fd = open (SPW_LOCK, O_RDWR)) == -1 ||
X			(len = read (fd, buf, BUFSIZ)) <= 0) {
X		errno = EINVAL;
X		return 0;
X	}
X	buf[len] = '\0';
X	if ((pid = strtol (buf, (char **) 0, 10)) == 0) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (kill (pid, 0) == 0)  {
X		errno = EEXIST;
X		return 0;
X	}
X	if (unlink (SPW_LOCK)) {
X		(void) close (fd);
X		(void) unlink (file);
X
X		return 0;
X	}
X
X	/*
X	 * Re-try lock -
X	 *	The invalid lock has now been removed and I should
X	 *	be able to acquire a lock for myself just fine.  If
X	 *	this fails there will be no retry.  The link count
X	 *	test here makes certain someone executing the previous
X	 *	block of code didn't just remove the lock we just
X	 *	linked to.
X	 */
X
X	if (link (file, SPW_LOCK) == 0) {
X		if (stat (file, &sb) != 0)
X			return 0;
X
X		if (sb.st_nlink != 2)
X			return 0;
X
X		(void) unlink (file);
X		islocked = 1;
X		return 1;
X	}
X	(void) unlink (file);
X	return 0;
X}
X
X/*
X * spw_unlock - logically unlock a shadow file
X *
X *	spw_unlock() removes the lock which was set by an earlier
X *	invocation of spw_lock().
X */
X
Xint
Xspw_unlock ()
X{
X	if (islocked) {
X		if (isopen) {
X			open_modes = O_RDONLY;
X			(void) spw_close ();
X		}
X		unlink (SPW_LOCK);
X		islocked = 0;
X		return 1;
X	} else
X		return 0;
X}
X
X/*
X * spw_open - open a password file
X *
X *	spw_open() encapsulates the open operation.  it returns
X *	TRUE or FALSE depending on the shadow file being
X *	properly opened.
X */
X
Xint
Xspw_open (mode)
Xint	mode;
X{
X	char	buf[8192];
X	struct	spw_file_entry	*spwf;
X	struct	spwd	*spwd;
X
X	if (isopen || (mode != O_RDONLY && mode != O_RDWR))
X		return 0;
X
X	if (mode != O_RDONLY && ! islocked)
X		return 0;
X
X	if ((spwfp = fopen (SHADOW, mode == O_RDONLY ? "r":"r+")) == 0)
X		return 0;
X
X	spwf_head = spwf_tail = spwf_cursor = 0;
X	sp_changed = 0;
X
X	while (fgets (buf, sizeof buf, spwfp) != (char *) 0) {
X		if (! (spwf = (struct spw_file_entry *) malloc (sizeof *spwf)))
X			return 0;
X
X		spwf->spwf_changed = 0;
X		spwf->spwf_line = strdup (buf);
X		if ((spwd = sgetspent (buf)) && ! (spwd = spw_dup (spwd)))
X			return 0;
X
X		spwf->spwf_entry = spwd;
X
X		if (spwf_head == 0) {
X			spwf_head = spwf_tail = spwf;
X			spwf->spwf_next = 0;
X		} else {
X			spwf_tail->spwf_next = spwf;
X			spwf->spwf_next = 0;
X			spwf_tail = spwf;
X		}
X	}
X	isopen++;
X	open_modes = mode;
X
X	return 1;
X}
X
X/*
X * spw_close - close the password file
X *
X *	spw_close() outputs any modified password file entries and
X *	frees any allocated memory.
X */
X
Xint
Xspw_close ()
X{
X	int	fd;
X	int	mask;
X	int	c;
X	int	i;
X	int	errors = 0;
X	FILE	*bkfp;
X	struct	spw_file_entry *spwf;
X	struct	spw_file_entry *ospwf;
X
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (open_modes == O_RDWR && sp_changed) {
X		mask = umask (077);
X		if ((bkfp = fopen (OSHADOW, "w")) == 0) {
X			umask (mask);
X			return 0;
X		}
X		umask (mask);
X
X		rewind (spwfp);
X		while ((c = getc (spwfp)) != EOF) {
X			if (putc (c, bkfp) == EOF) {
X				fclose (bkfp);
X				return 0;
X			}
X		}
X		if (fclose (bkfp))
X			return 0;
X
X		isopen = 0;
X		(void) fclose (spwfp);
X
X		mask = umask (077);
X		if (! (spwfp = fopen (SHADOW, "w"))) {
X			umask (mask);
X			return 0;
X		}
X		umask (mask);
X
X		for (spwf = spwf_head;errors == 0 && spwf;
X						spwf = spwf->spwf_next) {
X			if (spwf->spwf_changed) {
X				if (putspent (spwf->spwf_entry, spwfp))
X					errors++;
X			} else {
X				if (fputs (spwf->spwf_line, spwfp) == EOF)
X					errors++;
X			}
X		}
X		if (fflush (spwfp))
X			errors++;
X
X		if (errors) {
X			unlink (SHADOW);
X			link (OSHADOW, SHADOW);
X			unlink (OSHADOW);
X			return 0;
X		}
X	}
X	if (fclose (spwfp))
X		return 0;
X
X	spwfp = 0;
X
X	while (spwf_head != 0) {
X		spwf = spwf_head;
X		spwf_head = spwf->spwf_next;
X
X		if (spwf->spwf_entry) {
X			spw_free (spwf->spwf_entry);
X			free (spwf->spwf_entry);
X		}
X		if (spwf->spwf_line)
X			free (spwf->spwf_line);
X
X		free (spwf);
X	}
X	spwf_tail = 0;
X	return 1;
X}
X
Xint
Xspw_update (spwd)
Xstruct	spwd	*spwd;
X{
X	struct	spw_file_entry	*spwf;
X	struct	spwd	*nspwd;
X
X	if (! isopen || open_modes == O_RDONLY) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (spwf = spwf_head;spwf != 0;spwf = spwf->spwf_next) {
X		if (spwf->spwf_entry == 0)
X			continue;
X
X		if (strcmp (spwd->sp_namp, spwf->spwf_entry->sp_namp) != 0)
X			continue;
X
X		if (! (nspwd = spw_dup (spwd)))
X			return 0;
X		else {
X			spw_free (spwf->spwf_entry);
X			*(spwf->spwf_entry) = *nspwd;
X		}
X		spwf->spwf_changed = 1;
X		spwf_cursor = spwf;
X		return sp_changed = 1;
X	}
X	spwf = (struct spw_file_entry *) malloc (sizeof *spwf);
X	if (! (spwf->spwf_entry = spw_dup (spwd)))
X		return 0;
X
X	spwf->spwf_changed = 1;
X	spwf->spwf_next = 0;
X	spwf->spwf_line = 0;
X
X	if (spwf_tail)
X		spwf_tail->spwf_next = spwf;
X
X	if (! spwf_head)
X		spwf_head = spwf;
X
X	spwf_tail = spwf;
X
X	return sp_changed = 1;
X}
X
Xint
Xspw_remove (name)
Xchar	*name;
X{
X	struct	spw_file_entry	*spwf;
X	struct	spw_file_entry	*ospwf;
X
X	if (! isopen || open_modes == O_RDONLY) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (ospwf = 0, spwf = spwf_head;spwf != 0;
X			ospwf = spwf, spwf = spwf->spwf_next) {
X		if (! spwf->spwf_entry)
X			continue;
X
X		if (strcmp (name, spwf->spwf_entry->sp_namp) != 0)
X			continue;
X
X		if (spwf == spwf_cursor)
X			spwf_cursor = ospwf;
X
X		if (ospwf != 0)
X			ospwf->spwf_next = spwf->spwf_next;
X		else
X			spwf_head = spwf->spwf_next;
X
X		if (spwf == spwf_tail)
X			spwf_tail = ospwf;
X
X		return sp_changed = 1;
X	}
X	errno = ENOENT;
X	return 0;
X}
X
Xstruct spwd *
Xspw_locate (name)
Xchar	*name;
X{
X	struct	spw_file_entry	*spwf;
X
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (spwf = spwf_head;spwf != 0;spwf = spwf->spwf_next) {
X		if (spwf->spwf_entry == 0)
X			continue;
X
X		if (strcmp (name, spwf->spwf_entry->sp_namp) == 0) {
X			spwf_cursor = spwf;
X			return spwf->spwf_entry;
X		}
X	}
X	errno = ENOENT;
X	return 0;
X}
X
Xint
Xspw_rewind ()
X{
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	spwf_cursor = 0;
X	return 1;
X}
X
Xstruct spwd *
Xspw_next ()
X{
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (spwf_cursor == 0)
X		spwf_cursor = spwf_head;
X	else
X		spwf_cursor = spwf_cursor->spwf_next;
X
X	while (spwf_cursor) {
X		if (spwf_cursor->spwf_entry)
X			return spwf_cursor->spwf_entry;
X
X		spwf_cursor = spwf_cursor->spwf_next;
X	}
X	return 0;
X}
SHAR_EOF
if test 9862 -ne "`wc -c < 'shadowio.c'`"
then
	echo shar: "error transmitting 'shadowio.c'" '(should have been 9862 characters)'
fi
fi
echo shar: "extracting 'pwio.c'" '(9982 characters)'
if test -f 'pwio.c'
then
	echo shar: "will not over-write existing file 'pwio.c'"
else
sed 's/^X//' << \SHAR_EOF > 'pwio.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X *
X *	This file implements a transaction oriented password database
X *	library.  The password file is updated one entry at a time.
X *	After each transaction the file must be logically closed and
X *	transferred to the existing password file.  The sequence of
X *	events is
X *
X *	pw_lock				-- lock password file
X *	pw_open				-- logically open password file
X *	while transaction to process
X *		pw_(locate,update,remove) -- perform transaction
X *	done
X *	pw_close			-- commit transactions
X *	pw_unlock			-- remove password lock
X */
X
X#include <sys/stat.h>
X#include <fcntl.h>
X#include <errno.h>
X#include <pwd.h>
X#include <stdio.h>
X
X#ifndef lint
Xstatic	char	sccsid[] = "@(#)pwio.c	3.2 01:57:59 11/14/90";
X#endif
X
Xstatic	int	islocked;
Xstatic	int	isopen;
Xstatic	int	open_modes;
Xstatic	FILE	*pwfp;
X
Xstruct	pw_file_entry {
X	char	*pwf_line;
X	int	pwf_changed;
X	struct	passwd	*pwf_entry;
X	struct	pw_file_entry *pwf_next;
X};
X
Xstatic	struct	pw_file_entry	*pwf_head;
Xstatic	struct	pw_file_entry	*pwf_tail;
Xstatic	struct	pw_file_entry	*pwf_cursor;
Xstatic	int	pw_changed;
X
X#define	PW_LOCK	"/etc/passwd.lock"
X#define	PW_TEMP "/etc/pwd.%d"
X#define	PASSWD	"/etc/passwd"
X#define	OPASSWD	"/etc/passwd-"
X
Xextern	char	*strdup();
Xextern	struct	passwd	*sgetpwent();
X
X/*
X * pw_dup - duplicate a password file entry
X *
X *	pw_dup() accepts a pointer to a password file entry and
X *	returns a pointer to a password file entry in allocated
X *	memory.
X */
X
Xstatic struct passwd *
Xpw_dup (pwent)
Xstruct	passwd	*pwent;
X{
X	struct	passwd	*pw;
X
X	if (! (pw = (struct passwd *) malloc (sizeof *pw)))
X		return 0;
X
X	if ((pw->pw_name = strdup (pwent->pw_name)) == 0 ||
X			(pw->pw_passwd = strdup (pwent->pw_passwd)) == 0 ||
X			(pw->pw_age = strdup (pwent->pw_age)) == 0 ||
X			(pw->pw_gecos = strdup (pwent->pw_gecos)) == 0 ||
X			(pw->pw_dir = strdup (pwent->pw_dir)) == 0 ||
X			(pw->pw_shell = strdup (pwent->pw_shell)) == 0)
X		return 0;
X
X	pw->pw_uid = pwent->pw_uid;
X	pw->pw_gid = pwent->pw_gid;
X
X	return pw;
X}
X
X/*
X * pw_free - free a dynamically allocated password file entry
X *
X *	pw_free() frees up the memory which was allocated for the
X *	pointed to entry.
X */
X
Xstatic void
Xpw_free (pwent)
Xstruct	passwd	*pwent;
X{
X	free (pwent->pw_name);
X	free (pwent->pw_passwd);
X	free (pwent->pw_gecos);
X	free (pwent->pw_dir);
X	free (pwent->pw_shell);
X}
X
X/*
X * pw_lock - lock a password file
X *
X *	pw_lock() encapsulates the lock operation.  it returns
X *	TRUE or FALSE depending on the password file being
X *	properly locked.  the lock is set by creating a semaphore
X *	file, PW_LOCK.
X */
X
Xint
Xpw_lock ()
X{
X	int	fd;
X	int	pid;
X	int	len;
X	char	file[BUFSIZ];
X	char	buf[32];
X	struct	stat	sb;
X
X	if (islocked)
X		return 1;
X
X	/*
X	 * Create a lock file which can be switched into place
X	 */
X
X	sprintf (file, PW_TEMP, getpid ());
X	if ((fd = open (file, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1)
X		return 0;
X
X	sprintf (buf, "%d", getpid ());
X	if (write (fd, buf, strlen (buf) + 1) != strlen (buf) + 1) {
X		(void) close (fd);
X		(void) unlink (file);
X		return 0;
X	}
X	close (fd);
X
X	/*
X	 * Simple case first -
X	 *	Link fails (in a sane environment ...) if the target
X	 *	exists already.  So we try to switch in a new lock
X	 *	file.  If that succeeds, we assume we have the only
X	 *	valid lock.  Needs work for NFS where this assumption
X	 *	may not hold.  The simple hack is to check the link
X	 *	count on the source file, which should be 2 iff the
X	 *	link =really= worked.
X	 */
X
X	if (link (file, PW_LOCK) == 0) {
X		if (stat (file, &sb) != 0)
X			return 0;
X
X		if (sb.st_nlink != 2)
X			return 0;
X
X		(void) unlink (file);
X		islocked = 1;
X		return 1;
X	}
X
X	/*
X	 * Invalid lock test -
X	 *	Open the lock file and see if the lock is valid.
X	 *	The PID of the lock file is checked, and if the PID
X	 *	is not valid, the lock file is removed.  If the unlink
X	 *	of the lock file fails, it should mean that someone
X	 *	else is executing this code.  They will get success,
X	 *	and we will fail.
X	 */
X
X	if ((fd = open (PW_LOCK, O_RDWR)) == -1 ||
X			(len = read (fd, buf, BUFSIZ)) <= 0) {
X		errno = EINVAL;
X		return 0;
X	}
X	buf[len] = '\0';
X	if ((pid = strtol (buf, (char **) 0, 10)) == 0) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (kill (pid, 0) == 0)  {
X		errno = EEXIST;
X		return 0;
X	}
X	if (unlink (PW_LOCK)) {
X		(void) close (fd);
X		(void) unlink (file);
X
X		return 0;
X	}
X
X	/*
X	 * Re-try lock -
X	 *	The invalid lock has now been removed and I should
X	 *	be able to acquire a lock for myself just fine.  If
X	 *	this fails there will be no retry.  The link count
X	 *	test here makes certain someone executing the previous
X	 *	block of code didn't just remove the lock we just
X	 *	linked to.
X	 */
X
X	if (link (file, PW_LOCK) == 0) {
X		if (stat (file, &sb) != 0)
X			return 0;
X
X		if (sb.st_nlink != 2)
X			return 0;
X
X		(void) unlink (file);
X		islocked = 1;
X		return 1;
X	}
X	(void) unlink (file);
X	return 0;
X}
X
X/*
X * pw_unlock - logically unlock a password file
X *
X *	pw_unlock() removes the lock which was set by an earlier
X *	invocation of pw_lock().
X */
X
Xint
Xpw_unlock ()
X{
X	if (islocked) {
X		if (isopen) {
X			open_modes = O_RDONLY;
X			pw_close ();
X		}
X		unlink (PW_LOCK);
X		islocked = 0;
X		return 1;
X	} else
X		return 0;
X}
X
X/*
X * pw_open - open a password file
X *
X *	pw_open() encapsulates the open operation.  it returns
X *	TRUE or FALSE depending on the password file being
X *	properly opened.
X */
X
Xint
Xpw_open (mode)
Xint	mode;
X{
X	char	buf[8192];
X	struct	pw_file_entry	*pwf;
X	struct	passwd	*pwent;
X
X	if (isopen || (mode != O_RDONLY && mode != O_RDWR))
X		return 0;
X
X	if (mode != O_RDONLY && ! islocked)
X		return 0;
X
X	if ((pwfp = fopen (PASSWD, mode == O_RDONLY ? "r":"r+")) == 0)
X		return 0;
X
X	pwf_head = pwf_tail = pwf_cursor = 0;
X	pw_changed = 0;
X
X	while (fgets (buf, sizeof buf, pwfp) != (char *) 0) {
X		if (! (pwf = (struct pw_file_entry *) malloc (sizeof *pwf)))
X			return 0;
X
X		pwf->pwf_changed = 0;
X		pwf->pwf_line = strdup (buf);
X		if ((pwent = sgetpwent (buf)) && ! (pwent = pw_dup (pwent)))
X			return 0;
X
X		pwf->pwf_entry = pwent;
X
X		if (pwf_head == 0) {
X			pwf_head = pwf_tail = pwf;
X			pwf->pwf_next = 0;
X		} else {
X			pwf_tail->pwf_next = pwf;
X			pwf->pwf_next = 0;
X			pwf_tail = pwf;
X		}
X	}
X	isopen++;
X	open_modes = mode;
X
X	return 1;
X}
X
X/*
X * pw_close - close the password file
X *
X *	pw_close() outputs any modified password file entries and
X *	frees any allocated memory.
X */
X
Xint
Xpw_close ()
X{
X	int	fd;
X	int	mask;
X	int	c;
X	int	i;
X	int	errors = 0;
X	FILE	*bkfp;
X	struct	pw_file_entry *pwf;
X	struct	pw_file_entry *opwf;
X
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (open_modes == O_RDWR && pw_changed) {
X		mask = umask (022);
X		if ((bkfp = fopen (OPASSWD, "w")) == 0) {
X			umask (mask);
X			return 0;
X		}
X		umask (mask);
X
X		rewind (pwfp);
X		while ((c = getc (pwfp)) != EOF) {
X			if (putc (c, bkfp) == EOF) {
X				fclose (bkfp);
X				return 0;
X			}
X		}
X		if (fclose (bkfp))
X			return 0;
X
X		isopen = 0;
X		(void) fclose (pwfp);
X
X		mask = umask (022);
X		if (! (pwfp = fopen (PASSWD, "w"))) {
X			umask (mask);
X			return 0;
X		}
X		umask (mask);
X
X		for (pwf = pwf_head;errors == 0 && pwf;pwf = pwf->pwf_next) {
X			if (pwf->pwf_changed) {
X				if (putpwent (pwf->pwf_entry, pwfp))
X					errors++;
X			} else {
X				if (fputs (pwf->pwf_line, pwfp) == EOF)
X					errors++;
X			}
X		}
X		if (fflush (pwfp))
X			errors++;
X
X		if (errors) {
X			unlink (PASSWD);
X			link (OPASSWD, PASSWD);
X			unlink (OPASSWD);
X			return 0;
X		}
X	}
X	if (fclose (pwfp))
X		return 0;
X
X	pwfp = 0;
X
X	while (pwf_head != 0) {
X		pwf = pwf_head;
X		pwf_head = pwf->pwf_next;
X
X		if (pwf->pwf_entry) {
X			pw_free (pwf->pwf_entry);
X			free (pwf->pwf_entry);
X		}
X		if (pwf->pwf_line)
X			free (pwf->pwf_line);
X
X		free (pwf);
X	}
X	pwf_tail = 0;
X	return 1;
X}
X
Xint
Xpw_update (pwent)
Xstruct	passwd	*pwent;
X{
X	struct	pw_file_entry	*pwf;
X	struct	passwd	*npw;
X
X	if (! isopen || open_modes == O_RDONLY) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (pwf = pwf_head;pwf != 0;pwf = pwf->pwf_next) {
X		if (pwf->pwf_entry == 0)
X			continue;
X
X		if (strcmp (pwent->pw_name, pwf->pwf_entry->pw_name) != 0)
X			continue;
X
X		if (! (npw = pw_dup (pwent)))
X			return 0;
X		else {
X			pw_free (pwf->pwf_entry);
X			*(pwf->pwf_entry) = *npw;
X		}
X		pwf->pwf_changed = 1;
X		pwf_cursor = pwf;
X		return pw_changed = 1;
X	}
X	pwf = (struct pw_file_entry *) malloc (sizeof *pwf);
X	if (! (pwf->pwf_entry = pw_dup (pwent)))
X		return 0;
X
X	pwf->pwf_changed = 1;
X	pwf->pwf_next = 0;
X	pwf->pwf_line = 0;
X
X	if (pwf_tail)
X		pwf_tail->pwf_next = pwf;
X
X	if (! pwf_head)
X		pwf_head = pwf;
X
X	pwf_tail = pwf;
X
X	return pw_changed = 1;
X}
X
Xint
Xpw_remove (name)
Xchar	*name;
X{
X	struct	pw_file_entry	*pwf;
X	struct	pw_file_entry	*opwf;
X
X	if (! isopen || open_modes == O_RDONLY) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (opwf = 0, pwf = pwf_head;pwf != 0;
X			opwf = pwf, pwf = pwf->pwf_next) {
X		if (! pwf->pwf_entry)
X			continue;
X
X		if (strcmp (name, pwf->pwf_entry->pw_name) != 0)
X			continue;
X
X		if (pwf == pwf_cursor)
X			pwf_cursor = opwf;
X
X		if (opwf != 0)
X			opwf->pwf_next = pwf->pwf_next;
X		else
X			pwf_head = pwf->pwf_next;
X
X		if (pwf == pwf_tail)
X			pwf_tail = opwf;
X
X		return pw_changed = 1;
X	}
X	errno = ENOENT;
X	return 0;
X}
X
Xstruct passwd *
Xpw_locate (name)
Xchar	*name;
X{
X	struct	pw_file_entry	*pwf;
X
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (pwf = pwf_head;pwf != 0;pwf = pwf->pwf_next) {
X		if (pwf->pwf_entry == 0)
X			continue;
X
X		if (strcmp (name, pwf->pwf_entry->pw_name) == 0) {
X			pwf_cursor = pwf;
X			return pwf->pwf_entry;
X		}
X	}
X	errno = ENOENT;
X	return 0;
X}
X
Xint
Xpw_rewind ()
X{
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	pwf_cursor = 0;
X	return 1;
X}
X
Xstruct passwd *
Xpw_next ()
X{
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (pwf_cursor == 0)
X		pwf_cursor = pwf_head;
X	else
X		pwf_cursor = pwf_cursor->pwf_next;
X
X	while (pwf_cursor) {
X		if (pwf_cursor->pwf_entry)
X			return pwf_cursor->pwf_entry;
X
X		pwf_cursor = pwf_cursor->pwf_next;
X	}
X	return 0;
X}
SHAR_EOF
if test 9982 -ne "`wc -c < 'pwio.c'`"
then
	echo shar: "error transmitting 'pwio.c'" '(should have been 9982 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@rpp386.cactus.org
"SCCS, the source motel!  Programs check in and never check out!"
		-- Ken Thompson

jfh@rpp386.cactus.org (John F. Haugh II) (11/17/90)

#! /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:
#	encrypt.c
#	rad64.c
#	config.h
#	pwpack.c
#	shadow.c
#	pwent.c
#	groupio.c
#	newusers.c
# This archive created: Fri Nov 16 22:38:09 1990
# By:	John F. Haugh II (River Parishes Programming, Austin TX)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'encrypt.c'" '(1097 characters)'
if test -f 'encrypt.c'
then
	echo shar: "will not over-write existing file 'encrypt.c'"
else
sed 's/^X//' << \SHAR_EOF > 'encrypt.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X#include <string.h>
X#include "config.h"
X
X#ifndef lint
Xstatic	char	sccsid[] = "@(#)encrypt.c	3.3 01:05:46 11/14/90";
X#endif
X
Xextern	char	*crypt();
X
Xchar *
Xpw_encrypt (clear, salt)
Xchar	*clear;
Xchar	*salt;
X{
X	static	char	cipher[32];
X	static	int	count;
X	char	newsalt[2];
X	char	*cp;
X	long	now;
X
X	/*
X	 * See if a new salt is needed and get a few random
X	 * bits of information.  The amount of randomness is
X	 * probably not all that crucial since the salt only
X	 * serves to thwart a dictionary attack.
X	 */
X
X	if (salt == (char *) 0) {
X		now = time ((long *) 0) + count++;
X		now ^= clock ();
X		now ^= getpid ();
X		now = ((now >> 12) ^ (now)) & 07777;
X		newsalt[0] = i64c ((now >> 6) & 077);
X		newsalt[1] = i64c (now & 077);
X		salt = newsalt;
X	}
X	cp = crypt (clear, salt);
X	strcpy (cipher, cp);
X
X#ifdef	DOUBLESIZE
X	if (strlen (clear) > 8) {
X		cp = crypt (clear + 8, salt);
X		strcat (cipher, cp + 2);
X	}
X#endif
X	return cipher;
X}
SHAR_EOF
if test 1097 -ne "`wc -c < 'encrypt.c'`"
then
	echo shar: "error transmitting 'encrypt.c'" '(should have been 1097 characters)'
fi
fi
echo shar: "extracting 'rad64.c'" '(1229 characters)'
if test -f 'rad64.c'
then
	echo shar: "will not over-write existing file 'rad64.c'"
else
sed 's/^X//' << \SHAR_EOF > 'rad64.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X#ifndef	lint
Xstatic	char	_sccsid[] = "@(#)rad64.c	3.1	23:00:35	11/11/90";
X#endif
X
Xint	c64i (c)
Xchar	c;
X{
X	if (c == '.')
X		return (0);
X
X	if (c == '/')
X		return (1);
X
X	if (c >= '0' && c <= '9')
X		return (c - '0' + 2);
X
X	if (c >= 'A' && c <= 'Z')
X		return (c - 'A' + 12);
X
X	if (c >= 'a' && c <= 'z')
X		return (c - 'a' + 38);
X	else
X		return (-1);
X}
X
Xint	i64c (i)
Xint	i;
X{
X	if (i < 0)
X		return ('.');
X	else if (i > 63)
X		return ('z');
X
X	if (i == 0)
X		return ('.');
X
X	if (i == 1)
X		return ('/');
X
X	if (i >= 2 && i <= 11)
X		return ('0' - 2 + i);
X
X	if (i >= 12 && i <= 37)
X		return ('A' - 12 + i);
X
X	if (i >= 38 && i <= 63)
X		return ('a' - 38 + i);
X
X	return ('\0');
X}
X
Xchar	*l64a (l)
Xlong	l;
X{
X	static	char	buf[8];
X	int	i = 0;
X
X	if (i < 0L)
X		return ((char *) 0);
X
X	do {
X		buf[i++] = i64c ((int) (l % 64));
X		buf[i] = '\0';
X	} while (l /= 64L, l > 0 && i < 6);
X
X	return (buf);
X}
X
Xlong	a64l (s)
Xchar	*s;
X{
X	int	i;
X	long	value;
X	long	shift = 0;
X
X	for (i = 0, value = 0L;i < 6 && *s;s++) {
X		value += (c64i (*s) << shift);
X		shift += 6;
X	}
X	return (value);
X}
SHAR_EOF
if test 1229 -ne "`wc -c < 'rad64.c'`"
then
	echo shar: "error transmitting 'rad64.c'" '(should have been 1229 characters)'
fi
fi
echo shar: "extracting 'config.h'" '(1452 characters)'
if test -f 'config.h'
then
	echo shar: "will not over-write existing file 'config.h'"
else
sed 's/^X//' << \SHAR_EOF > 'config.h'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X/*
X * Configuration file for login. (Hacked on badly ...)
X *
X *	@(#)config.h	2.6	08:26:28	8/20/90
X */
X
X/*
X * Define SHADOWPWD to use shadow [ unreadable ] password file
X */
X
X#define	SHADOWPWD
X
X/*
X * Define DOUBLESIZE to use 16 character passwords
X */
X
X#define DOUBLESIZE
X
X/*
X * Define MAXDAYS to be the default maximum number of days a password
X * is valid for when converting to shadow passwords.  Define MINDAYS
X * to be the minimum number of days before a password may be changed.
X * See pwconv.c for more details.
X */
X
X#define	MAXDAYS	10000
X#define	MINDAYS	0
X
X/*
X * Define WARNAGE to be the number of days notice a user receives
X * of a soon to expire password.  Setting this to a value other than
X * -1 will force SVR4-style shadow password entries to be emitted.
X */
X
X#define	WARNAGE	10
X
X/*
X * Pick your version of DBM.  Only DBM is presently supported, NDBM will
X * follow.  You must also define the GETPWENT macro below.
X */
X
X#define	DBM
X
X/*
X * Wierd stuff follows ...
X *
X *	The following macros exist solely to override stuff ...
X *	You will probably want to change their values to suit your
X *	fancy.
X */
X
X#define	UMASK		022
X
X#define	FGETPWENT	/* Define if library does not include FGETPWENT */
X#define	GETPWENT	/* Define if you want my GETPWENT(3) routines */
SHAR_EOF
if test 1452 -ne "`wc -c < 'config.h'`"
then
	echo shar: "error transmitting 'config.h'" '(should have been 1452 characters)'
fi
fi
echo shar: "extracting 'pwpack.c'" '(2139 characters)'
if test -f 'pwpack.c'
then
	echo shar: "will not over-write existing file 'pwpack.c'"
else
sed 's/^X//' << \SHAR_EOF > 'pwpack.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X *
X * Duplication is permitted for non-commercial [ profit making ]
X * purposes provided this and other copyright notices remain
X * intact.
X */
X
X#include <stdio.h>
X#include <pwd.h>
X#ifdef	BSD
X#include <strings.h>
X#else
X#include <string.h>
X#endif
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)pwpack.c	2.3	23:06:29	8/5/90";
X#endif
X
Xint	pw_pack (passwd, buf)
Xstruct	passwd	*passwd;
Xchar	*buf;
X{
X	char	*cp;
X
X	cp = buf;
X	strcpy (cp, passwd->pw_name);
X	cp += strlen (cp) + 1;
X
X	strcpy (cp, passwd->pw_passwd);
X	if (passwd->pw_age && passwd->pw_age[0]) {
X		cp += strlen (cp);
X		*cp++ = ',';
X		strcpy (cp, passwd->pw_age);
X	}
X	cp += strlen (cp) + 1;
X
X	memcpy (cp, (void *) &passwd->pw_uid, sizeof passwd->pw_uid);
X	cp += sizeof passwd->pw_uid;
X
X	memcpy (cp, (void *) &passwd->pw_gid, sizeof passwd->pw_gid);
X	cp += sizeof passwd->pw_gid;
X
X	strcpy (cp, passwd->pw_gecos);
X	cp += strlen (cp) + 1;
X
X	strcpy (cp, passwd->pw_dir);
X	cp += strlen (cp) + 1;
X
X	strcpy (cp, passwd->pw_shell);
X		cp += strlen (cp) + 1;
X
X	return cp - buf;
X}
X
Xint	pw_unpack (buf, len, passwd)
Xchar	*buf;
Xint	len;
Xstruct	passwd	*passwd;
X{
X	char	*org = buf;
X	char	*cp;
X
X	passwd->pw_name = buf;
X	buf += strlen (buf) + 1;
X	if (buf - org > len)
X		return -1;
X
X	passwd->pw_passwd = buf;
X	buf += strlen (buf) + 1;
X	if (buf - org > len)
X		return -1;
X
X	if (cp = strchr (passwd->pw_passwd, ',')) {
X		*cp++ = '\0';
X		passwd->pw_age = cp;
X	} else
X		passwd->pw_age = "";
X
X	memcpy ((void *) &passwd->pw_uid, (void *) buf, sizeof passwd->pw_uid);
X	buf += sizeof passwd->pw_uid;
X	if (buf - org > len)
X		return -1;
X
X	memcpy ((void *) &passwd->pw_gid, (void *) buf, sizeof passwd->pw_gid);
X	buf += sizeof passwd->pw_gid;
X	if (buf - org > len)
X		return -1;
X
X	passwd->pw_gecos = buf;
X	buf += strlen (buf) + 1;
X	if (buf - org > len)
X		return -1;
X
X	passwd->pw_dir = buf;
X	buf += strlen (buf) + 1;
X	if (buf - org > len)
X		return -1;
X
X	passwd->pw_shell = buf;
X	buf += strlen (buf) + 1;
X	if (buf - org > len)
X		return -1;
X
X	return 0;
X}
SHAR_EOF
if test 2139 -ne "`wc -c < 'pwpack.c'`"
then
	echo shar: "error transmitting 'pwpack.c'" '(should have been 2139 characters)'
fi
fi
echo shar: "extracting 'shadow.c'" '(4426 characters)'
if test -f 'shadow.c'
then
	echo shar: "will not over-write existing file 'shadow.c'"
else
sed 's/^X//' << \SHAR_EOF > 'shadow.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X#include "shadow.h"
X#include <stdio.h>
X#ifndef	BSD
X#include <string.h>
X#include <memory.h>
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X
X#ifndef	lint
Xstatic	char	_sccsid[] = "@(#)shadow.c	3.3	22:02:10	11/16/90";
X#endif
X
Xstatic	FILE	*shadow;
X#define	FIELDS	9
X#define	OFIELDS	5
X
Xvoid
Xsetspent ()
X{
X	if (shadow)
X		rewind (shadow);
X	else
X		shadow = fopen (SHADOW, "r");
X}
X
Xvoid
Xendspent ()
X{
X	if (shadow)
X		(void) fclose (shadow);
X
X	shadow = (FILE *) 0;
X}
X
Xstruct spwd *
Xsgetspent (string)
Xchar	*string;
X{
X	static	char	buf[BUFSIZ];
X	static	struct	spwd	spwd;
X	char	*fields[FIELDS];
X	char	*cp;
X	char	*cpp;
X	int	atoi ();
X	long	atol ();
X	int	i;
X
X	strncpy (buf, string, BUFSIZ-1);
X	buf[BUFSIZ-1] = '\0';
X
X	if (cp = strrchr (buf, '\n'))
X		*cp = '\0';
X
X	for (cp = buf, i = 0;*cp && i < FIELDS;i++) {
X		fields[i] = cp;
X		while (*cp && *cp != ':')
X			cp++;
X
X		if (*cp)
X			*cp++ = '\0';
X	}
X	if (*cp || (i != FIELDS && i != OFIELDS))
X		return 0;
X
X	spwd.sp_namp = fields[0];
X	spwd.sp_pwdp = fields[1];
X
X	if ((spwd.sp_lstchg = strtol (fields[2], &cpp, 10)) == 0 && *cpp)
X		if (fields[2][0] == '\0')
X			spwd.sp_lstchg = -1;
X		else
X			return 0;
X
X	if ((spwd.sp_min = strtol (fields[3], &cpp, 10)) == 0 && *cpp)
X		if (fields[3][0] == '\0')
X			spwd.sp_min = -1;
X		else
X			return 0;
X
X	if ((spwd.sp_max = strtol (fields[4], &cpp, 10)) == 0 && *cpp)
X		if (fields[4][0] == '\0')
X			spwd.sp_max = -1;
X		else
X			return 0;
X
X	if (i == OFIELDS) {
X		spwd.sp_warn = spwd.sp_inact = spwd.sp_expire =
X			spwd.sp_flag = -1;
X
X		return &spwd;
X	}
X	if ((spwd.sp_warn = strtol (fields[5], &cpp, 10)) == 0 && *cpp)
X		if (fields[5][0] == '\0')
X			spwd.sp_warn = -1;
X		else
X			return 0;
X
X	if ((spwd.sp_inact = strtol (fields[6], &cpp, 10)) == 0 && *cpp)
X		if (fields[6][0] == '\0')
X			spwd.sp_inact = -1;
X		else
X			return 0;
X
X	if ((spwd.sp_expire = strtol (fields[7], &cpp, 10)) == 0 && *cpp)
X		if (fields[7][0] == '\0')
X			spwd.sp_expire = -1;
X		else
X			return 0;
X
X	if ((spwd.sp_flag = strtol (fields[8], &cpp, 10)) == 0 && *cpp)
X		if (fields[8][0] == '\0')
X			spwd.sp_flag = -1;
X		else
X			return 0;
X
X	return (&spwd);
X}
X
Xstruct spwd
X*fgetspent (fp)
XFILE	*fp;
X{
X	char	buf[BUFSIZ];
X
X	if (! fp)
X		return (0);
X
X	if (fgets (buf, BUFSIZ, fp) == (char *) 0)
X		return (0);
X
X	return sgetspent (buf);
X}
X
Xstruct spwd
X*getspent ()
X{
X	if (! shadow)
X		setspent ();
X
X	return (fgetspent (shadow));
X}
X
Xstruct spwd
X*getspnam (name)
Xchar	*name;
X{
X	struct	spwd	*spwd;
X
X	setspent ();
X
X	while ((spwd = getspent ()) != (struct spwd *) 0) {
X		if (strcmp (name, spwd->sp_namp) == 0)
X			return (spwd);
X	}
X	return (0);
X}
X
Xint
Xputspent (spwd, fp)
Xstruct	spwd	*spwd;
XFILE	*fp;
X{
X	int	errors = 0;
X
X	if (! fp || ! spwd)
X		return -1;
X
X	if (fprintf (fp, "%s:%s:", spwd->sp_namp, spwd->sp_pwdp) < 0)
X		errors++;
X
X	if (spwd->sp_lstchg != -1) {
X		if (fprintf (fp, "%ld:", spwd->sp_lstchg) < 0)
X			errors++;
X	} else if (putc (':', fp) == EOF)
X		errors++;
X
X	if (spwd->sp_min != -1) {
X		if (fprintf (fp, "%ld:", spwd->sp_min) < 0)
X			errors++;
X	} else if (putc (':', fp) == EOF)
X		errors++;
X
X	if (spwd->sp_max != -1) {
X		if (fprintf (fp, "%ld", spwd->sp_max) < 0)
X			errors++;
X	}
X
X	/*
X	 * See if the structure has any of the SVR4 fields in
X	 * it.  If none of those fields have any data there is
X	 * no reason to write them out since they will be filled
X	 * in the same way when they are read back in.  Otherwise
X	 * there is at least one SVR4 field that must be output.
X	 */
X
X	if (spwd->sp_warn == -1 && spwd->sp_inact == -1 &&
X			spwd->sp_expire == -1 && spwd->sp_flag == -1) {
X		if (putc ('\n', fp) == EOF || errors)
X			return -1;
X		else
X			return 0;
X	} else if (putc (':', fp) == EOF)
X		errors++;
X
X	if (spwd->sp_warn != -1) {
X		if (fprintf (fp, "%ld:", spwd->sp_warn) < 0)
X			errors++;
X	} else if (putc (':', fp) == EOF)
X		errors++;
X
X	if (spwd->sp_inact != -1) {
X		if (fprintf (fp, "%ld:", spwd->sp_inact) < 0)
X			errors++;
X	} else if (putc (':', fp) == EOF)
X		errors++;
X
X	if (spwd->sp_expire != -1) {
X		if (fprintf (fp, "%ld:", spwd->sp_expire) < 0)
X			errors++;
X	} else if (putc (':', fp) == EOF)
X		errors++;
X
X	if (spwd->sp_flag != -1) {
X		if (fprintf (fp, "%ld", spwd->sp_flag) < 0)
X			errors++;
X	}
X	if (putc ('\n', fp) == EOF)
X		errors++;
X
X	if (errors)
X		return -1;
X	else
X		return 0;
X}
SHAR_EOF
if test 4426 -ne "`wc -c < 'shadow.c'`"
then
	echo shar: "error transmitting 'shadow.c'" '(should have been 4426 characters)'
fi
fi
echo shar: "extracting 'pwent.c'" '(6808 characters)'
if test -f 'pwent.c'
then
	echo shar: "will not over-write existing file 'pwent.c'"
else
sed 's/^X//' << \SHAR_EOF > 'pwent.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X *
X * Duplication is permitted for non-commercial [ profit making ]
X * purposes provided this and other copyright notices remain
X * intact.
X */
X
X#include <stdio.h>
X#include <pwd.h>
X#include <string.h>
X#include "config.h"
X
X#ifdef	DBM
X#include <dbm.h>
X#endif
X
X#ifndef	lint
Xstatic	char	_sccsid[] = "@(#)pwent.c	2.4	23:41:33	10/28/90";
X#endif
X
X#define	SBUFSIZ	64
X#define	NFIELDS	7
X
Xstatic	FILE	*pwdfp;
Xstatic	char	pwdbuf[BUFSIZ];
Xstatic	char	*pwdfile = "/etc/passwd";
X#ifdef	DBM
Xstatic	int	dbmopened;
Xstatic	int	dbmerror;
X#endif
Xstatic	char	*pwdfields[NFIELDS];
Xstatic	struct	passwd	pwent;
X
X/*
X * sgetpwent - convert a string to a (struct passwd)
X *
X * sgetpwent() parses a string into the parts required for a password
X * structure.  Strict checking is made for the UID and GID fields and
X * presence of the correct number of colons.  Any failing tests result
X * in a NULL pointer being returned.
X */
X
Xstruct	passwd	*sgetpwent (buf)
Xchar	*buf;
X{
X	int	i;
X	char	*cp;
X
X	/*
X	 * Copy the string to a static buffer so the pointers into
X	 * the password structure remain valid.
X	 */
X
X	strncpy (pwdbuf, buf, BUFSIZ);
X	pwdbuf[BUFSIZ-1] = '\0';
X
X	/*
X	 * Save a pointer to the start of each colon separated
X	 * field.  The fields are converted into NUL terminated strings.
X	 */
X
X	for (cp = pwdbuf, i = 0;i < NFIELDS && cp;i++) {
X		pwdfields[i] = cp;
X		if (cp = strchr (cp, ':'))
X			*cp++ = 0;
X	}
X
X	/*
X	 * There must be exactly NFIELDS colon separated fields or
X	 * the entry is invalid.  Also, the UID and GID must be non-blank.
X	 */
X
X	if (i != NFIELDS || *pwdfields[2] == '\0' || *pwdfields[3] == '\0')
X		return 0;
X
X	/*
X	 * Each of the fields is converted the appropriate data type
X	 * and the result assigned to the password structure.  If the
X	 * UID or GID does not convert to an integer value, a NULL
X	 * pointer is returned.
X	 */
X
X	pwent.pw_name = pwdfields[0];
X	pwent.pw_passwd = pwdfields[1];
X	if ((pwent.pw_uid = strtol (pwdfields[2], &cp, 10)) == 0 && *cp)
X		return 0;
X
X	if ((pwent.pw_gid = strtol (pwdfields[3], &cp, 10)) == 0 && *cp)
X		return 0;
X
X	if (cp = strchr (pwent.pw_passwd, ',')) {
X		pwent.pw_age = cp + 1;
X		*cp = '\0';
X	} else
X		pwent.pw_age = "";
X
X	pwent.pw_gecos = pwdfields[4];
X	pwent.pw_dir = pwdfields[5];
X	pwent.pw_shell = pwdfields[6];
X
X	return (&pwent);
X}
X#ifdef FGETPWENT
X/*
X * fgetpwent - get a password file entry from a stream
X *
X * fgetpwent() reads the next line from a password file formatted stream
X * and returns a pointer to the password structure for that line.
X */
X
Xstruct	passwd	*fgetpwent (fp)
XFILE	*fp;
X{
X	char	buf[BUFSIZ];
X
X	while (fgets (buf, BUFSIZ, fp) != (char *) 0) {
X		buf[strlen (buf) - 1] = '\0';
X		return (sgetpwent (buf));
X	}
X	return 0;
X}
X#endif
X#ifdef	GETPWENT
X
X/*
X * endpwent - close a password file
X *
X * endpwent() closes the password file if open.
X */
X
Xint	endpwent ()
X{
X	if (pwdfp)
X		if (fclose (pwdfp))
X			return -1;
X
X	return 0;
X}
X
X/*
X * getpwent - get a password entry from the password file
X *
X * getpwent() opens the password file, if not already opened, and reads
X * a single entry.  NULL is returned if any errors are encountered reading
X * the password file.
X */
X
Xstruct	passwd	*getpwent ()
X{
X	if (! pwdfp && setpwent ())
X		return 0;
X
X	return fgetpwent (pwdfp);
X}
X
X/*
X * getpwuid - locate the password entry for a given UID
X *
X * getpwuid() locates the first password file entry for the given UID.
X * If there is a valid DBM file, the DBM files are queried first for
X * the entry.  Otherwise, a linear search is begun of the password file
X * searching for an entry which matches the provided UID.
X */
X
Xstruct	passwd	*getpwuid (uid)
Xint	uid;
X{
X	struct	passwd	*pwd;
X#ifdef	DBM
X	datum	key;
X	datum	content;
X
X	/*
X	 * Attempt to open the DBM files if they have never been opened
X	 * and an error has never been returned.
X	 */
X
X	if (! dbmerror && ! dbmopened) {
X		char	dbmfiles[BUFSIZ];
X
X		strcpy (dbmfiles, pwdfile);
X		strcat (dbmfiles, ".pag");
X
X		if (access (dbmfiles, 0) || dbminit (pwdfile))
X			dbmerror = 1;
X		else
X			dbmopened = 1;
X	}
X
X	/*
X	 * If the DBM file are now open, create a key for this UID and
X	 * try to fetch the entry from the database.  A matching record
X	 * will be unpacked into a static structure and returned to
X	 * the user.
X	 */
X
X	if (dbmopened) {
X		pwent.pw_uid = uid;
X		key.dsize = sizeof pwent.pw_uid;
X		key.dptr = (char *) &pwent.pw_uid;
X		content = fetch (key);
X		if (content.dptr != 0) {
X			memcpy (pwdbuf, content.dptr, content.dsize);
X			pw_unpack (pwdbuf, content.dsize, &pwent);
X			return &pwent;
X		}
X	}
X#endif
X	/*
X	 * Rewind the database and begin searching for an entry which
X	 * matches the UID.  Return the entry when a match is found.
X	 */
X
X	if (setpwent ())
X		return 0;
X
X	while (pwd = getpwent ())
X		if (pwd->pw_uid == uid)
X			return pwd;
X
X	return 0;
X}
X
Xstruct	passwd	*getpwnam (name)
Xchar	*name;
X{
X	struct	passwd	*pwd;
X#ifdef	DBM
X	datum	key;
X	datum	content;
X
X	/*
X	 * Attempt to open the DBM files if they have never been opened
X	 * and an error has never been returned.
X	 */
X
X	if (! dbmerror && ! dbmopened) {
X		char	dbmfiles[BUFSIZ];
X
X		strcpy (dbmfiles, pwdfile);
X		strcat (dbmfiles, ".pag");
X
X		if (access (dbmfiles, 0) || dbminit (pwdfile))
X			dbmerror = 1;
X		else
X			dbmopened = 1;
X	}
X
X	/*
X	 * If the DBM file are now open, create a key for this UID and
X	 * try to fetch the entry from the database.  A matching record
X	 * will be unpacked into a static structure and returned to
X	 * the user.
X	 */
X
X	if (dbmopened) {
X		key.dsize = strlen (name);
X		key.dptr = name;
X		content = fetch (key);
X		if (content.dptr != 0) {
X			memcpy (pwdbuf, content.dptr, content.dsize);
X			pw_unpack (pwdbuf, content.dsize, &pwent);
X			return &pwent;
X		}
X	}
X#endif
X	/*
X	 * Rewind the database and begin searching for an entry which
X	 * matches the name.  Return the entry when a match is found.
X	 */
X
X	if (setpwent ())
X		return 0;
X
X	while (pwd = getpwent ())
X		if (strcmp (pwd->pw_name, name) == 0)
X			return pwd;
X
X	return 0;
X}
X
X/*
X * setpwent - open the password file
X *
X * setpwent() opens the system password file, and the DBM password files
X * if they are present.  The system password file is rewound if it was
X * open already.
X */
X
Xint	setpwent ()
X{
X	if (! pwdfp) {
X		if (! (pwdfp = fopen (pwdfile, "r")))
X			return -1;
X	} else {
X		if (fseek (pwdfp, 0L, 0) != 0)
X			return -1;
X	}
X#ifdef	DBM
X	/*
X	 * Attempt to open the DBM files if they have never been opened
X	 * and an error has never been returned.
X	 */
X
X	if (! dbmerror && ! dbmopened) {
X		char	dbmfiles[BUFSIZ];
X
X		strcpy (dbmfiles, pwdfile);
X		strcat (dbmfiles, ".pag");
X
X		if (access (dbmfiles, 0) || dbminit (pwdfile))
X			dbmerror = 1;
X		else
X			dbmopened = 1;
X	}
X#endif
X	return 0;
X}
X#endif
SHAR_EOF
if test 6808 -ne "`wc -c < 'pwent.c'`"
then
	echo shar: "error transmitting 'pwent.c'" '(should have been 6808 characters)'
fi
fi
echo shar: "extracting 'groupio.c'" '(9953 characters)'
if test -f 'groupio.c'
then
	echo shar: "will not over-write existing file 'groupio.c'"
else
sed 's/^X//' << \SHAR_EOF > 'groupio.c'
X/*
X * Copyright 1990, John F. Haugh II
X * An unpublished work.
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X *
X *	This file implements a transaction oriented group database
X *	library.  The group file is updated one entry at a time.
X *	After each transaction the file must be logically closed and
X *	transferred to the existing group file.  The sequence of
X *	events is
X *
X *	gr_lock				-- lock group file
X *	gr_open				-- logically open group file
X *	while transaction to process
X *		gr_(locate,update,remove) -- perform transaction
X *	done
X *	gr_close			-- commit transactions
X *	gr_unlock			-- remove group lock
X */
X
X#include <sys/stat.h>
X#include <fcntl.h>
X#include <errno.h>
X#include <grp.h>
X#include <stdio.h>
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)groupio.c	3.2 22:14:38 11/16/90";
X#endif
X
Xstatic	int	islocked;
Xstatic	int	isopen;
Xstatic	int	open_modes;
Xstatic	FILE	*grfp;
X
Xstruct	gr_file_entry {
X	char	*grf_line;
X	int	grf_changed;
X	struct	group	*grf_entry;
X	struct	gr_file_entry *grf_next;
X};
X
Xstatic	struct	gr_file_entry	*grf_head;
Xstatic	struct	gr_file_entry	*grf_tail;
Xstatic	struct	gr_file_entry	*grf_cursor;
Xstatic	int	gr_changed;
X
X#define	GR_LOCK	"/etc/group.lock"
X#define	GR_TEMP "/etc/grp.%d"
X#define	GROUP	"/etc/group"
X#define	OGROUP	"/etc/group-"
X
Xextern	char	*strdup();
Xextern	struct	group	*sgetgrent();
X
X/*
X * gr_dup - duplicate a group file entry
X *
X *	gr_dup() accepts a pointer to a group file entry and
X *	returns a pointer to a group file entry in allocated
X *	memory.
X */
X
Xstatic struct group *
Xgr_dup (grent)
Xstruct	group	*grent;
X{
X	struct	group	*gr;
X	int	i;
X
X	if (! (gr = (struct group *) malloc (sizeof *gr)))
X		return 0;
X
X	if ((gr->gr_name = strdup (grent->gr_name)) == 0 ||
X			(gr->gr_passwd = strdup (grent->gr_passwd)) == 0)
X		return 0;
X
X	for (i = 0;grent->gr_mem[i];i++)
X		;
X
X	gr->gr_mem = (char **) malloc (sizeof (char *) * (i + 1));
X	for (i = 0;grent->gr_mem[i];i++)
X		if (! (gr->gr_mem[i] = strdup (grent->gr_mem[i])))
X			return 0;
X
X	gr->gr_mem[i] = 0;
X	gr->gr_gid = grent->gr_gid;
X
X	return gr;
X}
X
X/*
X * gr_free - free a dynamically allocated group file entry
X *
X *	gr_free() frees up the memory which was allocated for the
X *	pointed to entry.
X */
X
Xstatic void
Xgr_free (grent)
Xstruct	group	*grent;
X{
X	int	i;
X
X	free (grent->gr_name);
X	free (grent->gr_passwd);
X
X	for (i = 0;grent->gr_mem[i];i++)
X		free (grent->gr_mem[i]);
X
X	free (grent->gr_mem);
X}
X
X/*
X * gr_lock - lock a group file
X *
X *	gr_lock() encapsulates the lock operation.  it returns
X *	TRUE or FALSE depending on the group file being
X *	properly locked.  the lock is set by creating a semaphore
X *	file, GR_LOCK.
X */
X
Xint
Xgr_lock ()
X{
X	int	fd;
X	int	pid;
X	int	len;
X	char	file[BUFSIZ];
X	char	buf[32];
X	struct	stat	sb;
X
X	if (islocked)
X		return 1;
X
X	/*
X	 * Create a lock file which can be switched into place
X	 */
X
X	sprintf (file, GR_TEMP, getpid ());
X	if ((fd = open (file, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1)
X		return 0;
X
X	sprintf (buf, "%d", getpid ());
X	if (write (fd, buf, strlen (buf) + 1) != strlen (buf) + 1) {
X		(void) close (fd);
X		(void) unlink (file);
X		return 0;
X	}
X	close (fd);
X
X	/*
X	 * Simple case first -
X	 *	Link fails (in a sane environment ...) if the target
X	 *	exists already.  So we try to switch in a new lock
X	 *	file.  If that succeeds, we assume we have the only
X	 *	valid lock.  Needs work for NFS where this assumption
X	 *	may not hold.  The simple hack is to check the link
X	 *	count on the source file, which should be 2 iff the
X	 *	link =really= worked.
X	 */
X
X	if (link (file, GR_LOCK) == 0) {
X		if (stat (file, &sb) != 0)
X			return 0;
X
X		if (sb.st_nlink != 2)
X			return 0;
X
X		(void) unlink (file);
X		islocked = 1;
X		return 1;
X	}
X
X	/*
X	 * Invalid lock test -
X	 *	Open the lock file and see if the lock is valid.
X	 *	The PID of the lock file is checked, and if the PID
X	 *	is not valid, the lock file is removed.  If the unlink
X	 *	of the lock file fails, it should mean that someone
X	 *	else is executing this code.  They will get success,
X	 *	and we will fail.
X	 */
X
X	if ((fd = open (GR_LOCK, O_RDWR)) == -1 ||
X			(len = read (fd, buf, BUFSIZ)) <= 0) {
X		errno = EINVAL;
X		return 0;
X	}
X	buf[len] = '\0';
X	if ((pid = strtol (buf, (char **) 0, 10)) == 0) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (kill (pid, 0) == 0)  {
X		errno = EEXIST;
X		return 0;
X	}
X	if (unlink (GR_LOCK)) {
X		(void) close (fd);
X		(void) unlink (file);
X
X		return 0;
X	}
X
X	/*
X	 * Re-try lock -
X	 *	The invalid lock has now been removed and I should
X	 *	be able to acquire a lock for myself just fine.  If
X	 *	this fails there will be no retry.  The link count
X	 *	test here makes certain someone executing the previous
X	 *	block of code didn't just remove the lock we just
X	 *	linked to.
X	 */
X
X	if (link (file, GR_LOCK) == 0) {
X		if (stat (file, &sb) != 0)
X			return 0;
X
X		if (sb.st_nlink != 2)
X			return 0;
X
X		(void) unlink (file);
X		islocked = 1;
X		return 1;
X	}
X	(void) unlink (file);
X	return 0;
X}
X
X/*
X * gr_unlock - logically unlock a group file
X *
X *	gr_unlock() removes the lock which was set by an earlier
X *	invocation of gr_lock().
X */
X
Xint
Xgr_unlock ()
X{
X	if (islocked) {
X		if (isopen) {
X			open_modes = O_RDONLY;
X			gr_close ();
X		}
X		unlink (GR_LOCK);
X		islocked = 0;
X		return 1;
X	} else
X		return 0;
X}
X
X/*
X * gr_open - open a group file
X *
X *	gr_open() encapsulates the open operation.  it returns
X *	TRUE or FALSE depending on the group file being
X *	properly opened.
X */
X
Xint
Xgr_open (mode)
Xint	mode;
X{
X	char	buf[8192];
X	struct	gr_file_entry	*grf;
X	struct	group	*grent;
X
X	if (isopen || (mode != O_RDONLY && mode != O_RDWR))
X		return 0;
X
X	if (mode != O_RDONLY && ! islocked)
X		return 0;
X
X	if ((grfp = fopen (GROUP, mode == O_RDONLY ? "r":"r+")) == 0)
X		return 0;
X
X	grf_head = grf_tail = grf_cursor = 0;
X	gr_changed = 0;
X
X	while (fgets (buf, sizeof buf, grfp) != (char *) 0) {
X		if (! (grf = (struct gr_file_entry *) malloc (sizeof *grf)))
X			return 0;
X
X		grf->grf_changed = 0;
X		grf->grf_line = strdup (buf);
X		if ((grent = sgetgrent (buf)) && ! (grent = gr_dup (grent)))
X			return 0;
X
X		grf->grf_entry = grent;
X
X		if (grf_head == 0) {
X			grf_head = grf_tail = grf;
X			grf->grf_next = 0;
X		} else {
X			grf_tail->grf_next = grf;
X			grf->grf_next = 0;
X			grf_tail = grf;
X		}
X	}
X	isopen++;
X	open_modes = mode;
X
X	return 1;
X}
X
X/*
X * gr_close - close the group file
X *
X *	gr_close() outputs any modified group file entries and
X *	frees any allocated memory.
X */
X
Xint
Xgr_close ()
X{
X	int	fd;
X	int	mask;
X	int	c;
X	int	i;
X	int	errors = 0;
X	FILE	*bkfp;
X	struct	gr_file_entry *grf;
X	struct	gr_file_entry *ogrf;
X
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (open_modes == O_RDWR && gr_changed) {
X		mask = umask (0222);
X		if ((bkfp = fopen (OGROUP, "w")) == 0) {
X			umask (mask);
X			return 0;
X		}
X		umask (mask);
X
X		rewind (grfp);
X		while ((c = getc (grfp)) != EOF) {
X			if (putc (c, bkfp) == EOF) {
X				fclose (bkfp);
X				return 0;
X			}
X		}
X		if (fclose (bkfp))
X			return 0;
X
X		isopen = 0;
X		(void) fclose (grfp);
X
X		mask = umask (0222);
X		if (! (grfp = fopen (GROUP, "w"))) {
X			umask (mask);
X			return 0;
X		}
X		umask (mask);
X
X		for (grf = grf_head;errors == 0 && grf;grf = grf->grf_next) {
X			if (grf->grf_changed) {
X				if (putgrent (grf->grf_entry, grfp))
X					errors++;
X			} else {
X				if (fputs (grf->grf_line, grfp) == EOF)
X					errors++;
X			}
X		}
X		if (fflush (grfp))
X			errors++;
X
X		if (errors) {
X			unlink (GROUP);
X			link (OGROUP, GROUP);
X			unlink (OGROUP);
X			return 0;
X		}
X	}
X	if (fclose (grfp))
X		return 0;
X
X	grfp = 0;
X
X	while (grf_head != 0) {
X		grf = grf_head;
X		grf_head = grf->grf_next;
X
X		if (grf->grf_entry) {
X			gr_free (grf->grf_entry);
X			free (grf->grf_entry);
X		}
X		if (grf->grf_line)
X			free (grf->grf_line);
X
X		free (grf);
X	}
X	grf_tail = 0;
X	return 1;
X}
X
Xint
Xgr_update (grent)
Xstruct	group	*grent;
X{
X	struct	gr_file_entry	*grf;
X	struct	group	*ngr;
X
X	if (! isopen || open_modes == O_RDONLY) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (grf = grf_head;grf != 0;grf = grf->grf_next) {
X		if (grf->grf_entry == 0)
X			continue;
X
X		if (strcmp (grent->gr_name, grf->grf_entry->gr_name) != 0)
X			continue;
X
X		if (! (ngr = gr_dup (grent)))
X			return 0;
X		else {
X			gr_free (grf->grf_entry);
X			*(grf->grf_entry) = *ngr;
X		}
X		grf->grf_changed = 1;
X		grf_cursor = grf;
X		return gr_changed = 1;
X	}
X	grf = (struct gr_file_entry *) malloc (sizeof *grf);
X	if (! (grf->grf_entry = gr_dup (grent)))
X		return 0;
X
X	grf->grf_changed = 1;
X	grf->grf_next = 0;
X	grf->grf_line = 0;
X
X	if (grf_tail)
X		grf_tail->grf_next = grf;
X
X	if (! grf_head)
X		grf_head = grf;
X
X	grf_tail = grf;
X
X	return gr_changed = 1;
X}
X
Xint
Xgr_remove (name)
Xchar	*name;
X{
X	struct	gr_file_entry	*grf;
X	struct	gr_file_entry	*ogrf;
X
X	if (! isopen || open_modes == O_RDONLY) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (ogrf = 0, grf = grf_head;grf != 0;
X			ogrf = grf, grf = grf->grf_next) {
X		if (! grf->grf_entry)
X			continue;
X
X		if (strcmp (name, grf->grf_entry->gr_name) != 0)
X			continue;
X
X		if (grf == grf_cursor)
X			grf_cursor = ogrf;
X
X		if (ogrf != 0)
X			ogrf->grf_next = grf->grf_next;
X		else
X			grf_head = grf->grf_next;
X
X		if (grf == grf_tail)
X			grf_tail = ogrf;
X
X		return gr_changed = 1;
X	}
X	errno = ENOENT;
X	return 0;
X}
X
Xstruct group *
Xgr_locate (name)
Xchar	*name;
X{
X	struct	gr_file_entry	*grf;
X
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (grf = grf_head;grf != 0;grf = grf->grf_next) {
X		if (grf->grf_entry == 0)
X			continue;
X
X		if (strcmp (name, grf->grf_entry->gr_name) == 0) {
X			grf_cursor = grf;
X			return grf->grf_entry;
X		}
X	}
X	errno = ENOENT;
X	return 0;
X}
X
Xint
Xgr_rewind ()
X{
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	grf_cursor = 0;
X	return 1;
X}
X
Xstruct group *
Xgr_next ()
X{
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (grf_cursor == 0)
X		grf_cursor = grf_head;
X	else
X		grf_cursor = grf_cursor->grf_next;
X
X	while (grf_cursor) {
X		if (grf_cursor->grf_entry)
X			return grf_cursor->grf_entry;
X
X		grf_cursor = grf_cursor->grf_next;
X	}
X	return 0;
X}
SHAR_EOF
if test 9953 -ne "`wc -c < 'groupio.c'`"
then
	echo shar: "error transmitting 'groupio.c'" '(should have been 9953 characters)'
fi
fi
echo shar: "extracting 'newusers.c'" '(12980 characters)'
if test -f 'newusers.c'
then
	echo shar: "will not over-write existing file 'newusers.c'"
else
sed 's/^X//' << \SHAR_EOF > 'newusers.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X *
X *	newusers - create users from a batch file
X *
X *	newusers creates a collection of entries in /etc/passwd
X *	and related files by reading a passwd-format file and
X *	adding entries in the related directories.
X */
X
X#include <stdio.h>
X#include <pwd.h>
X#include <grp.h>
X#include <fcntl.h>
X#include <string.h>
X#include "config.h"
X#ifdef	SHADOWPWD
X#include "shadow.h"
X#endif
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)newusers.c	3.1 22:33:23 11/16/90";
X#endif
X
Xchar	*Prog;
X
Xextern	char	*pw_encrypt();
X
Xint	pw_lock(), gr_lock();
Xint	pw_open(), gr_open();
Xstruct	passwd	*pw_locate(), *pw_next();
Xstruct	group	*gr_locate(), *gr_next();
Xint	pw_update(), gr_update();
Xint	pw_close(), gr_close();
Xint	pw_unlock(), gr_unlock();
X
X#ifdef	SHADOWPWD
Xint	spw_lock(), spw_open(), spw_update(), spw_close(), spw_unlock();
Xstruct	spwd	*spw_locate(), *spw_next();
X#endif
X
X#ifndef	MKDIR
X
X/*
X * mkdir - for those of us with no mkdir() system call.
X */
X
Xmkdir (dir, mode)
Xchar	*dir;
Xint	mode;
X{
X	int	mask;
X	int	status;
X	int	pid;
X	int	i;
X	char	buf[BUFSIZ];
X
X	mode = (~mode & 0777);
X	mask = umask (mode);
X	if ((pid = fork ()) == 0) {
X		execl ("/bin/mkdir", "mkdir", dir, (char *) 0);
X		perror ("/bin/mkdir");
X		_exit (1);
X	} else {
X		while ((i = wait (&status)) != pid && i != -1)
X			;
X	}
X	umask (mask);
X	return status;
X}
X#endif
X
X/*
X * usage - display usage message and exit
X */
X
Xusage ()
X{
X	fprintf (stderr, "Usage: %s [ input ]\n", Prog);
X	exit (1);
X}
X
X/*
X * add_group - create a new group or add a user to an existing group
X */
X
Xint
Xadd_group (name, uid, gid, ngid)
Xchar	*name;
Xchar	*uid;
Xchar	*gid;
Xint	*ngid;
X{
X	struct	passwd	*pwd;
X	struct	group	*grp;
X	struct	group	grent;
X	char	*members[2];
X	int	i;
X
X	/*
X	 * Start by seeing if the named group already exists.  This
X	 * will be very easy to deal with if it does.
X	 */
X
X	if (grp = gr_locate (gid)) {
Xadd_member:
X		grent = *grp;
X		*ngid = grent.gr_gid;
X		for (i = 0;grent.gr_mem[i] != (char *) 0;i++)
X			if (strcmp (grent.gr_mem[i], name) == 0)
X				return 0;
X
X		if (! (grent.gr_mem = (char **)
X				malloc (sizeof (char *) * (i + 2)))) {
X			fprintf (stderr, "%s: Out of Memory\n", Prog);
X			return -1;
X		}
X		memcpy (grent.gr_mem, grp->gr_mem, sizeof (char *) * (i + 2));
X		grent.gr_mem[i] = strdup (name);
X		grent.gr_mem[i + 1] = (char *) 0;
X
X		return ! gr_update (&grent);
X	}
X
X	/*
X	 * The group did not exist, so I try to figure out what the
X	 * GID is going to be.  The gid parameter is probably "", meaning
X	 * I figure out the GID from the password file.  I want the UID
X	 * and GID to match, unless the GID is already used.
X	 */
X
X	if (gid[0] == '\0') {
X		i = 100;
X		for (pw_rewind ();pwd = pw_next ();) {
X			if (pwd->pw_uid >= i)
X				i = pwd->pw_uid + 1;
X		}
X		for (gr_rewind ();grp = gr_next ();) {
X			if (grp->gr_gid == i) {
X				i = -1;
X				break;
X			}
X		}
X	} else if (gid[0] >= '0' && gid[0] <= '9') {
X
X	/*
X	 * The GID is a number, which means either this is a brand new
X	 * group, or an existing group.  For existing groups I just add
X	 * myself as a member, just like I did earlier.
X	 */
X
X		i = atoi (gid);
X		for (gr_rewind ();grp = gr_next ();)
X			if (grp->gr_gid == i)
X				goto add_member;
X	} else
X
X	/*
X	 * The last alternative is that the GID is a name which is not
X	 * already the name of an existing group, and I need to figure
X	 * out what group ID that group name is going to have.
X	 */
X
X		i = -1;
X
X	/*
X	 * If I don't have a group ID by now, I'll go get the
X	 * next one.
X	 */
X
X	if (i == -1) {
X		for (i = 100, gr_rewind;grp = gr_next ();)
X			if (grp->gr_gid >= i)
X				i = grp->gr_gid + 1;
X	}
X
X	/*
X	 * Now I have all of the fields required to create the new
X	 * group.
X	 */
X
X	if (gid[0] && (gid[0] <= '0' || gid[0] >= '9'))
X		grent.gr_name = gid;
X	else
X		grent.gr_name = name;
X
X	grent.gr_passwd = "!";
X	grent.gr_gid = i;
X	members[0] = name;
X	members[1] = (char *) 0;
X	grent.gr_mem = members;
X
X	*ngid = grent.gr_gid;
X	return ! gr_update (&grent);
X}
X
X/*
X * add_user - create a new user ID
X */
X
Xadd_user (name, uid, nuid, gid)
Xchar	*name;
Xchar	*uid;
Xint	*nuid;
Xint	gid;
X{
X	struct	passwd	*pwd;
X	struct	passwd	pwent;
X	int	i;
X
X	/*
X	 * The first guess for the UID is either the numerical UID
X	 * that the caller provided, or the next available UID.
X	 */
X
X	if (uid[0] >= '0' && uid[0] <= '9') {
X		i = atoi (uid);
X	} if (uid[0] && (pwd = pw_locate (uid))) {
X		i = pwd->pw_uid;
X	} else {
X		i = 100;
X		for (pw_rewind ();pwd = pw_next ();)
X			if (pwd->pw_uid >= i)
X				i = pwd->pw_uid + 1;
X	}
X
X	/*
X	 * I don't want to fill in the entire password structure
X	 * members JUST YET, since there is still more data to be
X	 * added.  So, I fill in the parts that I have.
X	 */
X
X	pwent.pw_name = name;
X	pwent.pw_passwd = "!";
X	pwent.pw_age = "";
X	pwent.pw_uid = i;
X	pwent.pw_gid = gid;
X	pwent.pw_gecos = "";
X	pwent.pw_dir = "";
X	pwent.pw_shell = "";
X
X	*nuid = i;
X	return ! pw_update (&pwent);
X}
X
X/*
X * add_passwd - add or update the encrypted password
X */
X
Xadd_passwd (pwd, passwd)
Xstruct	passwd	*pwd;
Xchar	*passwd;
X{
X#ifdef	SHADOWPWD
X	struct	spwd	*sp;
X	struct	spwd	spent;
X#endif
X	struct	passwd	*pw;
X	struct	passwd	pwent;
X	static	char	newage[5];
X
X	/*
X	 * In the case of regular password files, this is real
X	 * easy - pwd points to the entry in the password file.
X	 * Shadow files are harder since there are zillions of
X	 * things to do ...
X	 */
X
X#ifndef	SHADOWPWD
X	pwd->pw_passwd = pw_encrypt (passwd, (char *) 0);
X	if (strlen (pwd->pw_age) == 4) {
X		strcpy (newage, pwd->pw_age);
X		strcpy (newage + 2,
X			l64a (time ((long *) 0) / (7L*24L*3600L)));
X		pwd->pw_age = newage;
X	}
X	return 0;
X#else
X
X	/*
X	 * Do the first and easiest shadow file case.  The user
X	 * already exists in the shadow password file.
X	 */
X
X	if (sp = spw_locate (pwd->pw_name)) {
X		spent = *sp;
X		spent.sp_pwdp = pw_encrypt (passwd, (char *) 0);
X		return ! spw_update (sp);
X	}
X
X	/*
X	 * Pick the next easiest case - the user has an encrypted
X	 * password which isn't equal to "!".  The password was set
X	 * to "!" earlier when the entry was created, so this user
X	 * would have to have had the password set someplace else.
X	 */
X
X	if (strcmp (pwd->pw_passwd, "!") != 0) {
X		pwd->pw_passwd = pw_encrypt (passwd, (char *) 0);
X		if (strlen (pwd->pw_age) == 4) {
X			strcpy (newage, pwd->pw_age);
X			strcpy (newage + 2,
X				l64a (time ((long *) 0) / (7L*24L*3600L)));
X			pwd->pw_age = newage;
X		}
X		return 0;
X	}
X
X	/*
X	 * Now the really hard case - I need to create an entirely
X	 * shadow password file entry.
X	 */
X
X	spent.sp_namp = pwd->pw_name;
X	spent.sp_pwdp = pw_encrypt (passwd, (char *) 0);
X	spent.sp_lstchg = time ((long *) 0) / (24L*3600L);
X#ifdef	MINDAYS
X	spent.sp_min = MINDAYS;
X#else
X	spent.sp_min = 0;
X#endif
X#ifdef	MAXDAYS
X	spent.sp_max = MAXDAYS;
X#else
X	spent.sp_max = 10000;		/* 10000 is infinity this week */
X#endif
X#ifdef	WARNAGE
X	spent.sp_warn = WARNAGE;
X#else
X	spent.sp_warn = -1;
X#endif
X	spent.sp_inact = -1;
X	spent.sp_expire = -1;
X	spent.sp_flag = -1;
X
X	return ! spw_update (&spent);
X#endif
X}
X
Xmain (argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	char	buf[BUFSIZ];
X	char	*fields[8];
X	int	nfields;
X	char	*name;
X	char	*newpwd;
X	char	*cp;
X#ifdef	SHADOWPWD
X	struct	spwd	*sp;
X	struct	spwd	newsp;
X	struct	spwd	*spw_locate();
X#endif
X	struct	passwd	*pw;
X	struct	passwd	newpw;
X	struct	passwd	*pw_locate();
X	char	newage[5];
X	int	errors = 0;
X	int	line = 0;
X	long	now = time ((long *) 0) / (24L*3600L);
X	int	uid;
X	int	gid;
X	int	i;
X
X	if (Prog = strrchr (argv[0], '/'))
X		Prog++;
X	else
X		Prog = argv[0];
X
X	if (argc > 1 && argv[1][0] == '-')
X		usage ();
X
X	if (argc == 2) {
X		if (! freopen (argv[1], "r", stdin)) {
X			sprintf (buf, "%s: %s", Prog, argv[1]);
X			perror (buf);
X			exit (1);
X		}
X	}
X
X	/*
X	 * Lock the password files and open them for update.  This will
X	 * bring all of the entries into memory where they may be
X	 * searched for an modified, or new entries added.  The password
X	 * file is the key - if it gets locked, assume the others can
X	 * be locked right away.
X	 */
X
X	for (i = 0;i < 30;i++) {
X		if (pw_lock ())
X			break;
X	}
X	if (i == 30) {
X		fprintf (stderr, "%s: can't lock /etc/passwd.\n", Prog);
X		exit (1);
X	}
X#ifdef	SHADOWPWD
X	if (! spw_lock () || ! gr_lock ())
X#else
X	if (! gr_lock ())
X#endif
X	{
X		fprintf (stderr, "%s: can't lock files, try again later\n",
X			Prog);
X		(void) pw_unlock ();
X#ifdef	SHADOWPWD
X		(void) spw_unlock ();
X#endif
X		exit (1);
X	}
X#ifdef	SHADOWPWD
X	if (! pw_open (O_RDWR) || ! spw_open (O_RDWR) || ! gr_open (O_RDWR))
X#else
X	if (! pw_open (O_RDWR) || ! gr_open (O_RDWR))
X#endif
X	{
X		fprintf (stderr, "%s: can't open files\n", Prog);
X		(void) pw_unlock ();
X#ifdef	SHADOWPWD
X		(void) spw_unlock ();
X#endif
X		(void) gr_unlock ();
X		exit (1);
X	}
X
X	/*
X	 * Read each line.  The line has the same format as a password
X	 * file entry, except that certain fields are not contrained to
X	 * be numerical values.  If a group ID is entered which does
X	 * not already exist, an attempt is made to allocate the same
X	 * group ID as the numerical user ID.  Should that fail, the
X	 * next available group ID over 100 is allocated.  The pw_gid
X	 * field will be updated with that value.
X	 */
X
X	while (fgets (buf, sizeof buf, stdin) != (char *) 0) {
X		line++;
X		if (cp = strrchr (buf, '\n')) {
X			*cp = '\0';
X		} else {
X			fprintf (stderr, "%s: line %d: line too long\n",
X				Prog, line);
X			errors++;
X			continue;
X		}
X
X		/*
X		 * Break the string into fields and screw around with
X		 * them.  There MUST be 7 colon separated fields,
X		 * although the values aren't that particular.
X		 */
X
X		for (cp = buf, nfields = 0;nfields < 7;nfields++) {
X			fields[nfields] = cp;
X			if (cp = strchr (cp, ':'))
X				*cp++ = '\0';
X			else
X				break;
X		}
X		if (*cp || nfields != 6) {
X			fprintf (stderr, "%s: line %d: invalid line\n",
X				Prog, line);
X			continue;
X		}
X
X		/*
X		 * Now the fields are processed one by one.  The first
X		 * field to be processed is the group name.  A new
X		 * group will be created if the group name is non-numeric
X		 * and does not already exist.  The named user will be
X		 * the only member.  If there is no named group to be a
X		 * member of, the UID will be figured out and that value
X		 * will be a candidate for a new group, if that group ID
X		 * exists, a whole new group ID will be made up.
X		 */
X		
X		if (! (pw = pw_locate (fields[0])) &&
X			add_group (fields[0], fields[2], fields[3], &gid)) {
X			fprintf (stderr, "%s: %d: can't create GID\n",
X				Prog, line);
X			errors++;
X			continue;
X		}
X
X		/*
X		 * Now we work on the user ID.  It has to be specified
X		 * either as a numerical value, or left blank.  If it
X		 * is a numerical value, that value will be used, otherwise
X		 * the next available user ID is computed and used.  After
X		 * this there will at least be a (struct passwd) for the
X		 * user.
X		 */
X
X		if (! pw && add_user (fields[0], fields[2], &uid, gid)) {
X			fprintf (stderr, "%s: line %d: can't create UID\n",
X				Prog, line);
X			errors++;
X			continue;
X		}
X
X		/*
X		 * The password, gecos field, directory, and shell fields
X		 * all come next.
X		 */
X
X		if (! (pw = pw_locate (fields[0]))) {
X			fprintf (stderr, "%s: line %d: cannot find user %s\n",
X				Prog, line, fields[0]);
X			errors++;
X			continue;
X		}
X		newpw = *pw;
X
X		if (add_passwd (&newpw, fields[1])) {
X			fprintf (stderr, "%s: line %d: can't update password\n",
X				Prog, line);
X			errors++;
X			continue;
X		}
X		if (fields[4][0])
X			newpw.pw_gecos = fields[4];
X
X		if (fields[5][0])
X			newpw.pw_dir = fields[5];
X
X		if (fields[6][0])
X			newpw.pw_shell = fields[6];
X
X		if (newpw.pw_dir[0] && access (newpw.pw_dir, 0)) {
X#ifdef	UMASK
X			if (mkdir (newpw.pw_dir, 0777 & (~UMASK)))
X#else
X			if (mkdir (newpw.pw_dir, 0777))
X#endif
X				fprintf (stderr, "%s: line %d: mkdir failed\n",
X					Prog, line);
X			else if (chown (newpw.pw_dir,
X					newpw.pw_uid, newpw.pw_gid))
X				fprintf (stderr, "%s: line %d: chown failed\n",
X					Prog, line);
X		}
X
X		/*
X		 * Update the password entry with the new changes made.
X		 */
X
X		if (! pw_update (&newpw)) {
X			fprintf (stderr, "%s: line %d: can't update entry\n",
X				Prog, line);
X			errors++;
X			continue;
X		}
X	}
X
X	/*
X	 * Any detected errors will cause the entire set of changes
X	 * to be aborted.  Unlocking the password file will cause
X	 * all of the changes to be ignored.  Otherwise the file is
X	 * closed, causing the changes to be written out all at
X	 * once, and then unlocked afterwards.
X	 */
X
X	if (errors) {
X		fprintf ("%s: error detected, changes ignored\n", Prog);
X		(void) gr_unlock ();
X#ifdef	SHADOWPWD
X		(void) spw_unlock ();
X#endif
X		(void) pw_unlock ();
X		exit (1);
X	}
X#ifdef	SHADOWPWD
X	if (! pw_close () || ! spw_close () || ! gr_close ())
X#else
X	if (! pw_close () || ! gr_close ())
X#endif
X	{
X		fprintf ("%s: error updating files\n", Prog);
X		(void) gr_unlock ();
X#ifdef	SHADOWPWD
X		(void) spw_unlock ();
X#endif
X		(void) pw_unlock ();
X		exit (1);
X	}
X	(void) gr_unlock ();
X#ifdef	SHADOWPWD
X	(void) spw_unlock ();
X#endif
X	(void) pw_unlock ();
X
X	exit (0);
X}
SHAR_EOF
if test 12980 -ne "`wc -c < 'newusers.c'`"
then
	echo shar: "error transmitting 'newusers.c'" '(should have been 12980 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@rpp386.cactus.org
"SCCS, the source motel!  Programs check in and never check out!"
		-- Ken Thompson