Newsgroups: comp.sources.unix
From: clyde@emx.utexas.edu (Clyde Hoover)
Subject: v25i075: npasswd - replacement for passwd(1), Part02/03
Sender: sources-moderator@pa.dec.com
Approved: vixie@pa.dec.com

Submitted-By: clyde@emx.utexas.edu (Clyde Hoover)
Posting-Number: Volume 25, Issue 75
Archive-Name: npasswd/part02

From clyde@emx.utexas.edu Fri Jan 25 12:45:25 1991
Received: from BBN.COM by pineapple.bbn.com id <AA22522@pineapple.bbn.com>; Fri, 25 Jan 91 12:45:04 -0500
Received: from uunet.UU.NET by BBN.COM id aa03147; 25 Jan 91 12:38 EST
Received: from cs.utexas.edu by uunet.uu.net (5.61/1.14) with SMTP 
	id AA17158; Fri, 25 Jan 91 12:38:31 -0500
Received: from emx.utexas.edu by cs.utexas.edu (5.64/1.93) via SMTP
	id AA17940; Fri, 25 Jan 91 11:37:52 -0600
Posted-Date:  25 Jan 91 17:02:50 GMT
Received: by emx.utexas.edu (5.61/1.8)
	id AA04098; Fri, 25 Jan 91 11:02:54 -0600
To: comp-sources-unix@emx.utexas.edu
Path: ut-emx!clyde
From: Head UNIX Hacquer <ut-emx!clyde@emx.utexas.edu>
Newsgroups: comp.sources.unix
Subject: npasswd, a replacement for passwd(1) (part 2 of 3)
Keywords: Password changing program
Message-Id: <43165@ut-emx.uucp>
Date: 25 Jan 91 17:02:50 GMT
Organization: Moose & Squirrel Software
Lines: 1725
Status: R


Npasswd is a pretty-much-plug-compatable replacement for passwd(1).
This version incorporates a password checking system
that disallows simple-minded passwords.

It does exactly ONE thing - change login passwords, though it would
not be too difficult to make it do shells and GECOS stuff also.

I have modeled npasswd after passwd(1) from 4.3BSD and SunOS 4.0, but
it does not impliment the options those versions have.
I have also included support for Sys VR3 password aging.

This version runs at our site under SunOS 4.X, Ultrix 4.0, UMAX 4.3 and
MORE/BSD.

It is also available via anonymous FTP from emx.utexas.edu in the directory
pub/npasswd.

----------- cut here -----------
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 3)."
# Contents:  Makefile.dist checkpasswd/README checkpasswd/checkpasswd.8
#   checkpasswd/pwck_dict.c checkpasswd/pwck_lexical.c pw_passwd.c
#   pw_userinfo.c
# Wrapped by clyde@tigger.cc.utexas.edu on Fri Jan 25 10:35:07 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Makefile.dist' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile.dist'\"
else
echo shar: Extracting \"'Makefile.dist'\" \(7149 characters\)
sed "s/^X//" >'Makefile.dist' <<'END_OF_FILE'
X
X# --------------------------------------------------------------------  #
X#                                                                       #
X#                         Author: Clyde Hoover                          #
X#                          Computation Center                           #
X#                   The University of Texas at Austin                   #
X#                          Austin, Texas 78712                          #
X#                         clyde@emx.utexas.edu                          #
X#                   uunet!cs.utexas.edu!ut-emx!clyde                    #
X#                                                                       #
X#This code may be distributed freely, provided this notice is retained. #
X#                                                                       #
X# --------------------------------------------------------------------  #
X#
X#	Makefile for npasswd
X
X#	@(#)Makefile.dist	1.11 11/26/90 (cc.utexas.edu)
X
X#	Top of install tree
XDEST	=
X
X#	Where the binary lives
XBINDIR = /bin
X
X#	Where condfiguration files live
XADMDIR	= /usr/adm
X
X#	Where manual pages live
XMANDIR	= /usr/man/man1
X
X#	Mode for binary
XIMODE	= 4511
X
X#	Name of binary
XPASSWD	= npasswd
X
X#	Things to build in subdirectories
XSUBDIRS = checkpasswd 
X
X#	Name of password check library
XCHKLIB = checkpasswd/checkpasswd.a
X
X#	Name of distribution files
XDISTNAME = npasswd
X
X#	Name of library to get Sun RPC client routines from
XLIB_RPCSVC	= -lrpcsvc 
X
X#	Flags to pass down to password checker
XCHECKPW_FLAGS	=
X
X# ---------------------------------------------------------------
X#	Npasswd configuration
X# ---------------------------------------------------------------
X
X#	The password file location
X# PWF = /etc/passwd
X
X#	The temp and lock file for passwd file changes
X# PWT = /etc/ptmp
X
X#	The saved password file
X# PWS = /etc/opasswd
X
X# PWFD = -DPASSWD_FILE=\"$(PWF)\"
X# PWTD = -DPASSWD_TEMP=\"$(PWT)\"
X# PWSD = -DPASSWD_SAVE=\"$(PWS)\"
X
X#	The configuration file
XCF = $(DEST)$(ADMDIR)/$(PASSWD).conf
XCFD = -DCONFIG_FILE=\"$(CF)\"
X
X#	The help file
XHF = $(DEST)$(ADMDIR)/$(PASSWD).help
XHFD = -DHELP_FILE=\"$(HF)\"
X
X#	The message file
XMF = $(DEST)$(ADMDIR)/$(PASSWD).motd
XMFD = -DMOTD_FILE=\"$(MF)\"
X
X# ---------------------------------------------------------------
X#	Enable only ONE of the following options
X# ---------------------------------------------------------------
X
X#
X#	Ultrix 4.0 (highly mutated 4.2)
X# ULTRIX	= -DBSD4_3 -DNDBM -DNO_CLNT_SPERRNO -DXFGETPWENT -DXPUTPWENT
X# LIB_RPCSVC	=
X
X#	Using 4.3BSD hashed password file
X# BSD = -DBSD4_3 -DNDBM
X
X#	Running under System V
X# SYS5 = -DSYSV
X
X#	If running under SunOS 4.X (funky tty ioctls)
X# SUNOS = -DSUNOS4
X
X
X# ---------------------------------------------------------------
X#	Any number of the following options can be enabled
X# ---------------------------------------------------------------
X
X#	Use syslog(3) for recording password changes and errors
XSYSLOG = -DSYSLOG 
X
X#	Use private version of getpass(3) which is better behaved than
X#	the version in libc (at least the 4.3BSD version).
XGETPASS = -DXGETPASS
X
X
X#	-DXPUTPWENT provides putpwent() if not in libc.
X# PUTPWENT = -DXPUTPWENT
X
X#	-DXFGETPWENT provides fgetpwent() if not in libc.
X# FGETPWENT = -DXFGETPWENT
X
X# ---------------------------------------------------------------
X#	Program building
X# ---------------------------------------------------------------
X
X#	Debugging switches
XDEBUG	= -g -DDEBUG
X
X#	'XFLAGS' are the configuration flags exported to sub-makes.
X#	Change '-O' in XFLAGS to $(DEBUG) for development work,
X#	change to '-g' to use source debugger.
XXFLAGS	= -O $(BSD) $(SYS5) $(SUNOS) $(ULTRIX)
X
X#	'CFLAGS' are the flags for npasswd only
XCFLAGS	= $(XFLAGS) $(SYSLOG) $(GETPASS) $(PUTPWENT) $(FGETPWENT) \
X	$(HFD) $(CFD) $(MFD) $(PWFD) $(PWTD) $(PWSD)
X
X#	Change the following line to $(DEBUG) for debugging
XLDFLAGS	= 
X
X# ---------------------------------------------------------------
X#	Start of make rules
X# ---------------------------------------------------------------
X#
X#	Remove the leading comment of ONE of the following entries
X#	for 'all'
X#
X# all:	yp_passwd		# Build YP version
X# all:	pw_passwd		# Build standard version
X# all:	ui_passwd		# Build UT CC userinfo version
X
Xfirst::
X	-@echo Do \"make yp_passwd\" to build YP/NIS version.
X	-@echo Do \"make pw_passwd\" to build standard version.
X	-@echo
X	-@echo You should also edit this Makefile to pick the
X	-@echo target for 'all' and configure npasswd for your system.
X
X# ---------------------------------------------------------------
X#	Standard password file version
X# ---------------------------------------------------------------
XOBJ_PW	= npasswd.o pw_passwd.o $(CHKLIB)
X
Xpw_passwd:	$(OBJ_PW)
X	$(CC) -o $(PASSWD) $(LDFLAGS) $(OBJ_PW) 
X
X# ---------------------------------------------------------------
X#	Yellow Pages version
X# ---------------------------------------------------------------
XOBJ_YP	= npasswd.o pw_yp.o $(CHKLIB)
X
Xyp_passwd:	$(OBJ_YP)
X	$(CC) -o $(PASSWD) $(LDFLAGS) $(OBJ_YP) $(LIB_RPCSVC)
X
X# ---------------------------------------------------------------
X#	UTEXAS CC database version
X# ---------------------------------------------------------------
XOBJ_UI	= npasswd.o pw_userinfo.o $(CHKLIB)
X
Xui_passwd:	$(OBJ_UI)
X	$(CC) -o $(PASSWD) $(LDFLAGS) $(OBJ_UI) -luserinfo 
X
X# ---------------------------------------------------------------
X#	Make password checker library
X# ---------------------------------------------------------------
X$(CHKLIB):
X	cd checkpasswd; \
X	make $(MFLAGS) "CFLAGS=$(XFLAGS)" $(CHECKPW_FLAGS) checkpasswd.a
X
X
X# ---------------------------------------------------------------
X#	Misc stuff
X# ---------------------------------------------------------------
Xclean::
X	-rm -f *.o a.out n*passwd core
X	-rm -f passwd passwd.dir passwd.pag opasswd
X	-for f in $(SUBDIRS); do \
X		(cd $$f; make clean); done
X
X# ---------------------------------------------------------------
Xinstall:	$(PASSWD)
X	@if [ `whoami` != root ]; then\
X		echo Must be super-user to install; \
X		exit 1; \
X	else \
X		exit 0; \
X	fi
X	install -s -m $(IMODE) $(PASSWD) $(DEST)$(BINDIR)/$(PASSWD)
X	@if [ ! -r $(CF) ]; then\
X		echo install -c -m 0644 npasswd.conf $(CF);\
X		install -c -m 0644 npasswd.conf $(CF);\
X	else \
X		echo $(CF) already exists.. edit to change; fi
X	@if [ ! -r $(HF) ]; then\
X		echo install -c -m 0644 npasswd.help $(HF);\
X		install -c -m 0644 npasswd.help $(HF);\
X	else \
X		echo $(HF) already exists.. edit to change; fi
X	@echo Put site-specific information in $(MF)
X
Xinstall.man::
X	@echo Customize a manual page
X#	install -c -m 0444 npasswd.1 $(DEST)$(MANDIR)
X
X# ---------------------------------------------------------------
X#	Make copy of password file for testing
Xsetup::
X	-rm -f passwd passwd.dir passwd.pag passwd.old
X	cp /etc/passwd passwd
X	-if [ -f /etc/mkpasswd ]; then \
X		/etc/mkpasswd passwd; fi
X
X# ---------------------------------------------------------------
Xdist::
X	@stuff/makedist $(DISTNAME)
X 
X# ---------------------------------------------------------------
X#
X# Source dependancies
X#
Xnpasswd.o:	npasswd.c version.h
Xpw_passwd.o:	pw_passwd.c
Xpw_yp.o:	pw_yp.c
Xpw_userinfo.o:	pw_userinfo.c
X
X#	End Makefile.dist
END_OF_FILE
if test 7149 -ne `wc -c <'Makefile.dist'`; then
    echo shar: \"'Makefile.dist'\" unpacked with wrong size!
fi
# end of 'Makefile.dist'
fi
if test -f 'checkpasswd/README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'checkpasswd/README'\"
else
echo shar: Extracting \"'checkpasswd/README'\" \(6143 characters\)
sed "s/^X//" >'checkpasswd/README' <<'END_OF_FILE'
XREADME for checkpasswd
X
X	@(#)README	1.1 5/18/89
X
X** NOTE **
XOriginally the 'mdbm' library was to be distributed along with this program,
Xbut license problems have led to its removal.  There are hooks in some of the
Xcode for "MDBM".  Should I locate with a public domain DBM workalike, 
Xthose hooks will be reworked to use it and it will be distributed.
X
X* Why this code...
X
XThe infamous Internet worm of November 1988 used among its attacks
Xa password guesser.  It is frightening how many passwords on how many UNIX
Xsystems were guessed by this algorithim.  With the publication of various
Xpapers about the internals of the worm, the password cracking algorithims
Xcame to light.
X
XI replicated the worm password cracker from those papers, ran it
Xagainst my password file and got too many hits to be comfortable.
XI decided that some more rigourous validity checking was needed for passwords.
X
XSo I took the worm algorithim and rewrote it into a password checker.
X
XA previous version of this code has appeared on comp.sources.misc.
X
X* What this code does...
X
XThe sequence of checks done is:
X
X	1. Enforcement of a minimal length.
X
X	2. Simple 'lexical' checks to catch some dumb passwords, such as
X	   repeats of the same letter (e.g. 'aaa').  Strange characters
X	   are also checked for.
X
X	3. Password is compared against some host-specific information, such as
X	   the hostname.
X
X	4. Password is checked against a number of permutations on the users'
X	   passwd information (login name reversed, login name duplicated).
X
X	5. Password is checked against the user's FINGER information.
X
X	6. A list of dictionaries are scanned for the password.
X
XAll string comparsions are case-independant.
X
XWhat kinds of passwords will be accepted:
X	Passwords which use mixed upper and lower case.
X	Passwords which use punctuation characters.
X	Passwords which use numbers.
X	Passwords which use allowed control characters.
X
XBy default, the following control characters are NOT allowed in passwords:
X	control-c control-d control-h control-j control-m
X	control-o control-r control-s control-q control-y
X	control-z control-\ control-[ control-] DELETE
X
XThese are typical tty special characters on UCB-derived UNIX systems.
XA user could put these characters in their password by quoting them
Xwith control-v, but there is no guarantee that the tty modes
Xset by login(1) would allow them to be properly entered, so they
Xare best avoided.
X
XThis table may be replaced or supplimented via the configuration file.
X
X* About checkpasswd....
X
XThe checking routine is table driven (in checkpasswd.c) so that new routines
Xcan be added easily.
X
XI did not use some other password check routines that have been made available
Xbecause they rejected many passwords which I thought was reasonable.  These
Xchecks are concerned with filtering out words that might be easily guessed.
XI believe that my tests, especially the dictionary lookup, perform this
Xfunction.  Checks can be inserted or deleted as desired.
X
XThis version is much more functional and configurable that the original
Xsubroutine package provided.  I chose to convert the subroutine to a
Xstandalone program that is self-contained and can be executed from other
Xprograms.
X
XVery soon afterwards I realized that the standalone program could be 
Xrefitted to work as a library.  So checkpasswd comes in two forms:
Xstandalone and library.
X
XThe standalone program reads from standard input and reports
Xto standard output.
X
XThe library version can be called from a program and will return
X1 or 0 depending on whether the password should be used.  It will
Xalso print a diagnostic message to stdout if it does not deem the password
Xto be desirable.
X
XThe routine setcheckpasswd() can be used to set options when using the library.
XSee checkpasswd(3) about setting options.
X
X* Dictionaries...
X
XI figured that why no password checker to date did dictionary lookups is 
Xbecause of the overhead in doing so.  I use a DBM database version of
Xa dictionary to make the lookups fast.  On my system, the DBM version of
X/usr/dict/words takes up only about 800K - worth the space to make
Xpassword checking fast.
X
XSince multiple dictionaries can be searched, any new words that you want
Xto 'take out' of use can be added to one of them.  As an aside, this
Xis why I just couldn't use the vanilla V7 dbm - it can not handle more
Xthan one database.
X
XThe reason for the dictionary checking is to thwart crackers who go through
Xthe system dictionary looking for passwords.  If we prevent people from
Xchoosing passwords which are in publicly readable dictionaries, that
Xcracking strategy should then fail.
X
XPasswords which use punctuation or control characters or have multiple 
Xchanges of case are not looked up.  Since these would not be found in the
Xspelling dictionary, there is no reason to look for them.
X
X*** Important note ***
XIt is a good idea to have dictionary data bases because if egrep(1)
Xhas to be used to search an ASCII file, its arguments are prey to being
Xread with ps(1).
X
X* How to use checkpasswd...
X
XThe standalone checkpasswd is not designed to be an end-user program.
XIt should be installed in a library directory somewhere and used from
Xanother program.  The -o, -s and -e options are provided to facilitate
Xsuch use, and the 'call_ckpasswd.c' module included shows an example of this.
X
X* To do...
X
XProvide some way to pass an encrypted password on the command line.
XClose the window of vunerablity on egrep argument sniffing.
X
X* Administrativa...
X
XI have done considerable testing but do not guarantee that this program
Xis bullet-proof.
X
XThere is no copyright on this version of checkpasswd. 
X
X* Building checkpasswd...
X
X1. Edit Makefile and select which DBM method to use and where to install
X   checkpasswd and config file
X2. Edit dict/Makefile and select which DBM method to use.
X3. Do a 'make all'.
X4. Use dict/makedict to build DBM dictionaries.
X
X** If you are not using the standalone application version, stop now **
X
X5. Edit checkpasswd.cf to reflect your preferences.
X6. Do a 'make install'.
X
X> Bugs & enhancements...
X
XSend bug report & enhancements to:
X	clyde@emx.utexas.edu
X		or
X	uunet!cs.utexas.edu!ut-emx!clyde
X
END_OF_FILE
if test 6143 -ne `wc -c <'checkpasswd/README'`; then
    echo shar: \"'checkpasswd/README'\" unpacked with wrong size!
fi
# end of 'checkpasswd/README'
fi
if test -f 'checkpasswd/checkpasswd.8' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'checkpasswd/checkpasswd.8'\"
else
echo shar: Extracting \"'checkpasswd/checkpasswd.8'\" \(5705 characters\)
sed "s/^X//" >'checkpasswd/checkpasswd.8' <<'END_OF_FILE'
X'\"
X'\"	@(#)checkpasswd.8	1.1 5/18/89 (cc.utexas.edu) /home/emx/u2/cc/clyde/src/new/passwd/checkpasswd/SCCS/s.checkpasswd.8
X'\"
X.TH CHECKPASSWD 8
X.SH NAME
Xcheckpasswd \- check passwords
X.SH SYNOPSIS
X.B checkpasswd
X[
X.B \-\^c
Xconfig_file ] [
X.B \-\^e
X] [
X.B \-\^o
X] [
X.B \-\^s
X] [
X.B \-\^u
Xuser id ] [
X.B \-\^V
X] [
X.B \-\^?
X]
X.SH DESCRIPTION
X.I Checkpasswd
Xreads passwords from the standard input and subjects them to suitablity tests.
XThese tests are detailed below.
XIt purposely does
X.B not
Xtake a password argument since process
Xarguments are not secure on most UNIX systems - they can be printed by 
X.IR ps (1).
X.PP
X.I Checkpasswd
Xis not meant as a replacement for 
X.IR passwd (1)
Xand so does not suppress character echo while reading passwords.
XThe standard input can be redirected from a pipe or file, which facilitates
Xit being called from another program such as 
X.IR passwd (1).
X.PP
X.I Checkpasswd
Xexits with the check status for the most recent password entered.
XThe check status is one of the following:
X.nf
X-1	A serious error occured.
X0	The password passed all checks.
X1	The password was a null string.
X2	The password was \'too easy\' to guess.
X3	The password was part of the users \'\fIfinger\fP\' information.
X4	The password was found in a dictionary search.
X5	The password contained an illegal character or sequence.
X6	The password was too short.
X.fi
X.PP
XIn addition to the check status, an informative message is printed to
Xthe standard output (unless supressed).
X.PP
XThe options available are:
X.TP
X.B \-\^c
XTake the next argument as the configuration file to read.
XSee the CONFIGURATION section for information on the configuration file.
X.TP
X.B \-\^e
XPrint the check status in numeric form as well as the informative message.
XThis is useful for programs which want to process the output from
X.IR checkpasswd .
X.TP
X.B \-\^o
XTest one password only and exit with check status.
X.TP
X.B \-\^s
XSupress output of informative messages.
X.TP
X.B \-\^u
XTake the next argument as the user id (in numeric form) to use for password
Xfile data checking.
XThis option is restricted to the super-user.
X.TP
X.B \-\^V
XPrint version information.
X.TP
X.B \-\^?
XPrint help message.
X.SH "CHECKS PERFORMED"
X.PP
X.I Checkpasswd
Xperforms the following tests on each password:
X.IP 1.
XLength is checked to see if it is within bounds.
XPasswords which are too short are rejected, while passwords that are
X\'too long\' are passed but a warning message issued.
X.IP 2.
XThe password is scanned for illegal characters and character combinations,
Xsuch as runs of the same character (e.g. \'aaa\').
XPasswords that do not use upper and lower case alphabetics will be rejected
Xunless otherwise specified via the configuration file.
X.IP 3.
XThe password is checked against various per-site information, such as the
Xhost name.
X.IP 4.
XThe password is checked against the
X.IR passwd (5)
Xinformation for the user.
XThe password is compared against a number of permutations of this data.
X.IP 5.
XThe password is checked against the user\'s \'finger\' information.
X.IP 6.
XThe password is search for in a list of dictionaries and is rejected if it
Xis found in any of them.
X.SH CONFIGURATION
X.PP
XMany of the criteria used by
X.I checkpasswd
Xcan be set via a configuration file.
XThe default configuration file is 
X.BR /usr/adm/checkpasswd.cf .
X.PP
XLines in the configuration file which start with '#' ignored.
XItems in \'[ ]\' are optional, the default value is in \'{ }\'.
X.sp
X.nf
X\fBdictionary\fP	/path/to/dictionary	[description of dictionary]
X.fi
X.RS
XSpecifies a dictionary to look in.
XIf there is a DBM data base version of the dictionary, that will be used,
Xelse 
X.IR egrep
Xwill be used on the file.
XThere may be multiple
X.B dictionary
Xdirectives in the configuration file, and they will be searched in
Xthe order given.
XA list of default dictionaries may be built into
X.IR checkpasswd .
X.br
XThe
X.I makedict
Xprogram can be used to build DBM dictionaries, and
X.I viewdict
Xused to review them.
X.RE
X.sp
X.nf
X\fBsinglecase\fP	yes | {no}
X.fi
X.RS
XSpecifies whether or not single-case passwords are legal.
XThe default is to reject single-case passwords.
X.RE
X.sp
X.nf
X\fBminlength\fP	N {5}	
X.fi
X.RS
XSpecifies the minimum password length to accept.
XPasswords shorter that this will not be accepted.
X.RE
X.sp
X.nf
X\fBmaxlength\fP	N {8}
X.fi
X.RS
XSpecifies the maximum effective password length.
XThis does not affect acceptance of a password.
XThe 
X.IR crypt (3)
Xroutine usually encrypts only the first 8 characters of a string due to the
Xalgorithim used.
XIf the password is longer than this, a warning message is printed to inform the
Xuser that not the entire password will be used.
X.RE
X.sp
X.nf
X\fBprintonly\fP	yes | {no}
X.fi
X.RS
XSpecifies that only printable ASCII characters are to be allowed in passwords.
XUse of control characters should be encouraged but may cause problems on
Xsome systems.
X.RE
X.sp
X.nf
X\fBbadchars\fP	"list-of-characters"
X.br
X\fBbadchars\fP	+"list-of-characters"
X.fi
X.RS
XSpecifies a list of characters which are not allowed in passwords.
XThe first form replaces the illegal character table.
XThe second form adds to the illegal character table.
XCharacters may be given in the following forms:
X.br
X.ti +.25i
XC special character escapes (e.g. \\\|t )
X.br
X.ti +.25i
X\\\|0[0\-7] (e.g. \\\|013)
X.br
X.ti +.25i
X\\\|0x[0\-9\| \|a\-f] (e.g. \\\|0xa)
X.br
X.ti +.25i
X^[a\-z\|,\|A\-Z\|,\|[\|,\|\\\|,\|]\|,\|^\|,\|-] (e.g.,^c)
X.br
X.sp
XThe default content of the illegal characters table is a collection
Xof terminal special characters.
X.RE
X.in 0
X.SH SEE ALSO
Xmakedict, viewdict, passwd(1)
X.SH AUTHOR
XClyde Hoover
X.br
XComputation Center
X.br
XThe University of Texas at Austin
X.br
XAustin, Texas
X.br
Xclyde@emx.utexas.edu, uunet!cs.utexas.edu!ut-emx!clyde
X.PP
END_OF_FILE
if test 5705 -ne `wc -c <'checkpasswd/checkpasswd.8'`; then
    echo shar: \"'checkpasswd/checkpasswd.8'\" unpacked with wrong size!
fi
# end of 'checkpasswd/checkpasswd.8'
fi
if test -f 'checkpasswd/pwck_dict.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'checkpasswd/pwck_dict.c'\"
else
echo shar: Extracting \"'checkpasswd/pwck_dict.c'\" \(4785 characters\)
sed "s/^X//" >'checkpasswd/pwck_dict.c' <<'END_OF_FILE'
X
X/* --------------------------------------------------------------------  */
X/*                                                                       */
X/*                         Author: Clyde Hoover                          */
X/*                          Computation Center                           */
X/*                   The University of Texas at Austin                   */
X/*                          Austin, Texas 78712                          */
X/*                         clyde@emx.utexas.edu                          */
X/*                   uunet!cs.utexas.edu!ut-emx!clyde                    */
X/*                                                                       */
X/*This code may be distributed freely, provided this notice is retained. */
X/*                                                                       */
X/* --------------------------------------------------------------------  */
X/*
X *	pwck_dictionary - Look in the forbidden password dictionaries.
X *	Returns:
X *		PWCK_INDICT if <password> was in any dictionary
X *		PWCK_OK if not
X */
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)pwck_dict.c	1.2 11/26/90 (cc.utexas.edu)";
X#endif
X
X#include "checkpasswd.h"
X
Xdictionary	*dictionaries = 0;	/* List of dictionaries */
Xstatic char	*egrep = "PATH=/bin:/usr/bin:/usr/ucb; egrep -s"; /* egrep */
X
Xpwck_dictionary(password, userid, mesgbuf)
Xchar	*password;	/* Password to check */
Xint	userid;		/* NOTUSED */
Xchar	*mesgbuf;	/* Message buffer */
X{
X	int	rcode;		/* Return code temp */
X	char	*p;		/* Scratch */
X	dictionary *d;		/* Current dictionary */
X
X	/*
X	 * If there are any non-alpha characters 
X	 * don't bother with the dictionary checks.
X	 */
X	for (p = password; *p; p++) {
X		if (!isalpha(*p))
X			return(PWCK_OK);
X	}
X#ifdef	DEBUG
X	printf("pwck_dictionary: \"%s\"\n", password);
X#endif
X	for (d = dictionaries; d; d = d->dict_next) {
X#ifdef	DEBUG
X		printf("\tdictionary '%s'\n", d->dict_path);
X#endif
X		if ((rcode = InDictionary(d->dict_path, password)) != PWCK_OK){
X			(void) sprintf(mesgbuf,
X				"Password found in dictionary '%s'",
X				d->dict_path);
X			return(rcode);
X		}
X	}
X	return(PWCK_OK);
X}
X
X#ifdef  MDBM
X/*
X *	Use the 'mdbm' package by Chris Torek and others
X */
X#include "mdbm.h"
X#define	DBM		struct mdbm
X#define	DBM_FETCH	mdbm_fetch
X#define	DBM_CLOSE	mdbm_close
X#endif
X
X/*
X *	Using the 4.3BSD 'ndbm' routines
X */
X#ifdef  NDBM
X#include <ndbm.h>
X#define DBM_FETCH	dbm_fetch
X#define DBM_CLOSE	dbm_close
X#endif
X
X/*
X *	InDictionary - look for <password> in <dictionary>
X *
X *	Look in a DBM version of the dictionary if present, 
X *	else use egrep to search the flat file.
X *
X *	Look for <password>, then if the first letter
X *	is capitalized, force to lower and look again.  I don't care
X *	if <password> is in the dictionary but has mixed case letters.
X *	BUT if the first letter has been capitalized, I care because
X *	that's not a sufficent permutation to be secure.
X *
X *	If more than the first letter is capitalized, then the dictionary
X *	lookup will fail.
X *
X *	Returns:
X *		PWCK_INDICT if <password> was found in <dictionary>
X *		PWCK_OK if not
X */
Xstatic
XInDictionary(which_dictionary, password)
Xchar	*which_dictionary,		/* Pathname of dictionary */
X	*password;		/* Plaintext of password */
X{
X#if	defined(NDBM) || defined(MDBM)
X	DBM	*dbp;		/* DBM database pointer */
X	datum	k,		/* DBM lookup key */
X		d;		/* DBM lookup datum */
X#endif
X	int	uc = isupper(password[0]);	/* Is first char UC? */
X	char	pwtemp[BUFSIZ];			/* Scratch buffer */
X#ifdef	MDBM
X	if ((dbp = mdbm_open(which_dictionary, 0, 0,
X	    (int *)0, (int *)0, (char *)0)) == (DBM *)0)
X#endif
X#ifdef	NDBM
X	if ((dbp = dbm_open(which_dictionary, 0, 0)) == (DBM *)0)
X#endif
X	{
X		char	command[BUFSIZ];	/* Command build buffer */
X		int	rc;			/* Return code from sytem(3) */
X
X		if ((rc = open(which_dictionary, 0)) < 0)
X			return(PWCK_OK);
X		(void) close(rc);
X		/*
X		 * If the first letter is capitalized, look for
X		 * "[wW]ord" else look for "word"
X		 */
X		if (uc) 
X			(void) sprintf(command,
X				"%s '^[%c%c]%s$' %s > /dev/null",
X				egrep, password[0], password[0] | 040,
X				&password[1], which_dictionary);
X		else
X			(void) sprintf(command, "%s '^%s$' %s > /dev/null",
X				egrep, password, which_dictionary);
X		rc = system(command);
X		if (rc == 0) 
X			return(PWCK_INDICT);
X		else
X			return(PWCK_OK);
X	} 
X#if	defined(NDBM) || defined(MDBM)
X#define	returnwith(code) { DBM_CLOSE(dbp); return(code); }
X	/*
X	 * Look in the DBM version of the dictionary.
X	 */
X	(void) strcpy(pwtemp, password);
X	k.dptr = pwtemp;
X	k.dsize = strlen(pwtemp);
X	d = DBM_FETCH(dbp, k);
X	if (d.dptr)
X		returnwith(PWCK_INDICT);
X	if (uc) {
X		pwtemp[0] |= 040;
X		d = DBM_FETCH(dbp, k);
X		if (d.dptr)
X			returnwith(PWCK_INDICT);
X	}
X	returnwith(PWCK_OK);
X#endif	/* defined(NDBM) || defined(MDBM) */
X}
X/*	End pwck_dict.c */
END_OF_FILE
if test 4785 -ne `wc -c <'checkpasswd/pwck_dict.c'`; then
    echo shar: \"'checkpasswd/pwck_dict.c'\" unpacked with wrong size!
fi
# end of 'checkpasswd/pwck_dict.c'
fi
if test -f 'checkpasswd/pwck_lexical.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'checkpasswd/pwck_lexical.c'\"
else
echo shar: Extracting \"'checkpasswd/pwck_lexical.c'\" \(4195 characters\)
sed "s/^X//" >'checkpasswd/pwck_lexical.c' <<'END_OF_FILE'
X
X/* --------------------------------------------------------------------  */
X/*                                                                       */
X/*                         Author: Clyde Hoover                          */
X/*                          Computation Center                           */
X/*                   The University of Texas at Austin                   */
X/*                          Austin, Texas 78712                          */
X/*                         clyde@emx.utexas.edu                          */
X/*                   uunet!cs.utexas.edu!ut-emx!clyde                    */
X/*                                                                       */
X/*This code may be distributed freely, provided this notice is retained. */
X/*                                                                       */
X/* --------------------------------------------------------------------  */
X/*
X *	pwck_lexical - Perform lexical analysis of password candidate.
X *
X *	Things which are ok:
X *		Mixed case
X *		Digits
X *		Punctutation
X *		Control characters (except for those in the forbidden table)
X *
X *	Things which are NOT ok:
X *		Passwords less than 'min_length' characters
X *		Runs of more than <run_length> of the same character
X *			(e.g. 'zzz')
X *		Single-case strings (selectable via the config file)
X *
X *	Things NOT checked for:
X *		Cycles of character groups (e.g. 'aabbcc' or 'ababab')
X *		Sequential characters 'abcdef' or '123456'
X */
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)pwck_lexical.c	1.3 11/7/89 (cc.utexas.edu)";
X#endif
X
X#include "checkpasswd.h"
X
X#define	P_U	0x1 	/* Upper case in password */
X#define	P_L	0x2 	/* Lower case in password */
X#define	P_C	0x4 	/* Control chars in password */
X#define	P_D	0x8 	/* Digits in password */
X#define	P_P	0x10 	/* Punctutation chars in password */
X
X#define	hasone(P)	(what |= (P))
X#define	hasany(P)	((what & (P)) == (P))
X
Xpwck_lexical(password, userid, mesg)
Xchar	*password;		/* Password to check */
Xint	userid;			/* NOTUSED */
Xchar	*mesg;		/* Message buffer */
X{
X	int	rc;		/* Duplicate character run count */
X	char	*p = password;	/* Scratch */
X	char	what = 0,	/* Lexical analysis result flags */
X		last = 0;	/* Last character seen (for run checks) */
X
X	mesg[0] = 0;
X#ifdef	DEBUG
X	printf("pwck_lexical: \"%s\"\n", password);
X#endif
X	rc = strlen(password);
X	if (min_length && rc < min_length)
X		return(PWCK_SHORT);
X	/*
X	 * Only the first <max_length> characters of a password are actually
X	 * used due to the limitations of crypt(3).  If the given
X	 * password is longer than this, issue warning message.
X	 */
X	if (max_length && rc > max_length) {
X		printf("WARNING: Only the first %d characters of this password will be used \n",
X			max_length);
X	}
X
X	for (p = password; *p; p++) {
X		if (*p != last) {
X			last = *p;
X			rc = 1;
X		}
X		else {		/* Run of same characters */
X			if (run_length && ++rc >= run_length) {
X				(void) sprintf(mesg,
X			"This password has %d or more repeated characters",
X					run_length);
X				return(PWCK_OBVIOUS);
X			}
X		}
X		if (*p < ' ' || *p > '~') {	/* Non-printing character */
X			char	*_ctran();
X
X			if (print_only) {
X				(void) strcpy(mesg,
X			"This password has non-printing characters");
X				return(PWCK_ILLCHAR);
X			}
X			if (index(illegalcc, *p)) {
X				(void) sprintf(mesg,
X				"Illegal character '%s' in this password",
X					_ctran(*p));
X				return(PWCK_ILLCHAR);
X			}
X			hasone(P_C);
X		}
X		else if (isupper(*p))	hasone(P_U);
X		else if (islower(*p))	hasone(P_L);
X		else if (ispunct(*p))	hasone(P_P);
X		else if (isdigit(*p))	hasone(P_D);
X	}
X	if (hasany(P_U | P_L))	return(PWCK_OK);	/* UC+lc */
X	if (hasany(P_D))	return(PWCK_OK);	/* Numbers */
X	if (hasany(P_P))	return(PWCK_OK);	/* Punctutation chars */
X	if (hasany(P_C))	return(PWCK_OK);	/* Control chars */
X	/*
X	 *	Check for mono-case passwords 
X	 */
X	if (!hasany(P_U) && single_case)	/* All lower case alpha */
X		return(PWCK_OK);
X	if (!hasany(P_L) && single_case)	/* All upper case alpha */
X		return(PWCK_OK);
X
X	if (!hasany(P_L))
X		(void) strcpy(mesg,
X			"Upper-case only passwords not allowed");
X	if (!hasany(P_U))
X		(void) strcpy(mesg,
X			"Lower-case only passwords not allowed");
X	return(PWCK_ILLCHAR);
X}
X/*	End pwck_lexical.c */
END_OF_FILE
if test 4195 -ne `wc -c <'checkpasswd/pwck_lexical.c'`; then
    echo shar: \"'checkpasswd/pwck_lexical.c'\" unpacked with wrong size!
fi
# end of 'checkpasswd/pwck_lexical.c'
fi
if test -f 'pw_passwd.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'pw_passwd.c'\"
else
echo shar: Extracting \"'pw_passwd.c'\" \(9416 characters\)
sed "s/^X//" >'pw_passwd.c' <<'END_OF_FILE'
X
X/* --------------------------------------------------------------------  */
X/*                                                                       */
X/*                         Author: Clyde Hoover                          */
X/*                          Computation Center                           */
X/*                   The University of Texas at Austin                   */
X/*                          Austin, Texas 78712                          */
X/*                         clyde@emx.utexas.edu                          */
X/*                   uunet!cs.utexas.edu!ut-emx!clyde                    */
X/*                                                                       */
X/*This code may be distributed freely, provided this notice is retained. */
X/*                                                                       */
X/* --------------------------------------------------------------------  */
X/*
X *	pw_passwd - Routines for dealing with password files.
X *		Handles V7 / *.* BSD / Sys V format.
X */
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <signal.h>
X#include <errno.h>
X#include <pwd.h>
X#include <fcntl.h>
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)pw_passwd.c	1.9 1/24/91 (cc.utexas.edu)";
X#endif
X
X#define	SLOP	128	/*  Size difference tolerated old <> new passwd file */
X
X#ifdef	SYSV
X/*
X *	System V password aging stuff
X */
X#define	SEC_PER_WEEK	((long )24 * 7 * 60 * 60)
X
Xextern long a64l();
Xextern char *l64a();
X
Xstatic time_t	pwage = 0,
X		maxpwtime = 0,
X		minpwtime = 0,
X		now;
X#endif
X
Xtypedef struct passwd	passwd;
Xtypedef	struct passwd	*passwdp;
X
Xstatic passwd	theUser,		/* The user to change */
X		Me;			/* The user who invoked command */
Xstatic int	myuid,			/* Uid of program */
X		mytempfile = 0;		/* Does PASSWD_TEMP belong to me? */
X
X/*
X *	File names
X */
X#ifndef	PASSWD_FILE
X#define	PASSWD_FILE	"/etc/passwd"
X#endif
X
X#ifndef	PASSWD_SAVE
X#define	PASSWD_SAVE	"/etc/opasswd"
X#endif
X
X#ifndef	PASSWD_TEMP
X#define	PASSWD_TEMP	"/etc/ptmp"
X#endif
X
X#define	PASSWD_MODE	0644
X
X#ifdef	DEBUG
Xstatic char	*passwdtemp = "./etc_ptmp",
X		*passwdfile = "./etc_passwd",
X		*savefile = "./etc_opasswd";
X#else
Xstatic char	*passwdtemp = PASSWD_TEMP,
X		*passwdfile = PASSWD_FILE,
X		*savefile = PASSWD_SAVE;
X#endif
X
Xextern int	errno;
X
Xchar	*getlogin(),
X	*crypt();
X
X/*
X *	pw_initialize - set up
X */
Xpw_initialize()
X{
X	passwdp	me;	/* Temp */
X	char	*myname = getlogin();	/* Name of invoker */
X
X#ifdef	DEBUG
X	setpwfile(passwdfile);
X#endif
X	myuid = getuid();
X	if (myname && *myname) {
X		if ((me = getpwnam(myname)) == NULL)
X			quit(1, "Cannot get user identification from name.\n");
X	} else {
X		if ((me = getpwuid(myuid)) == NULL)
X			quit(1, "Cannot get user identification from uid.\n");
X	}
X
X	_cppasswd(me, &Me);
X	return(1);
X}
X
X/*
X *	pw_getuserbyname - get password 
X *
X *	Returns 1 if passwd info found for <name>
X *		0 otherwise
X */
Xpw_getuserbyname(name, passwdb)
Xchar	*name,		/* User name */
X	*passwdb;	/* Where to stuff password */
X{
X	passwdp	p;	/* Temp */
X
X	if ((p = getpwnam(name)) == NULL)
X		return(0);
X	_cppasswd(p, &theUser);
X	(void) strcpy(passwdb, p->pw_passwd);
X	return(1);
X}
X
X/*
X *	pw_permission - check password change permission
X *
X *	Returns 1 if password can be changed
X *		0 if not
X */
Xpw_permission()
X{
X	if (strcmp(Me.pw_name, theUser.pw_name) && myuid)
X		return(0);
X
X	/*
X	 * Other checks can be put here to determine if the invoker should
X	 * be allowed to change this password.
X	 */
X#ifdef	SYSV
X	if (theUser.pw_age) {
X		pwage = a64l(theUser.pw_age);
X		maxpwtime = pwage & 077;
X		minpwtime = (pwage >> 6) & 077;
X		pwage >>= 12;
X		(void) time(&now);
X		now /= SEC_PER_WEEK;
X		if (pwage <= now) {
X			if (myuid && (now < (pwage + minpwtime))) {
X				fprintf(stderr, 
X				     "Sorry: < %ld  weeks since last change\n",
X				     minpwtime);
X				return(0);
X			}
X			if ((minpwtime > maxpwtime) && myuid) {
X				fprintf(stderr,
X					"You may not change this password.\n");
X				return(0);
X			}
X		}
X	}
X#endif
X	return(1);
X}
X
X/*
X *	pw_compare - compare old and new passwords
X *
X *	Returns 1 if check = new, 0 if not
X */
Xpw_compare(current, check)
Xchar	*current,		/* Current pw (encrypted) */
X	*check;			/* check pw (plain) */
X{
X	if (!*current)
X		return(0);
X	return(!strcmp(current, crypt(check, current)));
X}
X
X/*
X *	pw_check - sanity check password.  Right now just calls
X *		the password check program
X *
X *	Returns 1 if password is ok to use, 0 otherwise
X */
Xpw_check(newpw)
Xchar	*newpw;		/* New password (plain) */
X{
X	/* Put other administrative checks here */
X	return(checkpasswd(theUser.pw_uid, newpw));
X}
X
X/*
X *	pw_replace - replace password in passwd file 
X */
Xpw_replace(newpwd, curpwd)
Xchar	*newpwd,		/* New password (plain) */
X	*curpwd;		/* Old password (plain) */
X{
X#ifdef	SYSV
X	int	(*sigint)(),		/* Save SIGINT */
X		(*sigquit)();		/* Save SIGQUIT */
X#else
X	long    oldsigs,		/* Signal mask save */
X		blocksigs = sigmask(SIGINT) |	/* Signals to block */
X			    sigmask(SIGQUIT) |	/* while updating */
X			    sigmask(SIGTSTP);	/* password file */
X#endif
X	passwdp px;		/* Temp */
X	char	salt[4];	/* Encryption salt */
X	FILE	*tf;		/* File ptr to new passwd file */
X	int	fd;		/* File desc. to new passwd file */
X	struct stat	oldstat,	/* Stat of current passwd file */
X			newstat;	/* Stat of new passwd file */
X
X	/*
X	 * Prepare password entry 'theUser' for replacement
X	 */
X	randomstring(salt, sizeof(salt));
X	theUser.pw_passwd = crypt(newpwd, salt);
X#ifdef	SYSV
X	/*
X	 * Update password age field
X	 */
X	if (theUser.pw_age) {
X		if (maxpwtime == 0)
X			*theUser.pw_age = '\0';
X		else {
X			now = time((time_t *)0) / SEC_PER_WEEK;
X			pwage = maxpwtime
X				+ (minpwtime << 6)
X				+ (now << 12);
X			theUser.pw_age = l64a(pwage);
X		}
X	}
X#endif
X	(void) umask(0);
X	(void) stat(passwdfile, &oldstat);
X	fd = open(passwdtemp, O_WRONLY|O_CREAT|O_EXCL, PASSWD_MODE);
X	if (fd < 0) {
X		if (errno == EEXIST)
X			quit(0, "Password file busy - try again.\n");
X		perror("Tempfile create");
X		quit(1, "Cannot create temp file.\n");
X	}
X	mytempfile = 1;
X	if ((tf = fdopen(fd, "w")) == NULL)
X		quit(1, "Cannot fdopen temp file.");
X#ifdef	SYSV
X	sigint = signal(SIGINT, SIG_IGN);
X	sigquit = signal(SIGQUIT, SIG_IGN);
X#else
X	oldsigs = sigblock(blocksigs);
X#endif
X	setpwent();
X	while ((px = getpwent()) != NULL) {
X		if (px->pw_name == 0 || px->pw_name[0] == 0) /* Sanity check */
X			continue;
X		if (strcmp(px->pw_name, theUser.pw_name) == 0)
X			px = &theUser;
X		(void) putpwent(px, tf);
X	}
X	endpwent();
X	(void) fflush(tf);
X	(void) fstat(fileno(tf), &newstat);
X	(void) fclose(tf);
X	/*
X	 * Check if the new password file is complete.  Since the encrypted
X	 * password is of a fixed length, the new file should be roughly
X	 * the same size as the old one.
X	 */
X	if (newstat.st_size < (oldstat.st_size - SLOP))
X		quit(1, "New password file appears to be incomplete - aborting.\n");
X
X	if (rename(passwdfile, savefile) < 0) {
X		perror("Password file save");
X		unlink(passwdtemp);
X		quit(1, "Can't save password file");
X	}
X	if (rename(passwdtemp, passwdfile) < 0) {
X		perror("Password file replace");
X		(void) unlink(passwdtemp);
X		(void) link(savefile, passwdfile);
X		quit(1, "Can't replace password file");
X	}
X#ifdef	BSD4_3
X	updatedbm();
X#endif
X#ifdef	SYSV
X	(void) signal(SIGINT, sigint);
X	(void) signal(SIGQUIT, sigquit);
X#else
X	(void) sigsetmask(oldsigs);
X#endif
X}
X
X/*
X *	pw_cleanup - clean up after myself
X */
Xpw_cleanup(code)
Xint	code;		/* 0 for normal, 1 for abort */ /*NOTUSED*/
X{
X	if (mytempfile)
X		(void) unlink(passwdtemp);
X}
X
X/*
X *	_newstr - copy string into new storage
X */
Xstatic char *
X_newstr(s)
Xchar	*s;		/* String to copy */
X{
X	register char	*t;	/* Temp */
X	char	*malloc();
X
X	if (s == NULL)
X		return(0);
X	t = malloc(strlen(s) + 1);
X	if (t == NULL)
X		quit(1, "No memory.\n");
X	(void) strcpy(t, s);
X	return(t);
X}
X
X/*
X *	_cppasswd - copy a passwd structure
X */
Xstatic
X_cppasswd(f,t)
Xpasswdp	f,		/* From */
X	t;		/* To */
X{
X	*t = *f;
X	t->pw_name = _newstr(f->pw_name);
X#ifdef	SYSV
X	t->pw_age = _newstr(f->pw_age);
X#endif
X	t->pw_passwd = _newstr(f->pw_passwd);
X	t->pw_comment = _newstr(f->pw_comment);
X	t->pw_gecos = _newstr(f->pw_gecos);
X	t->pw_dir = _newstr(f->pw_dir);
X	t->pw_shell = _newstr(f->pw_shell);
X}
X
X#ifdef	BSD4_3
X/*
X *	Update the hashed password data base
X */
X#include <ndbm.h>
X
X#define	SCOPY(S) xp = (S); while (*cp++ = *xp++)
X#define	BCOPY(B) bcopy((char *)&(B), cp, sizeof(int)); cp += sizeof(int)
X
Xupdatedbm()
X{
X	DBM	*pwd;		/* DBM data base passwd */
X	register char	*cp,	/* Data storage pointer */
X			*xp;	/* String copy pointer */
X	datum	key,		/* DBM key datum */
X		data;		/* DBM data store datum */
X	char	buf[512];	/* Data buffer */
X
X	pwd = dbm_open(passwdfile, O_RDWR, 0);
X	if (pwd == 0)
X		return;
X	cp = buf;
X	/* Pack passwd entry in the form expected by the getpw* routines */
X	SCOPY(theUser.pw_name);
X	SCOPY(theUser.pw_passwd);
X	BCOPY(theUser.pw_uid);
X	BCOPY(theUser.pw_gid);
X	BCOPY(theUser.pw_quota);
X	SCOPY(theUser.pw_comment);
X	SCOPY(theUser.pw_gecos);
X	SCOPY(theUser.pw_dir);
X	SCOPY(theUser.pw_shell);
X
X	data.dptr = buf;
X	data.dsize = cp - buf;
X	key.dptr = theUser.pw_name;
X	key.dsize = strlen(theUser.pw_name);
X	if (dbm_store(pwd, key, data, DBM_REPLACE) < 0) {
X		perror("dbm_store (name)");
X		quit(1, "Can't store passwd entry (name key).\n");
X	}
X	key.dptr = (char *)&theUser.pw_uid;
X	key.dsize = sizeof (int);
X	if (dbm_store(pwd, key, data, DBM_REPLACE) < 0) {
X		perror("dbm_store (uid)");
X		quit(1, "Can't store passwd entry (uid key).\n");
X	}
X	dbm_close(pwd);
X}
X#endif
END_OF_FILE
if test 9416 -ne `wc -c <'pw_passwd.c'`; then
    echo shar: \"'pw_passwd.c'\" unpacked with wrong size!
fi
# end of 'pw_passwd.c'
fi
if test -f 'pw_userinfo.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'pw_userinfo.c'\"
else
echo shar: Extracting \"'pw_userinfo.c'\" \(6714 characters\)
sed "s/^X//" >'pw_userinfo.c' <<'END_OF_FILE'
X
X/* --------------------------------------------------------------------  */
X/*                                                                       */
X/*                         Author: Clyde Hoover                          */
X/*                          Computation Center                           */
X/*                   The University of Texas at Austin                   */
X/*                          Austin, Texas 78712                          */
X/*                         clyde@emx.utexas.edu                          */
X/*                   uunet!cs.utexas.edu!ut-emx!clyde                    */
X/*                                                                       */
X/*This code may be distributed freely, provided this notice is retained. */
X/*                                                                       */
X/* --------------------------------------------------------------------  */
X/*
X *	pw_userinfo.c - UTEXAS CC UNIX User Information Data Base
X *		backend for npasswd
X */
X#ifndef lint
Xstatic char sccsid[] = "@(#)pw_userinfo.c	1.4 8/7/90 (cc.utexas.edu) /tmp_mnt/usr/share/src/private/ut/share/bin/passwd/SCCS/s.pw_userinfo.c";
X#endif
X
X#include <stdio.h>
X#include <errno.h>
X#include <syslog.h>
X#include <strings.h>
X#include <signal.h>
X#include <pwd.h>
X#include <local/userinfo.h>
X
Xstatic userdata	theUser,	/* User having password changed */
X		Me;		/* User doing password change */
X
X#define	P_USER	1
X#define	P_PRIV	2
X#define	P_SU	3
X
Xstatic short	priv = P_USER;	/* Privlege level of <Me> */
X
X#define	QUOTEC	'"'		/* Character to start plaintext pwd */
X#define	XPWLEN	3		/* Length of 'original CDC password' */
X
Xextern char	*getlogin(),
X		*crypt(),
X		*index(),
X		*rindex();
X
X/*
X *	pw_initialize - set up
X */
Xpw_initialize()
X{
X	char	*myname = getlogin();		/* Login name */
X	struct passwd *pw;			/* If getlogin() fails... */
X	userptr	u;			/* Temp */
X
X	if (myname == NULL || *myname == '\0') {
X		if ((pw = getpwuid(getuid())) == ((struct passwd *)NULL))
X			quit(1, "Cannot get user name.\n");
X		else
X			myname = pw->pw_name;
X	}
X	bzero((char *)&theUser, sizeof(theUser));
X	bzero((char *)&Me, sizeof(Me));
X	if ((u = getuserbyname(myname)) == NULL)
X		quit(1, "Cannot get user identification.\n");
X	Me = *u;
X	if (Me.ui_priv.p_acct_maint)	/* Account maintenance priv? */
X		priv = P_PRIV;
X	if (getuid() == 0)		/* SuperUser? */
X		priv = P_SU;
X}
X
X/*
X *	pw_getuserbyname - Get userinfo data by name
X *
X *	Returns 1 if passwd info found for <name>
X *		0 otherwise
X */
Xpw_getuserbyname(name, passwdb)
Xchar	*name,			/* Login name */
X	*passwdb;		/* Where to stash password */
X{
X	userptr	u;			/* Temp */
X
X	if ((u = getuserbyname(name)) == NULL)
X		return(0);
X	theUser = *u;
X	(void) strcpy(passwdb, theUser.ui_password);
X	return(1);
X}
X
X/*
X *	pw_permission - check if this user can change this password
X */
Xpw_permission()
X{
X	int	mypasswd		/* Wanting to change own password? */
X		= (theUser.ui_uid == Me.ui_uid);
X
X	/*
X	 * Must be su to change root password.
X	 */
X	if (theUser.ui_uid == 0 && priv != P_SU) {
X		fprintf(stderr, "Permission denied.\n");
X		return(0);
X	}
X
X	/*
X	 * Must be su or have 'account maintenace' capability to change
X	 * someone else's password.
X	 */
X	if (!mypasswd && priv < P_PRIV) {
X		fprintf(stderr, "Permission denied.\n");
X		return(0);
X	}
X
X	/*
X	 * If 'password change' capability denied, then user cannot
X	 * change their own password.
X	 */
X	if (theUser.ui_priv.p_nopwchange && mypasswd) {
X		fprintf(stderr, "Permission denied.\n");
X		return(0);
X	}
X	/*
X	 * We know at this point that the
X	 * invoker does have permission to change the password.
X	 */
X	return(1);
X}
X
X/*
X *	pw_compare - compare old and new passwords
X *
X *	Returns 1 if check = new, 0 if not
X */
Xpw_compare(current, check)
Xchar	*current,
X	*check;
X{
X	if (!*current)
X		return(1);
X	return(strcmp(current, crypt(check, current)) == 0);
X}
X
X/*
X *	pw_check - sanity check password.  Performs some site-specific
X *		checks, then calls the checkpasswd() code.
X *
X *	Returns 1 if password is ok to use, 0 otherwise
X */
Xpw_check(new)
Xchar	*new;		/* New password (plaintext) */
X{
X	/* Setting null password? */
X	if (strcmp(new, "@") == 0) {
X		if (theUser.ui_priv.p_null_pass == 0 || priv < P_PRIV) {
X			fprintf(stderr, "Cannot set null password.\n");
X			return(0);
X		}
X		else
X			return(1);
X	}
X
X	/* A plain text password (enclosed in ""s)? */
X	if (*new == QUOTEC) {
X		char	*p = &new[1];
X
X		while (*p) p++;
X		if (p[-1] == QUOTEC) {
X			if (priv == P_SU)	/* Reserved for superuser */
X				return(1);
X			else {
X				fprintf(stderr,
X					"Cannot set plaintext password.\n");
X				return(0);
X			}
X		}
X	}
X
X	/* Special password (reserved for superuser) */
X	if (strlen(new) == XPWLEN && priv == P_SU)
X		return(1); 
X
X	/* Dispatch to general password checker */
X	return(checkpasswd(theUser.ui_uid, new));
X}
X
X/*
X *	pw_replace - Replace password in Userinfo database
X */
Xpw_replace(new, current)
Xchar	*new,		/* New password (plaintext) */
X	*current;	/* Current password (plaintext) [unused] */
X{
X	userptr	newu;			/* Temp */
X	int	rc;			/* Temp */
X	long	oldsigs,		/* Saved signal mask */
X		blockedsigs = sigmask(SIGINT) |		/* Signals to block */
X			      sigmask(SIGQUIT) |	/* while updating */
X			      sigmask(SIGTSTP);		/* the database */
X	extern int	errno;
X
X	/*
X	 * Password has already been validated by pw_check()
X	 */
X	if ((newu = getuserbyuid(theUser.ui_uid)) == NULL)
X		quit(1, "pw_replace: Cannot refetch user information.\n");
X
X	if (strcmp(new, "@") == 0) {
X		printf("Password removed from %s\n", theUser.ui_name);
X#ifndef	DEBUG
X		syslog(LOG_INFO, "Password removed from %s\n", theUser.ui_name);
X#endif
X		newu->ui_password[0] = 0;
X	}
X	else {
X		char	salt[2];
X
X		randomstring(salt, sizeof(salt));
X		(void) strcpy(newu->ui_password, crypt(new, salt));
X		if (*new == QUOTEC && priv == P_SU) {
X			char	*p = new;
X
X			while (*p) p++;
X			if (*--p == QUOTEC) {
X				*p = 0;
X				(void) strcpy(newu->ui_password, &new[1]);
X				printf("Setting plain text password.\n");
X			}
X		}
X	}
X	ui_acct(newu)->a_pwchanged = time((time_t *)0);
X
X#if	0
X	if (UIRecordChanged(newu))
X		quit(1, "Record synchronization error\n");
X#endif
X#ifdef	DEBUG
X	printf("replace %s %s\n", theUser.ui_password, newu->ui_password);
X#else
X	errno = 0;
X	oldsigs = sigblock(blockedsigs);
X	if (lockuser(theUser.ui_uid) < 0) {
X		if (errno == ETXTBSY)
X			quit(1,
X				"pw_replace: Data for %s locked out.\n",
X				theUser.ui_name);
X		else
X			quit(1,
X				"pw_replace: Data lock failure for user %s\n",
X				theUser.ui_name);
X	}
X	rc = UIReplaceEntry(newu);
X	(void) sigsetmask(oldsigs);
X	unlockuser(theUser.ui_uid);
X	if (rc < 0)
X		quit(1, "Userinfo update failure %s\n", UIErrorMessage);
X#endif
X}
X
X/*
X *	pw_cleanup - cleanup routine
X */
Xpw_cleanup()
X{
X	/* Do nothing */
X}
X/*	End pw_userinfo.c		*/
END_OF_FILE
if test 6714 -ne `wc -c <'pw_userinfo.c'`; then
    echo shar: \"'pw_userinfo.c'\" unpacked with wrong size!
fi
# end of 'pw_userinfo.c'
fi
echo shar: End of archive 2 \(of 3\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 3 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Clyde Hoover (Shouter-To-Dead-Parrots)	|
UNIX/VMS Services			| "Any sufficently advanced technology 
Compuatation Center, UT Austin 		| is indisguishable from a rigged demo."
clyde@emx.utexas.edu			|

