[alt.sources] Password file conversion utilities for SVR4

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

[ Do not archive this source code.  It is a very preliminary
  version to get some feedback started.  New versions will
  be posted as the code firms up. - jfh. ]

This is the start of the SVR4 version of the shadow login suite
I started several years ago.  This is just the first alpha
release of some code so those of you who are using any of this
will get an advanced chance to look at the new code.  The
important changes are all in shadow.c and shadow.h.  You can
ignore just about everything else for the moment.

You will notice that some files are 3.x versions, and some are
2.x.  When they all get up to 3.x, I will post the entire suite
here.  The conversion from old-format to new-format is just
starting.  I'm sorry this is a moving target right now, but I
hope it will stop moving SOON.  I will soon be posting entire
new password file utilities and libraries.  I think this is a
great improvement over the last release and hope that you will
agree once you get to see the code.

This code implments the pwconv and pwunconv utilities which
come with SVR4.  It will also convert SVR3.2 "old-style"
shadow password files to SVR4 "new-style" format automagically,
not that there is much to change.

I appreciate bug reports, as always.  I would like to thanks
those people who provided the SVR4 password file formats.  I
don't have their names in front of my right now, but they will
be appearing in a README file sometime soon.  I am still
looking for information on extended (long) password formats,
if your vendor is providing you with passwords over 8 characters
in length, I want to know how.
--
#! /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:
#	Makefile
#	age.c
#	config.h
#	pwconv.c
#	pwent.c
#	pwpack.c
#	pwunconv.c
#	shadow.c
#	shadow.h
# This archive created: Fri Nov  9 10:22:06 1990
# By:	John F. Haugh II (River Parishes Programming, Austin TX)
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'Makefile'
then
	echo shar: "will not over-write existing file 'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
#
# Copyright 1988,1989,1990, John F. Haugh II
# All rights reserved.
#
# Non-commercial distribution permitted.  You must provide this source
# code in any distribution.  This notice must remain intact.
#
#	%W%	%U%  - Shadow password system
#
#	%W%	%U%	%G%
#
SHELL = /bin/sh

#
# Set this flag to decide what level of code "get" returns.
# The base USENET release was release 1.  It is no longer supported.
# The unreleased version with the utilities added was release 2.
# The unreleased version with database-like file access is release 3.
RELEASE = 3
GFLAGS = -t -r$(RELEASE)

# Define the directory login is copied to.  BE VERY CAREFUL!!!
# LOGINDIR = /bin
LOGINDIR = /etc

# Pick your favorite C compiler and tags command
CC = cc
TAGS = ctags

# OS.  Currently only BSD and USG are defined.  If you don't use BSD,
# USG (System V) is assumed.
OS = -DUSG
# OS = -DBSD

# Do you have to do ranlib?  Sorry to hear that ...
RANLIB = ranlib
# RANLIB = echo

# Flags for SCO Xenix/386
CFLAGS = -O -M3 -g $(PWDEF) $(AL64DEF) $(OS)
LIBS = -lcrypt -ldbm
LDFLAGS = -M3 -g
LTFLAGS = 
# This should be Slibsec.a for small model, or Llibsec.a for
# large model or whatever.  MUST AGREE WITH CFLAGS!!!
LIBSEC = Slibsec.a

# Flags for normal machines
# CFLAGS = -O -g $(PWDEF) $(AL64DEF) $(OS)
# LIBS =
# LDFLAGS = -g
# LIBSEC = libsec.a

# Rules for .L (lint) files
.SUFFIXES: .L
LINT = lint
LINTFLAGS = $(OS)

.c.L:
	$(LINT) $(LINTFLAGS) $< > $*.L

LOBJS = lmain.o login.o env.o password.o entry.o valid.o setup.o shell.o age.o \
	pwent.o utmp.o sub.o mail.o motd.o log.o shadow.o dialup.o dialchk.o \
	ttytype.o failure.o port.o pwpack.o

LSRCS = lmain.c login.c env.c password.c entry.c valid.c setup.c shell.c age.c \
	pwent.c utmp.c sub.c mail.c motd.c log.c shadow.c dialup.c dialchk.c \
	ttytype.c failure.c port.c pwpack.c

SOBJS = smain.o env.o password.o entry.o suvalid.o susetup.o sushell.o \
	pwent.o susub.o mail.o motd.o sulog.o shadow.o suage.o pwpack.o

SSRCS = smain.c env.c password.c entry.c valid.c setup.c shell.c \
	pwent.c sub.c mail.c motd.c sulog.c shadow.c age.c pwpack.c

POBJS = pmain.o password.o entry.o valid.o pwage.o pwent.o obscure.o shadow.o \
	pwpack.o

PSRCS = pmain.c password.c entry.c valid.c age.c pwent.c obscure.c shadow.c \
	pwpack.c

GPSRCS = gpmain.c password.c grent.c

GPOBJS = gpmain.o password.o grent.o

PWOBJS = pwconv.o pwent.o shadow.o pwage.o pwpack.o

PWSRCS = pwconv.c pwent.c shadow.c age.c pwpack.c

PWUNOBJS = pwunconv.o pwent.o shadow.o pwage.o pwpack.o

PWUNSRCS = pwunconv.c pwent.c shadow.c age.c pwpack.c

SULOGOBJS = sulogin.o entry.o env.o password.o pwage.o pwent.o setup.o \
	shadow.o shell.o valid.o pwpack.o

SULOGSRCS = sulogin.c entry.c env.c password.c age.c pwent.c setup.c \
	shadow.c shell.c valid.c pwpack.c

DBOBJS = mkpasswd.o pwent.o pwpack.o

DBSRCS = mkpasswd.c pwent.c pwpack.c

NGSRCS = newgrp.c shadow.c password.c

NGOBJS = newgrp.o shadow.o password.o

CHFNSRCS = chfn.c pwent.c pwpack.c

CHFNOBJS = chfn.o pwent.o pwpack.o

CHSHSRCS = chsh.c pwent.c pwpack.c

CHSHOBJS = chsh.o pwent.o pwpack.o

CHAGEOBJS = chage.o pwent.o pwpack.o pwage.o shadow.o

CHAGESRCS = chage.c pwent.c pwpack.c age.c shadow.c

ALLSRCS = age.c dialchk.c dialup.c entry.c env.c lmain.c log.c login.c mail.c \
	motd.c obscure.c password.c pmain.c pwconv.c pwent.c pwunconv.c \
	setup.c shadow.c shell.c smain.c sub.c sulog.c sulogin.c ttytype.c \
	utmp.c valid.c port.c newgrp.c gpmain.c grent.c mkpasswd.c pwpack.c \
	chfn.c chsh.c chage.c

FILES1 = README newgrp.c Makefile config.h pwunconv.c obscure.c age.c \
	sub.c login.c shell.c lastlog.h

FILES2 = pmain.c port.c lmain.c mkpasswd.c sulogin.c pwpack.c dialup.c \
	sulog.c password.c env.c mail.c dialchk.c

FILES3 = chfn.c chsh.c smain.c faillog.c pwconv.c failure.c utmp.c shadow.c \
	log.c shadow.h faillog.h

FILES4 = gpmain.c chage.c pwent.c valid.c setup.c entry.c ttytype.c port.h \
	grent.c motd.c dialup.h

MAN_1 = chage.1 chfn.1 chsh.1 login.1 passwd.1 su.1
MAN_3 = shadow.3
MAN_4 = faillog.4 passwd.4 porttime.4 shadow.4
MAN_8 = faillog.8 pwconv.8 pwunconv.8 sulogin.8

DOCS = $(MAN_1) $(MAN_3) $(MAN_4) $(MAN_8)

BINS = su login pwconv pwunconv passwd sulogin faillog newgrp gpasswd \
	mkpasswd chfn chsh chage

all:	$(BINS) $(DOCS)

libsec: shadow.o
	ar rv $(LIBSEC) shadow.o
	$(RANLIB) $(LIBSEC)

install: all
	strip $(BINS)
	cp login $(LOGINDIR)/login
	cp mkpasswd pwconv pwunconv sulogin /etc
	cp su passwd gpasswd faillog newgrp chfn chsh /bin
	cp shadow.h /usr/include
	chown root $(LOGINDIR)/login /etc/pwconv /etc/pwunconv /etc/sulogin \
		/bin/su /bin/passwd /bin/gpasswd /bin/newgrp /etc/mkpasswd
	chgrp root $(LOGINDIR)/login /etc/pwconv /etc/pwunconv /etc/sulogin \
		/bin/su /bin/passwd /bin/gpasswd /bin/newgrp /etc/mkpasswd
	chown bin /bin/faillog /usr/include/shadow.h
	chgrp bin /bin/faillog /usr/include/shadow.h
	chmod 700 /etc/pwconv /etc/pwunconv /etc/sulogin /etc/mkpasswd
	chmod 4711 $(LOGINDIR)/login /bin/su /bin/passwd /bin/gpasswd \
		/bin/newgrp /bin/chfn
	chmod 711 /bin/faillog
	chmod 444 /usr/include/shadow.h

lint:	su.lint login.lint pwconv.lint pwunconv.lint passwd.lint sulogin.lint \
	faillog.lint newgrp.lint gpasswd.lint mkpasswd.lint chfn.lint \
	chsh.lint chage.lint $(ALLSRCS:.c=.L)

tags:	$(ALLSRCS)
	$(TAGS) $(ALLSRCS)

README:	s.README
	get -t -r$(RELEASE) s.README
	
$(DOCS):
	get -t -r$(RELEASE) s.$@

login:	$(LOBJS)
	$(CC) -o login $(LDFLAGS) $(LOBJS) $(LIBS)

login.lint: $(LSRCS)
	$(LINT) $(LINTFLAGS) $(LSRCS) > login.lint

su:	$(SOBJS)
	$(CC) -o su $(LDFLAGS) $(SOBJS) $(LIBS)

su.lint:	$(SSRCS)
	$(LINT) $(LINTFLAGS) -DSU $(SSRCS) > su.lint

passwd:	$(POBJS)
	$(CC) -o passwd $(LDFLAGS) $(POBJS) $(LIBS)

passwd.lint: $(PSRCS)
	$(LINT) $(LINTFLAGS) -DPASSWD $(PSRCS) > passwd.lint

gpasswd: $(GPOBJS)
	$(CC) -o gpasswd $(LDFLAGS) $(GPOBJS) $(LIBS)

gpasswd.lint: $(GPSRCS)
	$(LINT) $(LINTFLAGS) $(GPSRCS) > gpasswd.lint

pwconv:	$(PWOBJS) config.h
	$(CC) -o pwconv $(LDFLAGS) $(PWOBJS) $(LIBS)

pwconv.lint: $(PWSRCS) config.h
	$(LINT) $(LINTFLAGS) -DPASSWD $(PWSRCS) > pwconv.lint

pwunconv: $(PWUNOBJS)
	$(CC) -o pwunconv $(LDFLAGS) $(PWUNOBJS) $(LIBS)

pwunconv.lint: $(PWUNSRCS)
	$(LINT) $(LINTFLAGS) -DPASSWD $(PWUNSRCS) > pwunconv.lint

sulogin: $(SULOGOBJS)
	$(CC) -o sulogin $(LDFLAGS) $(SULOGOBJS) $(LIBS)

sulogin.lint: $(SULOGSRCS)
	$(LINT) $(LINTFLAGS) $(SULOGSRCS) > sulogin.lint

faillog: faillog.o
	$(CC) -o faillog $(LDFLAGS) faillog.o $(LIBS)

faillog.lint: faillog.c faillog.h config.h
	$(LINT) $(LINTFLAGS) faillog.c > faillog.lint

mkpasswd: $(DBOBJS)
	$(CC) -o mkpasswd $(LDFLAGS) $(DBOBJS) $(LIBS)

mkpasswd.lint: $(DBSRCS)
	$(LINT) $(LINTFLAGS) $(DBSRCS) > mkpasswd.lint

newgrp: $(NGOBJS)
	$(CC) -o newgrp $(LDFLAGS) $(NGOBJS) $(LIBS)

newgrp.lint: $(NGSRCS)
	$(LINT) $(LINTFLAGS) $(NGSRCS) > newgrp.lint

chfn:	$(CHFNOBJS)
	$(CC) -o chfn $(LDFLAGS) $(CHFNOBJS) $(LIBS)

chfn.lint:	$(CHFNSRCS)
	$(LINT) $(LINTFLAGS) $(CHFNSRCS) > chfn.lint

chsh:	$(CHSHOBJS)
	$(CC) -o chsh $(LDFLAGS) $(CHSHOBJS) $(LIBS)

chsh.lint: $(CHSHSRCS)
	$(LINT) $(LINTFLAGS) $(CHSHSRCS) > chsh.lint

chage:	$(CHAGEOBJS)
	$(CC) -o chage $(LDFLAGS) $(CHAGEOBJS) $(LIBS)

chage.lint: $(CHAGESRCS)
	$(LINT) $(LINTFLAGS) -DPASSWD $(CHAGESRCS) > chage.lint

sushell.c: shell.c
	cp shell.c sushell.c

sushell.o: config.h sushell.c
	$(CC) -c $(CFLAGS) -DSU sushell.c

susub.c: sub.c
	cp sub.c susub.c

susub.o: config.h susub.c
	$(CC) -c $(CFLAGS) -DSU susub.c

sulog.o: config.h

susetup.c: setup.c
	cp setup.c susetup.c

susetup.o: config.h setup.c
	$(CC) -c $(CFLAGS) -DSU susetup.c

suvalid.c: valid.c
	cp valid.c suvalid.c

suvalid.o: config.h valid.c
	$(CC) -c $(CFLAGS) -DSU suvalid.c

pmain.o: config.h lastlog.h shadow.h

pwage.o: age.c config.h
	cp age.c pwage.c
	$(CC) -c $(CFLAGS) -DPASSWD pwage.c
	rm pwage.c

suage.o: age.c config.h
	cp age.c suage.c
	$(CC) -c $(CFLAGS) -DSU suage.c
	rm suage.c

lmain.o: config.h lastlog.h faillog.h

smain.o: config.h lastlog.h

setup.o: config.h

utmp.o: config.h

mail.o: config.h

motd.o: config.h

age.o: config.h

log.o: config.h lastlog.h

shell.o: config.h

entry.o: config.h shadow.h

shadow.o: shadow.h

dialup.o: dialup.h

dialchk.o: dialup.h config.h

valid.o: config.h

failure.o: faillog.h config.h

faillog.o: faillog.h config.h

pwent.o: config.h

port.o: port.h

newgrp.o: config.h shadow.h

mkpasswd.o: config.h

gpmain.o: config.h

chfn.o: config.h

chsh.o: config.h

chage.o: config.h shadow.h

pwconv.o: config.h shadow.h

pwunconv.o: config.h shadow.h

clean:
	-rm -f *.o a.out core npasswd nshadow *.pag *.dir

clobber: clean
	-rm -f $(BINS) *.lint *.L sushell.c susetup.c susub.c suvalid.c

nuke:	clobber
	-for file in * ; do \
		if [ -f s.$$file -a ! -f p.$$file ] ; then \
			rm -f $$file ;\
		fi ;\
	done

shar:	login.sh.1 login.sh.2 login.sh.3 login.sh.4 login.sh.5

login.sh.1: $(FILES1)
	shar -a $(FILES1) > login.sh.1

login.sh.2: $(FILES2)
	shar -a $(FILES2) > login.sh.2

login.sh.3: $(FILES3)
	shar -a $(FILES3) > login.sh.3

login.sh.4: $(FILES4)
	shar -a $(FILES4) > login.sh.4

login.sh.5: $(DOCS)
	shar -a $(DOCS) > login.sh.5
SHAR_EOF
fi
if test -f 'age.c'
then
	echo shar: "will not over-write existing file 'age.c'"
else
cat << \SHAR_EOF > 'age.c'
/*
 * Copyright 1989, 1990, John F. Haugh II
 * All rights reserved.
 *
 * Use, duplication, and disclosure prohibited without
 * the express written permission of the author.
 */

#include <sys/types.h>
#include <stdio.h>
#include <pwd.h>
#include "config.h"

#ifndef	lint
static	char	_sccsid[] = "@(#)age.c	2.6	10:20:04	11/9/90";
#endif

#ifndef	PASSWD
extern	char	*newenvp[];
#endif

#ifndef	WARNAGE
#define	WARNAGE	10
#endif

time_t	time ();

int	c64i (c)
char	c;
{
	if (c == '.')
		return (0);

	if (c == '/')
		return (1);

	if (c >= '0' && c <= '9')
		return (c - '0' + 2);

	if (c >= 'A' && c <= 'Z')
		return (c - 'A' + 12);

	if (c >= 'a' && c <= 'z')
		return (c - 'a' + 38);
	else
		return (-1);
}

int	i64c (i)
int	i;
{
	if (i < 0)
		return ('.');
	else if (i > 63)
		return ('z');

	if (i == 0)
		return ('.');

	if (i == 1)
		return ('/');

	if (i >= 2 && i <= 11)
		return ('0' - 2 + i);

	if (i >= 12 && i <= 37)
		return ('A' - 12 + i);

	if (i >= 38 && i <= 63)
		return ('a' - 38 + i);

	return ('\0');
}

#ifdef	AGING
#ifdef	NEED_AL64
#ifdef	PASSWD
char	*l64a (l)
long	l;
{
	static	char	buf[8];
	int	i = 0;

	if (i < 0L)
		return ((char *) 0);

	do {
		buf[i++] = i64c ((int) (l % 64));
		buf[i] = '\0';
	} while (l /= 64L, l > 0 && i < 6);

	return (buf);
}
#endif

long	a64l (s)
char	*s;
{
	int	i;
	long	value;
	long	shift = 0;

	for (i = 0, value = 0L;i < 6 && *s;s++) {
		value += (c64i (*s) << shift);
		shift += 6;
	}
	return (value);
}
#endif
#ifndef	PASSWD
void	expire (name, last, min, max)
char	*name;
long	last;
int	min;
int	max;
{
	long	clock;
	long	week;
	long	expires;
	extern	int	errno;

	(void) time (&clock);
	clock /= (24L * 60L * 60L);

	if (min < 0)
		min = 0;

	if (max < 0)
		max = 10000;	/* 10000 is infinity */

	if (last <= 0L)
		expires = 0L;
	else
		expires = last + max;

	if (max < 10000 && (clock >= expires || min == max)) {
#ifndef	SU
		printf ("Your password has expired.");

		if (max < min) {
			puts ("  Contact the system administrator.\n");
			exit (1);
		}
		puts ("  Choose a new one.\n");

		execl ("/bin/passwd", "-passwd", name, (char *) 0);
		puts ("Can't execute /bin/passwd");
		exit (errno);
#else
		printf ("Your password has expired.\n");
#ifdef	SULOG
		sulog (0);
#endif
		exit (1);
#endif
	}
}

void	agecheck (last, min, max, warn)
long	last;
int	min;
int	max;
int	warn;
{
	long	clock = time ((long *) 0) / (24L * 3600);
	long	remain;

	if (last == 0)
		return;

	if ((remain = (last + max) - clock) <= warn)
		printf ("Your password will expire in %d %s.\n",
			remain, remain == 1 ? "day":"days");
}
#endif
#endif
SHAR_EOF
fi
if test -f 'config.h'
then
	echo shar: "will not over-write existing file 'config.h'"
else
cat << \SHAR_EOF > 'config.h'
/*
 * Copyright 1989, 1990, John F. Haugh II
 * All rights reserved.
 *
 * Use, duplication, and disclosure prohibited without
 * the express written permission of the author.
 */

/*
 * Configuration file for login.
 *
 *	@(#)config.h	2.6	08:26:28	8/20/90
 */

/*
 * Define DIALUP to use dialup password files.  Define PORTTIME
 * to use the port time restriction file, see port.h for more
 * information.
 */

#define	DIALUP
#define	PORTTIME

/*
 * Define SHADOWPWD to use shadow [ unreadable ] password file
 */

#define	SHADOWPWD

/*
 * Define DOUBLESIZE to use 16 character passwords
 */

#define DOUBLESIZE

/*
 * Define OBSCURE to include hard password testing code.
 */

#define	OBSCURE

/*
 * Define PASSLENGTH to be shortest legal password
 */

#define	PASSLENGTH	5

/*
 * Define NOBLANK if you want all passwords prompted for, including
 * empty ones.

#undef	NOBLANK

/*
 * Define MAXDAYS to be the default maximum number of days a password
 * is valid for when converting to shadow passwords.  Define MINDAYS
 * to be the minimum number of days before a password may be changed.
 * See pwconv.c for more details.
 */

#define	MAXDAYS	10000
#define	MINDAYS	0

/*
 * Define NDEBUG for production versions
 */

#define	NDEBUG

/*
 * Define HZ if login must set HZ value
 */

#define	HZ	"HZ=50"

/*
 * Define TZ if login must set timezone
 *
 * The first example sets the variable directly.  The
 * second example names a file which is read to determine
 * the proper value.  The file consists of a single line
 * of the form 'TZ=zone-name'
 */

/* #define	TZ	"TZ=CST6CDT" */
#define	TZ	"/etc/tzname"

/*
 * Define the default PATH and SUPATH here.  PATH is for non-privileged
 * users, SUPATH is for root.  The first pair are for real trusting
 * systems, the second pair are for the paranoid ...
 */

/* #define	PATH	"PATH=:/bin:/usr/bin"	*/
/* #define	SUPATH	"PATH=:/bin:/usr/bin:/etc" */
#define	PATH	"PATH=/bin:/usr/bin"
#define	SUPATH	"PATH=/bin:/usr/bin:/etc"

/*
 * Define the mailbox directory
 */

#define	MAILDIR	"/usr/spool/mail/"

/*
 * Define AGING if you want the password aging checks made.
 * Define WARNAGE to be the number of days notice a user receives
 * of a soon to expire password.
 */

#define	AGING
#define	WARNAGE	10

/*
 * Define MAILCHECK if you want the mailbox checked for new mail
 *
 * One of two messages are printed - `You have new mail.' or
 * `You have mail.'.
 */

#define	MAILCHECK

/*
 * Define CONSOLE if you want ROOT restricted to a particular terminal.
 *
 * Use the name of the tty line if you only want a single line, or use
 * the name of the file containing the permissible ports if you wish to
 * allow root logins on more than one port.
 */

/* #define	CONSOLE	"console"	/* root on /dev/console only */
#define	CONSOLE	"/etc/consoles"		/* check /etc/consoles for a list */

/*
 * Define NOLOGINS if you want to be able to deny non-root users logins.
 * Logins will not be permitted if this file exists.
 */

#define	NOLOGINS	"/etc/nologin"

/*
 * Define NOUSE if you want to be able to declare accounts which can't
 * be logged into.  Define NOLOGIN if you want it to be an su-only account.
 */

#define	NOUSE	"NOUSE"
#define	NOLOGIN	"NOLOGIN"

/*
 * Define MOTD if you want the message of the day (/etc/motd) printed
 * at login time.
 */

#define	MOTD

/*
 * Define HUSHLOGIN if you want the code added to avoid printing the
 * motd if a file $HOME/.hushlogin exists.  This obviously only matters
 * if any of MOTD, MAILCHECK or LASTLOG are #define'd.
 */

#define	HUSHLOGIN

/*
 * Define LASTLOG if you want a record made of logins in /usr/adm/lastlog.
 */

#define	LASTLOG

/*
 * Define FAILLOG if you want a record make of failed logins in
 * /usr/adm/faillog.  See faillog.h for more details.  See fail(1L)
 * for even still more details ...  Also, define FTMP to record utmp
 * style records for failed logins.  FTMP is the name of a utmp-like
 * file.  You can use who(1) instead of faillog(L), which is an
 * advantage.  Define UNKNOWNS if you do want unknown user names
 * recorded.  This can be a security hole since passwords are often
 * entered mistakenly as user names.
 */

#define	FAILLOG
#define	FTMP	"/etc/ftmp"
#define	UNKNOWNS

/*
 * Define TTYPERM to be the initial terminal permissions.  Defining
 * as 0600 will not allow messages, 0622 will.
 */

#define	TTYPERM	0600

/*
 * Define TTYTYPE to the be name of the port to terminal type
 * mapping file.  This is used to set the environmental variable
 * "TERM" to the correct terminal type.
 */

#define	TTYTYPE	"/etc/ttytype"

/*
 * Define QUOTAS if you want the code added in setup.c to support
 * file ulimit and nice [ and umask as well ] setting from the password
 * file.
 */

#define	QUOTAS

/*
 * Pick your version of DBM.  Only DBM is presently supported, NDBM will
 * follow.  You must also define the GETPWENT macro below.
 */

#define	DBM

/*
 * Define file name for sulog.  If SULOG is not defined, there will be
 * no logging.  This is NOT a good idea ...  We also define other file
 * names.
 */

#define	SULOG	"/usr/adm/sulog"
#define	SUCON	"/dev/console"
#define	PWDFILE	"/etc/passwd"
#define	OPWDFILE "/etc/-passwd"
#define	NPWDFILE "/etc/npasswd"
#define	OSHADOW "/etc/-shadow"
#define	NSHADOW "/etc/nshadow"
#define	GRPFILE	"/etc/group"
#define	OGRPFILE "/etc/-group"
#define	NGRPFILE "/etc/ngroup"

/*
 * Define PWDLOCK to be a locking semaphore for updating the password
 * file.  GRPLOCK is the same for the group file.
 */

#define	PWDLOCK	"/etc/.pwdlock"
#define	GRPLOCK "/etc/.grplock"

/*
 * Wierd stuff follows ...
 *
 *	The following macros exist solely to override stuff ...
 *	You will probably want to change their values to suit your
 *	fancy.
 */

#define	ERASECHAR	'\b'
#define	KILLCHAR	'\025'
#define	UMASK		022

#define	ULIMIT	(1L<<20) /* Define if your UNIX supports ulimit() */
#define	FGETPWENT	/* Define if library does not include FGETPWENT */
#define	GETPWENT	/* Define if you want my GETPWENT(3) routines */
#define	NEED_AL64	/* Define if library does not include a64l() */
SHAR_EOF
fi
if test -f 'pwconv.c'
then
	echo shar: "will not over-write existing file 'pwconv.c'"
else
cat << \SHAR_EOF > 'pwconv.c'
/*
 * Copyright 1989, 1990, John F. Haugh II
 * All rights reserved.
 *
 * Use, duplication, and disclosure prohibited without
 * the express written permission of the author.
 */

/*
 * pwconv - convert and update shadow password files
 *
 *	Pwconv copies the old password file information to a new shadow
 *	password file, merging entries from an optional existing shadow
 *	file.
 *
 *	The new password file is left in npasswd, the new shadow file is
 *	left in nshadow.  Existing shadow entries are copied as is.
 *	New entries are created with passwords which expire in MAXDAYS days,
 *	with a last changed date of today, unless password aging
 *	information was already present.  Likewise, the minimum number of
 *	days before which the password may be changed is controlled by
 *	MINDAYS.  The number of warning days is set to WARNAGE if that
 *	macro exists.  Entries with blank passwordsare not copied to the
 *	shadow file at all.
 */

#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <pwd.h>
#ifndef	BSD
#include <string.h>
#else
#define	strchr	index
#define	strrchr	rindex
#include <strings.h>
#endif
#include "config.h"
#include "shadow.h"

#ifndef	lint
static	char	_sccsid[] = "@(#)pwconv.c	3.1	08:21:33	11/9/90";
#endif

char	buf[BUFSIZ];

long	time ();
long	a64l ();

int	main ()
{
	long	today;
	struct	passwd	*pw;
	struct	passwd	*sgetpwent ();
	FILE	*pwd;
	FILE	*npwd;
	FILE	*shadow;
	struct	spwd	*spwd;
	struct	spwd	tspwd;
	int	fd;
	char	*cp;

	if (! (pwd = fopen (PWDFILE, "r"))) {
		perror (PWDFILE);
		exit (1);
	}
	unlink ("npasswd");
	if ((fd = open ("npasswd", O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0 ||
			! (npwd = fdopen (fd, "w"))) {
		perror ("npasswd");
		exit (1);
	}
	unlink  ("nshadow");
	if ((fd = open ("nshadow", O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0 ||
			! (shadow = fdopen (fd, "w"))) {
		perror ("nshadow");
		(void) unlink ("npasswd");
		(void) unlink ("nshadow");
		exit (1);
	}

	(void) time (&today);
	today /= (24L * 60L * 60L);

	while (fgets (buf, BUFSIZ, pwd) == buf) {
		if (cp = strrchr (buf, '\n'))
			*cp = '\0';

		if (buf[0] == '#') {	/* comment line */
			(void) fprintf (npwd, "%s\n", buf);
			continue;
		}
		if (! (pw = sgetpwent (buf))) { /* copy bad lines verbatim */
			(void) fprintf (npwd, "%s\n", buf);
			continue;
		}
		if (pw->pw_passwd[0] == '\0') { /* no password, skip */
			(void) fprintf (npwd, "%s\n", buf);
			continue;
		}
		setspent ();		/* rewind old shadow file */

		if (spwd = getspnam (pw->pw_name)) {
			if (putspent (spwd, shadow)) { /* copy old entry */
				perror ("nshadow");
				goto error;
			}
		} else {		/* need a new entry. */
			tspwd.sp_namp = pw->pw_name;
			tspwd.sp_pwdp = pw->pw_passwd;
			pw->pw_passwd = "x";

			if (pw->pw_age) { /* copy old password age stuff */
				if (strlen (pw->pw_age) >= 2) {
					tspwd.sp_min = c64i (pw->pw_age[1]);
					tspwd.sp_max = c64i (pw->pw_age[0]);
				} else {
					tspwd.sp_min = tspwd.sp_max = -1;
				}
				if (strlen (pw->pw_age) == 4)
					tspwd.sp_lstchg = a64l (&pw->pw_age[2]);
				else
					tspwd.sp_lstchg = -1;

				/*
				 * Convert weeks to days
				 */

				if (tspwd.sp_min != -1)
					tspwd.sp_min *= 7;

				if (tspwd.sp_max != -1)
					tspwd.sp_max *= 7;

				if (tspwd.sp_lstchg != -1)
					tspwd.sp_lstchg *= 7;
			} else {	/* fake up new password age stuff */
				tspwd.sp_max = MAXDAYS;
				tspwd.sp_min = MINDAYS;
				tspwd.sp_lstchg = today;
			}
#ifdef	WARNAGE
			tspwd.sp_warn = WARNAGE;
			tspwd.sp_inact = tspwd.sp_expire = tspwd.sp_flag = -1;
#else
			tspwd.sp_warn = tspwd.sp_inact = tspwd.sp_expire =
				tspwd.sp_flag = -1;
#endif
			if (putspent (&tspwd, shadow)) { /* output entry */
				perror ("nshadow");
				goto error;
			}
		}
		(void) fprintf (npwd, "%s:%s:%d:%d:%s:%s:",
				pw->pw_name, pw->pw_passwd,
				pw->pw_uid, pw->pw_gid,
				pw->pw_gecos, pw->pw_dir);

		if (fprintf (npwd, "%s\n",
				pw->pw_shell ? pw->pw_shell:"") == EOF) {
			perror ("npasswd");
			goto error;
		}
	}
	endspent ();

	if (ferror (npwd) || ferror (shadow)) {
		perror ("pwconv");
error:
		(void) unlink ("npasswd");
		(void) unlink ("nshadow");
		exit (1);
	}
	(void) fclose (pwd);
	(void) fclose (npwd);
	(void) fclose (shadow);

	exit (0);
}
SHAR_EOF
fi
if test -f 'pwent.c'
then
	echo shar: "will not over-write existing file 'pwent.c'"
else
cat << \SHAR_EOF > 'pwent.c'
/*
 * Copyright 1989, 1990, John F. Haugh II
 * All rights reserved.
 *
 * Use, duplication, and disclosure prohibited without
 * the express written permission of the author.
 *
 * Duplication is permitted for non-commercial [ profit making ]
 * purposes provided this and other copyright notices remain
 * intact.
 */

#include <stdio.h>
#include <pwd.h>
#include <string.h>
#include "config.h"

#ifdef	DBM
#include <dbm.h>
#endif

#ifndef	lint
static	char	_sccsid[] = "@(#)pwent.c	2.4	23:41:33	10/28/90";
#endif

#define	SBUFSIZ	64
#define	NFIELDS	7

static	FILE	*pwdfp;
static	char	pwdbuf[BUFSIZ];
static	char	*pwdfile = "/etc/passwd";
#ifdef	DBM
static	int	dbmopened;
static	int	dbmerror;
#endif
static	char	*pwdfields[NFIELDS];
static	struct	passwd	pwent;

/*
 * sgetpwent - convert a string to a (struct passwd)
 *
 * sgetpwent() parses a string into the parts required for a password
 * structure.  Strict checking is made for the UID and GID fields and
 * presence of the correct number of colons.  Any failing tests result
 * in a NULL pointer being returned.
 */

struct	passwd	*sgetpwent (buf)
char	*buf;
{
	int	i;
	char	*cp;

	/*
	 * Copy the string to a static buffer so the pointers into
	 * the password structure remain valid.
	 */

	strncpy (pwdbuf, buf, BUFSIZ);
	pwdbuf[BUFSIZ-1] = '\0';

	/*
	 * Save a pointer to the start of each colon separated
	 * field.  The fields are converted into NUL terminated strings.
	 */

	for (cp = pwdbuf, i = 0;i < NFIELDS && cp;i++) {
		pwdfields[i] = cp;
		if (cp = strchr (cp, ':'))
			*cp++ = 0;
	}

	/*
	 * There must be exactly NFIELDS colon separated fields or
	 * the entry is invalid.  Also, the UID and GID must be non-blank.
	 */

	if (i != NFIELDS || *pwdfields[2] == '\0' || *pwdfields[3] == '\0')
		return 0;

	/*
	 * Each of the fields is converted the appropriate data type
	 * and the result assigned to the password structure.  If the
	 * UID or GID does not convert to an integer value, a NULL
	 * pointer is returned.
	 */

	pwent.pw_name = pwdfields[0];
	pwent.pw_passwd = pwdfields[1];
	if ((pwent.pw_uid = strtol (pwdfields[2], &cp, 10)) == 0 && *cp)
		return 0;

	if ((pwent.pw_gid = strtol (pwdfields[3], &cp, 10)) == 0 && *cp)
		return 0;

	if (cp = strchr (pwent.pw_passwd, ',')) {
		pwent.pw_age = cp + 1;
		*cp = '\0';
	} else
		pwent.pw_age = "";

	pwent.pw_gecos = pwdfields[4];
	pwent.pw_dir = pwdfields[5];
	pwent.pw_shell = pwdfields[6];

	return (&pwent);
}
#ifdef FGETPWENT
/*
 * fgetpwent - get a password file entry from a stream
 *
 * fgetpwent() reads the next line from a password file formatted stream
 * and returns a pointer to the password structure for that line.
 */

struct	passwd	*fgetpwent (fp)
FILE	*fp;
{
	char	buf[BUFSIZ];

	while (fgets (buf, BUFSIZ, fp) != (char *) 0) {
		buf[strlen (buf) - 1] = '\0';
		return (sgetpwent (buf));
	}
	return 0;
}
#endif
#ifdef	GETPWENT

/*
 * endpwent - close a password file
 *
 * endpwent() closes the password file if open.
 */

int	endpwent ()
{
	if (pwdfp)
		if (fclose (pwdfp))
			return -1;

	return 0;
}

/*
 * getpwent - get a password entry from the password file
 *
 * getpwent() opens the password file, if not already opened, and reads
 * a single entry.  NULL is returned if any errors are encountered reading
 * the password file.
 */

struct	passwd	*getpwent ()
{
	if (! pwdfp && setpwent ())
		return 0;

	return fgetpwent (pwdfp);
}

/*
 * getpwuid - locate the password entry for a given UID
 *
 * getpwuid() locates the first password file entry for the given UID.
 * If there is a valid DBM file, the DBM files are queried first for
 * the entry.  Otherwise, a linear search is begun of the password file
 * searching for an entry which matches the provided UID.
 */

struct	passwd	*getpwuid (uid)
int	uid;
{
	struct	passwd	*pwd;
#ifdef	DBM
	datum	key;
	datum	content;

	/*
	 * Attempt to open the DBM files if they have never been opened
	 * and an error has never been returned.
	 */

	if (! dbmerror && ! dbmopened) {
		char	dbmfiles[BUFSIZ];

		strcpy (dbmfiles, pwdfile);
		strcat (dbmfiles, ".pag");

		if (access (dbmfiles, 0) || dbminit (pwdfile))
			dbmerror = 1;
		else
			dbmopened = 1;
	}

	/*
	 * If the DBM file are now open, create a key for this UID and
	 * try to fetch the entry from the database.  A matching record
	 * will be unpacked into a static structure and returned to
	 * the user.
	 */

	if (dbmopened) {
		pwent.pw_uid = uid;
		key.dsize = sizeof pwent.pw_uid;
		key.dptr = (char *) &pwent.pw_uid;
		content = fetch (key);
		if (content.dptr != 0) {
			memcpy (pwdbuf, content.dptr, content.dsize);
			pw_unpack (pwdbuf, content.dsize, &pwent);
			return &pwent;
		}
	}
#endif
	/*
	 * Rewind the database and begin searching for an entry which
	 * matches the UID.  Return the entry when a match is found.
	 */

	if (setpwent ())
		return 0;

	while (pwd = getpwent ())
		if (pwd->pw_uid == uid)
			return pwd;

	return 0;
}

struct	passwd	*getpwnam (name)
char	*name;
{
	struct	passwd	*pwd;
#ifdef	DBM
	datum	key;
	datum	content;

	/*
	 * Attempt to open the DBM files if they have never been opened
	 * and an error has never been returned.
	 */

	if (! dbmerror && ! dbmopened) {
		char	dbmfiles[BUFSIZ];

		strcpy (dbmfiles, pwdfile);
		strcat (dbmfiles, ".pag");

		if (access (dbmfiles, 0) || dbminit (pwdfile))
			dbmerror = 1;
		else
			dbmopened = 1;
	}

	/*
	 * If the DBM file are now open, create a key for this UID and
	 * try to fetch the entry from the database.  A matching record
	 * will be unpacked into a static structure and returned to
	 * the user.
	 */

	if (dbmopened) {
		key.dsize = strlen (name);
		key.dptr = name;
		content = fetch (key);
		if (content.dptr != 0) {
			memcpy (pwdbuf, content.dptr, content.dsize);
			pw_unpack (pwdbuf, content.dsize, &pwent);
			return &pwent;
		}
	}
#endif
	/*
	 * Rewind the database and begin searching for an entry which
	 * matches the name.  Return the entry when a match is found.
	 */

	if (setpwent ())
		return 0;

	while (pwd = getpwent ())
		if (strcmp (pwd->pw_name, name) == 0)
			return pwd;

	return 0;
}

/*
 * setpwent - open the password file
 *
 * setpwent() opens the system password file, and the DBM password files
 * if they are present.  The system password file is rewound if it was
 * open already.
 */

int	setpwent ()
{
	if (! pwdfp) {
		if (! (pwdfp = fopen (pwdfile, "r")))
			return -1;
	} else {
		if (fseek (pwdfp, 0L, 0) != 0)
			return -1;
	}
#ifdef	DBM
	/*
	 * Attempt to open the DBM files if they have never been opened
	 * and an error has never been returned.
	 */

	if (! dbmerror && ! dbmopened) {
		char	dbmfiles[BUFSIZ];

		strcpy (dbmfiles, pwdfile);
		strcat (dbmfiles, ".pag");

		if (access (dbmfiles, 0) || dbminit (pwdfile))
			dbmerror = 1;
		else
			dbmopened = 1;
	}
#endif
	return 0;
}
#endif
SHAR_EOF
fi
if test -f 'pwpack.c'
then
	echo shar: "will not over-write existing file 'pwpack.c'"
else
cat << \SHAR_EOF > 'pwpack.c'
/*
 * Copyright 1990, John F. Haugh II
 * All rights reserved.
 *
 * Use, duplication, and disclosure prohibited without
 * the express written permission of the author.
 *
 * Duplication is permitted for non-commercial [ profit making ]
 * purposes provided this and other copyright notices remain
 * intact.
 */

#include <stdio.h>
#include <pwd.h>
#ifdef	BSD
#include <strings.h>
#else
#include <string.h>
#endif

#ifndef	lint
static	char	sccsid[] = "@(#)pwpack.c	2.3	23:06:29	8/5/90";
#endif

int	pw_pack (passwd, buf)
struct	passwd	*passwd;
char	*buf;
{
	char	*cp;

	cp = buf;
	strcpy (cp, passwd->pw_name);
	cp += strlen (cp) + 1;

	strcpy (cp, passwd->pw_passwd);
	if (passwd->pw_age && passwd->pw_age[0]) {
		cp += strlen (cp);
		*cp++ = ',';
		strcpy (cp, passwd->pw_age);
	}
	cp += strlen (cp) + 1;

	memcpy (cp, (void *) &passwd->pw_uid, sizeof passwd->pw_uid);
	cp += sizeof passwd->pw_uid;

	memcpy (cp, (void *) &passwd->pw_gid, sizeof passwd->pw_gid);
	cp += sizeof passwd->pw_gid;

	strcpy (cp, passwd->pw_gecos);
	cp += strlen (cp) + 1;

	strcpy (cp, passwd->pw_dir);
	cp += strlen (cp) + 1;

	strcpy (cp, passwd->pw_shell);
		cp += strlen (cp) + 1;

	return cp - buf;
}

int	pw_unpack (buf, len, passwd)
char	*buf;
int	len;
struct	passwd	*passwd;
{
	char	*org = buf;
	char	*cp;

	passwd->pw_name = buf;
	buf += strlen (buf) + 1;
	if (buf - org > len)
		return -1;

	passwd->pw_passwd = buf;
	buf += strlen (buf) + 1;
	if (buf - org > len)
		return -1;

	if (cp = strchr (passwd->pw_passwd, ',')) {
		*cp++ = '\0';
		passwd->pw_age = cp;
	} else
		passwd->pw_age = "";

	memcpy ((void *) &passwd->pw_uid, (void *) buf, sizeof passwd->pw_uid);
	buf += sizeof passwd->pw_uid;
	if (buf - org > len)
		return -1;

	memcpy ((void *) &passwd->pw_gid, (void *) buf, sizeof passwd->pw_gid);
	buf += sizeof passwd->pw_gid;
	if (buf - org > len)
		return -1;

	passwd->pw_gecos = buf;
	buf += strlen (buf) + 1;
	if (buf - org > len)
		return -1;

	passwd->pw_dir = buf;
	buf += strlen (buf) + 1;
	if (buf - org > len)
		return -1;

	passwd->pw_shell = buf;
	buf += strlen (buf) + 1;
	if (buf - org > len)
		return -1;

	return 0;
}
SHAR_EOF
fi
if test -f 'pwunconv.c'
then
	echo shar: "will not over-write existing file 'pwunconv.c'"
else
cat << \SHAR_EOF > 'pwunconv.c'
/*
 * Copyright 1989, 1990, John F. Haugh II
 * All rights reserved.
 *
 * Use, duplication, and disclosure prohibited without
 * the express written permission of the author.
 */

/*
 * pwunconv - restore old password file from shadow password file.
 *
 *	Pwunconv copies the password file information from the shadow
 *	password file, merging entries from an optional existing shadow
 *	file.
 *
 *	The new password file is left in npasswd.  There is no new
 *	shadow file.  Password aging information is translated where
 *	possible.
 */

#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <pwd.h>
#include "config.h"
#include "shadow.h"

#ifndef	lint
static	char	_sccsid[] = "@(#)pwunconv.c	3.1	08:47:18	11/9/90";
#endif

char	buf[BUFSIZ];
char	*l64a ();

int	main ()
{
	struct	passwd	*pw;
	struct	passwd	*sgetpwent ();
	FILE	*pwd;
	FILE	*npwd;
	struct	spwd	*spwd;
	int	fd;
	char	newage[5];

	if (! (pwd = fopen (PWDFILE, "r"))) {
		perror (PWDFILE);
		return (1);
	}
	unlink ("npasswd");
	if ((fd = open ("npasswd", O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0 ||
			! (npwd = fdopen (fd, "w"))) {
		perror ("npasswd");
		return (1);
	}
	while (fgets (buf, BUFSIZ, pwd) == buf) {
		buf[strlen (buf) - 1] = '\0'; /* remove '\n' character */

		if (buf[0] == '#') {	/* comment line */
			(void) fprintf (npwd, "%s\n", buf);
			continue;
		}
		if (! (pw = sgetpwent (buf))) { /* copy bad lines verbatim */
			(void) fprintf (npwd, "%s\n", buf);
			continue;
		}
		setspent ();		/* rewind shadow file */

		if (! (spwd = getspnam (pw->pw_name))) {
			(void) fprintf (npwd, "%s\n", buf);
			continue;
		}
		pw->pw_passwd = spwd->sp_pwdp;

	/*
	 * Password aging works differently in the two different systems.
	 * With shadow password files you apparently must have some aging
	 * information.  The maxweeks or minweeks may not map exactly.
	 * In pwconv we set max == 10000, which is about 30 years.  Here
	 * we have to undo that kludge.  So, if maxdays == 10000, no aging
	 * information is put into the new file.  Otherwise, the days are
	 * converted to weeks and so on.
	 */

		if (spwd->sp_max > (63*7) && spwd->sp_max < 10000)
			spwd->sp_max = (63*7); /* 10000 is infinity this week */

		if (spwd->sp_min >= 0 && spwd->sp_min <= 63*7 &&
				spwd->sp_max >= 0 && spwd->sp_max <= 63*7) {
			if (spwd->sp_lstchg == -1)
				spwd->sp_lstchg = clock ((long *) 0) /
					(24L*60L*60L);

			spwd->sp_max /= 7;	/* turn it into weeks */
			spwd->sp_min /= 7;
			spwd->sp_lstchg /= 7;

			strncpy (newage, l64a (spwd->sp_lstchg * (64L*64L) +
				  spwd->sp_min * (64L) + spwd->sp_max), 5);
			pw->pw_age = newage;
		} else
			pw->pw_age = "";

		if (putpwent (pw, npwd)) {
			perror (stderr, "pwunconv: write error");
			exit (1);
		}
	}
	endspent ();

	if (ferror (npwd)) {
		perror ("pwunconv");
		(void) unlink ("npasswd");
	}
	(void) fclose (npwd);
	(void) fclose (pwd);
	return (0);
}
SHAR_EOF
fi
if test -f 'shadow.c'
then
	echo shar: "will not over-write existing file 'shadow.c'"
else
cat << \SHAR_EOF > 'shadow.c'
/*
 * Copyright 1989, 1990, John F. Haugh II
 * All rights reserved.
 *
 * Use, duplication, and disclosure prohibited without
 * the express written permission of the author.
 */

#include "shadow.h"
#include <stdio.h>
#ifndef	BSD
#include <string.h>
#include <memory.h>
#else
#include <strings.h>
#define	strchr	index
#define	strrchr	rindex
#endif

#ifndef	lint
static	char	_sccsid[] = "@(#)shadow.c	3.1	08:12:34	11/9/90";
#endif

static	FILE	*shadow;
#define	FIELDS	9
#define	OFIELDS	5

void
setspent ()
{
	if (shadow)
		rewind (shadow);
	else
		shadow = fopen (SHADOW, "r");
}

void
endspent ()
{
	if (shadow)
		(void) fclose (shadow);

	shadow = (FILE *) 0;
}

struct spwd *
sgetspent (string)
char	*string;
{
	static	char	buf[BUFSIZ];
	static	struct	spwd	spwd;
	char	*fields[FIELDS];
	char	*cp;
	char	*cpp;
	int	atoi ();
	long	atol ();
	int	i;

	strncpy (buf, string, BUFSIZ-1);
	buf[BUFSIZ-1] = '\0';

	if (cp = strrchr (buf, '\n'))
		*cp = '\0';

	for (cp = buf, i = 0;*cp && i < FIELDS;i++) {
		fields[i] = cp;
		while (*cp && *cp != ':')
			cp++;

		if (*cp)
			*cp++ = '\0';
	}
	if (*cp || (i != FIELDS && i != OFIELDS))
		return 0;

	spwd.sp_namp = fields[0];
	spwd.sp_pwdp = fields[1];

	if ((spwd.sp_lstchg = strtol (fields[2], &cpp, 10)) == 0 && *cpp)
		if (fields[2][0] == '\0')
			spwd.sp_lstchg = -1;
		else
			return 0;

	if ((spwd.sp_min = strtol (fields[3], &cpp, 10)) == 0 && *cpp)
		if (fields[3][0] == '\0')
			spwd.sp_min = -1;
		else
			return 0;

	if ((spwd.sp_max = strtol (fields[4], &cpp, 10)) == 0 && *cpp)
		if (fields[4][0] == '\0')
			spwd.sp_max = -1;
		else
			return 0;

	if (i == OFIELDS) {
		spwd.sp_warn = spwd.sp_inact = spwd.sp_expire =
			spwd.sp_flag = -1;

		return &spwd;
	}
	if ((spwd.sp_warn = strtol (fields[5], &cpp, 10)) == 0 && *cpp)
		if (fields[5][0] == '\0')
			spwd.sp_warn = -1;
		else
			return 0;

	if ((spwd.sp_inact = strtol (fields[6], &cpp, 10)) == 0 && *cpp)
		if (fields[6][0] == '\0')
			spwd.sp_inact = -1;
		else
			return 0;

	if ((spwd.sp_expire = strtol (fields[7], &cpp, 10)) == 0 && *cpp)
		if (fields[7][0] == '\0')
			spwd.sp_expire = -1;
		else
			return 0;

	if ((spwd.sp_flag = strtol (fields[8], &cpp, 10)) == 0 && *cpp)
		if (fields[8][0] == '\0')
			spwd.sp_flag = -1;
		else
			return 0;

	return (&spwd);
}

struct spwd
*fgetspent (fp)
FILE	*fp;
{
	char	buf[BUFSIZ];

	if (! fp)
		return (0);

	if (fgets (buf, BUFSIZ, fp) == (char *) 0)
		return (0);

	return sgetspent (buf);
}

struct spwd
*getspent ()
{
	if (! shadow)
		setspent ();

	return (fgetspent (shadow));
}

struct spwd
*getspnam (name)
char	*name;
{
	struct	spwd	*spwd;

	setspent ();

	while ((spwd = getspent ()) != (struct spwd *) 0) {
		if (strcmp (name, spwd->sp_namp) == 0)
			return (spwd);
	}
	return (0);
}

int
putspent (spwd, fp)
struct	spwd	*spwd;
FILE	*fp;
{
	int	errors = 0;

	if (! fp || ! spwd)
		return -1;

	if (fprintf (fp, "%s:%s:", spwd->sp_namp, spwd->sp_pwdp) < 0)
		errors++;

	if (spwd->sp_lstchg != -1) {
		if (fprintf (fp, "%ld:", spwd->sp_lstchg) < 0)
			errors++;
	} else if (putc (':', fp) == EOF)
		errors++;

	if (spwd->sp_min != -1) {
		if (fprintf (fp, "%ld:", spwd->sp_min) < 0)
			errors++;
	} else if (putc (':', fp) == EOF)
		errors++;

	if (spwd->sp_max != -1) {
		if (fprintf (fp, "%ld:", spwd->sp_max) < 0)
			errors++;
	} else if (putc (':', fp) == EOF)
		errors++;

	if (spwd->sp_warn != -1) {
		if (fprintf (fp, "%ld:", spwd->sp_warn) < 0)
			errors++;
	} else if (putc (':', fp) == EOF)
		errors++;

	if (spwd->sp_inact != -1) {
		if (fprintf (fp, "%ld:", spwd->sp_inact) < 0)
			errors++;
	} else if (putc (':', fp) == EOF)
		errors++;

	if (spwd->sp_expire != -1) {
		if (fprintf (fp, "%ld:", spwd->sp_expire) < 0)
			errors++;
	} else if (putc (':', fp) == EOF)
		errors++;

	if (spwd->sp_flag != -1) {
		if (fprintf (fp, "%ld:", spwd->sp_flag) < 0)
			errors++;
	} else if (putc (':', fp) == EOF)
		errors++;

	if (putc ('\n', fp) == EOF)
		errors++;

	if (errors)
		return -1;
	else
		return 0;
}
SHAR_EOF
fi
if test -f 'shadow.h'
then
	echo shar: "will not over-write existing file 'shadow.h'"
else
cat << \SHAR_EOF > 'shadow.h'
/*
 * Copyright 1988, 1989, 1990, John F. Haugh II
 * All rights reserved.
 *
 * Use, duplication, and disclosure prohibited without
 * the express written permission of the author.
 */

/*
 * This information is not derived from AT&T licensed sources.  Posted
 * to the USENET 11/88, and updated 11/90 with information from SVR4.
 *
 *	@(#)shadow.h	3.1	10:14:23	11/9/90
 */

/*
 * Shadow password security file structure.
 */

struct	spwd {
	char	*sp_namp;	/* login name */
	char	*sp_pwdp;	/* encrypted password */
	long	sp_lstchg;	/* date of last change */
	long	sp_min;		/* minimum number of days between changes */
	long	sp_max;		/* maximum number of days between changes */
	long	sp_warn;	/* number of days of warning before password
				   expires */
	long	sp_inact;	/* number of days after password expires
				   until the account becomes unusable. */
	long	sp_expire;	/* days since 1/1/70 until account expires */
	unsigned long	sp_flag; /* reserved for future use */
};

/*
 * Shadow password security file functions.
 */

struct	spwd	*getspent ();
struct	spwd	*getspnam ();
struct	spwd	*sgetspent ();
void	setspent ();
void	endspent ();
struct	spwd	*fgetspent ();
int	putspent ();

#define  SHADOW "/etc/shadow"
SHAR_EOF
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