#! /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