#! /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:
#	log.c
#	mail.c
#	shadow.h
#	sulog.c
#	Makefile
#	entry.c
#	obscure.c
#	setup.c
#	sub.c
#	config.h
#	shadow.info
#	pmain.c
#	sulogin.c
#	dialup.h
# This archive created: Sun Jan 22 22:24:51 1989
# By:	John F. Haugh II (River Parishes Programming, Dallas TX)
export PATH; PATH=/bin:/usr/bin:$PATH
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>
#include <string.h>
#include "config.h"

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

long	lseek ();
time_t	time ();

#ifdef	LASTLOG

#include "lastlog.h"

void	log ()
{
	int	fd;
	long	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)
		return;

	if (read (fd, (char *) &lastlog, sizeof lastlog) != sizeof lastlog)
		memset ((char *) &lastlog, sizeof lastlog, 0);

	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>
#include <string.h>
#include "config.h"

extern	char	mail[];

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

	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 password security file structure.
 */

struct	spwd {
	char	*sp_namp;	/* login name */
	char	*sp_pwdp;	/* encrypted password */
	long	sp_lstchg;	/* date of last change */
	int	sp_max;		/* maximum number of days between changes */
	int	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>
#include <string.h>
#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'
SHELL = /bin/sh

# Flags for SCO Xenix/386
CFLAGS = -O -M3 -g -DFGETPWENT
LIBS = -lcrypt
LDFLAGS = -M3 -g
LTFLAGS = 

# Flags for normal machines
# CFLAGS = -O -g
# LIBS =
# LDFLAGS = -g

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

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.o dialchk.o

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

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

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

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

all:	su login pwconv pwunconv passwd sulogin

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

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

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

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

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

clobber: clean
	-rm -f su login passwd pwconv pwunconv sulogin *.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>
#include <string.h>
#include "config.h"
#ifdef	SHADOWPWD
#include "shadow.h"
#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;
				endspent ();
				return;
			}
			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);

		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>
#include "config.h"

/*
 * 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 */
char	mono[32];			/* a monocase version of pass */

int	obscure ()
{
	int	i;

	if (orig[0] == '\0')
		return (1);

	if (strlen (pass) < 6) {	/* 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>
#include <string.h>
#include "config.h"

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

#ifndef	SU
extern	struct	utmp	utent;
#endif

#ifdef	QUOTAS
long	strtol ();
long	ulimit ();
#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;
		}
		if (strncmp (cp, "ulimit=", 6) == 0) {
			l = strtol (cp + 6, (char **) 0, 10);
			(void) ulimit (2, l);

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

			continue;
		}
	}
#endif
	if (setgid (info->pw_gid) == -1) {
		puts ("Bad group id");
		exit (errno);
	}
	if (setuid (info->pw_uid) == -1) {
		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);

#ifndef	SU
	(void) strcat (strcpy (logname, "LOGNAME="), name);
	addenv (logname);
#endif
	(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>
#include <string.h>

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 (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[1] == '\0')
		pwent.pw_shell = "/bin/sh";
	else
		pwent.pw_shell++;
#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.
 */

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

#define	DIALUP

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

#define	SHADOWPWD

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

#define	TZ	"TZ=CST6CDT"

/*
 * Define the default PATH and SUPATH here.  PATH is for non-privileged
 * users, SUPATH is for root.
 */

#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 single terminal
 */

#define	CONSOLE	"tty01"

/*
 * 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 MOTD is #define'd.
 */

#define	HUSHLOGIN

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

#define	LASTLOG

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

#define	TTYPERM	0600

/*
 * 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"
SHAR_EOF
fi
if test -f 'shadow.info'
then
	echo shar: "will not over-write existing file 'shadow.info'"
else
cat << \SHAR_EOF > 'shadow.info'
>From vector!killer!osu-cis!att!cuuxb!dlm Sun Nov 13 08:11:08 CST 1988
Article 5025 of comp.unix.wizards:
Path: rpp386!vector!killer!osu-cis!att!cuuxb!dlm
>From: d...@cuuxb.ATT.COM (Dennis L. Mumaugh)
Newsgroups: comp.unix.wizards
Subject: /etc/shadow
Summary: See release notes for SVR3.2
Keywords: shadow password
Message-ID: <2...@cuuxb.ATT.COM>
Date: 11 Nov 88 21:33:37 GMT
References: <16...@agate.BERKELEY.EDU> <2...@cuuxb.ATT.COM> <16...@agate.BERKELEY.EDU> <17...@glacier.STANFORD.EDU> <2...@cuuxb.ATT.COM> <8
Reply-To: d...@cuuxb.UUCP (Dennis L. Mumaugh)
Organization: ATT Data Systems Group, Lisle, Ill.
Lines: 60

In article <8...@smoke.BRL.MIL> g...@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>It would be a great service to the community if specifications for
>this feature were posted or at least sent to developers who want
>to enable a similar feature on their (typically BSD-based) systems.
>For example, what is the shadow file called, what is its format,
>what sort of stuff is left in the password field in /etc/passwd,
>what facilities are there to validate a password against the
>shadow encrypted password file?

The documentation is scattered in the Release Notes for System V
Release 3.2.  Of course they don't have a page shadow(4) but:

The file is /etc/shadow and is owned by root and mode 400.
It contains one line per login.  Fields are separated by colons:
	username \- users login name
	password \- A 13 character encrypted password or a lock string to
		    indicater the login is not accessible
	lastchanged \- number of days since January 1, 1970 that the password
	               has been modified
	min \- the number of days required between password changes
	max \- the maximum number of days the password is valid.

Routines to work with /etc/shadow:
	#include <shadow.h>
	struct spwd *getspent();
	struct spwd *getspnam(char * name);
	void setspent();
	void endspent();
	struct spwd *fgetspent(FILE *fp);
	int putspent(struct spwd *p,FILE *fp);

Programs allied with this are 
	pwconv \- install and/or update /etc/shadow with information
		  from /etc/passwd
	pwunconv \- restore /etc/password from /etc/shadown 
 
Programs like login, su and passwd work with  either  /etc/passwd
ONLY  or  with  the  added  /etc/shadow.  If there is no entry in
/etc/shadow we accept the /etc/passwd as gospel [in case  someone
forgot to run /usr/lib/pwconv after adding a user.]

Also /usr/include/shadow.h:

struct spwd {
	char	*sp_namp; /* users login name */
	char	*sp_pwdp; /* encrypted password */
	long	sp_lstchg; /* number of days since January 1, 1970 
			      that the password has been modified */
	int	sp_max; /* the number of days required between password changes */
	int	sp_min; /* the maximum number of days the password is valid. */
}
#define  SHADOW "/etc/shadow"

ATT doesn't provide any of the functions or the  header  file  as
part  of  its  product.  It  is in the source but not the binary.
Thus developers who need the  routines  must  contact  their  ATT
person [not me!] to obtain the shadow password security library
-- 
=Dennis L. Mumaugh
 Lisle, IL       ...!{att,lll-crg}!cuuxb!dlm  OR cuuxb!...@arpa.att.com


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>
#include <string.h>
#include "config.h"
#include "lastlog.h"
#include "shadow.h"

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;
{
	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

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

	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 */
	if (! amroot)
		force = 0;

	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);

	if (!force && ! obscure ()) {
#ifdef	OBSCURE
		puts ("Password not changed.");
		exit (1);
#else
		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 = crypt (pass, l64a (salttime));

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

		(void) putspent (spwd, npwd);
	}
	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;
	}
	if (link (NSHADOW, SHADOW) || unlink (NSHADOW)) {
		(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",
		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
	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;
	}
	if (link (NPWDFILE, PWDFILE) || unlink (NPWDFILE)) {
		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*/
}
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>
#include "config.h"
#include "lastlog.h"

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

struct	passwd	pwent;
struct	utmp	utent;
struct	lastlog	lastlog;

#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
	addenv (TZ);			/* set the default $TZ, if one */
#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 and let INIT go to runlevel 2.
	 */

					/* 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/ */
		strcpy (utent.ut_line, cp); /* needed for tty perms (setup) */
		
	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.
 */

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
exit 0
#	End of shell archive