[comp.sources.misc] v06i091: Password checking routine

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (04/24/89)

Posting-number: Volume 6, Issue 91
Submitted-by: ut-emx!clyde@cs.utexas.edu (Head UNIX Hacquer)
Archive-name: chkpwd

Enclosed is some code that I worked up to do some password checking.
I hope it can be useful to others.

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
-----cut here-----cut here-----cut here-----cut here-----
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	README
#	Makefile
#	checkpasswd.h
#	checkpasswd.c
#	makedict.c
#	viewdict.c
#	wormwords
# This archive created: Mon Jan  9 13:27:05 1989
# By:	Head UNIX Hacquer (Moose & Squirrel Software)
echo shar: extracting README '(1040 characters)'
sed 's/^XX//' << \SHAR_EOF > README
XX
XXThis is a password checking program that I wrote after the infamous Internet
XXworm.  I used the password cracking algorithim the worm used in order
XXto check the obviousness of a password.
XX
XXThe routine checkpasswd.c does this.  Read the source for the routine
XXfor details of how it works.  In this package are the following:
XX
XXREADME		This file
XXMakefile	Q.E.D.
XXcheckpasswd.c	Password check routine
XXcheckpasswd.h	Include for checkpasswd calling
XXdriver.c	Test program
XXmakedict.c	Program to build dbm dictionary
XXwormwords	The infamous worm password list
XXviewdict.c	Program to view dbm dictionary
XX
XXI hope this can be a kernel of intelligent password checking for 4BSD systems.
XX
XXI am working on a replacement for the BSD passwd program, which should be
XXavailable soon.
XX
XXContents copyright 1989 Clyde W. Hoover (Moose & Squirrel Software, NotInc.)
XX
XXDistribution unlimited provided copyright notice is retained.
XXPlease spread this around as much as useful.
XX
XX	Clyde Hoover
XX	Computation Center
XX	The University of Texas at Austin
XX	clyde@emx.utexas.edu
SHAR_EOF
if test 1040 -ne "`wc -c README`"
then
echo shar: error transmitting README '(should have been 1040 characters)'
fi
echo shar: extracting Makefile '(670 characters)'
sed 's/^XX//' << \SHAR_EOF > Makefile
XX#
XX#	Make password checking routines and test program
XX
XX#
XX#	The checkpasswd routine depends upon the existence of the ndbm
XX#	routines in the 4.3BSD libc.  Changes will have to be made to
XX#	checkpasswd.c if these are not available.
XX#
XXCFLAGS	= -g
XX
XXpwtest: driver.o checkpasswd.o
XX	cc $(CFLAGS) -o pwtest driver.o checkpasswd.o
XX
XXcheckpasswd.o: checkpasswd.c checkpasswd.h
XX	cc $(CFLAGS) -c checkpasswd.c
XX
XXmakedict: makedict.c
XX	cc $(CFLAGS) -o makedict makedict.c
XX
XXviewdict: viewdict.c
XX	cc $(CFLAGS) -o viewdict viewdict.c
XX
XXpwdict:	wormlist makedict
XX	-rm -f pwdict
XX	cat /usr/dict/words wormlist | makedict pwdict
XX	@echo install pwdict to where PWDICT points to in checkpasswd.c
SHAR_EOF
if test 670 -ne "`wc -c Makefile`"
then
echo shar: error transmitting Makefile '(should have been 670 characters)'
fi
echo shar: extracting checkpasswd.h '(1399 characters)'
sed 's/^XX//' << \SHAR_EOF > checkpasswd.h
XX/*
XX *	passwdcheck.h
XX *
XX *      Copyright 1989 Clyde W. Hoover (Moose & Squirrel Software, NotInc.)
XX *
XX */
XX
XX#define	EXPORT	/**/
XX#define	IMPORT	extern
XX
XX#define	PWCK_FAIL	-1
XX#define	PWCK_OK		0
XX#define	PWCK_NULL	1
XX#define	PWCK_OBVIOUS	2
XX#define	PWCK_FINGER	3
XX#define	PWCK_INDICT	4
XX#define	PWCK_ILLCHAR	5
XX#define	PWCK_SHORT	6
XX
XX/*
XX *	Return codes for checkpassword() are:
XX *
XX *	PWCK_OK if <password> is ok to use
XX *	PWCK_FAIL if something failed during the check process
XX *	PWCK_NULL if <password> is the null string
XX *	PWCK_OBVIOUS if <password> is too "obvious"
XX *		(equals login name, host name, 'zzzz' )
XX *	PWCK_FINGER if <password> is in the users' passwd/finger info
XX *	PWCK_INDICT if <password> is in the dictionaries checked
XX *	PWCK_ILLCHAR if <password> is lexically illegal
XX *	PWCK_SHORT if <password> is too short
XX */
XX
XX/*
XX *	Password checking peference block
XX */
XXstruct pwck_preferences {
XX	char	OneCaseOk,	/* Are single-case pwds acceptable */
XX		CtrlOk,		/* Are control characters acceptable */
XX		CharRunLen,	/* How long can run of characters be  */
XX		MinPwLen;	/* Minimum password length */
XX	char	*EgrepPath;	/* Path to the 'egrep' program */
XX};
XX
XX/*
XX *	Preference list
XX */
XXIMPORT struct pwck_preferences	pwck_preferences;
XX
XX/*
XX *	List of control characters not allowed in passwords
XX */
XXIMPORT char		pwck_illegalcc[];
XX
XX/*
XX *	List of dictionaries to check
XX */
XXIMPORT char		*pwck_dicitonaries[];
XX
SHAR_EOF
if test 1399 -ne "`wc -c checkpasswd.h`"
then
echo shar: error transmitting checkpasswd.h '(should have been 1399 characters)'
fi
echo shar: extracting checkpasswd.c '(13550 characters)'
sed 's/^XX//' << \SHAR_EOF > checkpasswd.c
XX/*
XX *	checkpasswd.c - Login password check routines.
XX *
XX *	Perform various sanity and security checks on a password candidate.
XX *
XX *	Written December, 1988
XX *
XX *	Copyright 1989 Clyde W. Hoover (Moose & Squirrel Software, NotInc.)
XX *
XX *		Clyde Hoover
XX *		Computation Center
XX *		The University of Texas at Austin
XX *		Austin, Texas
XX *		clyde@emx.utexas.edu
XX */
XX
XX#ifndef lint
XXstatic char sccsid[] = "%W% %G% (cc.utexas.edu) %P%";
XX#endif
XX
XX#include <sys/types.h>
XX#include <strings.h>
XX#include <ctype.h>
XX
XX#define	LOCAL	static
XX
XX#include "checkpasswd.h"
XX
XX/*
XX *	Special string compare defines.
XX */
XX#define	try(P,C,V)	{ if (cistrcmp(P,C) == 0) return(V); }
XX#define	mtry(P,C,V)	{ int i; if ((i = StrAllCmp(P,C,V)) != PWCK_OK) return(i); }
XX
XX/*
XX *	Table of operational parameter preferences.
XX *	May be modified by the caller.
XX */
XXEXPORT
XXstruct pwck_preferences	pwck_preferences = {
XX	1,		/* single-case pwds ok */
XX	1,		/* control chars ok */
XX	3,		/* dup length = 3 */
XX	4,		/* minimum length */
XX	"PATH=/bin:/usr/bin:/usr/ucb; egrep"	/* How to find egrep */
XX};
XX
XX/*
XX *	Table of control chars best avoided -
XX *	mostly commonly-used terminal special chars.
XX *	May be modified by the caller.
XX */
XX#define	ctrl(d)	('d' & 037)
XX
XXEXPORT
XXchar	pwck_illegalcc[128] = {
XX	ctrl(c), ctrl(d), ctrl(h), /* ctrl(i),*/  ctrl(j), ctrl(m),
XX	ctrl(o), ctrl(r), ctrl(s), ctrl(q), ctrl(z), ctrl(\\),
XX	ctrl([),	/* escape */
XX	'\0177',	/* rubout */
XX	0
XX};
XX
XX#define	PWDICT		"/usr/dict/pwwords"
XX/*
XX *	List of forbidden password dictionaries to look in.
XX *	May be modified by the caller.
XX */
XXstatic char	*pwck_dicitonaries[16] = {
XX	PWDICT,			/* illegal passwords list */
XX	0
XX};
XX
XX/*
XX *	The 'pwck_*' routines all use the PWCK_* return
XX *	codes, which are then propigated up to the caller of checkpassword().
XX *
XX *	All pwck_* routines in the table below are called thusly:
XX *		pwck_*(password, userid)
XX *			password = plaintext password string to test.
XX *			userid = the user id which wants to use <password>.
XX *
XX *	Table of check functions to be called by checkpassword()
XX */
XXint	pwck_lexical(), pwck_local(), pwck_passwd(), pwck_dictionary();
XX
XXtypedef	int	(*function)();
XX
XXLOCAL
XXfunction pwck_vector[] = {
XX	pwck_lexical,
XX	pwck_local,
XX	pwck_passwd,
XX	pwck_dictionary,
XX	0
XX};
XX
XX/* ------------------------------------------------------------------- */
XX
XX/*
XX *	checkpassword - Password candidate sanity checker.
XX *
XX *	Arguments;
XX *		password = plain text password string to check.
XX *		userid = the uid whom the password is for, -1 to disable.
XX *
XX *	Returns:
XX *		PWCK_OK if <password> is ok to use.
XX *		PWCK_FAIL if something failed during the check process.
XX *		PWCK_NULL if <password> is the null string
XX *		PWCK_OBVIOUS if <password> is too "obvious".
XX *			(equals login name, host name, 'zzzz' ).
XX *		PWCK_FINGER if <password> is in the users' passwd/finger info.
XX *		PWCK_INDICT if <password> is in the dictionaries checked.
XX *		PWCK_ILLCHAR if <password> is lexically illegal.
XX *		PWCK_SHORT if <password> is too short.
XX *
XX */
XXcheckpassword(password, userid)
XXchar	*password;		/* Plaintext of password to check */
XXint	userid;			/* The user this is for */
XX{
XX	int		rcode;		/* General purpose scratch */
XX	function	*checkfunc;	/* Check function pointer */
XX
XX	if (password == 0 || *password == 0)
XX		return(PWCK_NULL);		/* Null password */
XX
XX	for (checkfunc = pwck_vector; *checkfunc; checkfunc++) {
XX		if ((rcode = (*checkfunc)(password, userid)) != PWCK_OK)
XX			return(rcode);
XX	}
XX	return(PWCK_OK);
XX}
XX
XX
XX/* ------------------------------------------------------------------- */
XX
XX#define	P_U	0x1 	/* Upper case in password */
XX#define	P_L	0x2 	/* Lower case in password */
XX#define	P_C	0x4 	/* Control chars in password */
XX#define	P_D	0x8 	/* Digits in password */
XX#define	P_P	0x10 	/* Punctutation chars in password */
XX
XX#define	hasone(P)	(what |= (P))
XX#define	hasany(P)	((what & (P)) == (P))
XX
XX#define	ccok	pwck_preferences.CtrlOk
XX#define	mcok	pwck_preferences.OneCaseOk
XX#define	runl	pwck_preferences.CharRunLen
XX#define	minl	pwck_preferences.MinPwLen
XX
XX/*
XX *	pwck_lexical - Perform lexical analysis of password candidate.
XX *
XX *	Things which are ok:
XX *		Mixed case
XX *		Digits
XX *		Punctutation
XX *		Control characters (except for those in the forbidden table)
XX *			(controlled by the preferences)
XX *
XX *	Things which are NOT ok:
XX *		Passwords less that 'minl' length
XX *		Runs of more than <runl> of the same character (e.g. 'zzz')
XX *		Single-case strings
XX *			(controlled by the preferences)
XX *
XX *	Things not checked for:
XX *		Cycles of character groups (e.g. 'aabbcc' or 'ababab')
XX */
XXstatic int
XXpwck_lexical(password, userid)
XXchar	*password;
XXint	userid;		/* NOTUSED */
XX{
XX	int	rc;		/* Duplicate character run count */
XX	char	*p = password;	/* Scratch */
XX	char	what = 0,	/* Lexical analysis result flags */
XX		last = 0;	/* Last character seen (for run checks) */
XX
XX	if (minl && strlen(password) < minl)
XX		return(PWCK_SHORT);
XX
XX	for (p = password; *p; p++) {
XX		if (*p != last) {
XX			last = *p;
XX			rc = 0;
XX		}
XX		else {		/* Run of same characters */
XX			if (runl && ++rc >= runl)
XX				return(PWCK_OBVIOUS);
XX		}
XX		if (*p < ' ') {		/* Control character */
XX			if (!ccok)
XX				return(PWCK_ILLCHAR);
XX			if (index (pwck_illegalcc, *p))
XX				return(PWCK_ILLCHAR);
XX			hasone(P_C);
XX		}
XX		else if (isupper(*p))	hasone(P_U);
XX		else if (islower(*p))	hasone(P_L);
XX		else if (ispunct(*p))	hasone(P_P);
XX		else if (isdigit(*p))	hasone(P_D);
XX	}
XX	if (hasany(P_U | P_L))	return(PWCK_OK);	/* UC+lc */
XX	if (hasany(P_D))	return(PWCK_OK);	/* Numbers */
XX	if (hasany(P_P))	return(PWCK_OK);	/* Punctutation chars */
XX	if (hasany(P_C))	return(PWCK_OK);	/* Control chars */
XX	/*
XX	 *	Check for mono-case passwords 
XX	 */
XX	if (!hasany(P_U) && mcok)	/* All lower case alpha */
XX		return(PWCK_OK);
XX	if (!hasany(P_L) && mcok)	/* All upper case alpha */
XX		return(PWCK_OK);
XX
XX	return(PWCK_ILLCHAR);
XX}
XX#undef	hasone
XX#undef	hasany
XX
XX/*
XX *	pwck_local - Perform 'local' password checks.
XX *
XX *	Returns:
XX *		PWCK_OBVIOUS if <password> == hostname
XX *		PWCK_OK if otherwise
XX */
XXLOCAL int
XXpwck_local(password, userid)
XXchar	*password;
XXint	userid;		/* NOTUSED */
XX{
XX	char	myname[32];		/* Scratch */
XX
XX	(void) gethostname(myname, sizeof(myname));
XX	try(password, myname, PWCK_OBVIOUS);
XX	/*
XX	 * Want to try full canoncalized hostname here in case gethostname
XX	 * didn't get that for us.
XX	 *
XX	 * Then look in users' .rhosts and try those strings (maybe)
XX	 */
XX	return(PWCK_OK);
XX}
XX
XX/*
XX *	pwck_dictionary - Look in the forbidden password dictionaries.
XX *
XX *	Returns:
XX *		PWCK_INDICT if <password> was in any dictionary
XX *		PWCK_OK if not
XX */
XXLOCAL int
XXpwck_dictionary(password, userid)
XXchar	*password;
XXint	userid;		/* NOTUSED */
XX{
XX	int	i,		/* Counter */
XX		rcode;		/* Return code temp */
XX
XX	for (i = 0; pwck_dicitonaries[i]; i++) {
XX		if ((rcode =
XX		     IsInDictionary(pwck_dicitonaries[i], password)) != PWCK_OK)
XX			return(rcode);
XX	}
XX	return(PWCK_OK);
XX}
XX
XX/*
XX *	IsInDictionary - look for <password> in <dictionary>
XX *
XX *	Use a DBM version of the dictionary if present, 
XX *	use egrep to search the flat file if not.
XX *
XX *	Returns:
XX *		PWCK_INDICT if <password> was found in <dictionary>
XX *		PWCK_OK if not
XX */
XX#define	returnwith(code) { dbm_close(dbp); return(code); }
XX#define	EGREP	pwck_preferences.EgrepPath
XX
XX#include <ndbm.h>
XX
XXLOCAL int
XXIsInDictionary(dictionary, password)
XXchar	*dictionary,		/* Pathname of dictionary */
XX	*password;		/* Plaintext of password */
XX{
XX	DBM	*dbp;		/* DBM database pointer */
XX	datum	k,		/* DBM lookup key */
XX		d;		/* DBM lookup datum */
XX	int	uc = isupper(password[0]);	/* Is first char UC? */
XX	char	pwtemp[128];	/* Scratch buffer */
XX
XX	if ((dbp = dbm_open(dictionary, 0, 0)) == (DBM *)0) {
XX		char	command[128];	/* Command build buffer */
XX		int	rc;		/* System() return code */
XX
XX		if (access(dictionary, 0) < 0)
XX			return(PWCK_OK);
XX		/*
XX		 * If the first letter is capitalized, look for
XX		 * "[wW]ord" else look for "word"
XX		 */
XX		if (uc) 
XX			(void) sprintf(command,
XX				"%s -s '^[%c%c]%s$' %s > /dev/null",
XX				EGREP, password[0], password[0] & ~040,
XX				&password[1], dictionary);
XX		else
XX			(void) sprintf(command, "%s -s '^%s$' %s > /dev/null",
XX				EGREP, password, dictionary);
XX		rc = system(command);
XX		if (rc == 0)
XX			return(PWCK_INDICT);
XX		else
XX			return(PWCK_OK);
XX	} 
XX	/*
XX	 * Look in the DBM version of the dictionary.
XX	 * Look for <password>, then if the first letter
XX	 * is capitalized, force to lower and look again.  I don't care
XX	 * if <password> is in the dictionary but has mixed case letters,
XX	 * but if the first letter has been capitalized, I care because
XX	 * that's not a sufficent permutation to be secure.
XX	 */
XX	(void) strcpy(pwtemp, password);
XX	k.dptr = pwtemp;
XX	k.dsize = strlen(pwtemp);
XX	d = dbm_fetch(dbp, k);
XX	if (d.dptr)
XX		returnwith(PWCK_INDICT);
XX	if (uc) {
XX		pwtemp[0] |= 040;
XX		d = dbm_fetch(dbp, k);
XX		if (d.dptr)
XX			returnwith(PWCK_INDICT);
XX	}
XX	returnwith(PWCK_OK);
XX}
XX#undef	returnwith
XX
XX
XX/*
XX *	pwck_password - Check password candidate against the users' password
XX *		file information, or any other information that is publicly
XX *		available about this user that a bandit could use as
XX *		password guesses.
XX *
XX *	Here is the place to search your 'finger' database.
XX */
XX#include	<pwd.h>
XX
XXstatic int
XXpwck_passwd(password, userid)
XXchar	*password;
XXint	userid;
XX{
XX	char	temp[128];		/* Scratch */
XX	struct passwd	*pwp;		/* Pointer to user information */
XX
XX	if (userid < 0)			/* Can't do user checks */
XX		return(PWCK_OK);
XX
XX	pwp = getpwuid(userid);
XX	if (pwp == 0)
XX		return(PWCK_FAIL);
XX
XX	try(password, pwp->pw_name, PWCK_OBVIOUS); /* Checks 'name' and 'Name' */
XX
XX	(void) strcpy(temp, pwp->pw_name);
XX	(void) strcat(temp, pwp->pw_name);
XX	try(password, temp, PWCK_OBVIOUS);	/* Check 'namename' */
XX
XX	(void) strcpy(temp, pwp->pw_name);
XX	MirrorString(temp);
XX	try(password, temp, PWCK_OBVIOUS);	/* 'eman' */
XX
XX	/*
XX	 * Try every word in user's GECOS entry
XX	 */
XX	mtry(password, pwp->pw_gecos, PWCK_FINGER);
XX	return(PWCK_OK);
XX}
XX/* ------------------------------------------------------------------- */
XX/*
XX *	StrAllCmp - Compare all sub-strings (delineated by white space)
XX *
XX *	Returns:
XX *		PWCK_OK if no match found
XX *		rc if match found
XX */
XXLOCAL
XXStrAllCmp(s1, s2, rc)
XXchar	*s1,		/* String to look for */
XX	*s2;		/* String to look for <s1> in */
XXint	rc;		/* What to return on match */
XX{
XX	int	l;		/* Temp */
XX
XX	for (l = strlen(s1); *s2; s2++)
XX		if (cistrncmp(s1, s2, l) == 0)
XX			return (rc);
XX	return(PWCK_OK);
XX}
XX
XX/*
XX *	MirrorString - reverse a string in place
XX */
XXLOCAL
XXMirrorString(s)
XXchar	*s;		/* String to reverse */
XX{
XX	char	*p;	/* Scratch */
XX	char	t[128];	/* Scratch */
XX	
XX	(void) strcpy(t,s);
XX	p = t;
XX	while (*p) p++;		/* Find end of string */
XX	--p;
XX	for (; *s; )
XX		*s++ = *p--;
XX}
XX
XX/*
XX *	Case indepedant string comparasion routines swiped from
XX *	the source to MIT Hesiod.
XX */
XX/*
XX * Copyright (c) 1986 Regents of the University of California.
XX * All rights reserved.  The Berkeley software License Agreement
XX * specifies the terms and conditions for redistribution.
XX */
XX
XX/*
XX * This array is designed for mapping upper and lower case letter
XX * together for a case independent comparison.  The mappings are
XX * based upon ascii character sequences.
XX */
XX
XXLOCAL char charmap[] = {
XX	'\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
XX	'\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
XX	'\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
XX	'\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
XX	'\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
XX	'\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
XX	'\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
XX	'\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
XX	'\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
XX	'\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
XX	'\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
XX	'\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
XX	'\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
XX	'\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
XX	'\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
XX	'\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
XX	'\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
XX	'\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
XX	'\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
XX	'\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
XX	'\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
XX	'\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
XX	'\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
XX	'\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
XX	'\300', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
XX	'\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
XX	'\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
XX	'\370', '\371', '\372', '\333', '\334', '\335', '\336', '\337',
XX	'\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
XX	'\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
XX	'\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
XX	'\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
XX};
XX
XXLOCAL
XXcistrcmp(s1, s2)
XXregister char *s1, *s2;
XX{
XX	register char *cm = charmap;
XX
XX	while (cm[*s1] == cm[*s2++])
XX		if (*s1++=='\0')
XX			return(0);
XX	return(cm[*s1] - cm[*--s2]);
XX}
XX
XXLOCAL
XXcistrncmp(s1, s2, n)
XXregister char *s1, *s2;
XXregister n;
XX{
XX	register char *cm = charmap;
XX
XX	while (--n >= 0 && cm[*s1] == cm[*s2++])
XX		if (*s1++ == '\0')
XX			return(0);
XX	return(n<0 ? 0 : cm[*s1] - cm[*--s2]);
XX}
XX
XX/* END */
SHAR_EOF
if test 13550 -ne "`wc -c checkpasswd.c`"
then
echo shar: error transmitting checkpasswd.c '(should have been 13550 characters)'
fi
echo shar: extracting makedict.c '(653 characters)'
sed 's/^XX//' << \SHAR_EOF > makedict.c
XX/*
XX *	makedict - Make DBM version of password dictionary
XX */
XX
XX#include <sys/file.h>
XX#include <stdio.h>
XX#include <ndbm.h>
XX
XXextern char	*index();
XX
XXchar	line[80];
XX
XXmain(argc, argv)
XXchar	*argv[];
XX{
XX	DBM	*dp;
XX	int	recs = 0;
XX	datum	d,
XX		k;
XX
XX	dp = dbm_open(argv[1], O_RDWR, 0);
XX	if (dp == 0) {
XX		if ((dp = dbm_open(argv[1], O_RDWR|O_CREAT, 0644)) == 0) {
XX			perror("open dbm");
XX			exit(1);
XX		}
XX	}
XX	while (!feof(stdin)) {
XX		char	*p;
XX
XX		fgets(line, 80, stdin);
XX		p = index(line, '\n');
XX		if (p) *p = 0;
XX		d.dptr = line;
XX		d.dsize = strlen(line);
XX		dbm_store(dp, d, d, DBM_INSERT);
XX		recs++;
XX	}
XX	dbm_close(dp);
XX	printf("%s built, %d records\n", argv[1], recs);
XX}
SHAR_EOF
if test 653 -ne "`wc -c makedict.c`"
then
echo shar: error transmitting makedict.c '(should have been 653 characters)'
fi
echo shar: extracting viewdict.c '(406 characters)'
sed 's/^XX//' << \SHAR_EOF > viewdict.c
XX/*
XX *	viewdict - View DBM version of a password dictionary
XX */
XX
XX#include <sys/file.h>
XX#include <ndbm.h>
XX
XXmain(argc, argv)
XXchar	*argv[];
XX{
XX	DBM	*dp;
XX	datum	k;
XX	char	t[128];
XX
XX	if ((dp = dbm_open(argv[1], O_RDONLY, 0)) == 0) {
XX		perror(argv[1]);
XX		exit(1);
XX	}
XX	for (k = dbm_firstkey(dp); k.dptr != 0; k = dbm_nextkey(dp)) {
XX		strncpy(t, k.dptr, k.dsize);
XX		t[k.dsize] = 0;
XX		printf("%s\n", t);
XX	}
XX	exit(0);
XX}
SHAR_EOF
if test 406 -ne "`wc -c viewdict.c`"
then
echo shar: error transmitting viewdict.c '(should have been 406 characters)'
fi
echo shar: extracting wormwords '(3278 characters)'
sed 's/^XX//' << \SHAR_EOF > wormwords
XXaaa
XXacademia
XXaerobics
XXairplane
XXalbany
XXalbatross
XXalbert
XXalex
XXalexander
XXalgebra
XXaliases
XXalphabet
XXama
XXamorphous
XXanalog
XXanchor
XXandromache
XXanimals
XXanswer
XXanthropogenic
XXanvils
XXanything
XXaria
XXariadne
XXarrow
XXarthur
XXathena
XXatmosphere
XXaztecs
XXazure
XXbacchus
XXbailey
XXbanana
XXbananas
XXbandit
XXbanks
XXbarber
XXbaritone
XXbass
XXbassoon
XXbatman
XXbeater
XXbeauty
XXbeethoven
XXbeloved
XXbenz
XXbeowulf
XXberkeley
XXberliner
XXberyl
XXbeverly
XXbicameral
XXbob
XXbrenda
XXbrian
XXbridget
XXbroadway
XXbumbling
XXburgess
XXcampanile
XXcantor
XXcardinal
XXcarmen
XXcarolina
XXcaroline
XXcascades
XXcastle
XXcat
XXcayuga
XXceltics
XXcerulean
XXchange
XXcharles
XXcharming
XXcharon
XXchester
XXcigar
XXclassic
XXclusters
XXcoffee
XXcoke
XXcollins
XXcommrades
XXcomputer
XXcondo
XXcookie
XXcooper
XXcornelius
XXcouscous
XXcreation
XXcreosote
XXcretin
XXdaemon
XXdancer
XXdaniel
XXdanny
XXdave
XXdecember
XXdefoe
XXdeluge
XXdesperate
XXdevelop
XXdieter
XXdigital
XXdiscovery
XXdisney
XXdog
XXdrought
XXduncan
XXeager
XXeasier
XXedges
XXedinburgh
XXedwin
XXedwina
XXegghead
XXeiderdown
XXeileen
XXeinstein
XXelephant
XXelizabeth
XXellen
XXemerald
XXengine
XXengineer
XXenterprise
XXenzyme
XXersatz
XXestablish
XXestate
XXeuclid
XXevelyn
XXextension
XXfairway
XXfelicia
XXfender
XXfermat
XXfidelity
XXfinite
XXfishers
XXflakes
XXfloat
XXflower
XXflowers
XXfoolproof
XXfootball
XXforesight
XXformat
XXforsythe
XXfourier
XXfred
XXfriend
XXfrighten
XXfun
XXfungible
XXgabriel
XXgardner
XXgarfield
XXgauss
XXgeorge
XXgertrude
XXginger
XXglacier
XXgnu
XXgolfer
XXgorgeous
XXgorges
XXgosling
XXgouge
XXgraham
XXgryphon
XXguest
XXguitar
XXgumption
XXguntis
XXhacker
XXhamlet
XXhandily
XXhappening
XXharmony
XXharold
XXharvey
XXhebrides
XXheinlein
XXhello
XXhelp
XXherbert
XXhiawatha
XXhibernia
XXhoney
XXhorse
XXhorus
XXhutchins
XXimbroglio
XXimperial
XXinclude
XXingres
XXinna
XXinnocuous
XXirishman
XXisis
XXjapan
XXjessica
XXjester
XXjixian
XXjohnny
XXjoseph
XXjoshua
XXjudith
XXjuggle
XXjulia
XXkathleen
XXkermit
XXkernel
XXkirkland
XXknight
XXladle
XXlambda
XXlamination
XXlarkin
XXlarry
XXlazarus
XXlebesgue
XXlee
XXleland
XXleroy
XXlewis
XXlight
XXlisa
XXlouis
XXlynne
XXmacintosh
XXmack
XXmaggot
XXmagic
XXmalcolm
XXmark
XXmarkus
XXmarty
XXmarvin
XXmaster
XXmaurice
XXmellon
XXmerlin
XXmets
XXmichael
XXmichelle
XXmike
XXminimum
XXminsky
XXmoguls
XXmoose
XXmorley
XXmozart
XXnancy
XXnapoleon
XXnepenthe
XXness
XXnetwork
XXnewton
XXnext
XXnoxious
XXnutrition
XXnyquist
XXoceanography
XXocelot
XXolivetti
XXolivia
XXoracle
XXorca
XXorwell
XXosiris
XXoutlaw
XXoxford
XXpacific
XXpainless
XXpakistan
XXpam
XXpapers
XXpassword
XXpatricia
XXpenguin
XXpeoria
XXpercolate
XXpersimmon
XXpersona
XXpete
XXpeter
XXphilip
XXphoenix
XXpierre
XXpizza
XXplover
XXplymouth
XXpolynomial
XXpondering
XXpork
XXposter
XXpraise
XXprecious
XXprelude
XXprince
XXprinceton
XXprotect
XXprotozoa
XXpumpkin
XXpuneet
XXpuppet
XXrabbit
XXrachmaninoff
XXrainbow
XXraindrop
XXraleigh
XXrandom
XXrascal
XXreally
XXrebecca
XXremote
XXrick
XXripple
XXrobotics
XXrochester
XXrolex
XXromano
XXronald
XXrosebud
XXrosemary
XXroses
XXruben
XXrules
XXruth
XXsal
XXsaxon
XXscamper
XXscheme
XXscott
XXscotty
XXsecret
XXsensor
XXserenity
XXsharks
XXsharon
XXsheffield
XXsheldon
XXshiva
XXshivers
XXshuttle
XXsignature
XXsimon
XXsimple
XXsinger
XXsingle
XXsmile
XXsmiles
XXsmooch
XXsmother
XXsnatch
XXsnoopy
XXsoap
XXsocrates
XXsossina
XXsparrows
XXspit
XXspring
XXspringer
XXsquires
XXstrangle
XXstratford
XXstuttgart
XXsubway
XXsuccess
XXsummer
XXsuper
XXsuperstage
XXsupport
XXsupported
XXsurfer
XXsuzanne
XXswearer
XXsymmetry
XXtangerine
XXtape
XXtarget
XXtarragon
XXtaylor
XXtelephone
XXtemptation
XXthailand
XXtiger
XXtoggle
XXtomato
XXtopography
XXtortoise
XXtoyota
XXtrails
XXtrivial
XXtrombone
XXtubas
XXtuttle
XXumesh
XXunhappy
XXunicorn
XXunknown
XXurchin
XXutility
XXvasant
XXvertigo
XXvicky
XXvillage
XXvirginia
XXwarren
XXwater
XXweenie
XXwhatnot
XXwhiting
XXwhitney
XXwill
XXwilliam
XXwilliamsburg
XXwillie
XXwinston
XXwisconsin
XXwizard
XXwombat
XXwoodwind
XXwormwood
XXyacov
XXyang
XXyellowstone
XXyosemite
XXzap
XXzimmerman
SHAR_EOF
if test 3278 -ne "`wc -c wormwords`"
then
echo shar: error transmitting wormwords '(should have been 3278 characters)'
fi
#	End of shell archive
exit 0
-- 
Shouter-To-Dead-Parrots @ Univ. of Texas Computation Center; Austin, Texas  
	clyde@emx.utexas.edu; ...!cs.utexas.edu!ut-emx!clyde

"You really have to take a broad perspective when giving pat answers
 to other people's problems."  - Eyebeam