[alt.sources] Shadow login release 2

jfh@rpp386.Dallas.TX.US (John F. Haugh II) (06/20/89)

X-Archive-Name: shadow2/part0

The next three articles, hopefully, should constitute the
beta release of the second version of my shadow login
implementation.  The previous version is executing on
approximately 200 machines of a dozen or more configurations.

All known bugs have been corrected and a number of features
have been added.  Please read config.h and README for
details.  Minor anomolies exist in the handling of subsystem
logins [ I don't know exactly how they should work for the
different systems ].  Also, Altos and UNIX-PCs have some
serious problems.  Down with bogus UNIX implementations!

Major changes include the addition of some BSD code.  This
still needs much work.  The handling of dialup password,
console restriction, and general password failure is now
more standardized.  I would like someone to add code to
handle remote logins.

This code is presently in use on this system.  Most all
of the options have been turned on.  Problems may arise
if you use some combination of flags I haven't considered.
Please tell me your configuration when reporting bugs.
-- 
John F. Haugh II                        +-Button of the Week Club:-------------
VoiceNet: (512) 832-8832   Data: -8835  | "AIX is a three letter word,
InterNet: jfh@rpp386.Cactus.Org         |  and it's BLUE."
UucpNet : <backbone>!bigtex!rpp386!jfh  +--------------------------------------

jfh@rpp386.Dallas.TX.US (John F. Haugh II) (06/20/89)

X-Archive-Name: shadow2/part1

Part 1 of second USENET release
--
#! /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
#	log.c
#	mail.c
#	shadow.h
#	sulog.c
#	Makefile
#	entry.c
#	obscure.c
#	setup.c
#	sub.c
#	config.h
#	pmain.c
#	sulogin.c
#	dialup.h
#	ttytype.c
# This archive created: Tue Jun 20 01:28:03 1989
# By:	John F. Haugh II (River Parishes Programming, Plano TX)
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'README'
then
	echo shar: "will not over-write existing file 'README'"
else
cat << \SHAR_EOF > 'README'
This is the explanatory document for John F. Haugh II's login replacement.

This software is copyright 1989, John F. Haugh II.  All rights reserved.
Use, duplication and disclosure is permitted according to the guidelines
listed below.  At some point in the future this licence will be
modified to conform to the GNU General Public License.

This software is being provided as a freely redistributable login clone.
You may distribute this software provided you do not charge for other than
transmission costs.  You are free to transfer this software provided you
do not restrict the rights of the recipients to further transfer this 
software.

This software is being distributed AS-IS.  The author disclaims all
liability for any consequences of use.  The user is solely responsible
for the maintenance of this software package.  The author is under no
obligation to provide modifications or improvements.

Begin by reading and editing the config.h file.  All options are selected
by using #define's.  A brief description for each available option appears
below.  You may want to print this file out as it is LONG and you will
need to refer to it while editting config.h.  You will also have to edit
the Makefile.  The possible differences are documented there.  Pay close
attention to the install: rule.  DO NOT MAIL ME DIFFERENCES FOR VARIOUS
INSTALLATION PROBLEMS.  If you must share your experiences, do so on the
net.  Login now runs on about 15 different varieties of UNIX that I have
been made aware of.

Note that there are MANY options.  As distributed most options are turned
on, which produces a really nice package.  This is the system as used on
the authors machine.  [ The one exception is NOBLANK, that is turned off
because of anonymous UUCP logins. ]

Dialup Password Files -
	This option permits individual ports to have an additional
	password prompted for on a by-shell basis.  /etc/dialups
	contains a list of dialup ports, d_passwd contains the
	password for each shell requiring a dialup password.

	Select this option by defining the DIALUP macro.

Shadow [ unreadable ] Password Files -
	This option utilizes an alternate, non-readable file to
	contain the actual encrypted passwords.  This is presumed
	to increase system security by increasing the difficulty
	with which system crackers obtain encrypted passwords.

	Select this option by defining the SHADOWPWD macro.

Double Length Passwords -
	This option extends the maximum length of a user password
	to 16 characters from eight.

	Select this option by defining the DOUBLESIZE macro.
	Credit for this option is due Jonathan Bayer.

Obscure Password Testing -
	This option includes code to test user passwords for
	complexity.  The programmer is encouraged to edit the
	file obscure.c to add additional methods for detecting
	simplistic passwords.

	Select this option by defining the OBSCURE macro.

	Additionally, the PASSLENGTH macro must be defined to
	control the minimum length for a legal password.

Mandatory Password Prompting -
	This option requires all passwords, including null ones,
	to be prompted for.  Traditionally an account with a
	password field of '::' does not require prompting for.
	This option modifies this behavior to require even
	null passwords be prompted for.

	Select this option by defining the NOBLANK macro.

Password Aging Defaults -
	You may select the default number of days during which a
	password is valid.  The pwconv command adds aging
	information to accounts which do not include it already.

	The MINDAYS macro must be defined to be the minimum
	number of days which must pass before a password may be
	changed.  The MAXDAYS macro must be defined to be the
	maximum number of days which a password will remain
	valid during.

HZ Environmental Variable -
	This option pre-defines the HZ environmental variable.
	Certain systems require this variable be defined for
	system time reporting functions to work properly.

	Select this option by defining the HZ macro to have
	the desired environmental variable value.

TZ Environmental Variable -
	This option pre-defines the TZ environmental variable.
	This provides a default timezone variable for use by
	various utilities.

	Select this option by defining the TZ macro to have
	the desired environmental variable value, or the name
	of the file containing the desired value.

Password Aging -
	This option includes code to perform password aging.
	Password aging is presumed to increase system security
	by forcing users to change passwords on a regular
	basis.  The resolution on password age is in weeks for
	non-shadow password systems and in days otherwise.

	Select this option by defining the AGING macro.

Mailbox Checking -
	This option includes code to check the status of the
	user's mailbox.  One of three messages are produced
	depending on the status of the user's mailbox.

	Select this option by defining the MAILCHECK macro.

Console Restricted Root Logins -
	This option restricts the port which root may legally
	login on.  This option presumably increases system
	security by preventing outside attacks against the root
	account.

	Select this option by defining the CONSOLE macro to
	have the desired port name.  If this file is a regular
	file, it is considered to contain a list of legal port
	names, one per line.  Note that the port names DO NOT
	begin with "/dev/" and that a file name would have to
	be fully qualified.  See config.h for a pair of
	examples.

Restricted User Logins -
	This option permits you to specify a file which disables
	user logins.  This options permits you to keep normal
	users off of the system while performing maintenance
	functions.

	Select this option by defining NOLOGINS to be the name
	of the file to use.

Restricted Use Accounts -
	This option permits certain accounts to be used for
	identification purposes only.  This options associates
	login ID's with UID's, such as for disk space accounting
	or anonymous FTP accounts.  Passwords for these accounts
	may only be changed by root.

	Select this option by defining NOUSE to be the string
	to include in the password file in place of the user's
	shell.

Message of the Day Printing -
	This option causes the message of the day to be
	printed at login time.

	Select this option by defining the MOTD macro.

	If you wish this feature to be overriden on a per-user
	basis, define the macro HUSHLOGIN and users may then
	turn off the /etc/motd message by creating a file
	'.hushlogin' in their home directories.

Last Login Time Logging -
	This option causes a record to be made of successful
	logins in /usr/adm/lastlog.  The format of the
	structure is defined in lastlog.h.

	Select this option by defining the LASTLOG macro.

	You will need to determine if you system already has
	a lastlog.h file and use that file if present.

Failed Login Logging -
	This option causes a record to be kept of the most
	recent login failure by date and port.  A cummulative
	count of failures is maintained and compared against
	an allowable limit.

	Select this option by defining the FAILLOG macro.

	See the file faillog.h for more details.
Terminal Permissions - 
	This option allows the terminal modes to be set at
	login time.  This is particularly useful to disable
	messages on user's terminals.

	Select this option by defining the TTYPERM macro as
	having the desired mode.

Terminal Type Setup -
	This option allows the terminal type to be set at
	login time.  The environmental variable TERM will be
	set from the specified terminal to port mapping
	file.

	Select this option by defining the TTYTYPE macro as
	having the value of the name of the type to port
	mapping file.  Credit for this option is due Chip
	Rosenthal.

File Size Setting -
	This option includes code to set the user's ulimit
	at login time.  Additional code to set the umask and
	nice value is also included.

	Select this option by defining the QUOTAS macro.

Switch-User Logging -
	This option causes su(1) to log attempts to switch
	users.  Su(1) will log all attempt, giving the old
	and new user ID's, tty port, and time.  It also
	indicates if the attempt was successful.

	Select this option by defining the SULOG macro to
	have the value of the name of the file you want
	attempts logged to.

Configurable Editing Keys -
	This options allows the erase and kill characters to
	be selected.  A default value is provided.  By default
	ERASE will be ^H and KILL will be ^U.

	Select this option by defining the ERASECHAR macro
	to be the desired erase character and the KILLCHAR 
	macro to be the desired KILL character.

Default ulimit and umask Values -
	This option allows you to select the default values
	for ulimit and umask, allowing you to avoid
	regenerating your system kernel.  These values may be
	overriden with appropriate entries in the GECOS field.

	Select the default ulimit by defining the ULIMIT
	macro, and the default umask by defining the UMASK
	macro.
	
	Warning: These values will not apply to processes
	executed by /etc/cron or any of their children.

BSD Notes:	Steve Simmons	scs@iti.org

The full port of the shadow package to BSD is not complete; but some
of the issues have been worked out.  These notes describe the current
state of things:

In order to make use of password aging under BSD, minor changes to
/usr/include/pwd.h and getpwent() are needed.  These changes are to
keep the password age from messing up the encrypted password when not
using shadow passwords, and involve placing a new field in the password
data structure.  To use this, you should apply the following two patches:
	pwd.h.patch
	getpwent.c.patch
to the BSD /usr/include/pwd.h and /usr/src/lib/libc/gen/getpwent.c,
respectively.  After applying the patches, rebuild your standard C
library with the new getpwent.  Programs which use the old getpwent
will fail on password checking if they do a strcmp rather than a strncmp.

These changes are based on BSD4.3, not Tahoe

ToDo BSD:

I'm working on this in my copious spare time (hah!); any help would
be appreciated.  If you decide to help, do these independantly rather
than rework BSD code!  Keep it redistributable!

No dbm functions have been put in place.  Dbm functionality is needed
for both /etc/password and /etc/shadow management.

The BSD GECOS field gets used for lots more stuff than the USG.  At a
minimum this functionality should be duplicated under BSD; better is to put
it into USG as well; still better would be to make the chfn command for
both systems; best would be site-configurable data to be put into GECOS/chfn.
SHAR_EOF
fi
if test -f 'log.c'
then
	echo shar: "will not over-write existing file 'log.c'"
else
cat << \SHAR_EOF > 'log.c'
#include <sys/types.h>
#include <utmp.h>
#include <pwd.h>
#include <fcntl.h>
#include <time.h>
#ifndef	BSD
#include <string.h>
#include <memory.h>
#else
#include <strings.h>
#define	strchr	index
#define	strrchr	rindex
#endif
#include "config.h"

#ifndef	lint
static	char	_sccsid[] = "@(#)log.c	2.1	01:23:21	6/20/89";
#endif

#ifdef	LASTLOG

#include "lastlog.h"

extern	struct	utmp	utent;
extern	struct	passwd	pwent;
extern	struct	lastlog	lastlog;
extern	char	**environ;

long	lseek ();
time_t	time ();

void	log ()
{
	int	fd;
	off_t	offset;
	struct	lastlog	newlog;

	if ((fd = open ("/usr/adm/lastlog", O_RDWR)) == -1)
		return;

	offset = pwent.pw_uid * sizeof lastlog;

	if (lseek (fd, offset, 0) != offset) {
		(void) close (fd);
		return;
	}
	if (read (fd, (char *) &lastlog, sizeof lastlog) != sizeof lastlog)
#ifndef	BSD
		memset ((char *) &lastlog, sizeof lastlog, 0);
#else
		bzero ((char *) &lastlog, sizeof lastlog);
#endif
	newlog = lastlog;

	(void) time (&newlog.ll_time);
	(void) strncpy (newlog.ll_line, utent.ut_line, sizeof newlog.ll_line);
	(void) lseek (fd, offset, 0);
	(void) write (fd, (char *) &newlog, sizeof newlog);
	(void) close (fd);
}
#endif
SHAR_EOF
fi
if test -f 'mail.c'
then
	echo shar: "will not over-write existing file 'mail.c'"
else
cat << \SHAR_EOF > 'mail.c'
#include <sys/types.h>
#include <sys/stat.h>
#ifndef	BSD
#include <string.h>
#include <memory.h>
#else
#include <strings.h>
#define	strchr	index
#define	strrchr	rindex
#endif
#include "config.h"

#ifndef	lint
static	char	_sccsid[] = "@(#)mail.c	2.1	01:23:26	6/20/89";
#endif

extern	char	mail[];

#ifdef	HUSHLOGIN
extern	int	hushed;
#endif

#ifdef	MAILCHECK
void	mailcheck ()
{
	struct	stat	statbuf;
	char	*mailbox;

#ifdef	HUSHLOGIN
	if (hushed)
		return;
#endif
	if (mailbox = strchr (mail, '='))
		mailbox++;
	else
		return;

	if (stat (mailbox, &statbuf) == -1 || statbuf.st_size == 0)
		puts ("No mail.");
	else if (statbuf.st_atime > statbuf.st_mtime)
		puts ("You have mail.");
	else
		puts ("You have new mail.");
}
#endif
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'
/*
 * This information is not derived from AT&T licensed sources.  Posted
 * to the USENET 11/88.
 *
 *	@(#)shadow.h	2.1	01:23:51	6/20/89
 */

/*
 * 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_max;		/* maximum number of days between changes */
	long	sp_min;		/* minimum number of days between changes */
};

/*
 * Shadow password security file functions.
 */

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

#define  SHADOW "/etc/shadow"
SHAR_EOF
fi
if test -f 'sulog.c'
then
	echo shar: "will not over-write existing file 'sulog.c'"
else
cat << \SHAR_EOF > 'sulog.c'
#include <sys/types.h>
#include <stdio.h>
#include <time.h>
#ifndef	BSD
#include <string.h>
#include <memory.h>
#else
#include <strings.h>
#define	strchr	index
#define	strrchr	rindex
#endif
#include "config.h"

extern	char	name[];
extern	char	oldname[];

time_t	time ();

void	sulog (success)
int	success;
{
#ifdef	SULOG
	char	*tty;
	char	*cp;
	char	*ttyname ();
	time_t	clock;
	struct	tm	*tm;
	struct	tm	*localtime ();
	FILE	*fp;

	if ((fp = fopen (SULOG, "a+")) == (FILE *) 0)
		return;			/* can't open or create logfile */

	(void) time (&clock);
	tm = localtime (&clock);

	if (isatty (0) && (cp = ttyname (0))) {
		if (tty = strrchr (cp, '/'))
			tty++;
		else
			tty = cp;
	} else
		tty = "???";

	(void) fprintf (fp, "SU %.02d/%0.2d %.02d:%.02d %c %.6s %s-%s\n",
		tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
		success ? '+':'-', tty, oldname, name);

	fflush (fp);
	fclose (fp);
#endif
}
SHAR_EOF
fi
if test -f 'Makefile'
then
	echo shar: "will not over-write existing file 'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
#
#	%W%	%U%  - System V shadow password system
#
#	%W%	%U%	%G%
#
SHELL = /bin/sh

# 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 = -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
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

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

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

SOBJS = smain.o env.o password.o entry.o valid.o susetup.o sushell.o \
	pwent.o susub.o mail.o motd.o sulog.o shadow.o age.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

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

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

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

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

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

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

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

SULOGSRCS = sulogin.c entry.c env.c password.c age.c pwent.c setup.c \
	shadow.c shell.c valid.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

FILES1 = README log.c mail.c shadow.h sulog.c Makefile entry.c obscure.c \
	setup.c sub.c config.h pmain.c sulogin.c dialup.h ttytype.c

FILES2 = lastlog.h login.c motd.c password.c shell.c utmp.c age.c env.c \
	pwent.c shadow.c valid.c lmain.c smain.c pwconv.c dialup.c dialchk.c \
	pwunconv.c failure.c faillog.h faillog.c

DOCS = login.1 passwd.1 passwd.4 shadow.3 shadow.4 su.1 sulogin.8 pwconv.8 \
	pwunconv.8 faillog.8 faillog.4

all:	su login pwconv pwunconv passwd sulogin faillog

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

install: all
	cp login $(LOGINDIR)/login
	cp pwconv pwunconv sulogin /etc
	cp su passwd faillog /bin
	chown root $(LOGINDIR)/login /etc/pwconv /etc/pwunconv /etc/sulogin \
		/bin/su /bin/passwd
	chgrp root $(LOGINDIR)/login /etc/pwconv /etc/pwunconv /etc/sulogin \
		/bin/su /bin/passwd
	chown bin /bin/faillog
	chgrp bin /bin/faillog
	chmod 700 /etc/pwconv /etc/pwunconv /etc/sulogin
	chmod 4711 $(LOGINDIR)/login /bin/su /bin/passwd
	chmod 711 /bin/faillog

lint:	su.L login.L pwconv.L pwunconv.L passwd.L sulogin.L faillog.L

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

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

login.L: $(LSRCS)
	lint $(LSRCS) > login.L

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

su.L:	$(SSRCS)
	lint -DSU $(SSRCS) > su.L

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

passwd.L: $(PSRCS)
	lint -DPASSWD $(PSRCS) > passwd.L

pwconv:	$(PWOBJS)
	$(CC) -o pwconv $(LDFLAGS) $(PWOBJS) $(LIBS)

pwconv.L: $(PWSRCS)
	lint -DPASSWD $(PWSRCS) > pwconv.L

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

pwunconv.L: $(PWUNSRCS)
	lint -DPASSWD $(PWUNSRCS) > pwunconv.L

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

sulogin.L: $(SULOGSRCS)
	lint $(SULOGSRCS) > sulogin.L

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

faillog.L: faillog.c faillog.h config.h
	lint faillog.c > faillog.L

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

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

sulog.o: config.h

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

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

lmain.o: config.h lastlog.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

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

clobber: clean
	-rm -f su login passwd pwconv pwunconv sulogin faillog *.L

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

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

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

login.sh.3: $(DOCS)
	shar $(DOCS) > login.sh.3
SHAR_EOF
fi
if test -f 'entry.c'
then
	echo shar: "will not over-write existing file 'entry.c'"
else
cat << \SHAR_EOF > 'entry.c'
#include <stdio.h>
#include <pwd.h>
#ifndef	BSD
#include <string.h>
#else
#include <strings.h>
#define	strchr	index
#define	strrchr	rindex
#endif
#include "config.h"
#ifdef	SHADOWPWD
#include "shadow.h"
#endif

#ifndef	lint
static	char	_sccsid[] = "@(#)entry.c	2.1	01:23:09	6/20/89";
#endif

struct	passwd	*fgetpwent ();
char	*malloc ();

char	*strdup (s)
char	*s;
{
	char	*cp;

	if (s == (char *) 0)
		return ((char *) 0);

	if (! (cp = malloc ((unsigned) strlen (s) + 1)))
		return ((char *) 0);

	return (strcpy (cp, s));
}

void	entry (name, pwent)
char	*name;
struct	passwd	*pwent;
{
	FILE	*pwd;
	struct	passwd	*passwd;
#ifdef	SHADOWPWD
	struct	spwd	*spwd;
	char	*l64a ();
#endif
	char	*cp;

	if ((pwd = fopen (PWDFILE, "r")) == (FILE *) 0) {
		pwent->pw_passwd = (char *) 0;
		return;
	}
	while (passwd = fgetpwent (pwd)) {
		if (strcmp (name, passwd->pw_name) == 0)
			break;
	}
	fclose (pwd);

	if (passwd == (struct passwd *) 0) {
		pwent->pw_name = (char *) 0;
		pwent->pw_passwd = (char *) 0;
	} else  {
		pwent->pw_name = strdup (passwd->pw_name);
		pwent->pw_uid = passwd->pw_uid;
		pwent->pw_gid = passwd->pw_gid;
		pwent->pw_comment = (char *) 0;
		pwent->pw_gecos = strdup (passwd->pw_gecos);
		pwent->pw_dir = strdup (passwd->pw_dir);
		pwent->pw_shell = strdup (passwd->pw_shell);
#ifdef	SHADOWPWD
		setspent ();
		if (spwd = getspnam (name)) {
			pwent->pw_passwd = strdup (spwd->sp_pwdp);
			if (spwd->sp_lstchg != 0) {
				pwent->pw_age = (char *) 0;
			} else {
				pwent->pw_age = malloc (5);
				pwent->pw_age[0] = i64c (spwd->sp_max / 7);
				pwent->pw_age[1] = i64c (spwd->sp_min / 7);
				cp = l64a (spwd->sp_lstchg / 7);
				pwent->pw_age[2] = cp[0];
				pwent->pw_age[3] = cp[1];
				pwent->pw_age[4] = '\0';
			}
			endspent ();
			return;
		}
		endspent ();
		passwd->pw_age = pwent->pw_age = (char *) 0;
#endif
		if (passwd->pw_passwd)
			pwent->pw_passwd = strdup (passwd->pw_passwd);
		else
			pwent->pw_passwd = (char *) 0;

		if (passwd->pw_age) {
			pwent->pw_age = malloc (5);	/* longest legal time */
			(void) strncpy (pwent->pw_age, passwd->pw_age, 5);
		} else
			pwent->pw_age = (char *) 0;
	}
}
SHAR_EOF
fi
if test -f 'obscure.c'
then
	echo shar: "will not over-write existing file 'obscure.c'"
else
cat << \SHAR_EOF > 'obscure.c'
#include <ctype.h>
#ifndef	BSD
#include <string.h>
#include <memory.h>
#else
#include <strings.h>
#define	strchr	index
#define	strrchr	rindex
#endif
#include "config.h"

#ifndef	lint
static	char	_sccsid[] = "@(#)obscure.c	2.1	01:23:29	6/20/89";
#endif

/*
 * Obscure - see if password is obscure enough.
 *
 *	The programmer is encouraged to add as much complexity to this
 *	routine as desired.  Included are some of my favorite ways to
 *	check passwords.
 */

extern	char	pass[];			/* the new password */
extern	char	orig[];			/* the original password */
#ifdef	OBSCURE
char	mono[32];			/* a monocase version of pass */
#endif
int	obscure ()
{
#ifdef	OBSCURE
	int	i;
#endif
	if (orig[0] == '\0')
		return (1);

	if (strlen (pass) < PASSLENGTH) { /* too short */
		printf ("Too short.  ");
		return (0);
	}
#ifdef	OBSCURE
	for (i = 0;pass[i];i++)
		mono[i] = tolower (pass[i]);

	if (strcmp (pass, orig) == 0)	/* the same */
		return (0);

	if (palindrome ())		/* a palindrome */
		return (0);

	if (caseshift ())		/* upper/lower case changes only */
		return (0);

	if (similiar ())		/* jumbled version of original */
		return (0);
#endif
	return (1);
}

#ifdef	OBSCURE

/*
 * can't be a palindrome - like `R A D A R' or `M A D A M'
 */

int	palindrome ()
{
	int	i, j;

	i = strlen (pass);

	for (j = 0;j < i;j++)
		if (pass[i - j - 1] != pass[j])
			return (0);

	printf ("No palindromes.  ");
	return (1);
}

/*
 * may not be a shifted version of original
 */

int	caseshift ()
{
	int	i;

	for (i = 0;pass[i] && orig[i];i++) {
		if (tolower (pass[i]) == tolower (orig[i]))
			continue;
		else
			return (0);
	}
	printf ("May not be case-shifted.  ");
	return (1);
}

/*
 * more than half of the characters are different ones.
 */

int	similiar ()
{
	int	i, j;
	char	*strchr ();

	for (i = j = 0;pass[i] && orig[i];i++)
		if (strchr (mono, tolower (orig[i])))
			j++;

	if (i - j > 2)
		return (0);

	printf ("Too similiar.  ");
	return (1);
}
#endif
SHAR_EOF
fi
if test -f 'setup.c'
then
	echo shar: "will not over-write existing file 'setup.c'"
else
cat << \SHAR_EOF > 'setup.c'
#include <sys/types.h>
#include <pwd.h>
#include <utmp.h>
#ifndef	BSD
#include <string.h>
#include <memory.h>
#else
#include <strings.h>
#define	strchr	index
#define	strrchr	rindex
#endif
#include "config.h"

#ifndef	lint
static	char	_sccsid[] = "@(#)setup.c	2.1	01:23:45	6/20/89";
#endif

extern	char	home[];
extern	char	prog[];
extern	char	name[];
extern	char	mail[];

#ifndef	PATH
#define	PATH	":/bin:/usr/bin"
#endif

#ifndef	SUPATH
#define	SUPATH	":/bin:/usr/bin:/etc"
#endif

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

#ifndef	TTYPERM
#define	TTYPERM	0622
#endif

#ifndef	SU
extern	struct	utmp	utent;
#endif

#ifdef	QUOTAS
long	strtol ();
#ifdef	ULIMIT
long	ulimit ();
#endif
#endif

void	addenv ();

void	setup (info)
struct	passwd	*info;
{
	extern	int	errno;
	char	logname[30];
#ifndef	SU
	char	tty[30];
#endif
	char	*cp;
	int	i;
	long	l;

#ifndef	SU
	(void) strcat (strcpy (tty, "/dev/"), utent.ut_line);
	if (chown (tty, info->pw_uid, info->pw_gid) ||
					chmod (tty, TTYPERM))
		perror (tty);
#endif
	if (chdir (info->pw_dir) == -1) {
		(void) printf ("Unable to change directory to \"%s\"\n", info->pw_dir);
		exit (errno);
	}
#ifdef	QUOTAS
	for (cp = info->pw_gecos;cp != (char *) 0;cp = strchr (cp, ',')) {
		if (*cp == ',')
			cp++;

		if (strncmp (cp, "pri=", 4) == 0) {
			i = atoi (cp + 4);
			if (i >= -20 && i <= 20)
				(void) nice (i);

			continue;
		}
#ifdef	ULIMIT
		if (strncmp (cp, "ulimit=", 7) == 0) {
			l = strtol (cp + 7, (char **) 0, 10);
			(void) ulimit (2, l);

			continue;
		}
#endif
		if (strncmp (cp, "umask=", 6) == 0) {
			i = strtol (cp + 6, (char **) 0, 8) & 0777;
			(void) umask (i);

			continue;
		}
	}
#endif
	if (setgid (info->pw_gid) == -1) {
		puts ("Bad group id");
		exit (errno);
	}
#ifndef	BSD
	if (setuid (info->pw_uid))
#else
	if (setreuid (info->pw_uid, info->pw_uid))
#endif
	{
		puts ("Bad user id");
		exit (errno);
	}
	(void) strcat (strcpy (home, "HOME="), info->pw_dir);
	addenv (home);

	if (info->pw_shell == (char *) 0)
		info->pw_shell = "/bin/sh";

	(void) strcat (strcpy (prog, "SHELL="), info->pw_shell);
	addenv (prog);

	if (info->pw_uid == 0)
		addenv (SUPATH);
	else
		addenv (PATH);

	(void) strcat (strcpy (logname, "LOGNAME="), name);
	addenv (logname);

	(void) strcat (strcat (strcpy (mail, "MAIL="), MAILDIR), name);
	addenv (mail);
}
SHAR_EOF
fi
if test -f 'sub.c'
then
	echo shar: "will not over-write existing file 'sub.c'"
else
cat << \SHAR_EOF > 'sub.c'
#include <sys/types.h>
#include <pwd.h>
#include <utmp.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[] = "@(#)sub.c	2.1	01:23:58	6/20/89";
#endif

extern	struct	passwd	pwent;
#ifndef	SU
extern	struct	utmp	utent;
#endif

void	setutmp ();

/*
 * I have heard of two different types of behavior with subsystem roots.
 * One has you execute login no matter what.  The other has you execute
 * the command [ if one exists ] after the '*' in the shell name.  The
 * macro SUBLOGIN says to execute /bin/login [ followed by /etc/login ]
 * regardless.  Otherwise, pwent.pw_shell is fixed up and that command
 * is executed [ by returning to the caller ].  I prefer the latter since
 * it doesn't require having a "login" on the new root filesystem.
 */

void	subsystem ()
{
	char	*strdup ();

	if (pwent.pw_dir[0] != '/')
		exit (1);

	if (chdir (pwent.pw_dir) || chroot (pwent.pw_dir)) {
		printf ("Can't change to \"%s\"\n", pwent.pw_dir);
		exit (1);
	}
#ifndef	SU
	(void) strcpy (utent.ut_line, "<!sublogin>");

	setutmp ();
#endif
#ifdef	SUBLOGIN
	execl ("/bin/login", "login", name, (char *) 0);
	execl ("/etc/login", "login", name, (char *) 0);
	puts ("No /bin/login or /etc/login on root");
	exit (1);
#else
	if (! pwent.pw_shell || strlen (pwent.pw_shell) == 1)
		pwent.pw_shell = "/bin/sh";	/* default shell */
	else
		pwent.pw_shell++;		/* skip over '*' */
#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'
/*
 * Configuration file for login.
 *
 *	@(#)config.h	2.1	01:23:03	6/20/89
 */

/*
 * Define DIALUP to use dialup password files
 */

#define	DIALUP

/*
 * 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	AGING

/*
 * 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	NOUSE	"NOUSE"

/*
 * 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 ...
 */

#define	FAILLOG

/*
 * 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

/*
 * 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	PWDFILE	"/etc/passwd"
#define	OPWDFILE "/etc/-passwd"
#define	NPWDFILE "/etc/npasswd"
#define	OSHADOW "/etc/-shadow"
#define	NSHADOW "/etc/nshadow"

/*
 * Define PWDLOCK to be a locking semaphore for updating the password
 * file.
 */

#define	PWDLOCK	"/etc/.pwdlock"

/*
 * 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	NEED_AL64	/* Define if library does not include a64l() */
SHAR_EOF
fi
if test -f 'pmain.c'
then
	echo shar: "will not over-write existing file 'pmain.c'"
else
cat << \SHAR_EOF > 'pmain.c'
#include <sys/types.h>
#include <stdio.h>
#include <pwd.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#ifndef	BSD
#include <termio.h>
#include <string.h>
#include <memory.h>
#else
#include <sgtty.h>
#include <strings.h>
#define	strchr	index
#define	strrchr	rindex
#endif
#include "config.h"
#include "lastlog.h"
#include "shadow.h"

#ifndef	PASSLENGTH
#define	PASSLENGTH	5
#endif

#ifndef	lint
static	char	_sccsid[] = "@(#)pmain.c	2.1	01:23:35	6/20/89";
#endif

char	name[BUFSIZ];
char	orig[BUFSIZ];
char	pass[BUFSIZ];
char	pass2[BUFSIZ];

struct	passwd	pwent;

#ifndef	RETRIES
#define	RETRIES	3
#endif

char	*l64a ();
char	*crypt ();
extern	int	errno;
long	a64l ();
void	entry ();
time_t	time ();

int
main (argc, argv)
int	argc;
char	**argv;
{
	void	die ();
	char	*cp;
	char	*getlogin ();
	int	amroot;
	int	lockfd = -1;
#ifdef	OBSCURE
	int	force = 0;
#endif
	int	retries;
#ifdef	AGING
	long	week;
	long	lastweek;
#endif
	long	salttime;
	struct	passwd	*pw;
	struct	passwd	*getpwuid ();
	struct	passwd	*sgetpwent ();
	FILE	*npwd;
#ifdef	SHADOWPWD
	struct	spwd	*spwd;
	struct	spwd	tspwd;
#else
	FILE	*pwd;
	char	buf[BUFSIZ];
#endif
	char	tmp[BUFSIZ];

	argc--; argv++;			/* shift ... */

	if (! isatty (0) || ! isatty (1))
		exit (1);

	die (0);			/* save tty modes */

	signal (SIGHUP, die);		/* exit if SIGHUP */
	signal (SIGINT, die);		/* exit if SIGINT */
	signal (SIGQUIT, die);		/* exit if SIGQUIT */
	signal (SIGTERM, die);		/* exit if SIGTERM */

	if (! (pw = getpwuid (getuid ())))
		goto failure;		/* can't get my name ... */
		
#ifdef	OBSCURE
	if (argc > 0 && strcmp (argv[0], "-f") == 0) {
		force = 1;
		argc--; argv++;		/* shift ... */
	}
#endif
	if (argc > 0)
		(void) strcpy (name, argv[0]);
	else if (cp = getlogin ())	/* need user name */
		(void) strcpy (name, cp);
	else				/* can't find user name! */
		goto failure;

	printf ("Changing password for %s\n", name);

	amroot = getuid () == 0;	/* currently am super user */
#ifdef	OBSCURE
	if (! amroot)
		force = 0;
#endif
	if (! amroot && strcmp (name, pw->pw_name) != 0)
		goto failure;

	entry (name, &pwent);		/* get password file entry */

	if (! pwent.pw_name)		/* no entry for user??? */
		goto failure;

	if (! amroot) {
		if (! password ("Old Password:", orig))
			exit (1);

		if (! valid (orig, &pwent)) {
			puts ("Sorry.");
			exit (1);
		}
	}
#ifdef	AGING
	if (! amroot && pwent.pw_age) {	/* check out the age */
#ifdef	SHADOWPWD
		(void) time (&week);
		week /= (24L * 60L * 60L);	/* days since epoch */
		if (spwd = getspnam (name)) {	/* use entries in shadow */
			if (spwd->sp_min > spwd->sp_max) {
				puts ("You may not change this password");
				exit (1);
			}
			if (spwd->sp_lstchg + spwd->sp_min > week) {
				printf ("Sorry, less than %d days since the last change\n", spwd->sp_min);
				exit (1);
			}
		} else {
#endif	/* SHADOWPWD */
		(void) time (&week);
		week /= (7L * 24L * 60L * 60L);	/* weeks since epoch */
		lastweek = a64l (&pwent.pw_age[2]);

		if (c64i (pwent.pw_age[0]) < c64i (pwent.pw_age[1])) {
			puts ("You may not change this password");
			exit (1);
		}
		if (c64i (pwent.pw_age[1]) + lastweek > week) {
			printf ("Sorry, less than %d weeks since the last change\n", c64i (pwent.pw_age[1]));
			exit (1);
		}
#ifdef	SHADOWPWD
		}
#endif
	}
#endif
	printf ("Enter new password (minimum of %d characters)\n", PASSLENGTH);
#ifdef	OBSCURE
	puts ("Please use a combination of upper and lowercase letters and numbers");
#endif
	retries = RETRIES;
retry:
	if (! password ("New Password:", pass))
		exit (1);

#ifndef	OBSCURE
	if (! obscure ()) {
		puts ("Password not changed.");
		exit (1);
	}
#else
	if (! force && ! obscure ()) {
		if (retries-- > 0) {
			puts ("Please try again.");
			goto retry;
		} else
			goto toomany;
	}
#endif
	if (! password ("Re-enter new password:", pass2))
		exit (1);

	if (strcmp (pass, pass2) != 0) {
		puts ("They don't match; try again");

		if (retries-- > 0)
			goto retry;
		else
			goto toomany;
	}
#ifdef	AGING
	if (pwent.pw_age) {
		cp = l64a (week);

		pwent.pw_age[2] = cp[0];
		pwent.pw_age[3] = cp[1];
		pwent.pw_age[4] = '\0';
	}
#endif
	(void) time (&salttime);
	salttime = ((salttime & 07777) ^ ((salttime >> 14) & 07777)) & 07777;
	pwent.pw_passwd = tmp;
	strcpy (pwent.pw_passwd, crypt (pass, l64a (salttime)));
#ifdef	DOUBLESIZE
	if (strlen (pass) > 8) {
		strcpy (pwent.pw_passwd + 13,
			crypt (pass + 8, l64a (salttime)) + 2);
	}
#endif
	/*
	 * Now we get to race the bad guy.  I don't think he can get us.
	 *
	 * Ignore most reasonable signals.
	 *	Maybe we should ignore more?  He can't hurt us until the end.
	 *
	 * Get a lock file.
	 *
	 * Copy first part of password file to new file.
	 *	Illegal lines are copied verbatim.
	 *	File permissions are r--r--r--, owner root, group root.
	 *
	 * Output the new entry.
	 *	Only fields in struct passwd are output.
	 *
	 * Copy the rest of the file verbatim.
	 *
	 * Rename (link, unlink) password file to backup.
	 *	Kill me now and nothing changes or no one gets in.
	 *
	 * Rename (link, unlink) temporary file to password file.
	 *	Kill me now and no one gets in or lock is left.
	 *
	 * Remove locking file.
	 *
	 * That's all folks ...
	 */

	signal (SIGHUP, SIG_IGN);
	signal (SIGINT, SIG_IGN);
	signal (SIGQUIT, SIG_IGN);
	signal (SIGTERM, SIG_IGN);

	umask (0);			/* get new files modes correct */
#ifndef	NDEBUG
	if ((lockfd = open (".pwdlock", O_RDONLY|O_CREAT|O_EXCL), 0444) == -1)
#else
	if ((lockfd = open (PWDLOCK, O_RDONLY|O_CREAT|O_EXCL), 0444) == -1)
#endif	/* NDEBUG */
	{
		puts ("Can't get lock");
		exit (1);
	}
	umask (077);			/* close security holes to come ... */
#ifdef	SHADOWPWD
	if (access (NSHADOW, 0) == 0 && unlink (NSHADOW) == -1)
		goto failure;

	if ((npwd = fopen (NSHADOW, "w")) == (FILE *) 0)
		goto failure;

	if (chmod (NSHADOW, 0400) || chown (NSHADOW, 0, 0))
		goto failure;

	setspent ();

	while (spwd = getspent ()) {
		if (strcmp (spwd->sp_namp, name) == 0)
			break;

		if (putspent (spwd, npwd))
			goto failure;
	}
	if (spwd == (struct spwd *) 0) { /* didn't find a match */
		spwd = &tspwd;		/* use a local structure instead */
		spwd->sp_namp = pwent.pw_name;
		spwd->sp_max = 10000;	/* about as big as possible */
		spwd->sp_min = 0;	/* about as small as possible */
	}
	spwd->sp_pwdp = pwent.pw_passwd; /* fixup the password */

	(void) time (&lastweek);	/* get the current time ... */
	lastweek /= (24L*60L*60L);	/* ... turn it into days. */
	spwd->sp_lstchg = lastweek;	/* save it as date of last change */

	(void) putspent (spwd, npwd);	/* add the new entry */

	while (spwd = getspent ())	/* finish the other ones off */
		(void) putspent (spwd, npwd);

	endspent ();

	if (ferror (npwd)) {
		perror (NSHADOW);
		if (unlink (NPWDFILE) || unlink (PWDLOCK))
			fputs ("Help!\n", stderr);

		exit (1);
	}
	fflush (npwd);
	fclose (npwd);

	if (access (OSHADOW, 0) == 0) {
		if (unlink (OSHADOW)) {
			puts ("Can't remove backup file");
			goto unlock;
		}
	}
	if (link (SHADOW, OSHADOW) || unlink (SHADOW)) {
		puts ("Can't save backup file");
		goto unlock;
	}
#ifndef	BSD
	if (link (NSHADOW, SHADOW) || unlink (NSHADOW))
#else
	if (rename (NSHADOW, SHADOW))
#endif
	{
		(void) unlink (OSHADOW);
		puts ("Can't rename new file");
		goto unlock;
	}
	if (unlink (OSHADOW)) {
		puts ("Can't remove backup file");
		goto unlock;
	}
#else	/* ! SHADOWPWD */
	if (access (NPWDFILE, 0) == 0 && unlink (NPWDFILE) == -1)
		goto failure;

#ifndef	NDEBUG
	if ((npwd = fopen ("npasswd", "w")) == (FILE *) 0)
#else
	umask (077);		/* no permissions for non-roots */

	if ((npwd = fopen (NPWDFILE, "w")) == (FILE *) 0)
#endif	/* NDEBUG */
		goto failure;

#ifndef	NDEBUG
	chmod (NPWDFILE, 0444);		/* lets have some security here ... */
	chown (NPWDFILE, 0, 0);		/* ... and keep the bad guy away */
#endif	/* NDEBUG */
	if ((pwd = fopen (PWDFILE, "r")) == (FILE *) 0)
		goto failure;

	while (fgets (buf, BUFSIZ, pwd) != (char *) 0) {
		if (buf[0] == '#' || ! (pw = sgetpwent (buf))) {
			fputs (buf, npwd);
		} else if (strcmp (pw->pw_name, pwent.pw_name) != 0)
			fputs (buf, npwd);
		else
			break;
	}
	(void) fprintf (npwd, "%s:", pw->pw_name);
	if (pwent.pw_age)
		(void) fprintf (npwd, "%s,%s:", pwent.pw_passwd, pwent.pw_age);
	else
		(void) fprintf (npwd, "%s:", pwent.pw_passwd);

	(void) fprintf (npwd, "%d:%d:%s:%s:%s\n",
		pwent.pw_uid, pwent.pw_gid, pwent.pw_gecos, pwent.pw_dir,
		pwent.pw_shell ? pwent.pw_shell:"");

	while (fgets (buf, BUFSIZ, pwd) != (char *) 0)
		fputs (buf, npwd);

	if (ferror (npwd)) {
		perror (NPWDFILE);
		if (unlink (NPWDFILE) || unlink (PWDLOCK))
			fputs ("Help!\n", stderr);

		exit (1);
	}
	fflush (npwd);
	fclose (npwd);
#ifdef	NDEBUG
	chmod (NPWDFILE, 0644);
	if (unlink (OPWDFILE) == -1) {
		if (errno != ENOENT) {
			puts ("Can't unlink backup file");
			goto unlock;
		}
	}
	if (link (PWDFILE, OPWDFILE) || unlink (PWDFILE)) {
		puts ("Can't save backup file");
		goto unlock;
	}
#ifndef	BSD
	if (link (NPWDFILE, PWDFILE) || unlink (NPWDFILE))
#else
	if (rename (NPWDFILE, PWDFILE))
#endif
	{
		puts ("Can't rename new file");
		goto unlock;
	}
#endif	/* NDEBUG */
#endif	/* SHADOW */
#ifndef	NDEBUG
	(void) unlink (".pwdlock");
#else
	(void) unlink (PWDLOCK);
#endif
	exit (0);
	/*NOTREACHED*/

failure:
	puts ("Permission denied.");
unlock:
	if (lockfd >= 0)
		(void) unlink (PWDLOCK);

	(void) unlink (NPWDFILE);
	exit (1);
	/*NOTREACHED*/

toomany:
	puts ("Too many tries; try again later.");
	exit (1);
	/*NOTREACHED*/
}

/*
 * die - set or reset termio modes.
 *
 *	die() is called before processing begins.  signal() is then
 *	called with die() as the signal handler.  If signal later
 *	calls die() with a signal number, the terminal modes are
 *	then reset.
 */

void	die (killed)
int	killed;
{
#ifdef	BSD
	static	struct	sgtty	sgtty;

	if (killed)
		stty (0, &sgtty);
	else
		gtty (0, &sgtty);
#else
	struct	termio	sgtty;

	if (killed)
		ioctl (0, TCSETA, &sgtty);
	else
		ioctl (0, TCGETA, &sgtty);
#endif
	if (killed)
		exit (killed);
}
SHAR_EOF
fi
if test -f 'sulogin.c'
then
	echo shar: "will not over-write existing file 'sulogin.c'"
else
cat << \SHAR_EOF > 'sulogin.c'
#include <sys/types.h>
#include <stdio.h>
#include <pwd.h>
#include <utmp.h>
#ifndef	BSD
#include <string.h>
#include <memory.h>
#else
#include <strings.h>
#define	strchr	index
#define	strrchr	rindex
#endif
#include "config.h"

#ifndef	lint
static	char	_sccsid[] = "@(#)sulogin.c	2.1	01:24:02	6/20/89";
#endif

char	name[BUFSIZ];
char	pass[BUFSIZ];
char	home[BUFSIZ];
char	prog[BUFSIZ];
char	mail[BUFSIZ];

struct	passwd	pwent;
struct	utmp	utent;

#ifdef	TZ
FILE	*tzfile;
char	tzbuf[16] = TZ;
#endif

#ifndef	MAXENV
#define	MAXENV	64
#endif

char	*newenvp[MAXENV];
int	newenvc = 0;
int	maxenv = MAXENV;
extern	char	**environ;

#ifndef	ALARM
#define	ALARM	60
#endif

#ifndef	RETRIES
#define	RETRIES	3
#endif

int	main (argc, argv, envp)
int	argc;
char	**argv;
char	**envp;
{
	char	*getenv ();
	char	*ttyname ();
	char	*cp;

	if (access (PWDFILE, 0) == -1) { /* must be a password file! */
		printf ("No password file\n");
		exit (1);
	}
#ifndef	DEBUG
	if (getppid () != 1)		/* parent must be INIT */
		exit (1);
#endif
	if (! isatty (0))		/* must be a terminal */
		exit (1);

	while (*envp)			/* add inherited environment, */
		addenv (*envp++);	/* some variables change later */

#ifdef	TZ
	if (tzbuf[0] == '/') {
		if ((tzfile = fopen (tzbuf, "r")) != (FILE *) 0) {
			if (fgets (tzbuf, sizeof tzbuf, tzfile)) {
				tzbuf[strlen (tzbuf) - 1] = '\0';
				addenv (tzbuf);
			}
			fclose (tzfile);
		}
	} else {
		addenv (tzbuf);
	}
#endif
#ifdef	HZ
	addenv (HZ);			/* set the default $HZ, if one */
#endif
	(void) strcpy (name, "root");	/* KLUDGE!!! */

	while (1) {		/* repeatedly get login/password pairs */
		entry (name, &pwent);	/* get entry from password file */
		if (pwent.pw_name == (char *) 0) {
			printf ("No password entry for 'root'\n");
			exit (1);
		}

	/*
	 * Here we prompt for the root password, or if no password is
	 * given we just exit.
	 */

					/* get a password for root */
		if (! password ("Type control-d for normal startup,\n(or give root password for system maintenance):", pass))
			exit (0);

		if (valid (pass, &pwent)) /* check encrypted passwords ... */
			break;		/* ... encrypted passwords matched */

		puts ("Login incorrect");
	}
	environ = newenvp;		/* make new environment active */

	puts ("Entering System Maintenance Mode");

	/*
	 * Normally there would be a utmp entry for login to mung on
	 * to get the tty name, date, etc. from.  We don't need all that
	 * stuff because we won't update the utmp or wtmp files.  BUT!,
	 * we do need the tty name so we can set the permissions and
	 * ownership.
	 */

	if (cp = ttyname (0)) {		/* found entry in /dev/ */
		if (strrchr (cp, '/') != (char *) 0)
			strcpy (utent.ut_line, strrchr (cp, '/') + 1);
		else
			strcpy (utent.ut_line, cp);
	}
	if (getenv ("IFS"))		/* don't export user IFS ... */
		addenv ("IFS= \t\n");	/* ... instead, set a safe IFS */

	setup (&pwent);			/* set UID, GID, HOME, etc ... */

	shell (pwent.pw_shell);		/* exec the shell finally. */
	/*NOTREACHED*/
}
SHAR_EOF
fi
if test -f 'dialup.h'
then
	echo shar: "will not over-write existing file 'dialup.h'"
else
cat << \SHAR_EOF > 'dialup.h'
/*
 * Structure of d_passwd file
 *
 *	The d_passwd file contains the names of login shells which require
 *	dialup passwords.  Each line contains the fully qualified path name
 *	for the shell, followed by an optional password.  Each field is
 *	separated by a ':'.
 *
 * Structure of the dialups file
 *
 *	The dialups file contains the names of ports which may be dialup
 *	lines.  Each line consists of the last component of the path
 *	name.  Any leading directory names are removed.
 *
 *	@(#)dialup.h	2.1	01:23:08	6/20/89
 */

struct	dialup {
	char	*du_shell;
	char	*du_passwd;
};

void	setduent ();
void	endduent ();
struct	dialup	*getduent ();
struct	dialup	*getdushell ();

#define	DIALPWD	"/etc/d_passwd"
#define	DIALUPS	"/etc/dialups"
SHAR_EOF
fi
if test -f 'ttytype.c'
then
	echo shar: "will not over-write existing file 'ttytype.c'"
else
cat << \SHAR_EOF > 'ttytype.c'
#include <stdio.h>
#ifndef	BSD
#include <string.h>
#include <memory.h>
#else
#include <strings.h>
#define	strchr	index
#define	strrchr	rindex
#endif
#include "config.h"

#ifdef	TTYTYPE
#ifndef	lint
static	char	_sccsid[] = "@(#)ttytype.c	2.1	01:24:04	6/20/89";
#endif

/*
 * ttytype - set ttytype from port to terminal type mapping database
 */

void	ttytype (line)
char	*line;
{
	FILE	*fp;
	char	buf[BUFSIZ];
	char	termvar[BUFSIZ];
	char	*cp;
	char	*type;
	char	*port;
	char	*getenv ();

	if (getenv ("TERM"))
		return;

	if (! (fp = fopen (TTYTYPE, "r")))
		return;

	while (fgets (buf, BUFSIZ, fp)) {
		if (buf[0] == '#')
			continue;

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

		if ((type = strtok (buf, " \t"))
				&& (port = strtok ((char *) 0, " \t"))) {
			if (strcmp (line, port) == 0)
				break;
		}
	}
	if (! feof (fp) && ! ferror (fp)) {
		strcat (strcpy (termvar, "TERM="), type);
		addenv (termvar);
	}
	fclose (fp);
}
#endif
SHAR_EOF
fi
exit 0
#	End of shell archive
-- 
John F. Haugh II                        +-Button of the Week Club:-------------
VoiceNet: (512) 832-8832   Data: -8835  | "AIX is a three letter word,
InterNet: jfh@rpp386.Cactus.Org         |  and it's BLUE."
UucpNet : <backbone>!bigtex!rpp386!jfh  +--------------------------------------

jfh@rpp386.Dallas.TX.US (John F. Haugh II) (06/20/89)

X-Archive-Name: shadow2/part2

Part 2 of second USENET release
--
#! /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:
#	lastlog.h
#	login.c
#	motd.c
#	password.c
#	shell.c
#	utmp.c
#	age.c
#	env.c
#	pwent.c
#	shadow.c
#	valid.c
#	lmain.c
#	smain.c
#	pwconv.c
#	dialup.c
#	dialchk.c
#	pwunconv.c
#	failure.c
#	faillog.h
#	faillog.c
# This archive created: Tue Jun 20 01:24:59 1989
# By:	John F. Haugh II (River Parishes Programming, Plano TX)
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'lastlog.h'
then
	echo shar: "will not over-write existing file 'lastlog.h'"
else
cat << \SHAR_EOF > 'lastlog.h'
/*
 * lastlog.h - structure of lastlog file
 *
 *	This file defines a lastlog file structure which should be sufficient
 *	to hold the information required by login.  It should only be used if
 *	there is no real lastlog.h file.
 */

struct	lastlog	{
	time_t	ll_time;
	char	ll_line[8];
};
SHAR_EOF
fi
if test -f 'login.c'
then
	echo shar: "will not over-write existing file 'login.c'"
else
cat << \SHAR_EOF > 'login.c'
#include <stdio.h>
#include <ctype.h>
#ifndef	BSD
#include <string.h>
#include <memory.h>
#else
#include <strings.h>
#define	strchr	index
#define	strrchr	rindex
#endif

void	setenv ();

void	login (name)
char	*name;
{
	char	buf[BUFSIZ];
	char	*envp[32];
	int	envc;
	char	*cp;
	int	i;

#ifndef	BSD
	(void) memset (buf, '\0', sizeof buf);
#else
	bzero (buf, sizeof buf);
#endif
	fputs ("login: ", stdout);

	if (fgets (buf, BUFSIZ, stdin) != buf)
		exit (1);

	buf[strlen (buf) - 1] = '\0';	/* remove \n [ must be there ] */

	for (cp = buf;*cp == ' ' || *cp == '\t';cp++)
		;

	for (i = 0;i < BUFSIZ - 1 && isgraph (*cp);name[i++] = *cp++)
		;

	if (*cp)
		cp++;

	name[i] = '\0';

	if (*cp != '\0') {		/* process new variables */
		for (envc = 0;envc < 32;envc++) {
			envp[envc] = strtok (envc == 0 ? cp:(char *) 0, " \t,");

			if (envp[envc] == (char *) 0)
				break;
		}
		setenv (envc, envp);
	}
}
SHAR_EOF
fi
if test -f 'motd.c'
then
	echo shar: "will not over-write existing file 'motd.c'"
else
cat << \SHAR_EOF > 'motd.c'
#include <stdio.h>
#ifndef	BSD
#include <string.h>
#include <memory.h>
#else
#include <strings.h>
#define	strchr	index
#define	strrchr	rindex
#endif
#include "config.h"

#ifndef	lint
static	char	_sccsid[] = "@(#)motd.c	2.1	01:23:27	6/20/89";
#endif

extern	char	home[];
#ifdef	HUSHLOGIN
extern	int	hushed;
#endif

#ifdef	MOTD
void	motd ()
{
	FILE	*fp;
	register int	c;

#ifdef	HUSHLOGIN
	if (hushed)
		return;
#endif
	if ((fp = fopen ("/etc/motd", "r")) == (FILE *) 0)
		return;

	while ((c = getc (fp)) != EOF)
		putchar (c);

	fclose (fp);
	fflush (stdout);
}
#endif
SHAR_EOF
fi
if test -f 'password.c'
then
	echo shar: "will not over-write existing file 'password.c'"
else
cat << \SHAR_EOF > 'password.c'
#include <stdio.h>
#ifndef	BSD
#include <string.h>
#include <memory.h>
#else
#include <strings.h>
#define	strchr	index
#define	strrchr	rindex
#endif
#ifndef	BSD
#include <termio.h>
#else
#include <sgtty.h>
#endif

#include <fcntl.h>

/*
 * password - prompt for password and return entry
 *
 *	Need to fake up getpass().  Returns TRUE if a password
 *	was successfully input, and FALSE otherwise, including
 *	EOF on input or ioctl() failure.  pass is not modified
 *	on failure.  The input length limit may be set by
 *	changing the value of PASSLIMIT.
 */

#ifndef	lint
static	char	_sccsid[] = "@(#)password.c	2.1	01:23:33	6/20/89";
#endif

#define	PASSLIMIT	20

int	password (prompt, pass)
char	*prompt;
char	*pass;
{
	char	buf[BUFSIZ];
	char	*cp;
	int	eof;
	int	ttyopened = 0;
#ifndef	BSD
	struct	termio	termio;
	struct	termio	save;
#else
	struct	sgttyb	termio ;
	struct	sgttyb	save ;
#endif
	FILE	*fp;

	if ((fp = fopen ("/dev/tty", "r")) == (FILE *) 0)
		fp = stdin;
	else
		ttyopened = 1;

#ifndef	BSD
	if (ioctl (fileno (fp), TCGETA, &termio))
		return (0);
#else
	if ( gtty( fileno(fp), &termio ) )
		return (0);
#endif

	save = termio;
#ifndef	BSD
	termio.c_lflag &= ~(ECHO|ECHOE|ECHOK);
	ioctl (fileno (fp), TCSETAF, &termio);
#else
	termio.sg_flags &= ~ECHO ;
	stty( fileno( fp ), termio ) ;
#endif

	fputs (prompt, stdout);
	eof = fgets (buf, BUFSIZ, fp) == (char *) 0 || feof (fp) || ferror (fp);
	putchar ('\n');

#ifndef	BSD
	ioctl (fileno (fp), TCSETAF, &save);
#else
	stty( fileno( fp ), save ) ;
#endif

	if (! eof) {
		buf[PASSLIMIT] = '\0';
		if ((cp = strchr (buf, '\n')) || (cp = strchr (buf, '\r')))
			*cp = '\0'; 

		(void) strcpy (pass, buf);
	}
	if (ttyopened)
		fclose (fp);

	return (! eof);
}
SHAR_EOF
fi
if test -f 'shell.c'
then
	echo shar: "will not over-write existing file 'shell.c'"
else
cat << \SHAR_EOF > 'shell.c'
#include <stdio.h>
#ifndef	BSD
#include <string.h>
#include <memory.h>
#else
#include <strings.h>
#define	strchr	index
#define	strrchr	rindex
#endif
#include "config.h"

#ifndef	lint
static	char	_sccsid[] = "@(#)shell.c	2.1	01:23:53	6/20/89";
#endif

extern	char	*newenvp[];

void	shell (file)
char	*file;
{
	char	arg0[BUFSIZ];
	char	*path;
	extern	int	errno;

	if (file == (char *) 0)
		exit (1);

	if (path = strrchr (file, '/'))
		path++;
	else
		path = file;

	(void) strcpy (arg0 + 1, path);
	arg0[0] = '-';
#ifndef	NDEBUG
	printf ("Executing shell %s\n", file);
#endif
	execle (file, arg0, (char *) 0, newenvp);
	printf ("Can't execute %s\n", file);
	exit (errno);
}
SHAR_EOF
fi
if test -f 'utmp.c'
then
	echo shar: "will not over-write existing file 'utmp.c'"
else
cat << \SHAR_EOF > 'utmp.c'
#include <sys/types.h>
#include <utmp.h>
#ifndef	BSD
#include <string.h>
#include <memory.h>
#else
#include <strings.h>
#define	strchr	index
#define	strrchr	rindex
#endif
#include <stdio.h>
#include "config.h"

extern	struct	utmp	utent;
extern	char	name[];

struct	utmp	*getutent ();
void	setutent ();
void	endutent ();
void	pututline ();
time_t	time ();

void	checkutmp ()
{
	struct	utmp	*ut;
#ifndef	NDEBUG
	int	pid = getppid ();
#else
	int	pid = getpid ();
#endif
	setutent ();

	while (ut = getutent ())
		if (ut->ut_pid == pid)
			break;

	if (ut)
		utent = *ut;

	endutent ();

	if (ut && utent.ut_pid == pid)
		return;

	puts ("No utmp entry.  You must exec \"login\" from the lowest level \"sh\"");
	exit (1);
}

void	setutmp ()
{
	FILE	*wtmp;
	char	tty[sizeof utent.ut_line + 1];
	char	*line;

	setutent ();

	(void) strncpy (utent.ut_user, name, sizeof utent.ut_user);

	utent.ut_type = USER_PROCESS;

	if (line = strrchr (utent.ut_line, '/')) {
		(void) strcpy (tty, line + 1);
#ifndef	BSD
		(void) memset (utent.ut_line, '\0', sizeof utent.ut_line);
#else
		bzero (utent.ut_line, sizeof utent.ut_line);
#endif
		(void) strcpy (utent.ut_line, tty);
	}
	(void) time (&utent.ut_time);

	pututline (&utent);
	endutent ();

	if ((wtmp = fopen (WTMP_FILE, "a+"))) {
		fwrite (&utent, sizeof utent, 1, wtmp);
		fclose (wtmp);
	}
}
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'
#include <sys/types.h>
#include <stdio.h>
#include <pwd.h>
#include "config.h"

#ifndef	lint
static	char	_sccsid[] = "@(#)age.c	2.11	01:23:02	6/20/89";
#endif

#ifndef	PASSWD
extern	char	*newenvp[];
#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);
}

#ifdef	AGING
#ifdef	PASSWD
#ifdef	NEED_AL64
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
#endif
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	NEED_AL64
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 (age)
char	*age;
{
	long	clock;
	long	week;
	extern	char	name[];
	extern	int	errno;

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

	if (strlen (age) < 4)
		week = 0L;
	else
		week = a64l (age + 2);

	if (clock >= week + c64i (age[0])) {
		printf ("Your password has expired.");

		if (c64i (age[0]) < c64i (age[1])) {
			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);
	}
}
#endif
#endif
SHAR_EOF
fi
if test -f 'env.c'
then
	echo shar: "will not over-write existing file 'env.c'"
else
cat << \SHAR_EOF > 'env.c'
#include <stdio.h>
#ifndef	BSD
#include <string.h>
#else
#define	strchr	index
#define	strrchr	rindex
#include <strings.h>
#endif

#ifndef	lint
static	char	_sccsid[] = "@(#)env.c	2.1	01:23:11	6/20/89";
#endif

extern	char	**environ;
extern	char	*newenvp[];
extern	int	newenvc;
extern	int	maxenv;

char	*strdup ();
void	free ();

static	char	*forbid[] = {
	"HOME",
	"IFS",
	"PATH",
	"SHELL",
	(char *) 0
};

void	addenv (entry)
char	*entry;
{
	char	*cp;
	int	i;
	int	len;

	if (cp = strchr (entry, '='))
		len = cp - entry;
	else
		return;

	for (i = 0;i < newenvc;i++)
		if (strncmp (entry, newenvp[i], len) == 0 &&
			(newenvp[i][len] == '=' || newenvp[i][len] == '\0'))
			break;

	if (i == maxenv) {
		puts ("Environment overflow");
		return;
	}
	if (i == newenvc) {
		newenvp[newenvc++] = strdup (entry);
	} else {
		free (newenvp[i]);
		newenvp[i] = strdup (entry);
	}
}

void	setenv (argc, argv)
int	argc;
char	**argv;
{
	int	i;
	int	n;
	int	noname = 1;
	char	variable[BUFSIZ];
	char	*cp;

	for (i = 0;i < argc;i++) {
		if ((n = strlen (argv[i])) >= BUFSIZ)
			continue;	/* ignore long entries */

		if (! (cp = strchr (argv[i], '='))) {
			(void) strcpy (variable, argv[i]);
		} else {
			(void) strncpy (variable, argv[i], cp - argv[i]);
			variable[cp - argv[i]] = '\0';
		}
		for (n = 0;forbid[n] != (char *) 0;n++)
			if (strcmp (variable, forbid[n]) == 0)
				break;

		if (forbid[n] != (char *) 0) {
			printf ("You may not change $%s\n", forbid[n]);
			continue;
		}
		if (cp) {
			addenv (argv[i]);
		} else {
			sprintf (variable, "L%d=%s", noname++, argv[i]);
			addenv (variable);
		}
	}
}
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'
#include <stdio.h>
#include <pwd.h>
#include <string.h>
#include "config.h"

#ifndef	lint
static	char	_sccsid[] = "@(#)pwent.c	2.1	01:23:41	6/20/89";
#endif

#define	SBUFSIZ	64
#define	NFIELDS	7

static	char	pwdbuf[BUFSIZ];
static	char	*pwdfields[NFIELDS];

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

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

	cp = pwdbuf;
	for (i = 0;i < NFIELDS && cp;i++) {
		pwdfields[i] = cp;
		if (cp = strchr (cp, ':'))
			*cp++ = 0;
	}
	if (i < (NFIELDS-1) || *pwdfields[2] == '\0' || *pwdfields[3] == '\0')
		return ((struct passwd *) 0);

	for (;i < NFIELDS;i++)
		pwdfields[i] = 0;

	pwent.pw_name = pwdfields[0];
	pwent.pw_passwd = pwdfields[1];
	pwent.pw_uid = atoi (pwdfields[2]);
	pwent.pw_gid = atoi (pwdfields[3]);

	if (cp = strchr (pwent.pw_passwd, ',')) {
		pwent.pw_age = cp + 1;
		*cp = '\0';
	}
	pwent.pw_gecos = pwdfields[4];
	pwent.pw_dir = pwdfields[5];
	pwent.pw_shell = pwdfields[6];

	return (&pwent);
}
#ifdef FGETPWENT
struct	passwd	*fgetpwent (fp)
FILE	*fp;
{
	char	buf[BUFSIZ];

	while (fgets (buf, BUFSIZ, fp) != (char *) 0) {
		if (buf[0] == '#')
			continue;

		buf[strlen (buf) - 1] = '\0';
		return (sgetpwent (buf));
	}
	return ((struct passwd *) 0);
}
#endif
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'
#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	2.1	01:23:49	6/20/89";
#endif

static	FILE	*shadow;

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

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

	shadow = (FILE *) 0;
}

struct	spwd	*fgetspent (fp)
FILE	*fp;
{
	static	struct	spwd	spwd;
	static	char	name[32];
	static	char	pass[32];
	char	buf[BUFSIZ];
	char	*cp;
	int	atoi ();
	long	atol ();

	if (! fp)
		return (0);

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

	buf[strlen (buf) - 1] = '\0';

	if ((cp = strtok (buf, ":")) && *cp)
		(void) strcpy (name, cp);
	else
		return (0);

	if ((cp = strtok ((char *) 0, ":")) && *cp)
		(void) strcpy (pass, cp);
	else
		return (0);

	if ((cp = strtok ((char *) 0, ":")) && *cp)
		spwd.sp_lstchg = atol (cp);
	else
		return (0);

	if ((cp = strtok ((char *) 0, ":")) && *cp)
		spwd.sp_min = atoi (cp);
	else
		return (0);

	if ((cp = strtok ((char *) 0, ":")) && *cp)
		spwd.sp_max = atoi (cp);
	else
		return (0);

	spwd.sp_namp = name;
	spwd.sp_pwdp = pass;

	return (&spwd);
}

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;
{
	if (! fp)
		return (0);

	return (fprintf (fp, "%s:%s:%ld:%ld:%ld\n",
			spwd->sp_namp, spwd->sp_pwdp,
			spwd->sp_lstchg, spwd->sp_min, spwd->sp_max) != EOF);
}
SHAR_EOF
fi
if test -f 'valid.c'
then
	echo shar: "will not over-write existing file 'valid.c'"
else
cat << \SHAR_EOF > 'valid.c'
#include <stdio.h>
#include <pwd.h>
#ifndef	BSD
#include <string.h>
#include <memory.h>
#else
#include <strings.h>
#define	strchr	index
#define	strrchr	rindex
#endif
#include "config.h"

#ifndef	lint
static	char	_sccsid[] = "@(#)valid.c	2.1	01:24:08	6/20/89";
#endif

/*
 * valid - compare encrypted passwords
 *
 *	Valid() compares the DES encrypted password from the password file
 *	against the password which the user has entered after it has been
 *	encrypted using the same salt as the original.
 */

int	valid (password, entry)
char	*password;
struct	passwd	*entry;
{
	char	*encrypt;
	char	*salt;
	char	*crypt ();
#ifdef	DOUBLESIZE
	int	firsthalf;
	int	longpass;
#endif

	/*
	 * Start with blank or empty password entries.  Always encrypt
	 * a password if no such user exists.  Only if the ID exists and
	 * the password is really empty do you return quickly.  This
	 * routine is meant to waste CPU time.
	 */

	if (entry->pw_name &&
			(entry->pw_passwd == (char *) 0 ||
			 strlen (entry->pw_passwd) == 0)) {
		if (strlen (password) == 0)
			return (1);	/* user entered nothing */
		else
			return (0);	/* user entered something! */
	}

#ifdef	DOUBLESIZE
	longpass = entry->pw_passwd && strlen (entry->pw_passwd) > 13;
#endif

	/*
	 *
	/*
	 * If there is no entry then we need a salt to use.
	 */

	if (entry->pw_passwd == (char *) 0 || entry->pw_passwd[0] == '\0')
		salt = "xx";
	else
		salt = entry->pw_passwd;

	/*
	 * Now, perform the encryption using the salt from before on
	 * the users input.  Since we always encrypt the string, it
	 * should be very difficult to determine if the user exists by
	 * looking at execution time.
	 */

	encrypt = crypt (password, salt);
#ifdef	DOUBLESIZE
	firsthalf = entry->pw_passwd
		&& strncmp (encrypt + 2, entry->pw_passwd + 2, 11) == 0;

	if (strlen (password) > 8)
		encrypt = crypt (password + 8, salt);
	else {
		(void) crypt (password, salt);	/* waste time ... */
		encrypt = "";
	}
#endif
	/*
	 * One last time we must deal with there being no password file
	 * entry for the user.  We use the pw_passwd == NULL idiom to
	 * cause non-existent users to not be validated.  Even still,
	 * we are safe because if the string were == "", any encrypted
	 * string is not going to match - the output of crypt() begins
	 * with the salt, which is "xx", not "".
	 */

#ifdef	NOUSE
	if (entry->pw_shell && strcmp ("NO_USE", entry->pw_shell) == 0)
		return (0);
#endif
#ifndef	DOUBLESIZE
	if (entry->pw_passwd && strcmp (encrypt, entry->pw_passwd) == 0)
		return (1);
	else
		return (0);
#else
	if (! longpass)
		return (firsthalf);

	if (entry->pw_passwd && firsthalf
			&& strncmp (encrypt + 2, entry->pw_passwd + 13) == 0)
		return (1);
	else
		return (0);
#endif
}
SHAR_EOF
fi
if test -f 'lmain.c'
then
	echo shar: "will not over-write existing file 'lmain.c'"
else
cat << \SHAR_EOF > 'lmain.c'
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <pwd.h>
#include <utmp.h>
#include <time.h>
#include <signal.h>
#ifndef	BSD
#include <string.h>
#include <memory.h>
#else
#include <strings.h>
#define	strchr	index
#define	strrchr	rindex
#endif
#ifndef	BSD
#include <termio.h>
#else
#include <sgtty.h>
#endif
#include "config.h"
#include "lastlog.h"
#include "faillog.h"

#ifndef	lint
static	char	_sccsid[] = "@(#)lmain.c	2.1	01:23:18	6/20/89";
#endif

#ifndef	ERASECHAR
#define	ERASECHAR	'\b'		/* backspace */
#endif

#ifndef	KILLCHAR
#define	KILLCHAR	'\025'		/* control U */
#endif

char	name[BUFSIZ];
char	pass[BUFSIZ];
char	home[BUFSIZ];
char	prog[BUFSIZ];
char	mail[BUFSIZ];
#ifdef	HUSHLOGIN
char	hush[BUFSIZ];
int	hushed;
#endif

struct	passwd	pwent;
struct	utmp	utent;
struct	lastlog	lastlog;
#ifndef	BSD
struct	termio	termio;
#endif

#ifndef	MAXENV
#define	MAXENV	64
#endif

char	*newenvp[MAXENV];
int	newenvc = 0;
int	maxenv = MAXENV;
extern	char	**environ;

char	*getenv ();
void	checkutmp ();
void	addenv ();
void	setenv ();
unsigned alarm ();
void	login ();
void	entry ();
void	setutmp ();
void	subsystem ();
void	log ();
void	setup ();
void	expire ();
void	motd ();
void	mailcheck ();
void	shell ();

#ifdef	TZ
FILE	*tzfile;
char	tzbuf[32] = TZ;
#endif

#ifndef	ALARM
#define	ALARM	60
#endif

#ifndef	RETRIES
#define	RETRIES	3
#endif

#ifdef	FAILLOG
struct	faillog	faillog;
#endif

int	main (argc, argv, envp)
int	argc;
char	**argv;
char	**envp;
{
	int	retries = RETRIES;
	int	failed;
#ifdef	CONSOLE
	int	conflag;
	char	console[BUFSIZ];
	FILE	*fp;
	struct	stat	statbuf;
#endif

	checkutmp ();			/* must be lowest level shell */

	if (! isatty (0))		/* must be a terminal */
		exit (1);

#ifndef	BSD
	(void) ioctl (0, TCGETA, &termio); /* get terminal characteristics */

	/*
	 * Add your favorite terminal modes here ...
	 */

	termio.c_lflag |= ISIG;

	termio.c_cc[VERASE] = ERASECHAR;
	termio.c_cc[VKILL] = KILLCHAR;
	(void) ioctl (0, TCSETAF, &termio); /* set erase and kill characters */
#endif
#ifdef	UMASK
	umask (UMASK);			/* override the default umask */
#endif
#ifdef	ULIMIT
	ulimit (2, (long) ULIMIT);	/* override the default ulimit */
#endif
	while (*envp)			/* add inherited environment, */
		addenv (*envp++);	/* some variables change later */

#ifdef	TZ
	if (tzbuf[0] == '/') {
		if ((tzfile = fopen (tzbuf, "r")) != (FILE *) 0) {
			if (fgets (tzbuf, sizeof tzbuf, tzfile)) {
				tzbuf[strlen (tzbuf) - 1] = '\0';
				addenv (tzbuf);
			}
			fclose (tzfile);
		}
	} else {
		addenv (tzbuf);
	}
#endif
#ifdef	HZ
	addenv (HZ);			/* set the default $HZ, if one */
#endif
	if (argc >= 2) {		/* now set command line variables */
		setenv (argc - 2, &argv[2]);
		(void) strncpy (name, argv[1], sizeof name);
	}
	(void) alarm (ALARM);		/* only allow ALARM sec. for login */

	while (1) {		/* repeatedly get login/password pairs */
		if (! name[0]) {	/* need to get a login id */
			login (name);
			continue;
		}
		entry (name, &pwent);	/* get entry from password file */
		failed = 0;		/* hasn't failed validation yet */

	/*
	 * Here we have a sticky situation.  Some accounts may have no
	 * password entry in the password file.  So, we don't ask for a
	 * password.  Others, have a blank password entered - you be the
	 * judge.  The conditional compilation NOBLANK requires even
	 * blank passwords to be prompted for.  This may well break
	 * quite a few systems.  Use with discretion.
	 */

#ifdef	NOBLANK
					/* get a password from user */
		if (! password ("Password:", pass))
			continue;
#else
		if ((! pwent.pw_name || pwent.pw_passwd)
					&& ! password ("Password:", pass))
			continue;
#endif
		if (! valid (pass, &pwent)) /* check encrypted passwords ... */
			failed = 1;

#ifdef	DIALUP
		alarm (30);
		if (! dialcheck (utent.ut_line,
				pwent.pw_shell ? pwent.pw_shell:"/bin/sh"))
			failed = 1;
#endif
#ifdef	CONSOLE
		if (pwent.pw_uid == 0 && stat (CONSOLE, &statbuf) == 0) {
			if ((statbuf.st_mode & S_IFMT) == S_IFREG) {
				fp = fopen (CONSOLE, "r");
				while (fp && fgets (console, BUFSIZ, fp)
						== console) {
					console[strlen (console) - 1] = '\0';
					if (! strcmp (console, utent.ut_line))
						break;
				}
				if (! fp || feof (fp))
					failed = 1;

				fclose (fp);
			} else {
				if (strcmp (CONSOLE, utent.ut_line))
					failed = 1;
			}
		}
#endif
#ifdef	FAILLOG
		if (! failcheck (pwent.pw_uid, &faillog, failed))
			failed = 1;
#endif
		if (! failed)
			break;

		puts ("Login incorrect");
#ifdef	FAILLOG
		if (pwent.pw_name)	/* don't log non-existent users */
			failure (pwent.pw_uid, utent.ut_line, &faillog);
#endif
		if (--retries <= 0)	/* only allow so many failures */
			exit (1);

#ifndef	BSD
		(void) memset (name, '\0', sizeof name);
		(void) memset (pass, '\0', sizeof pass);
#else
		bzero (name, sizeof name);
		bzero (pass, sizeof pass);
#endif
	}
	(void) alarm (0);		/* turn off alarm clock */
#ifdef	NOLOGINS
	/*
	 * Check to see if system is turned off for non-root users.
	 * This would be useful to prevent users from logging in
	 * during system maintenance.
	 */

	if (pwent.pw_uid != 0 && access (NOLOGINS, 0) == 0) {
		printf ("\r\nSystem closed for routine maintenance\n");
		exit (0);
	}
#endif
	environ = newenvp;		/* make new environment active */

	if (getenv ("IFS"))		/* don't export user IFS ... */
		addenv ("IFS= \t\n");	/* ... instead, set a safe IFS */

	setutmp ();			/* make entry in utmp & wtmp files */
	if (pwent.pw_shell && pwent.pw_shell[0] == '*') /* subsystem root */
		subsystem ();		/* figure out what to execute */

#ifdef	LASTLOG
	log ();				/* give last login and log this one */
#endif
	setup (&pwent);			/* set UID, GID, HOME, etc ... */
#ifdef	AGING
	if (pwent.pw_age)		/* check for age of password ... */
		expire (pwent.pw_age);	/* ... ask for new one if expired */
#endif
#ifdef	HUSHLOGIN
	sprintf (hush, "%s/.hushlogin", strchr (home, '=') + 1);
	hushed = access (hush, 0) != -1;
#endif
#ifdef	MOTD
	motd ();			/* print the message of the day */
#endif
#ifdef	FAILLOG
	if (faillog.fail_cnt != 0)
		failprint (pwent.pw_uid, &faillog);
#endif
#ifdef	LASTLOG
	if (lastlog.ll_time != 0 && ! hushed)
		printf ("Last login: %.19s on %s\n",
			ctime (&lastlog.ll_time), lastlog.ll_line);
#endif
#ifdef	MAILCHECK
	mailcheck ();			/* report on the status of mail */
#endif
#ifdef	TTYTYPE
	ttytype (utent.ut_line);
#endif
	signal (SIGINT, SIG_DFL);	/* default interrupt signal */
	signal (SIGQUIT, SIG_DFL);	/* default quit signal */
	signal (SIGTERM, SIG_DFL);	/* default terminate signal */
	signal (SIGALRM, SIG_DFL);	/* default alarm signal */

	shell (pwent.pw_shell);		/* exec the shell finally. */
	/*NOTREACHED*/
}
SHAR_EOF
fi
if test -f 'smain.c'
then
	echo shar: "will not over-write existing file 'smain.c'"
else
cat << \SHAR_EOF > 'smain.c'
#include <sys/types.h>
#include <stdio.h>
#include <pwd.h>
#ifndef	BSD
#include <string.h>
#include <memory.h>
#include <termio.h>
#else
#include <strings.h>
#include <sgtty.h>
#define	strchr	index
#define	strrchr	rindex
#endif
#include <signal.h>
#include "config.h"
#include "lastlog.h"

#ifndef	lint
static	char	_sccsid[] = "@(#)smain.c	2.1	01:23:55	6/20/89";
#endif

#ifndef	MAXENV
#define	MAXENV	64
#endif

#ifndef	PATH
#define	PATH	":/bin:/usr/bin"
#endif

#ifndef	SUPATH
#define	SUPATH	":/bin:/usr/bin:/etc"
#endif

#ifdef	HUSHLOGIN
char	hush[BUFSIZ];
int	hushed;
#endif

char	name[BUFSIZ];
char	pass[BUFSIZ];
char	home[BUFSIZ];
char	prog[BUFSIZ];
char	mail[BUFSIZ];
char	oldname[BUFSIZ];
char	*newenvp[MAXENV];
int	newenvc = 0;
int	maxenv = MAXENV;
struct	passwd	pwent;

#ifdef	TZ
FILE	*tzfile;
char	tzbuf[16] = TZ;
#endif

void	addenv ();
void	entry ();
void	sulog ();
void	subsystem ();
void	setup ();
void	motd ();
void	mailcheck ();
void	shell ();

extern	char	**environ;

int	main (argc, argv, envp)
int	argc;
char	**argv;
char	**envp;
{
	void	die ();
	char	*getenv ();
	char	*cp;
	int	doshell;
	int	fakelogin = 0;
	int	amroot;
	struct	passwd	*pw;
	struct	passwd	*getpwuid ();

	while (*envp)			/* add inherited environment, */
		addenv (*envp++);	/* some variables change later */

#ifdef	TZ
	if (tzbuf[0] == '/') {
		if ((tzfile = fopen (tzbuf, "r")) != (FILE *) 0) {
			if (fgets (tzbuf, sizeof tzbuf, tzfile)) {
				tzbuf[strlen (tzbuf) - 1] = '\0';
				addenv (tzbuf);
			}
			fclose (tzfile);
		}
	} else {
		addenv (tzbuf);
	}
#endif
#ifdef	HZ
	addenv (HZ);			/* set the default $HZ, if one */
#endif
	argc--; argv++;			/* shift out command name */

	if (argc > 0 && argv[0][0] == '-' && argv[0][1] == '\0') {
		fakelogin = 1;
		argc--; argv++;		/* shift ... */
	}
	if (argc > 0 && argv[0][0] != '-') {
		(void) strcpy (name, argv[0]);	/* use this login id */
		argc--; argv++;		/* shift ... */
	}
	doshell = argc == 0;		/* any arguments remaining? */

	if (pw = getpwuid (getuid ()))	/* need old user name */
		(void) strcpy (oldname, pw->pw_name);
	else				/* user ID MUST exist */
		goto failure;

	amroot = getuid () == 0;	/* currently am super user */

	if (! name[0]) 			/* use default user ID */
		(void) strcpy (name, "root");

	entry (name, &pwent);		/* get password file entry */
	if (pwent.pw_shell == (char *) 0)
		pwent.pw_shell = "/bin/sh";

	if (pwent.pw_name == (char *) 0) { /* unknown user */
		(void) fprintf (stderr, "Unknown id: %s\n", name);
		exit (1);
	}

	/*
	 * Here we have a sticky situation.  Some accounts may have no
	 * password entry in the password file.  So, we don't ask for a
	 * password.  Others, have a blank password entered - you be the
	 * judge.  The conditional compilation NOBLANK requires even
	 * blank passwords to be prompted for.  This may well break
	 * quite a few systems.  Use with discretion.
	 */

	die (0);

	signal (SIGHUP, die);
	signal (SIGINT, die);
	signal (SIGQUIT, die);
	signal (SIGTERM, die);

#ifdef	NOBLANK
	if (! amroot && ! password ("Password:", pass))
		goto failure;
#else
	if (! amroot && (pwent.pw_name == (char *) 0 || pwent.pw_passwd)
			&& ! password ("Password:", pass))
		goto failure;
#endif
					/* check encrypted passwords ... */
	if (! amroot && ! valid (pass, &pwent)) {
failure:	sulog (0);		/* log failed attempt */
		puts ("Sorry.");
		exit (1);
	}
	signal (SIGHUP, SIG_DFL);
	signal (SIGINT, SIG_DFL);
	signal (SIGQUIT, SIG_DFL);
	signal (SIGTERM, SIG_DFL);

#ifdef	SULOG
	sulog (1);			/* save SU information */
#endif
	if (pwent.pw_uid == 0)
		addenv (SUPATH);
	else
		addenv (PATH);

	environ = newenvp;		/* make new environment active */

	if (getenv ("IFS"))		/* don't export user IFS ... */
		addenv ("IFS= \t\n");	/* ... instead, set a safe IFS */

	if (doshell && pwent.pw_shell[0] == '*') /* subsystem root required */
		subsystem ();		/* figure out what to execute */

	if (fakelogin)
		setup (&pwent);		/* set UID, GID, HOME, etc ... */
	else {
		if (setgid (pwent.pw_gid) || setuid (pwent.pw_uid))  {
			perror ("Can't set ID");
			exit (1);
		}
	}
	if (! doshell) {		/* execute arguments as command */
		if (cp = getenv ("SHELL"))
			pwent.pw_shell = cp;
		argv[-1] = pwent.pw_shell;
		(void) execv (pwent.pw_shell, &argv[-1]);
		(void) fprintf (stderr, "No shell\n");
		exit (1);
	}
	if (fakelogin) {
#ifdef	HUSHLOGIN
		sprintf (hush, "%s/.hushlogin", strchr (home, '=') + 1);
		hushed = access (hush, 0) != -1;
#endif
#ifdef	MOTD
		motd ();		/* print the message of the day */
#endif
#ifdef	MAILCHECK
		mailcheck ();		/* report on the status of mail */
#endif
		shell (pwent.pw_shell);	/* exec the shell finally. */
	} else {
		if (cp = strrchr (pwent.pw_shell, '/'))
			cp++;
		else
			cp = pwent.pw_shell;

		execl (pwent.pw_shell, cp, (char *) 0);
		perror (pwent.pw_shell);
		exit (1);
	}
	/*NOTREACHED*/
}

/*
 * die - set or reset termio modes.
 *
 *	die() is called before processing begins.  signal() is then
 *	called with die() as the signal handler.  If signal later
 *	calls die() with a signal number, the terminal modes are
 *	then reset.
 */

void	die (killed)
int	killed;
{
#ifdef	BSD
	static	struct	sgtty	sgtty;

	if (killed)
		stty (0, &sgtty);
	else
		gtty (0, &sgtty);
#else
	struct	termio	sgtty;

	if (killed)
		ioctl (0, TCSETA, &sgtty);
	else
		ioctl (0, TCGETA, &sgtty);
#endif
	if (killed)
		exit (killed);
}
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'
/*
 * 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.  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>
#include "config.h"
#include "shadow.h"

#ifndef	lint
static	char	_sccsid[] = "@(#)pwconv.c	2.1	01:23:40	6/20/89";
#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;

	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);
	}
	unlink  ("nshadow");
	if ((fd = open ("nshadow", O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0 ||
			! (shadow = fdopen (fd, "w"))) {
		perror ("nshadow");
		return (1);
	}

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

	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;
		}
		if (pw->pw_passwd == (char *) 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");
				return (1);
			}
		} 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 */
				tspwd.sp_min = c64i (pw->pw_age[1]);
				tspwd.sp_max = c64i (pw->pw_age[0]);
				if (strlen (pw->pw_age) == 4)
					tspwd.sp_lstchg = a64l (&pw->pw_age[2]);
				else
					tspwd.sp_lstchg = 0L;

				/*
				 * Convert weeks to days
				 */

				tspwd.sp_min *= 7;
				tspwd.sp_max *= 7;
				tspwd.sp_lstchg *= 7;
			} else {	/* fake up new password age stuff */
				tspwd.sp_max = MAXDAYS;
				tspwd.sp_min = MINDAYS;
				tspwd.sp_lstchg = today;
			}
			if (! putspent (&tspwd, shadow)) { /* output entry */
				perror ("nshadow");
				return (1);
			}
		}
		(void) fprintf (npwd, "%s:%s:%d:%d:%s:%s:",
				pw->pw_name, pw->pw_passwd ? 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");
			return (1);
		}
	}
	endspent ();

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

	return (0);
}
SHAR_EOF
fi
if test -f 'dialup.c'
then
	echo shar: "will not over-write existing file 'dialup.c'"
else
cat << \SHAR_EOF > 'dialup.c'
#include <stdio.h>
#ifndef	BSD
#include <string.h>
#else
#include <strings.h>
#define	strchr	index
#define	strrchr	rindex
#endif
#include "dialup.h"

#ifndef	lint
static	char	_sccsid[] = "@(#)dialup.c	2.1	01:23:06	6/20/89";
#endif

static	FILE	*dialpwd;

void	setduent ()
{
	if (dialpwd)
		rewind (dialpwd);
	else
		dialpwd = fopen (DIALPWD, "r");
}

void	endduent ()
{
	if (dialpwd)
		fclose (dialpwd);

	dialpwd = (FILE *) 0;
}

struct	dialup	*getduent ()
{
	static	struct	dialup	dialup;	/* static structure to point to */
	static	char	shell[64];	/* some space for a login shell */
	static	char	passwd[16];	/* some space for dialup password */
	char	buf[BUFSIZ];
	char	*cp;

	if (! dialpwd)
		setduent ();

	if (! dialpwd || feof (dialpwd))
		return ((struct dialup *) 0);

	while (fgets (buf, BUFSIZ, dialpwd) == buf && buf[0] == '#')
		;

	if (feof (dialpwd))
		return ((struct dialup *) 0);

	cp = strchr (buf, ':');
	if (cp - buf > sizeof shell)	/* something is fishy ... */
		return ((struct dialup *) 0);

	(void) strncpy (shell, buf, cp - buf);
	shell[cp - buf] = '\0';

	if (strlen (cp + 1) > sizeof passwd) /* something is REALLY fishy */
		return ((struct dialup *) 0);

	(void) strcpy (passwd, cp + 1);
	passwd[strlen (passwd) - 1] = '\0';
	if (cp = strchr (passwd, ':'))
		*cp = '\0';

	dialup.du_shell = shell;
	dialup.du_passwd = passwd;

	return (&dialup);
}

struct	dialup	*getdushell (shell)
char	*shell;
{
	struct	dialup	*dialup;

	while (dialup = getduent ()) {
		if (strcmp (shell, dialup->du_shell) == 0)
			return (dialup);

		if (strcmp (dialup->du_shell, "*") == 0)
			return (dialup);
	}
	return ((struct dialup *) 0);
}

int	isadialup (tty)
char	*tty;
{
	FILE	*fp;
	char	buf[BUFSIZ];
	int	dialup = 0;

	if (! (fp = fopen (DIALUPS, "r")))
		return (0);

	while (fgets (buf, BUFSIZ, fp) == buf) {
		if (buf[0] == '#')
			continue;

		buf[strlen (buf) - 1] = '\0';

		if (strcmp (buf, tty) == 0) {
			dialup = 1;
			break;
		}
	}
	fclose (fp);

	return (dialup);
}
SHAR_EOF
fi
if test -f 'dialchk.c'
then
	echo shar: "will not over-write existing file 'dialchk.c'"
else
cat << \SHAR_EOF > 'dialchk.c'
#include <stdio.h>
#include "config.h"
#include "dialup.h"

#ifndef	lint
static	char	_sccsid[] = "@(#)dialchk.c	2.1	01:23:05	6/20/89";
#endif

/*
 * Check for dialup password
 *
 *	dialcheck tests to see if tty is listed as being a dialup
 *	line.  If so, a dialup password may be required if the shell
 *	is listed as one which requires a second password.
 */

#ifdef	DIALUP
int	dialcheck (tty, shell)
char	*tty;
char	*shell;
{
	char	*crypt ();
	char	*getpass ();
	struct	dialup	*dialup;
	char	*pass;
	char	*cp;

	if (! isadialup (tty))
		return (1);

	if (! (dialup = getdushell (shell)))
		return (1);

	endduent ();

	if (dialup->du_passwd[0] == '\0')
		return (1);

	if (! (pass = getpass ("Dialup Password:")))
		return (0);

	cp = crypt (pass, dialup->du_passwd);
	return (strcmp (cp, dialup->du_passwd) == 0);
}
#endif
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'
/*
 * 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	2.1	01:23:44	6/20/89";
#endif

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

int	main ()
{
	struct	passwd	*pw;
	struct	passwd	*sgetpwent ();
	FILE	*pwd;
	FILE	*npwd;
	struct	spwd	*spwd;
	int	fd;

	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) {
			spwd->sp_max /= 7;	/* turn it into weeks */
			spwd->sp_min /= 7;
			spwd->sp_lstchg /= 7;
			pw->pw_age = l64a ((long) spwd->sp_lstchg * (64L*64L) +
						  spwd->sp_min * (64L) +
						  spwd->sp_max);
		} else
			pw->pw_age = (char *) 0;

		if (pw->pw_age)
			(void) fprintf (npwd, "%s:%s,%s:%d:%d:%s:%s:%s\n",
				pw->pw_name, pw->pw_passwd ? pw->pw_passwd:"",
				pw->pw_age, pw->pw_uid, pw->pw_gid,
				pw->pw_gecos, pw->pw_dir,
				pw->pw_shell ? pw->pw_shell:"");
		else
			(void) fprintf (npwd, "%s:%s:%d:%d:%s:%s:%s\n",
				pw->pw_name, pw->pw_passwd ? pw->pw_passwd:"",
				pw->pw_uid, pw->pw_gid,
				pw->pw_gecos, pw->pw_dir,
				pw->pw_shell ? pw->pw_shell:"");
	}
	endspent ();

	if (ferror (npwd)) {
		perror ("pwunconv");
		(void) unlink ("npasswd");
	}
	(void) fclose (npwd);
	(void) fclose (pwd);
	return (0);
}
SHAR_EOF
fi
if test -f 'failure.c'
then
	echo shar: "will not over-write existing file 'failure.c'"
else
cat << \SHAR_EOF > 'failure.c'
#include <sys/types.h>
#include <fcntl.h>
#include <time.h>
#ifndef	BSD
#include <string.h>
#include <memory.h>
#else
#include <strings.h>
#define	strchr	index
#define	strrchr	rindex
#endif
#include "faillog.h"
#include "config.h"

#ifndef	lint
static	char	_sccsid[] = "@(#)failure.c	2.1	01:23:16	6/20/89";
#endif

#ifdef	FAILLOG

#define	DAY	(24L*3600L)
#define	YEAR	(365L*DAY)
#define	NOW	(time ((time_t *) 0))

extern	struct	tm	*localtime ();
extern	char	*asctime ();
extern	void	failprint ();

/*
 * failure - make failure entry
 */

void
failure (uid, tty, faillog)
int	uid;
char	*tty;
struct	faillog	*faillog;
{
	int	fd;

	if ((fd = open (FAILFILE, O_RDWR)) < 0)
		return;

	lseek (fd, (off_t) (sizeof *faillog) * uid, 0);
	if (read (fd, (char *) faillog, sizeof *faillog)
			!= sizeof *faillog)
#ifndef	BSD
		memset ((void *) faillog, '\0', sizeof *faillog);
#else
		bzero ((char *) faillog, sizeof *faillog);
#endif

	if (faillog->fail_max == 0 || faillog->fail_cnt < faillog->fail_max)
		faillog->fail_cnt++;

	strncpy (faillog->fail_line, tty, sizeof faillog->fail_line);
	faillog->fail_time = time ((time_t *) 0);

	lseek (fd, (off_t) (sizeof *faillog) * uid, 0);
	write (fd, (char *) faillog, sizeof *faillog);
	close (fd);
}

/*
 * failcheck - check for failures > allowable
 *
 * failcheck() is called AFTER the password has been validated.
 */

int
failcheck (uid, faillog, failed)
int	uid;
struct	faillog	*faillog;
{
	int	fd;
	int	okay = 1;
	struct	faillog	fail;

	if ((fd = open (FAILFILE, O_RDWR)) < 0)
		return (1);

	lseek (fd, (off_t) (sizeof *faillog) * uid, 0);
	if (read (fd, (char *) faillog, sizeof *faillog) == sizeof *faillog) {
		if (faillog->fail_max != 0
				&& faillog->fail_cnt >= faillog->fail_max)
			okay = 0;
	}
	if (!failed && okay) {
		fail = *faillog;
		fail.fail_cnt = 0;

		lseek (fd, (off_t) sizeof fail * uid, 0);
		write (fd, (char *) &fail, sizeof fail);
	}
	close (fd);

	return (okay);
}

/*
 * failprint - print line of failure information
 */

void
failprint (uid, fail)
struct	faillog	*fail;
{
	int	fd;
	struct	tm	*tp;
	char	*lasttime;

	if (fail->fail_cnt == 0)
		return;

	tp = localtime (&fail->fail_time);
	lasttime = asctime (tp);
	lasttime[24] = '\0';

	if (NOW - fail->fail_time < YEAR)
		lasttime[19] = '\0';
	if (NOW - fail->fail_time < DAY)
		lasttime = lasttime + 11;

	if (*lasttime == ' ')
		lasttime++;

	printf ("%d %s since last login.  Last was %s on %s.\n",
		fail->fail_cnt, fail->fail_cnt > 1 ? "failures":"failure",
		lasttime, fail->fail_line);
}
#endif
SHAR_EOF
fi
if test -f 'faillog.h'
then
	echo shar: "will not over-write existing file 'faillog.h'"
else
cat << \SHAR_EOF > 'faillog.h'
/*
 * faillog.h - login failure logging file format
 *
 *	@(#)faillog.h	2.1	01:23:15	6/20/89
 *
 * The login failure file is maintained by login(1) and fail(1L)
 * Each record in the file represents a separate UID and the file
 * is indexed in that fashion.
 */

#define	FAILFILE	"/usr/adm/faillog"

struct	faillog {
	short	fail_cnt;	/* failures since last success */
	short	fail_max;	/* failures before turning account off */
	char	fail_line[12];	/* last failure occured here */
	time_t	fail_time;	/* last failure occured then */
};
SHAR_EOF
fi
if test -f 'faillog.c'
then
	echo shar: "will not over-write existing file 'faillog.c'"
else
cat << \SHAR_EOF > 'faillog.c'
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <pwd.h>
#include <time.h>
#ifndef	BSD
#include <string.h>
#include <memory.h>
#else
#include <strings.h>
#define	strchr	index
#define	strrchr	rindex
#endif
#include "config.h"
#include "faillog.h"

#ifndef	lint
static	char	_sccsid[] = "@(#)faillog.c	2.1	01:23:12	6/20/89";
#endif

FILE	*fail;		/* failure file stream */
off_t	user;		/* one single user, specified on command line */
int	days;		/* number of days to consider for print command */
time_t	seconds;	/* that number of days in seconds */
int	max;		/* maximum failure count for fail_max */

int	mflg;		/* set fail_max for a given user */
int	rflg;		/* reset fail_cnt for user or all user's */
int	uflg;		/* set if user is a valid user id */
int	tflg;		/* print is restricted to most recent days */
struct	faillog	faillog; /* scratch structure to play with ... */
struct	stat	statbuf; /* fstat buffer for file size */

extern	int	optind;
extern	char	*optarg;
extern	char	*asctime ();
extern	struct	passwd	*getpwuid ();
extern	struct	passwd	*getpwnam ();
extern	struct	passwd	*getpwent ();
extern	struct	tm	*localtime ();

#define	DAY	(24L*3600L)
#define	NOW	(time ((time_t *) 0))

main (argc, argv)
int	argc;
char	**argv;
{
	char	*mode;
	int	uid = 0;
	int	c;
	struct	passwd	*pwent;

	if (getuid () == 0)	/* only root can update anything */
		mode = "r+";
	else			/* all others can only look */
		mode = "r";

	if ((fail = fopen (FAILFILE, mode)) == (FILE *) 0) {
		perror (FAILFILE);
		exit (1);
	}
	while ((c = getopt (argc, argv, "m:pru:t:")) != EOF) {
		switch (c) {
			case 'm':
				max = atoi (optarg);
				setmax ();
				break;
			case 'p':
				print ();
				break;
			case 'r':
				reset ();
				break;
			case 'u':
				pwent = getpwnam (optarg);
				if (! pwent) {
					fprintf (stderr, "Unknown User: %s\n", optarg);
					exit (1);
				}
				uflg++;
				user = pwent->pw_uid;
				break;
			case 't':
				days = atoi (optarg);
				seconds = days * DAY;
				tflg++;
				break;
		}
	}
	fclose (fail);
	exit (0);
}

print ()
{
	int	uid;
	off_t	offset;

	if (uflg) {
		offset = user * sizeof faillog;
		fstat (fileno (fail), &statbuf);
		if (offset >= statbuf.st_size)
			return;

		fseek (fail, (off_t) user * sizeof faillog, 0);
		if (fread ((char *) &faillog, sizeof faillog, 1, fail) == 1)
			print_one (&faillog, user);
		else
			perror (FAILFILE);
	} else {
		for (uid = 0;
			fread ((char *) &faillog, sizeof faillog, 1, fail) == 1;
				uid++) {

			if (faillog.fail_cnt == 0)
				continue;

			if (tflg && NOW - faillog.fail_time > seconds)
				continue;

			print_one (&faillog, uid);
		}
	}
}

print_one (faillog, uid)
struct	faillog	*faillog;
{
	static	int	once;
	char	*cp;
	struct	tm	*tm;
	struct	passwd	*pwent;

	if (! once) {
		printf ("Username        Failures    Maximum     Latest\n");
		once++;
	}
	pwent = getpwuid (uid);
	tm = localtime (&faillog->fail_time);
	cp = asctime (tm);
	cp[24] = '\0';

	if (pwent) {
		printf ("%-16s    %4d       %4d",
			pwent->pw_name, faillog->fail_cnt, faillog->fail_max);
		if (faillog->fail_time)
			printf ("     %s on %s\n", cp, faillog->fail_line);
		else
			putchar ('\n');
	}
}

reset ()
{
	int	uid = 0;

	if (uflg)
		reset_one (user);
	else
		for (uid = 0;reset_one (uid);uid++)
			;
}

reset_one (uid)
int	uid;
{
	off_t	offset;

	offset = uid * sizeof faillog;
	fstat (fileno (fail), &statbuf);
	if (offset >= statbuf.st_size)
		return (0);

	if (fseek (fail, offset, 0) != 0) {
		perror (FAILFILE);
		return (0);
	}
	if (fread ((char *) &faillog, sizeof faillog, 1, fail) != 1) {
		if (! feof (fail))
			perror (FAILFILE);

		return (0);
	}
	if (faillog.fail_cnt == 0)
		return (1);	/* don't fill in no holes ... */

	faillog.fail_cnt = 0;

	if (fseek (fail, offset, 0) == 0
		&& fwrite ((char *) &faillog, sizeof faillog, 1, fail) == 1) {
		fflush (fail);
		return (1);
	} else {
		perror (FAILFILE);
	}
	return (0);
}

setmax ()
{
	int	uid = 0;
	struct	passwd	*pwent;

	if (uflg) {
		setmax_one (user);
	} else {
		setpwent ();
		while (pwent = getpwent ())
			setmax_one (pwent->pw_uid);
	}
}

setmax_one (uid)
int	uid;
{
	off_t	offset;

	offset = uid * sizeof faillog;

	if (fseek (fail, offset, 0) != 0) {
		perror (FAILFILE);
		return;
	}
	if (fread ((char *) &faillog, sizeof faillog, 1, fail) != 1) {
		if (! feof (fail))
			perror (FAILFILE);
	} else {
#ifndef	BSD
		memset ((char *) &faillog, '\0', sizeof faillog);
#else
		bzero ((char *) &faillog, sizeof faillog);
#endif
	}
	faillog.fail_max = max;

	if (fseek (fail, offset, 0) == 0
		&& fwrite ((char *) &faillog, sizeof faillog, 1, fail) == 1)
		fflush (fail);
	else
		perror (FAILFILE);
}
SHAR_EOF
fi
exit 0
#	End of shell archive
-- 
John F. Haugh II                        +-Button of the Week Club:-------------
VoiceNet: (512) 832-8832   Data: -8835  | "AIX is a three letter word,
InterNet: jfh@rpp386.Cactus.Org         |  and it's BLUE."
UucpNet : <backbone>!bigtex!rpp386!jfh  +--------------------------------------

jfh@rpp386.Dallas.TX.US (John F. Haugh II) (06/20/89)

X-Archive-Name: shadow2/part3

Part 3 of second USENET release
--
#! /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:
#	login.1
#	passwd.1
#	passwd.4
#	shadow.3
#	shadow.4
#	su.1
#	sulogin.8
#	pwconv.8
#	pwunconv.8
#	faillog.8
#	faillog.4
# This archive created: Tue Jun 20 01:28:09 1989
# By:	John F. Haugh II (River Parishes Programming, Plano TX)
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'login.1'
then
	echo shar: "will not over-write existing file 'login.1'"
else
cat << \SHAR_EOF > 'login.1'
.TH LOGIN 1
.SH NAME
login \- Begin session on the system
.SH SYNOPSIS
.B login
[ username [ environmental-variables ] ]
.SH DESCRIPTION
.I login
is used to establish a new session with the system.
It is normally invoked automatically by responding to the
.B login:
prompt on the user\'s terminal.
.I login
may be special to the shell and may not be invoked as a sub-process.
Typically,
.I login
is treated by the shell as \fBexec login\fR which causes the user
to exit from the current shell.
Attempting to execute \fIlogin\fR from any shell but the login shell
will produce an error message.
.PP
When invoked from the \fBlogin:\fR prompt, the user may enter
environmental variables after the username.
These variables are entered in the form \fBNAME=VALUE\fR.
Not all variables may be set in the fashion, notably \fBPATH\fR,
\fBHOME\fR and \fBSHELL\fR.
Additionally, \fBIFS\fR may be inhibited if the user\'s login
shell is \fB/bin/sh\fR.
.PP
The user is then prompted for a password, where appropriate.
Echoing is disabled to prevent revealing the password.
Only a small number of password failures are permitted before
\fIlogin\fR exits and the communications link is severed.
.PP
If password aging has been enabled for your account, you may be
prompted for a new password before proceeding.
You will be forced to provide your old password and the new
password before continuing.
Please refer to \fIpasswd(1)\fR for more information.
.PP
After a successful login,
you will be informed of any system messages and the presence
of mail.
You may turn off the printing of the system message file,
\fI/etc/motd\fR, by creating a zero-length file \fI.hushlogin\fR
in your login directory.
The mail message will be one of "\fIYou have new mail.\fR",
"\fIYou have mail.\fR", or "\fINo Mail.\fR" according to
the condition of your mailbox.
.PP
Your user and group ID will be set according to their values in
the \fI/etc/passwd\fR file.
The value for \fB$HOME\fR, \fB$SHELL\fR, \fB$PATH\fR, \fB$LOGNAME\fR,
and \fB$MAIL\fR are set according to the appropriate fields in the
password entry.
Ulimit, umask and nice values may also be set according to
entries in the GECOS field.
.PP
On some installations, the environmental variable \fB$TERM\fR will be
initialize to the terminal type on your tty line, as specified in
\fI/etc/ttytype\fR.
.PP
An initialization script for your command interpreter may also be
executed.
Please see the appropriate manual section for more information on
this function.
.SH CAVEATS
.PP
This version of \fIlogin\fR has many compilation options, only some of which
may be in use at any particular site.
.SH Files
/etc/utmp \- list of current login sessions
.br
/etc/wtmp \- list of previous login sessions
.br
/etc/passwd \- user account information
.br
/etc/shadow \- encrypted passwords and age information
.br
/etc/motd \- system message file
.br
/etc/ttytype \- list of terminal types
.br
$HOME/.profile \- initialization script for default shell
.br
$HOME/.hushlogin \- suppress printing of system messages
.br
.SH See Also
.PP
getty(1M),
mail(1),
passwd(1),
sh(1),
su(1),
d_passwd(4),
passwd(4)
SHAR_EOF
fi
if test -f 'passwd.1'
then
	echo shar: "will not over-write existing file 'passwd.1'"
else
cat << \SHAR_EOF > 'passwd.1'
.TH PASSWD 1
.SH NAME
passwd \- change user password
.SH SYNOPSIS
\fBpasswd\fR [ \fB-f\fR ] [ \fIname\fR ]
.SH DESCRIPTION
\fIpasswd\f changes passwords for user accounts.
A normal user may only change the password for their own account,
the super user may change the password for any account.
.PP
The user is first prompted for their old password,
if one is present.
This password is then encrypted and compared against the
stored password.
The user has only one chance to enter the correct password.
The super user is permitted to bypass this step so that forgotten
passwords may be changed.
.PP
After the password has been entered password aging information
is checked to see if the user is permitted to change their password
at this time.
If not, \fIpasswd\fR refuses to change the password and exits.
.PP
The user is then prompted for a replacement password.
This password is tested for complexity.
As a general guideline,
passwords should consist of 6 to 8 characters including
one or more from each of following sets:
.IP "" .5i
Lower case alphabetics
.IP "" .5i
Upper case alphabetics
.IP "" .5i
Digits 0 thru 9
.IP "" .5i
Punctuation marks
.PP
Care must be taken not to include the system default erase
or kill characters.
\fIpasswd\fR will reject any password which is not suitably
complex.
.PP
If the password is accepted,
\fIpasswd\fR will prompt again and compare the second entry
against the first.
Both entries are require to match in order for the password
to be changed.
.SH Hints
The security of a password depends upon the strength of the
encryption algorithm and the size of the key space.
The \fB\s-2UNIX\s+2\fR System encryption method is based on
the NBS DES algorithm and is very secure.
.PP
Compromises in password security normally result from careless
password selection or handling.
For this reason, you should select a password which does not
appear in a dictionary or which must be written down.
The password should also not be a proper name, your license
number, birth date, or street address.
Any of these may be used as guesses to violate system security.
.PP
Your password must easily remembered so that you will not
be forced to write it on a piece of paper.
This can be accomplished by appending two small words together
and separating each with a special character or digit.
For example, Pass%word.
.PP
Other methods of construction involve selecting an easily
remembered phrase from literature and selecting the first
or last letter from each.
An example of this is
.IP "" .5i
Ask not for whom the bell tolls.
.PP
which produces
.IP "" .5i
An4wtbt.
.PP
You may be reasonably sure few crackers will have
included this in their dictionary.
.SH CAVEATS
Not all options may be supported.
Password complexity checking may vary from site to site.
The user is urged to select as complex a password as they
feel comfortable with.
A \fB-f\fR option exists to permit the superuser to override
any password complexity testing,
normal users must create passwords which pass the complexity
test.
.SH Files
/etc/passwd \- user account information
.br
/etc/shadow \- encrypted user passwords
.SH See Also
passwd(3),
passwd(4)
SHAR_EOF
fi
if test -f 'passwd.4'
then
	echo shar: "will not over-write existing file 'passwd.4'"
else
cat << \SHAR_EOF > 'passwd.4'
.TH PASSWD 4
.SH NAME
passwd \- The password file
.SH DESCRIPTION
.I passwd
contains various pieces of information for each user account.
Included is
.IP "" .5i
Login name
.IP "" .5i
Optional encrypted password
.IP "" .5i
Numerical user ID
.IP "" .5i
Numerical group ID
.IP "" .5i
User name or comment field
.IP "" .5i
User home directory
.IP "" .5i
User command interpreter
.PP
The password field may not be filled if shadow passwords
have been enabled.
If shadow passwords are being used, the encrypted password will
be found in \fB/etc/shadow\fR.
The encryped password consists of 13 characters from the
64 character alphabet
a thru z, A thru Z, 0 thru 9, \. and /.
Refer to \fIcrypt(3)\fR for details on how this string is
interpreted.
.PP
An optional password age string may follow the encrypted
password, separated by a comma, from the same alphabet
as the password itself.
The first character gives the number of weeks during which the
password is valid.
The second character gives the number of weeks which must pass
before the user is permitted to change the password.
The last two characters give the week since Jan 1970 when the
password was last changed.
When the number of weeks during which the password is valid
have passed, the user will be required to provide a new
password.
.PP
The comment field is used by various system utilities, such as
\fIfinger(1)\fR.
Three additional values may be present in the comment field.
They are
.IP "" .5i
pri= \- set initial value of nice
.IP "" .5i
umask= \- set initial value of umask
.IP "" .5i
ulimit= \- set initial value of ulimit
.PP
These fields are separated from each other and from any other
comment field by a comma.
.PP
The home directory field provides the name of the initial
working directory.
\fILogin\fR uses this information to set the value of
the \fBHOME\fR environmental variable.
.PP
The command interpreter field provides the name of the user's
command language interpreter, or the name of the initial program
to execute.
\fILogin\fR uses this information to set the value of the
\fBSHELL\fR environmental variable.
If this field is empty, it defaults to the value \fB/bin/sh\fR.
.SH Files
/etc/passwd \- user account information
.SH See Also
login(1),
passwd(1),
su(1),
sulogin(1M),
shadow(4),
pwconv(8),
pwunconv(8)
SHAR_EOF
fi
if test -f 'shadow.3'
then
	echo shar: "will not over-write existing file 'shadow.3'"
else
cat << \SHAR_EOF > 'shadow.3'
.TH SHADOW 3
.SH NAME
shadow \- encrypted password file routines
.SH Syntax
.IP "" .5i
#include <shadow.h>
.IP "" .5i
struct spwd *getspent();
.br
struct spwd *getspnam(char * name);
.br
void setspent();
.br
void endspent();
.br
struct spwd *fgetspent(FILE *fp);
.br
int putspent(struct spwd *p,FILE *fp);
.SH DESCRIPTION
.I shadow
manipulates the contents of the shadow password file,
\fB/etc/shadow\fR.
The structure in the \fI#include\fR file is
.IP "" .5i
struct spwd {
.br
	char	*sp_namp; /* user login name */
.br
	char	*sp_pwdp; /* encrypted password */
.br
	long	sp_lstchg; /* last password change */
.br
	int	sp_max; /* days before change required */
.br
	int	sp_min; /* days until change allowed. */
.br
}
.PP
The meanings of each field are
.IP "" .5i
sp_namp \- pointer to null-terminated user name.
.IP "" .5i
sp_pwdp \- pointer to null-terminated password.
.IP "" .5i
sp_lstchg \- days since Jan 1, 1970 password was last changed.
.IP "" .5i
sp_max \- days after which password must be changed
.IP "" .5i
sp_min \- days before which password may not be changed.
.SH Description
\fIgetspent\fR, \fIgetspname\fR, and \fIfgetspent\fR each return
a pointer to a \fBstruct spent\fR.
\fIgetspent\fR returns the
next entry from the file, and \fIfgetspent\fR returns the next
entry from the given stream, which is assumed to be a file of
the proper format.
\fIgetspnam\fR searches from the current position in the file for
an entry matching \fIname\fR.
.PP
\fIsetspent\fR and \fIendspent\fR may be used to begin and end,
respectively, access to the shadow password file.
.SH Diagnostics
Routines return NULL if no more entries are available or if an
error occurs during processing.
.SH Caveats
These routines may only be used by the super user as access to
the shadow password file is restricted.
.SH Files
/etc/shadow \- encrypted user passwords
.SH See Also
getpwent(3),
shadow(4)
SHAR_EOF
fi
if test -f 'shadow.4'
then
	echo shar: "will not over-write existing file 'shadow.4'"
else
cat << \SHAR_EOF > 'shadow.4'
.TH SHADOW 4
.SH NAME
shadow \- encrypted password file
.SH DESCRIPTION
.I shadow
contains the encrypted password information for user's accounts
and optional the password aging information.
Included is
.IP "" .5i
Login name
.IP "" .5i
Encrypted password
.IP "" .5i
Date password last changed
.IP "" .5i
Days before password may be changed
.IP "" .5i
Days after which password must be changed
.PP
The password field must be filled.
The encryped password consists of 13 characters from the
64 character alphabet
a thru z, A thru Z, 0 thru 9, \. and /.
Refer to \fIcrypt(3)\fR for details on how this string is
interpreted.
.PP
The date of the last password change is given as the number
of days since Jan 1, 1970.
The password may not be changed again until the proper number
of days have passed, and must be changed after the maximum
number of days.
If the minimum number of days required is greater than the
maximum number of day allowed, this password may not be
changed by the user.
.PP
This information supercedes any password or password age
information present in \fB/etc/passwd\fR.
.PP
This file must not be readable by regular users if password
security is to be maintained.
.SH Files
/etc/passwd \- user account information
.br
/etc/shadow \- encrypted user passwords
.SH See Also
login(1),
passwd(1),
su(1),
sulogin(1M),
passwd(4),
pwconv(8),
pwunconv(8)
SHAR_EOF
fi
if test -f 'su.1'
then
	echo shar: "will not over-write existing file 'su.1'"
else
cat << \SHAR_EOF > 'su.1'
.TH SU 1
.SH NAME
su \- Change user ID or become super-user
.SH SYNOPSIS
.B su
[ - ] [ username [ args ] ]
.SH DESCRIPTION
.I su
is used to become another user during a login session.
Invoked without a username, \fIsu\fR defaults to becoming
the super user.
The optional argument \fB\-\fR may be used to provide an
environment similiar to what the user would expect had
the user logged in directly.
.PP
Additional arguments may be provided after the username,
in which case they are supplied to the user\'s login shell.
In particular, an argument of \fB-c\fR will cause the
next argument to be treated as a command by most command
interpreters.
The command will be executed under the shell specified by
\fB$SHELL\fR, or if undefined, by the one specified in
\fI/etc/passwd\fR.
.PP
The user will be prompted for a password, if appropriate.
Invalid passwords will produce an error message.
All attempts, both valid and invalid, are logged to detect
abuses of the system.
.PP
The current environment is passed to the new shell.
The value of \fB$PATH\fR is reset to \fB/bin:/usr/bin\fR
for normal users, or \fB/bin:/usr/bin:/etc\fR for the super user.
.SH CAVEATS
.PP
This version of \fIsu\fR has many compilation options, only some of which
may be in use at any particular site.
.SH Files
/etc/passwd \- user account information
.br
/etc/shadow \- encrypted passwords and age information
.br
$HOME/.profile \- initialization script for default shell
.SH See Also
login(1),
sh(1)
SHAR_EOF
fi
if test -f 'sulogin.8'
then
	echo shar: "will not over-write existing file 'sulogin.8'"
else
cat << \SHAR_EOF > 'sulogin.8'
.TH SULOGIN 8
.SH NAME
sulogin \- Single-user login
.SH DESCRIPTION
.I sulogin
is invoked by \fB/etc/init\fR prior to allowing the user
access to the system when in single user mode.
This feature may only be available on certain systems where
\fIinit\fR has been modified accordingly, or where the
\fB/etc/inittab\fR has an entry for a single user login.
.PP
The user is prompted
.IP "" .5i
Type control-d for normal startup,
.br
(or give root password for system maintenance):
.PP
If the user enters the correct root password, a login session
is initiated.
When \fBEOF\fR is pressed instead, the system enters multi-user
mode.
.PP
After the user exits the single-user shell, or presses \fBEOF\fR,
the system begins the initialization process required to enter
multi-user mode.
.SH CAVEATS
.PP
This command can only be used if \fIinit\fR has been modified to call
\fB/etc/sulogin\fR instead of \fB/bin/sh\fR,
or if the user has set the \fIinittab\fR to support a single user
login.
.PP
As complete an environment as possible is created.
However, various devices may be unmounted or uninitialized and many 
of the user commands may be unavailable or nonfunctional as a result.
.SH Files
/etc/passwd \- user account information
.br
/etc/shadow \- encrypted passwords and age information
.br
/.profile \- initialization script for single user shell
.SH See Also
login(1),
init(1M),
sh(1)
SHAR_EOF
fi
if test -f 'pwconv.8'
then
	echo shar: "will not over-write existing file 'pwconv.8'"
else
cat << \SHAR_EOF > 'pwconv.8'
.TH PWCONV 8
.SH NAME
pwconv \- convert and update shadow password files
.SH SYNOPSIS
/etc/pwconv
.SH DESCRIPTION
\fIPwconv\fR 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 \fBnpasswd\fR,
the new shadow file is left in \fBnshadow\fR.
Both of these are files are created with modes which only permit
read access to the owner.
Existing shadow entries are copied as is.
New entries are created with passwords which expire in 10000 days,
with a last changed date of today,
unless password aging information was already present.
Entries with blank passwords are not copied to the shadow file at all.
.SH Files
/etc/passwd \- old encrypted passwords and password aging
.br
/etc/shadow \- previously converted shadow password file
.br
./npasswd \- new password file
.br
./nshadow \- new shadow password file
.SH See Also
passwd(1),
passwd(4),
shadow(4),
pwunconv(8)
SHAR_EOF
fi
if test -f 'pwunconv.8'
then
	echo shar: "will not over-write existing file 'pwunconv.8'"
else
cat << \SHAR_EOF > 'pwunconv.8'
.TH PWUNCONV 8
.SH NAME
pwunconv \- restore old password file from shadow password file
.SH SYNOPSIS
/etc/pwunconv
.SH DESCRIPTION
\fIPwunconv\fR 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 \fBnpasswd\fR.
This file is created with modes which allow read access for
the owner only.
There is no new shadow file.
Password aging information is translated where possible.
.SH Files
/etc/passwd \- old encrypted passwords and password aging
.br
/etc/shadow \- previously converted shadow password file
.br
./npasswd \- new password file
.SH See Also
passwd(1),
passwd(4),
shadow(4),
pwconv(8)
SHAR_EOF
fi
if test -f 'faillog.8'
then
	echo shar: "will not over-write existing file 'faillog.8'"
else
cat << \SHAR_EOF > 'faillog.8'
.TH FAILLOG 8
.SH NAME
faillog \- examine faillog and set login failure limits
.SH SYNOPSIS
/etc/faillog [ -u uid ] [ -t days ] [ -m max ] [ -pr ] 
.SH DESCRIPTION
\fIPlastlog\fR formats the contents of the failure log,
\fI/usr/adm/faillog\fR, and maintains failure counts and
limits.
The order of the arguments to \fIfaillog\fR is significant.
Each argument is processed immediately in the order given.
.PP
The \fB-p\fR flag causes failure entries to be printed in UID
order.
Entering \fB-u login-name\fR flag will
cause the failure record for \fBlogin-name\fR only to be printed.
Entering \fB-t days\fR will cause only the
failures more recent than \fBdays\fR to be printed.
The \fB-t\fR flag overrides the use of \fB-u\fR.
.PP
The \fB-r\fR flag is used to reset the count of login failures.
Write access to \fI/usr/adm/faillog\fR is required for
this option.
Entering \fB-u login-name\fR will cause only the failure count
for \fBlogin-name\fR to be reset.
.PP
The \fB-m\fR flag is used to set the maximum number of login
failures before the account is disabled.
Write access to \fB/usr/adm/faillog\fR is required for this
option.
Entering \fB-m max\fR will cause all accounts to be disabled
after \fBmax\fR failed logins occur.
This may be modified with \fB-u login-name\fR to limit this
function to \fBlogin-name\fR only.
Selecting a \fBmax\fR value of 0 has the effect of not placing
a limit on the number of failed logins.
The maximum failure count
should always be 0 for \fBroot\fR to prevent
a denial of services attack against the system.
.PP
Options may be combined in virtually any fashion.
Each \fB-p\fR, \fB-r\fR, and \fB-m\fR option will cause
immediate execution using any \fB-u\fR or \fB-t\fR modifier.
.SH Files
/usr/adm/faillog \- failure logging file
.SH See Also
login(1),
faillog(4)
SHAR_EOF
fi
if test -f 'faillog.4'
then
	echo shar: "will not over-write existing file 'faillog.4'"
else
cat << \SHAR_EOF > 'faillog.4'
.TH FAILLOG 4
.SH NAME
faillog \- Login failure logging file
.SH DESCRIPTION
.I faillog
maintains a count of login failures and the limits for each account.
The file is fixed length record, indexed by numerical UID.
Each record contains the count of login failures since the last
successful login;
the maximum number of failures before the account is disabled;
the line the last login failure occured on;
and the date the last login failure occured.
.PP
The structure of the file is
.DS

        struct	faillog {
                short   fail_cnt;
                short   fail_max;
                char    fail_line[12];
                time_t  fail_time;
        };

.DE
.SH Files
/usr/adm/faillog \- login failure log
.SH See Also
faillog(8)
SHAR_EOF
fi
exit 0
#	End of shell archive
-- 
John F. Haugh II                        +-Button of the Week Club:-------------
VoiceNet: (512) 832-8832   Data: -8835  | "AIX is a three letter word,
InterNet: jfh@rpp386.Cactus.Org         |  and it's BLUE."
UucpNet : <backbone>!bigtex!rpp386!jfh  +--------------------------------------