Newsgroups: comp.sources.unix
From: clyde@emx.utexas.edu (Clyde Hoover)
Subject: v25i076: npasswd - replacement for passwd(1), Part03/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 76
Archive-Name: npasswd/part03

From clyde@emx.utexas.edu Fri Jan 25 12:45:55 1991
Received: from BBN.COM by pineapple.bbn.com id <AA22526@pineapple.bbn.com>; Fri, 25 Jan 91 12:45:27 -0500
Received: from uunet.UU.NET by BBN.COM id aa03197; 25 Jan 91 12:40 EST
Received: from cs.utexas.edu by uunet.uu.net (5.61/1.14) with SMTP 
	id AA17516; Fri, 25 Jan 91 12:39:43 -0500
Received: from emx.utexas.edu by cs.utexas.edu (5.64/1.93) via SMTP
	id AA18063; Fri, 25 Jan 91 11:38:53 -0600
Posted-Date:  25 Jan 91 17:04:17 GMT
Received: by emx.utexas.edu (5.61/1.8)
	id AA09974; Fri, 25 Jan 91 11:04:20 -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 3 of 3)
Keywords: Password changing program
Message-Id: <43166@ut-emx.uucp>
Date: 25 Jan 91 17:04:17 GMT
Organization: Moose & Squirrel Software
Lines: 2197
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 3 (of 3)."
# Contents:  CHANGE.LOG checkpasswd/util.c npasswd.c pw_yp.c
# Wrapped by clyde@tigger.cc.utexas.edu on Fri Jan 25 10:35:08 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'CHANGE.LOG' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'CHANGE.LOG'\"
else
echo shar: Extracting \"'CHANGE.LOG'\" \(9460 characters\)
sed "s/^X//" >'CHANGE.LOG' <<'END_OF_FILE'
X
XSCCS/s.npasswd.1:
X
XD 1.3	90/08/14 14:45:58 clyde	3 2	00016/00006/00053
XDocument -P flag
X
XD 1.2	89/09/21 14:31:45 clyde	2 1	00009/00016/00050
XMisc changes
X
XD 1.1	89/05/18 10:25:49 clyde	1 0	00066/00000/00000
Xdate and time created 89/05/18 10:25:49 by clyde
X
XUsers allowed to make deltas --
X	everyone
X
XFlags --
X	none
X
XDescription --
X	none
X
XSCCS/s.npasswd.c:
X
XD 1.15	90/08/14 14:23:33 clyde	15 14	00099/00026/00471
X1. Include <errno.h>
X2. Add -P option (input via pipe from program).
X3. Use #ifdefs for index() vs. strchr().
X4. Change usage messages.
X5. Don't prompt if input is from a pipe.
X6. Print all status messages to stdout.
X7. Terminate upon password entry/match error if stdin = pipe.
X8. Exit messages to stdout if stdin = pipe.
X9. Use 'cat' if stdin != tty.
X10. Allow stdin to be tty or pipe (pipe only with -P option).
X11. Add fgetpwent().
X12. In putpwent(), type uid/gid as unsigned if UNSIGNED_UID defined.
X
XD 1.14	90/08/07 15:55:18 mic	14 13	00009/00004/00488
XUse getpwuid() to get user id if getlogin() fails.  This can happen if run
Xfrom an rsh or by /bin/login before you are really logged in.
X
XD 1.13	90/06/05 11:07:10 clyde	13 12	00010/00003/00482
XFix problem with overwriting password buffers
X
XD 1.12	90/01/16 16:21:38 clyde	12 11	00001/00001/00484
XFix echo suppression for SunOS 4.0
X
XD 1.11	90/01/12 14:55:09 clyde	11 10	00006/00002/00478
XBuild properly under SunOS 4.0
X
XD 1.10	89/11/15 14:17:19 clyde	10 9	00002/00001/00478
XUpdate some comments
X
XD 1.9	89/11/01 16:21:29 clyde	9 8	00001/00000/00478
XFix messge-of-the-day output
X
XD 1.8	89/09/27 10:27:17 clyde	8 7	00006/00004/00472
XWork around syslogs with missing defines
X
XD 1.7	89/09/26 15:00:52 clyde	7 6	00015/00008/00461
X1. Add 'message of the day' capability.
X2. Change 'help()' to generalized 'motd()' routine.
X
XD 1.6	89/09/22 15:58:07 clyde	6 5	00033/00000/00436
XInsert putpwent() and rename() [under #ifdef control]
X
XD 1.5	89/09/21 17:16:46 clyde	5 4	00004/00004/00432
XChange occurances of 'npasswd' to 'passwd'
X
XD 1.4	89/09/21 17:08:49 clyde	4 3	00005/00004/00431
XMove some stuff to static storage (bug workaround)
X
XD 1.3	89/06/28 14:20:36 clyde	3 2	00004/00000/00431
XPut ifdefs around some configuration defines
X
XD 1.2	89/06/05 13:14:00 clyde	2 1	00056/00000/00375
XAdd getpass() replacement routine (#ifdef XGETPASS)
X
XD 1.1	89/05/18 10:25:50 clyde	1 0	00375/00000/00000
Xdate and time created 89/05/18 10:25:50 by clyde
X
XUsers allowed to make deltas --
X	everyone
X
XFlags --
X	none
X
XDescription --
X	none
X
XSCCS/s.npasswd.cf:
X
XD 1.1	89/05/18 10:25:52 clyde	1 0	00020/00000/00000
Xdate and time created 89/05/18 10:25:52 by clyde
X
XUsers allowed to make deltas --
X	everyone
X
XFlags --
X	none
X
XDescription --
X	none
X
XSCCS/s.npasswd.hf:
X
XD 1.1	89/05/18 10:25:53 clyde	1 0	00005/00000/00000
Xdate and time created 89/05/18 10:25:53 by clyde
X
XUsers allowed to make deltas --
X	everyone
X
XFlags --
X	none
X
XDescription --
X	none
X
XSCCS/s.pw_passwd.c:
X
XD 1.8	90/08/07 16:01:00 root	8 7	00007/00005/00380
XUse getpwuid() to get user id if getlogin() fails.  This can happen if run
Xfrom an rsh or by /bin/login before you are really logged in.
X
XD 1.7	90/07/23 09:37:18 clyde	7 6	00001/00001/00384
XFix pw_compare()
X
XD 1.6	90/06/22 10:39:16 clyde	6 5	00002/00000/00383
XMake password compare rtn deal with null password
X
XD 1.5	89/11/14 17:00:25 clyde	5 4	00005/00002/00378
XDon't remove temp file unless I created it
X
XD 1.4	89/11/01 16:24:47 clyde	4 3	00003/00003/00377
XChange DEBUG file names to './etc_XXXX'
X
XD 1.3	89/10/02 16:53:17 clyde	3 2	00018/00004/00362
X1. Set file names with #define(s).
X2. Set passwd file mode with a #define.
X
XD 1.2	89/09/22 15:57:24 clyde	2 1	00000/00033/00366
XRemove putpwent() [moved to npasswd.c]
X
XD 1.1	89/05/18 10:25:54 clyde	1 0	00399/00000/00000
Xdate and time created 89/05/18 10:25:54 by clyde
X
XUsers allowed to make deltas --
X	everyone
X
XFlags --
X	none
X
XDescription --
X	none
X
XSCCS/s.pw_userinfo.c:
X
XD 1.4	90/08/07 15:56:31 mic	4 3	00008/00002/00248
XUse getpwuid() to get user id if getlogin() fails.  This can happen if run
Xfrom an rsh or by /bin/login before you are really logged in.
X
XD 1.3	90/06/22 10:39:19 clyde	3 2	00002/00000/00248
XMake password compare rtn deal with null password
X
XD 1.2	89/06/05 13:48:30 clyde	2 1	00172/00105/00076
XFirst working version
X
XD 1.1	89/05/18 10:25:56 clyde	1 0	00181/00000/00000
Xdate and time created 89/05/18 10:25:56 by clyde
X
XUsers allowed to make deltas --
X	everyone
X
XFlags --
X	none
X
XDescription --
X	none
X
XSCCS/s.pw_yp.c:
X
XD 1.14	90/08/14 14:27:17 clyde	14 13	00056/00019/00558
X1. Include filename in passwd open error messages.
X2. Password history checking ifdef on PASSWORD_HISTORY.
X3. Ignore passwd entries starting with YP special cookies.
X4. Detect if local password is in adjunct passwd file.
X5. Add newlines to all quit() messages.
X6. Set real uid to effective uid before running YP update script.
X7. Allow su to override YP ypdate script (env YP_UPDATE_PROC).
X
XD 1.13	90/08/07 16:01:01 root	13 12	00007/00005/00570
XUse getpwuid() to get user id if getlogin() fails.  This can happen if run
Xfrom an rsh or by /bin/login before you are really logged in.
X
XD 1.12	90/06/22 10:39:13 clyde	12 11	00002/00000/00573
XMake password compare rtn deal with null password
X
XD 1.11	90/05/29 14:41:29 clyde	11 10	00002/00001/00571
XSet PATH before firing up YP make
X
XD 1.10	90/03/20 14:58:51 clyde	10 9	00003/00001/00569
XMake sure passwd.ptmp is removed if /etc/ptmp create fails
X
XD 1.9	90/03/19 16:34:51 clyde	9 8	00009/00004/00561
XPut secure RPC stuff in #ifdef SECURE_RPC
X
XD 1.8	90/03/19 16:09:27 clyde	8 7	00033/00014/00532
X1. Split code that does locked temp files into routine.
X2. If on YP master, create both "/etc/ptmp" and "PASSWD_FILE.ptmp".
X
XD 1.7	90/03/19 14:50:56 clyde	7 6	00154/00042/00392
X1. Use direct syslog of errors where needed.
X2. Add stub to call password history check.
X3. Always encrypt password with new salt.
X4. Use RPC calls directly for YP password change.
X5. Add error checking and reporting for YP password changes.
X6. Add stub for secure RPC key change.
X7. Rewrite is_yp_master() to call YP routines instead of using ypwhich.
X8. Add -DYPASSWDD_403 define to cope with SunOS 4.0.3 yppasswdd.
X
XD 1.6	89/11/14 17:00:39 clyde	6 5	00005/00002/00429
XDon't remove temp file unless I created it
X
XD 1.5	89/11/01 16:24:41 clyde	5 4	00003/00003/00428
XChange DEBUG file names to './etc_XXXX'
X
XD 1.4	89/10/02 16:52:59 clyde	4 3	00021/00004/00410
X1. Set file names with #define(s).
X2. Set passwd file mode with a #define.
X
XD 1.3	89/09/26 14:59:23 clyde	3 2	00040/00012/00374
X1. Try yppasswd() call multiple times.
X2. Move check for YP change capability from pw_replace() to pw_permission().
X
XD 1.2	89/05/19 11:01:35 clyde	2 1	00005/00001/00381
XInstall PATH for exec of ypwhich
X
XD 1.1	89/05/18 10:25:57 clyde	1 0	00382/00000/00000
Xdate and time created 89/05/18 10:25:57 by clyde
X
XUsers allowed to make deltas --
X	everyone
X
XFlags --
X	none
X
XDescription --
X	none
X
XSCCS/s.version.h:
X
XD 1.5	90/08/14 14:37:26 clyde	5 4	00001/00001/00007
XBump patch level
X
XD 1.4	90/03/19 13:58:10 clyde	4 3	00002/00002/00006
XBump overall version to 1.2
X
XD 1.3	89/11/28 12:32:30 clyde	3 2	00002/00002/00006
XUpdate version stamp
X
XD 1.2	89/09/21 17:25:54 clyde	2 1	00002/00002/00006
XUpdate date info
X
XD 1.1	89/05/18 10:25:59 clyde	1 0	00008/00000/00000
Xdate and time created 89/05/18 10:25:59 by clyde
X
XUsers allowed to make deltas --
X	everyone
X
XFlags --
X	none
X
XDescription --
X	none
X
XSCCS/s.README:
X
XD 1.6	90/03/19 16:36:31 clyde	6 5	00002/00000/00129
XAdd info about defining SECURE_RPC
X
XD 1.5	90/03/19 16:10:58 clyde	5 4	00000/00007/00129
XRemove stuff about YPPASSWDD_403
X
XD 1.4	90/03/19 15:05:05 clyde	4 3	00015/00007/00121
X1. Fix errors.
X2. Explain configuring for SunOS 4.0.3 
X
XD 1.3	89/11/28 12:24:45 clyde	3 2	00038/00004/00090
XAdd sketch about the configuration file
X
XD 1.2	89/10/24 10:48:21 clyde	2 1	00001/00002/00093
XRemove 'beta test' comment
X
XD 1.1	89/05/18 10:26:01 clyde	1 0	00095/00000/00000
Xdate and time created 89/05/18 10:26:01 by clyde
X
XUsers allowed to make deltas --
X	everyone
X
XFlags --
X	none
X
XDescription --
X	none
X
XSCCS/s.npasswd.help:
X
XD 1.1	89/09/21 14:20:53 clyde	1 0	00020/00000/00000
Xdate and time created 89/09/21 14:20:53 by clyde
X
XUsers allowed to make deltas --
X	everyone
X
XFlags --
X	none
X
XDescription --
X	none
X
XSCCS/s.Makefile.dist:
X
XD 1.8	90/07/23 09:59:01 clyde	8 7	00001/00001/00163
XFix error in BSD build options
X
XD 1.7	89/12/05 09:37:18 clyde	7 6	00001/00001/00163
XFix bug in install directives
X
XD 1.6	89/10/30 14:34:11 clyde	6 5	00006/00005/00158
XAdd DEST to install lines to allow install in alternate fs tree
X
XD 1.5	89/10/24 11:17:43 clyde	5 4	00019/00012/00144
X1. Add MANDIR for manual page install.
X2. Change npasswd to $(PASSWD) except for source file references.
X3. Don't replace config and help files if they already exist.
X
XD 1.4	89/10/04 09:54:42 clyde	4 3	00059/00016/00097
XAdd commentary, massive other changes
X
XD 1.3	89/09/27 10:25:28 clyde	3 2	00014/00007/00099
X1. Document  -DXPUTPWENT flag.
X2. Better paramaterization of file names.
X
XD 1.2	89/09/26 15:01:52 clyde	2 1	00007/00005/00099
XAdd message-of-the-day file
X
XD 1.1	89/09/21 17:22:20 clyde	1 0	00104/00000/00000
Xdate and time created 89/09/21 17:22:20 by clyde
X
XUsers allowed to make deltas --
X	everyone
X
XFlags --
X	none
X
XDescription --
X	none
X
XSCCS/s.passwd.1:
X
XD 1.1	89/10/24 10:51:25 clyde	1 0	00056/00000/00000
Xdate and time created 89/10/24 10:51:25 by clyde
X
XUsers allowed to make deltas --
X	everyone
X
XFlags --
X	none
X
XDescription --
X	none
END_OF_FILE
if test 9460 -ne `wc -c <'CHANGE.LOG'`; then
    echo shar: \"'CHANGE.LOG'\" unpacked with wrong size!
fi
# end of 'CHANGE.LOG'
fi
if test -f 'checkpasswd/util.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'checkpasswd/util.c'\"
else
echo shar: Extracting \"'checkpasswd/util.c'\" \(11926 characters\)
sed "s/^X//" >'checkpasswd/util.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 *	util.c - Miscellanous utility routines
X */
X#ifndef lint
Xstatic char sccsid[] = "@(#)util.c	1.4 11/14/89 (cc.utexas.edu)";
X#endif
X
X#include "checkpasswd.h"
X
X/*
X *	_instring - Compare all sub-strings
X *
X *	Returns:
X *		0 if match not found
X *		rc if match found
X */
X_instring(s1, s2, rc)
Xchar	*s1,		/* String to look for */
X	*s2;		/* String to look for <s1> in */
Xint	rc;		/* What to return on match */
X{
X	int	l;		/* Temp */
X
X	for (l = strlen(s1); *s2; s2++)
X		if (_cistrncmp(s1, s2, l) == 0)
X			return (rc);
X	return(0);
X}
X
X/*
X *	_flipstring - reverse a string in place
X */
X_flipstring(s)
Xchar	*s;		/* String to reverse */
X{
X	char	*p,	/* Scratch */
X		*t;	/* Scratch */
X	char	*malloc();
X
X	t = malloc(strlen(s) + 1);
X	(void) strcpy(t, s);
X	p = t;
X	while (*p) p++;		/* Find end of string */
X	--p;
X	for (; *s; )
X		*s++ = *p--;
X	free(t);
X}
X
X/*
X *	Case indepedant string comparasion routines swiped from
X *	the source to MIT Hesiod.
X *	Since these routines are publicly available,
X *	I presume to redistribute them is not in violation of copyright.
X */
X
X/*
X * Copyright (c) 1986 Regents of the University of California.
X * All rights reserved.  The Berkeley software License Agreement
X * specifies the terms and conditions for redistribution.
X */
X
X/*
X * This array is designed for mapping upper and lower case letter
X * together for a case independent comparison.  The mappings are
X * based upon ascii character sequences.
X */
Xstatic
Xchar charmap[] = {
X	'\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
X	'\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
X	'\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
X	'\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
X	'\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
X	'\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
X	'\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
X	'\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
X	'\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
X	'\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
X	'\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
X	'\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
X	'\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
X	'\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
X	'\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
X	'\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
X	'\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
X	'\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
X	'\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
X	'\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
X	'\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
X	'\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
X	'\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
X	'\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
X	'\300', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
X	'\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
X	'\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
X	'\370', '\371', '\372', '\333', '\334', '\335', '\336', '\337',
X	'\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
X	'\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
X	'\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
X	'\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
X};
X
X/*
X *	cistrcmp - case independant string compare
X */
X_cistrcmp(s1, s2)
Xregister char *s1, *s2;
X{
X	register char *cm = charmap;
X
X	while (cm[*s1] == cm[*s2++])
X		if (*s1++=='\0')
X			return(0);
X	return(cm[*s1] - cm[*--s2]);
X}
X
X/*
X *	cistrncmp - case independant string compare
X */
X_cistrncmp(s1, s2, n)
Xregister char *s1, *s2;
Xregister n;
X{
X	register char *cm = charmap;
X
X	while (--n >= 0 && cm[*s1] == cm[*s2++])
X		if (*s1++ == '\0')
X			return(0);
X	return(n<0 ? 0 : cm[*s1] - cm[*--s2]);
X}
X/* end of UCB copyrighted code 	*/
X
X/*
X *	_ctran - produce printable version of any ASCII character
X */
Xchar *
X_ctran (c)
Xchar	c;		/* Character to represent */
X{
X	static char	cbuf[8];	/* Return value buffer */
X	char	*p = cbuf;		/* Pointer to cbuf */
X	char	chr = c & 0177;		/* Scratch */
X
X	if (c & 0200) {		/* Meta char - weird but what the hey */
X		*p++ = 'M';
X		*p++ = '-';
X	}
X	if (chr >= ' ' && chr <= '~') {
X		*p++ = chr;
X		*p++ = 0;
X		return (cbuf);
X	}
X	if (chr == 0177)
X		return("DEL");
X	*p++ = '^';
X	*p++ = chr + '@';
X	*p++ = 0;
X	return (cbuf);
X}
X
X/*
X *	The following routines are used by the configuration file processor.
X */
X
X/*
X *	decode_boolean - decode a boolean value
X */
Xstatic
Xdecode_boolean(s)
Xchar	*s;
X{
X	return(*s == '1'
X		| streq(s, "true")
X		| streq(s, "yes")
X		| streq(s, "on"));
X}
X
X/*
X *	decode_int - decode an integer value
X */
Xstatic
Xdecode_int(s)
Xchar	*s;		/* String to decode */
X{
X	int	t;	/* Temp */
X
X	if (xatoi(s, (char *)0, &t))
X		return(t);
X	fprintf(stderr, "Bad numeric value '%s'\n", s);
X	return(-1);
X}
X
X/*
X *	Is argument an octal digit?
X */
Xstatic int	octdigit(c)
Xchar	c;
X{
X	return (c >= '0' && c <= '7');
X}
X
X/*
X *	Is argument a decimal digit?
X */
Xstatic int	decdigit(c)
Xchar	c;
X{
X	return (c >= '0' && c <= '9');
X}
X
X/*
X *	Is argument a hexidecimal digit?
X */
Xstatic int	hexdigit(c)
Xchar	c;
X{
X	return (decdigit(c) |
X		(c >= 'a' &&  c <= 'f') |
X		(c >= 'A' && c <= 'F'));
X}
X
X/*
X *	xatoi - Smart 'atoi' recognizes decimal, octal and hex constants
X */
Xstatic
Xxatoi(ip, ipp, iv)
Xchar	*ip,		/* Pointer to number string */
X	**ipp;		/* Stash pointer to end of string */ /* RETURN VALUE */
Xint	*iv;		/* RETURN VALUE */
X{
X	int	(*func)() = decdigit,	/* Function to check char */
X		base = 10;		/* Conversion base */
X	int	t = 0,			/* Return value */
X		mult = 1;		/* Sign of result */
X	char	*fcc = ip;		/* First char position */
X
X	if (*ip == '-') {		/* Negative number? */
X		ip++;
X		mult = -1;
X	}
X	if (*ip == '0') { 	/* Leading '0'? */
X		ip++;
X		if (*ip == 'x' || *ip == 'X') {	/* Hex */
X			base = 16;
X			func = hexdigit;
X			ip++;			/* Skip 'x' */
X		}
X		else {
X			base = 8;		/* Octal */
X			func = octdigit;
X		}
X	}
X	while (*ip && (*func)(*ip)) {
X		t *= base;
X		if (decdigit(*ip))
X			t += (*ip - '0');
X		else
X			t += (*ip >= 'a' ? *ip - 0x57 : *ip - 0x37);
X		ip++;
X	}
X	if (ip == fcc)		/* Nothing processed */
X		return(0);
X	if (ipp)		/* Stash new pointer location */
X		*ipp = ip;
X	*iv = (t * mult);
X	return(1);
X}
X
X/*
X *	decode_string - Copy string, converting backslash escapes
X *	Can handle most of the C backslash escape sequences
X */
Xstatic
Xdecode_string(dst, src, len)
Xchar	*dst,		/* Destination */
X	*src;		/* Source */
Xint	len;
X{
X	int	t;		/* Temp */
X	char	*dstx = dst;	/* Pointer to start of destination */
X	char	quote = 0;	/* Quote character */
X
X	if (*src == '"' || *src == '\'')
X		quote = *src++;
X
X#define	putxchar(P) *dst++ = (P)
X	for (; *src && (dst - dstx) < len; ) {
X		if (*src == '\\') {
X			src++;
X			switch(*src) {
X			case 'a':	putxchar('\007'); src++; break;
X			case 'b':	putxchar('\b'); src++; break;
X			case 'f':	putxchar('\f'); src++; break;
X			case 'n':	putxchar('\n'); src++; break;
X			case 'r':	putxchar('\r'); src++; break;
X			case 't':	putxchar('\t'); src++; break;
X			case '\\':	putxchar('\\'); src++; break;
X			case '0': case '1': case '2': case '3':
X			case '4': case '5': case '6': case '7':
X			case 'x':
X				if (xatoi(src, &src, &t))
X					putxchar(t & 0xff);
X				break;
X			default:
X				if (quote && *src == quote)
X					*dst++ = *src++;
X				break;
X			}
X			continue;
X		}
X		else if (*src == '^') {	/* ^C = control-c */
X			src++;
X			if (isupper(*src))
X				putxchar(*src - '@');
X			else if (islower(*src))
X				putxchar(*src - '`');
X			else switch (*src) {
X			     case '[':	putxchar('\033'); break;
X			     case '\\':	putxchar('\034'); break;
X			     case ']':	putxchar('\035'); break;
X			     case '^':	putxchar('\036'); break;
X			     case '-':	putxchar('\037'); break;
X			}
X			src++;
X			continue;
X		}
X		else if (quote && *src == quote)
X			break;
X		*dst++ = *src++;
X	}
X#undef	putxchar
X	*dst = 0;
X}
X
Xstatic char	*default_dicts[] = {    /* List of default dictionaries */
X#ifdef  DEFAULT_DICT
X	DEFAULT_DICT,
X#endif
X	0
X};
X
X/*
X *	readconfig - Read the configuration file 
X *		Returns 1 if success, 0 if not found and -1 if error
X */
Xreadconfig(filename)
Xchar	*filename;
X{
X	char	buf[BUFSIZ];	/* Read buffer */
X	FILE	*fp;		/* File pointer */
X	char	**p;		/* Scratch */
X	int	lineno = 0;	/* Current line number in config file */
X	extern int	standalone;	/* Am I a standalone application? */
X
X	/* "Load" default directories */
X	for (p = default_dicts; *p; p++)
X		add_dict(*p);
X
X	if ((fp = fopen(filename, "r")) == NULL) {
X#ifdef	DEBUG
X		printf("No config file\n");
X#endif
X		return(0);
X	}
X
X	while (fgets(buf, sizeof(buf), fp) != NULL) {
X		char	*key,		/* Key on line */
X			*data;		/* Data on line */
X
X		lineno++;
X		if (buf[0] == '\n')		/* Empty line */
X			continue;
X		if (key = index(buf, '\n'))
X			*key = 0;
X		for (data = buf; *data && *data <= ' '; data++);
X		if (*data == '#')
X			continue;
X		key = data;
X		for (; *data && *data > ' '; data++);	/* Skip to end */
X		if (*data)
X			*data++ = 0;
X		else {
X			if (standalone)
X				printf("\"%s\", line %d: Incomplete line.\n",
X					filename, lineno);
X			continue;
X		}
X		for (; *data && *data <= ' '; data++);	/* Skip whitespace */
X		if (streq(key, "dictionary"))
X			add_dict(data);
X		else if (streq(key, "singlecase"))
X			single_case = decode_boolean(data);
X		else if (streq(key, "minlength"))
X			min_length = decode_int(data);
X		else if (streq(key, "maxlength"))
X			max_length = decode_int(data);
X		else if (streq(key, "printonly"))
X			print_only = decode_boolean(data);
X		else if (streq(key, "badchars")) {
X			char	xcc[BUFSIZ];
X			char	append = 0;
X
X			if (*data == '+')	/* Add data to existing list */
X				append = *data++;
X			decode_string(xcc, data, BUFSIZ);
X			if (xcc[0] == 0)
X				continue;
X			if (append)
X				(void) strcat(illegalcc, xcc);
X			else
X				(void) strncpy(illegalcc, xcc, sizeof_illegalcc);
X		}
X		else {
X			if (standalone)
X				printf("\"%s\", line %d: Unrecognized keyword '%s'.\n",
X					filename, lineno, key);
X		}
X	}
X	(void) fclose(fp);
X	return(1);
X}
X
X/*
X *	add_dict - Add a dictionary to the search list
X *
X *	Arguments:
X *		The rest of the line from the configuration file
X *		which contains the path to the dictionary and optionally
X *		a descriptive phrase
X */
Xstatic
Xadd_dict(line)
Xchar	*line;		/* RHS of config line */
X{
X	dictionary	*dx,	/* Tail of directory list */
X			*dn;	/* New entry */
X	char	*tx,		/* Scratch */
X		*p;		/* Scratch */
X	char	*calloc();
X
X#ifdef	DEBUG
X	printf("Add dictionary '%s'\n", line);
X#endif
X	dn = (dictionary *)calloc(sizeof(dictionary), 1);
X	if (dictionaries == 0)
X		dictionaries = dn;
X
X	for (dx = dictionaries; dx->dict_next ; dx = dx->dict_next);
X
X	tx = malloc(strlen(line) + 1);
X	(void) strcpy(tx, line);
X	p = tx;
X	while (*p && *p > ' ') p++;
X	if (*p)
X		*p++ = 0;
X	dn->dict_path = tx;
X	dn->dict_desc = p;
X	dx->dict_next = dn;
X	dn->dict_next = 0;
X}
X/*	End util.c */
END_OF_FILE
if test 11926 -ne `wc -c <'checkpasswd/util.c'`; then
    echo shar: \"'checkpasswd/util.c'\" unpacked with wrong size!
fi
# end of 'checkpasswd/util.c'
fi
if test -f 'npasswd.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'npasswd.c'\"
else
echo shar: Extracting \"'npasswd.c'\" \(15199 characters\)
sed "s/^X//" >'npasswd.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 *	This program duplicates the manual page behavior of the 4.XBSD
X *	passwd(1) command.  It can be configured for use with a variety
X *	of passwd systems (/etc/passwd, /etc/shadow, databases).
X *
X *	The System V support is untested (by the author - other sites
X *	tell me it works).
X *
X *	Here we have only the most abstract data needed (login name,
X *	user id, current password, new password).
X *	All other information needed (the full password line, etc),
X *	is kept down in the 'method' routines.
X *
X *	The 'method' routines are:
X *
X *	pw_initalize()		Do initializations 
X *	pw_getuserbyname()	Get user information by name
X *	pw_permission()		Check if user has permission
X *				 to change this users' password
X *	pw_compare()		Compare passwords
X *	pw_check()		Check password
X *				 Returns 1 if ok, 0 otherwise
X *	pw_replace()		Replace the password
X *	pw_cleanup()		Cleanup
X */
X#ifdef	SYSLOG
X#include <syslog.h>
X# ifndef	LOG_AUTH
X#  define	LOG_AUTH	0
X# endif
X# ifndef	LOG_CONS
X#  define	LOG_CONS	0
X# endif
X#endif
X
X#include <ctype.h>
X#include <signal.h>
X#include <sys/types.h>
X#include <stdio.h>
X#include <pwd.h>
X#include <errno.h>
X#include "version.h"
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)npasswd.c	1.16 9/24/90 (cc.utexas.edu) /usr/share/src/private/ut/share/bin/passwd/SCCS/s.npasswd.c";
X#endif
X
X#ifndef	CONFIG_FILE
X#define	CONFIG_FILE	"/usr/adm/passwd.conf"
X#endif
X#ifndef	HELP_FILE
X#define	HELP_FILE	"/usr/adm/passwd.help"
X#endif
X#ifndef	MOTD_FILE
X#define	MOTD_FILE	"/usr/adm/passwd.motd"
X#endif
X
Xextern int	errno;			/* System error code */
X
X#ifdef	sun
Xstatic char	*options = "alyd:e:F:n:fPsVx:";	/* Command line options */
X#else
Xstatic char	*options = "fPsV";	/* Command line options */
X#endif
Xstatic int	retries = 3;		/* Retry limit */
Xstatic int	from_prog = 0;		/* Data source is a program */
X
Xstatic char	username[16],	/* Name of user changing password */
X		password[16];	/* Current password (encrypted) */
X
Xchar	pbuf[16],		/* Password read buffer 1 */
X	pbuf2[16],		/* Password read buffer 2 */
X	ppbuf[16],		/* Current password */
X	mylogin[16];		/* My login name (saved) */
X
Xchar	*getpass(),
X	*malloc(),
X	*ttyname(),
X	*getlogin();
X
X#ifdef	SYSV
X#define	index	strchr
X#endif
Xchar	*index();
X
Xint	catchit();		/* Signal catcher */
X
X/*
X *	passwd - change the password for a user.
X *
X *	This program impliments the 'passwd' command.
X */
Xmain(argc, argv)
Xint	argc;
Xchar	*argv[];
X{
X	char	*myname, mysavedname[16];/* My login name */
X	struct passwd *pw;		/* My passwd entry */
X	int	opt;			/* Option processing temp */
X	extern char	*optarg;	/* From getopt() */
X	extern int	optind;		/* From getopt() */
X
X	/*
X	 * Handle the 4.3BSD & SunOS 4.0 command line options.
X	 * Defer everything except password change to other programs.
X	 */
X	while ((opt = getopt(argc, argv, options)) != EOF) {
X		switch (opt) {
X#ifdef	sun
X		/*
X		 * Recognized the SunOS 4.1 switches
X		 * but just say that we don't handle them.
X		 */
X		case 'a':
X			printf("Option \"-a\" not supported.\n");
X			exit(1);
X		case 'l':
X			printf("Option \"-l\" not supported.\n");
X			exit(1);
X		case 'y':
X			printf("Option \"-y\" not supported.\n");
X			exit(1);
X		case 'd':
X			printf("Option \"-d\" not supported.\n");
X			exit(1);
X		case 'e':
X			printf("Option \"-e\" not supported.\n");
X			exit(1);
X		case 'n':
X			printf("Option \"-n\" not supported.\n");
X			exit(1);
X		case 'x':
X			printf("Option \"-x\" not supported.\n");
X			exit(1);
X		case 'F':
X			printf("Option \"-F\" not supported.\n");
X			exit(1);
X#endif
X		case 'f':
X			punt("chfn");
X			break;
X		case 's':
X			punt("chsh");
X			break;
X		case 'P':		/* Data source is a program */
X			if (getuid())
X				quit(0, "Option \"-P\" reserved for super-user.\n");
X			from_prog = 1;
X			break;
X		case 'V':
X			printf("%s; patch level %s\n", version, patchlevel);
X			exit(0);
X		}
X	}
X	bzero(ppbuf, sizeof(ppbuf));
X#ifndef	DEBUG
X	if (geteuid())
X		quit(0,"Permission denied.\n");
X#endif
X	checktty();
X	savetty();
X	myname = getlogin();
X	if (myname == 0 || *myname == '\0') {
X		if ((pw = getpwuid(getuid())) == ((struct passwd *)NULL))
X			quit(1, "Cannot get your login name.\n");
X		strncpy(mysavedname, pw->pw_name, sizeof(mysavedname));
X		myname = mysavedname;
X	}
X	(void) strcpy(mylogin, myname);
X	(void) signal(SIGINT, catchit);
X	(void) signal(SIGQUIT, catchit);
X
X#ifdef	SYSLOG
X	openlog("passwd", LOG_PID | LOG_CONS, LOG_AUTH);
X#endif
X	setcheckpasswd("-c", CONFIG_FILE, 0);
X	pw_initialize();
X
X	if (argv[optind])
X		(void) strcpy(username, argv[optind]);
X	else
X		(void) strcpy(username, mylogin);
X
X	if (strcmp(username, mylogin) == 0 && getuid()) {
X		if (pw_getuserbyname(username, password) == 0)
X			quit(1, "Cannot get your password information.\n");
X		if (password[0])
X			getpassword(password, ppbuf, sizeof(ppbuf));
X	}
X	else {
X		if (pw_getuserbyname(username, password) == 0)
X			quit(0, "No such user %s\n", argv[optind]);
X		if (pw_permission() == 0)
X			quit(0, "Permission denied.\n");
X		printf("Changing password for %s\n", username);
X	}
X	motd(MOTD_FILE, (char *)0);
X
X	for (;;) {
X		char	*px;		/* Temp */
X		int	ntries = 0;	/* Password match counter */
X
X		px = getpass(from_prog ? "" : "New password (? for help): ");
X		if (px == NULL)
X			quit(0, "EOF during new password read.\n");
X		(void) strcpy(pbuf, px);
X		if (pbuf[0] == '?') {
X			motd(HELP_FILE, "Missing help file");
X			continue;
X		}
X		/* Sanity check the new password */
X		if (pw_check(pbuf) == 0)
X			continue;
X
X		/* Get confirmation */
X		px = getpass(from_prog ? "" : "New password (again): ");
X		if (px == NULL)
X			quit(0, "EOF during new password read.\n");
X		(void) strcpy(pbuf2, px);
X		if (strcmp(pbuf, pbuf2)) {
X			if (ntries++ >= retries) 
X				quit(0, "Too many attempts.\n");
X			else
X				printf("They don't match; try again.\n");
X			if (from_prog)
X				quit(0, (char *)0);
X			else
X				continue;
X		}
X		/* Disallow new password == old password */
X		if (pw_compare(password, pbuf)) {
X			printf("New password must be different than old; try again.\n");
X			if (from_prog)
X				quit(0, (char *)0);
X			else
X				continue;
X		}
X		else
X			break;
X	}
X	pw_replace(pbuf, ppbuf);
X#ifdef	SYSLOG
X	syslog(LOG_INFO, "Password changed for %s by %s\n",
X		username, mylogin);
X#endif
X	printf("Password changed for %s\n", username);
X	pw_cleanup(0);
X	exit(0);
X}
X
X
X/*
X *	getpassword -- read password and check against current.
X */
Xgetpassword(pwd_crypt, pwd_plain, pwlen)
Xchar	*pwd_crypt,		/* Present password (encrypted) */
X	*pwd_plain;		/* Present password (plain)  */
Xint	pwlen;			/* Length of present password buffer */
X{
X	int	ntries = 0;	/* Match attempt counter */
X	char	*px;		/* Temp */
X
X	for (;;) {
X		px = getpass(from_prog ? "" : "Current password: ");
X		if (px == 0)
X			quit(0, "EOF during password read.\n");
X		if (*px == '\0')
X			continue;
X		if (!pw_compare(pwd_crypt, px)) {
X			printf("Password incorrect.\n");
X			if (ntries++ == retries)
X				quit(0, "Password not matched.\n");
X		}
X		else
X			break;
X	}
X	if (pwd_plain)
X		(void) strncpy(pwd_plain, px, pwlen);
X}
X
X/* 
X *	randomstring - create a string of random characters
X */
Xrandomstring(buf, len)
Xchar	buf[];		/* String buffer */
Xint	len;		/* Length of buf */
X{
X	register int	i,		/* Temp */
X			n;		/* Temp */
X	time_t		tv;		/* Current time */
X	char		proto[128];	/* Build buffer */
X
X	(void) time (&tv);
X	/*
X	 * Assumes (implicitly) that sizeof(int) == sizeof(long)
X	 */
X	(void) srand ( (tv & 0x38d9fcff) ^ getpid ());
X	for (i = 0; i < sizeof(proto); i++) {	/* fill proto vector */
X		int	c;		/* Temp */
X
X		for (;;) {
X			c = rand () % 0x7f;	/* turn into ASCII */
X			if (isalnum (c))
X				break;
X		}
X		proto[i] = (char )c;
X	}
X	(void) srand(((unsigned )tv & 0x1a90fefc) ^ getpid());
X	for (i = 0; i < len; i++) {
X		n = rand() % sizeof(proto);
X		buf[i] = proto[n];
X	}
X	buf[len] = 0;
X}
X
X/*
X *	quit - print/log error message and exit
X */
Xquit(logit, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
X/*VARARGS2*/
Xint	logit;		/* 0 = don't log, <> 0 = log message */
Xchar	*message;	/* Message */
Xint	*a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9, *a10;	/* Args */
X{
X	if (message) {
X		/*
X		 * If used from program, direct failure messages to stdout,
X		 * else send to stderr.
X		 */
X		fprintf(from_prog ? stdout : stderr,  message,
X			a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
X#ifdef	SYSLOG
X		if (logit)
X			syslog(LOG_ERR,  message,
X				a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
X#endif
X	}
X	pw_cleanup(1);
X	exit(1);
X}
X
X/*
X *	motd - issue 'message of the day'
X */
Xmotd(fn, complaint)
Xchar	*fn,			/* Name of file to present */
X	*complaint;		/* Complaint if missing */
X{
X	char	cmdbuf[BUFSIZ];		/* Buffer to build command in */
X
X	if (access(fn, 0) < 0) {
X		if (complaint)
X			printf("%s\n", complaint);
X		return;
X	}
X	if (isatty(0))
X#ifdef	SYSV
X		(void) sprintf(cmdbuf, "pg -n -s %s", fn);
X#else
X		(void) sprintf(cmdbuf, "more -d %s", fn);
X#endif
X	else
X		(void) sprintf(cmdbuf, "cat %s", fn);
X
X	if (fork() == 0) {
X		(void) setgid(getgid());
X		(void) setuid(getuid());
X		(void) system(cmdbuf);
X		exit(0);
X	}
X	(void) wait((int *)0);		/* "Wrong" for BSD, right for SYS V */
X}
X
X/*
X *	checktty - Attempt to check against being pipe-fed
X */
Xchecktty()
X{
X	char	*stdin_tty,	/* ttyname(0) */
X		*stdout_tty,	/* ttyname(1) */
X		*t;		/* Temp */
X
X	if (!isatty(0)) {
X		if (from_prog == 0)
X			quit(0, "Input not a tty.\n");
X		if (lseek(0, 0L, 1) < 0) {
X			if (errno != ESPIPE)
X				quit(0, "Input not a tty or pipe.\n");
X			else
X				return;
X		}
X	}
X	from_prog = 0;		/* Stdin is a tty - behave normal */
X	stdin_tty = ttyname(0);
X	if (stdin_tty == NULL || *stdin_tty == 0)
X		quit(0, "Cannot get name (stdin).\n");
X	t = malloc(strlen(stdin_tty) + 1);
X	if (t == NULL)
X		quit(1, "Cannot allocate temp memory.");
X	(void) strcpy(t, stdin_tty);
X	stdin_tty = t;
X
X	stdout_tty = ttyname(1);
X	if (stdout_tty == NULL || *stdout_tty == 0)
X		quit(0, "Cannot get name (stdout).\n");
X	if (strcmp(stdin_tty, stdout_tty))
X		quit(0, "Input and output are not the same tty.\n");
X	free(stdin_tty);
X}
X
X/*
X *	catchit - tty interrupt catcher
X */
Xcatchit()
X{
X	fixtty();
X	pw_cleanup(1);
X	quit(0, "\nInterrupted; changes discarded.\n");
X}
X
X
X#if	defined(SYSV)
X#	include <termio.h>		/* Vanilla SV termio */
X	struct termio saved_tty_mode;
X#endif
X
X#if	defined(SUNOS4)
X#	include <sys/termios.h>		/* SUN OS 4.0 termio */
X#define	TCGETA	TCGETS
X#define	TCSETA	TCSETS
X	struct termios saved_tty_mode;
X#endif
X
X#if	!defined(SUNOS4) && !defined(SYSV)
X#	include <sgtty.h>		/* BSD tty */
X	struct sgttyb saved_tty_mode;
X	int	saved_local_flags;
X#endif
Xchar	saves_valid  = 0;		/* Are the saved values valid? */
X
X/*
X *	savetty - save current terminal settings
X */
Xsavetty()
X{
X#if	defined(SYSV) || defined(SUNOS4)
X	(void) ioctl(0, TCGETA, &saved_tty_mode);
X#else
X	(void) ioctl(0, TIOCGETP, &saved_tty_mode);
X	(void) ioctl(0, TIOCLGET, &saved_local_flags);
X#endif
X	saves_valid++;
X}
X
X/*
X *	fixtty - restore saved terminal settings
X */
Xfixtty()
X{
X	if (saves_valid) {
X#if	defined(SYSV) || defined(SUNOS4)
X		(void) ioctl(0, TCSETA, &saved_tty_mode);
X#else
X		(void) ioctl(0, TIOCSETP, &saved_tty_mode);
X		(void) ioctl(0, TIOCLSET, &saved_local_flags);
X#endif
X	}
X}
X
X#ifdef	XGETPASS
X/*
X *	The system getpass() throws away all but the first 8 characters
X *	of a password string.  If this isn't enough for you, use this
X *	routine instead.  This code assumes that stdin is the terminal.
X */
Xchar	*
Xgetpass(prompt)
Xchar	*prompt;
X{
X#if	defined(SYSV)
X	struct termio	saved,		/* Saved tty characteristics */
X			noecho;		/* No-echo tty characteristics */
X	char	*strchr();
X#endif
X#if	defined(SUNOS4)
X	struct termios	saved,		/* Saved tty characteristics */
X			noecho;		/* No-echo tty characteristics */
X#else
X	struct sgttyb	saved,		/* Saved tty characteristics */
X			noecho;		/* No-echo tty characteristics */
X#endif
X	static char	ib[64];		/* Input buffer */
X	char	*rc;			/* Temp */
X
X#if	defined(SYSV) || defined(SUNOS4)
X	(void) ioctl(0, TCGETA, &saved);
X	noecho = saved;
X	noecho.c_lflag &= ~ECHO;
X	(void) ioctl(0, TCSETA, &noecho);
X#else
X	(void) ioctl(0, TIOCGETP, &saved);
X	noecho = saved;
X	noecho.sg_flags &= ~ECHO;
X	(void) ioctl(0, TIOCSETP, &noecho);
X#endif
X	fprintf(stderr, "%s", prompt);
X	fflush(stderr);
X	rc = fgets(ib, sizeof(ib), stdin);
X	putc('\n', stderr);
X	fflush(stderr);
X
X#if	defined(SYSV) || defined(SUNOS4)
X	(void) ioctl(0, TCSETA, &saved);
X#else
X	(void) ioctl(0, TIOCSETP, &saved);
X#endif
X	if (rc == NULL)
X		return(NULL);
X	if (rc = index(ib, '\n'))
X		*rc = 0;
X	return(ib);
X}
X#endif
X
X#ifdef	XPUTPWENT
X/*
X *	putpwent - replacement for the System V routine
X *		This writes the "standard" passwd file format.
X */
Xputpwent(p, f)
Xstruct passwd	*p;	/* Passwd entry to put */
XFILE	*f;		/* File pointer */
X{
X#ifdef	UNSIGNED_UID
X	fprintf(f, "%s:%s:%u:%u:%s:%s:%s\n",
X#else
X	fprintf(f, "%s:%s:%d:%d:%s:%s:%s\n",
X#endif
X		p->pw_name, p->pw_passwd, p->pw_uid, p->pw_gid,
X		p->pw_gecos, p->pw_dir, p->pw_shell);
X}
X#endif
X
X#ifdef	XFGETPWENT
X/*
X *	fgetpwent() - read passwd(5) entry from a file
X *		This reads the "standard" passwd file format.
X */
Xstruct passwd *
Xfgetpwent(f)
XFILE	*f;			/* Pointer to open passwd format file */
X{
X	static struct passwd	pwdata;	/* Return data */
X	static char	ibuf[BUFSIZ];	/* Input and return data buffer */
X	char		*p;		/* ACME Pointer Works, Inc */
X
X	bzero((char *)&pwdata, sizeof(pwdata));
X	pwdata.pw_name = pwdata.pw_passwd = pwdata.pw_comment =
X	pwdata.pw_gecos = pwdata.pw_dir = pwdata.pw_shell = "";
X
X	if (fgets(ibuf, sizeof(ibuf), f) == NULL)
X		return(0);
X	if ((p = index(ibuf, '\n')) == 0)		/* Zap newline */
X		quit(1, "Ill-formed passwd entry \"%s\".\n", ibuf);
X	else
X		*p = 0;
X#define	skipc while (*p && *p != ':' && *p != '\n') ++p; if (*p) *p++ = 0
X	p = ibuf;
X	pwdata.pw_name = p;	skipc;
X	pwdata.pw_passwd = p;	skipc;
X	pwdata.pw_uid = atoi(p); skipc;
X	pwdata.pw_gid = atoi(p); skipc;
X	pwdata.pw_gecos = p;	skipc; 
X	pwdata.pw_dir = p;	skipc;
X	pwdata.pw_shell = p;
X	return(&pwdata);
X#undef	skipc
X}
X#endif
X
X#ifdef	SYSV
X/*
X *	rename - replacement for the 4.2/4.3 BSD rename system call
X */
Xrename(src, dst)
Xchar	*src,		/* Source path */
X	*dst;		/* Destination path */
X{
X	if (unlink(dst) < 0) {
X		if (errno != ENOENT)
X			return(-1);
X	}
X	if (link(src, dst) < 0)
X		return(-1);
X	return(unlink(src));
X}
X#endif
X
X/*
X *	punt() - run another program to do what we don't do
X */
Xpunt(prog)
Xchar	*prog;		/* Program to run */
X{
X	(void) setgid(getgid());
X	(void) setuid(getuid());
X	(void) execlp(prog, prog, 0);
X	perror(prog);
X	exit(1);
X}
X/*		End npasswd.c		*/
END_OF_FILE
if test 15199 -ne `wc -c <'npasswd.c'`; then
    echo shar: \"'npasswd.c'\" unpacked with wrong size!
fi
# end of 'npasswd.c'
fi
if test -f 'pw_yp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'pw_yp.c'\"
else
echo shar: Extracting \"'pw_yp.c'\" \(16110 characters\)
sed "s/^X//" >'pw_yp.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_yp - Routines for dealing with SUN Yellow Pages password files
X *
X *	This code can update local password file, can cause rebuilding of
X *	local YP maps and can use yppasswdd(8) to change YP passwords.
X *
X *	Must be linked with -lrpcsvc
X */
X#include <stdio.h>
X#include <sys/param.h>
X#include <sys/stat.h>
X#include <signal.h>
X#include <errno.h>
X#include <pwd.h>
X#include <fcntl.h>
X#include <rpc/rpc.h>
X#include <rpcsvc/ypclnt.h>
X#include <rpcsvc/yppasswd.h>
X#include <sys/socket.h>
X
X#ifdef	SECURE_RPC
X#include <rpc/key_prot.h>
X#endif
X
X#ifdef	SYSV
X#define	index	strchr
X#endif
X
X#ifdef	SYSLOG
X#include <syslog.h>
X#endif
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)pw_yp.c	1.15 1/25/91 (cc.utexas.edu)";
X#endif
X
X#define	NONE	-1	/* YP not active */
X#define	NOT	0	/* YP active - we are not master */
X#define	IS	1	/* YP active - we have the password file */
X
X#define	SLOP	128	/* Size difference tolerated old <> new passwd file */
X
Xtypedef struct passwd	passwd;
Xtypedef	struct passwd	*passwdp;
X
Xstatic passwd	theUser,	/* User being changed */
X		Me;		/* User invoking passwd */
Xstatic int	myuid,		/* Uid of invoker */
X		mytempfile = 0;	/* Does PASSWD_TEMP belong to me? */
Xstatic char	*ypmaster,	/* Name of the YP master */
X		*ypdomain;	/* YP domain name */
X
X#define	PASSWD_MAP	"passwd.byname"		/* Name of YP passwd map */
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",		/* Temp file */
X		*passwdfile = "./etc_passwd",		/* Password file */
X		*savefile = "./etc_opasswd";		/* Save file */
X#else
Xstatic char	*passwdtemp = PASSWD_TEMP,
X		*passwdfile = PASSWD_FILE,
X		*savefile = PASSWD_SAVE;
X#endif
Xstatic char	auxlockfile[MAXPATHLEN];		/* Aux lock file */
X
Xextern int	errno;
X
Xchar	*getlogin(),
X	*crypt();
X
X/*
X *	pw_initialize - set up
X */
Xpw_initialize()
X{
X	passwdp	me;			/* Passwd for invoker */
X	char	*myname = getlogin();	/* Invoker login name */
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 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	/*
X	 * Is this my password or someone elses?
X	 */
X	if (strcmp(Me.pw_name, theUser.pw_name) && myuid)
X		return(0);
X
X	/*
X	 * If on a YP client, root cannot change another
X	 * users' password via yppasswd(), since we can't
X	 * get a plain text password to pass to yppasswdd().
X	 */
X	if (myuid == 0 && is_yp_master() == NOT) {
X		FILE	*pf;
X		passwdp px,		/* Password file traversal */
X			fgetpwent();
X		int	rc = 0;
X
X		/* What if passwdfile != /etc/passwd?  */
X		if ((pf = fopen(passwdfile, "r")) == NULL)
X			quit(1, "Cannot open password file \"%s\".\n", passwdfile);
X		/*
X		 * Scan local password file, looking for user
X		 * Cannot use getpwnam() because it will use YP - I want to know
X		 * if the user's password file entry is >>local<<
X		 */
X		while ((px = fgetpwent(pf)) != NULL) {
X			if (strcmp(px->pw_name, theUser.pw_name) == 0) {
X				rc = 1;
X				break;
X			}
X		}
X		fclose(pf);
X		if (rc) {
X			if (strncmp(px->pw_passwd, "##", 2) == 0)
X			    quit(0,
X				"Changing of adjunct passwords not supported.\n");
X			return(1);
X		}
X		else
X			quit(0, "Password for %s can only be changed on YP server %s.\n",
X				theUser.pw_name, ypmaster);
X	}
X	/* Check if passwd is '##username' - can't do that yet */
X
X	/*
X	 * Other checks can be put here to determine if the invoker should
X	 * be allowed to change this 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, new)
Xchar	*current,		/* Current pw (encrypted) */
X	*new;			/* check pw (plain) */
X{
X	if (!*current)		/* Is current password null? */
X		return(0);
X	/* Put other administrative checks here */
X	return(!strcmp(current, crypt(new, current)));
X}
X
X/*
X *      pw_check - sanity check password.  Right now just calls
X *              the password check code
X *
X *      Returns 1 if password is ok to use, 0 otherwise
X */
Xpw_check(pwd)
Xchar	*pwd;
X{
X	int	rc = checkpasswd(theUser.pw_uid, pwd);
X
X#ifdef	PASSWORD_HISTORY
X	if (rc)
X		return(rc);
X	/* Call password history checker to prevent password reuse */
X	rc = passwd_history(theUser.pw_uid, pwd);
X#endif
X	return(rc);
X}
X
X/*	Error message for when yppasswdd fails with error code 1.  */
Xstatic char *yperrmsg =
X"Password change failed: Problem with yppasswdd.\n\n\
XThis is probably because the YP maps are out of sync\n\
Xwith the YP passwd file for %s on %s.\n\n\
XPlease try again later.\n";
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	passwdp px,		/* Password file traversal */
X		fgetpwent();
X	long    oldsigs,	/* Old signal mask */
X		blocksigs = sigmask(SIGINT) |	/* Sigs to block */
X			    sigmask(SIGQUIT) |
X			    sigmask(SIGTSTP);
X	FILE	*tf,		/* New password file output */
X		*pf;		/* Current password file input */
X	int	fd,		/* Temp file create fd */
X		islocal = 0;	/* Is user in local password file */
X	struct stat	oldstat,	/* Old password file stat */
X			newstat;	/* New password file stat */
X
X	if ((pf = fopen(passwdfile, "r")) == NULL)
X		quit(1, "Cannot open password file \"%s\".\n", passwdfile);
X	/*
X	 * Scan local password file, looking for user
X	 * Cannot use getpwnam() because it will use YP - I want to know
X	 * if I have to change a >>local<< password file.
X	 */
X	while ((px = fgetpwent(pf)) != NULL) {
X		if (*px->pw_name == '+' || *px->pw_name == '-')
X			continue;
X		if (strcmp(px->pw_name, theUser.pw_name) == 0) {
X			if (strncmp(px->pw_passwd, "##", 2) == 0)
X			    quit(0,
X				"Changing of adjunct passwords not supported.\n");
X			islocal++;
X			break;
X		}
X	}
X	rewind(pf);
X
X	/*
X	 * If the user was not in the local password file, use RPC
X	 * to update the Yellow Pages (NIS) password file.
X	 */
X	if (islocal == 0) {
X		if (is_yp_master() == NOT) {
X			int	rc;		/* Return code from ypasswdd */
X			int	why;		/* RPC call return code */
X			int	ypport;		/* Port for RPC call */
X			struct yppasswd yppasswd; /* YP passwd change block */
X			char	salt[4];	/* Password encryption salt */
X
X			if (curpwd[0] == 0)
X				quit(0, "Cannot change YP password without old password.\n");
X			randomstring(salt, sizeof(salt));
X			theUser.pw_passwd = crypt(newpwd, salt);
X			yppasswd.oldpass = curpwd;
X			_cppasswd(&theUser, &yppasswd.newpw);
X#ifdef	DEBUG
X			printf("yppasswd(%s, %s)\n", curpwd, theUser.pw_passwd);
X#else
X			if ((ypport = getrpcport(ypmaster, YPPASSWDPROG,
X			     YPPASSWDPROC_UPDATE, IPPROTO_UDP)) == 0)
X				quit(1, "%s is not running ypassswdd.\n",
X				     ypmaster);
X
X			if (ypport >= IPPORT_RESERVED)
X				quit(1, "yppasswdd on %s not privleged.\n",
X					ypmaster);
X			rc = callrpc(ypmaster, YPPASSWDPROG, YPPASSWDVERS,
X				YPPASSWDPROC_UPDATE, xdr_yppasswd, &yppasswd,
X				xdr_int, &why);
X
X			/* RPC call error */
X			if (rc)
X#if	NO_CLNT_SPERRNO
X				clnt_perrno(rc);
X				quit(1, "Password change failed (%s)\n",
X					ypmaster);
X#else
X				quit(1, "Password change failed (%s): %s\n",
X					ypmaster, clnt_sperrno(rc));
X#endif
X
X			/* Error returned from yppasswdd */
X			if (why) {
X#ifdef	SYSLOG
X				syslog(LOG_ERR,
X					"yppasswdd error %d on %s for %s",
X					why, ypmaster, theUser.pw_name);
X#endif
X				if (why == 1)
X				   quit(0, yperrmsg,  ypdomain, ypmaster);
X				else
X				   quit(1, "Password change failed.\n");
X			}
X# ifdef	SECURE_RPC
X			reset_secret_key(curpwd);
X# endif /* SECURE_RPC */
X#endif	/* DEBUG */
X			return;
X		}
X		else	/* User not in local passwd, and not in YP passwd */
X			quit(1, "User %s missing from password file.\n",
X				theUser.pw_name);
X	}
X
X	/*
X	 * There is a local password file to change
X	 */
X	(void) umask(0);
X	(void) fstat(fileno(pf), &oldstat);
X	/*
X	 * Use different temp file if on YP master.
X	 * This deals with the SunOS 4.0.3 yppasswdd which creates temp files
X	 * named "passwd-file.ptmp", rather than the traditional "/etc/ptmp".
X	 * But there are still a lot of applications which use /etc/ptmp,
X	 * so is it used as the passwd temp file and the 'auxlockfile' is
X	 * also made --- >>>GROAN<<<.
X	 */
X	auxlockfile[0] = 0;
X	if (is_yp_master() == IS) {
X		(void) sprintf(auxlockfile, "%s.ptmp", passwdfile);
X		close(mklocktemp(auxlockfile));
X	}
X	mytempfile = 1;
X	fd = mklocktemp(passwdtemp);
X	if ((tf = fdopen(fd, "w")) == NULL)
X		quit(1, "Cannot fdopen temp file.\n");
X
X	oldsigs = sigblock(blocksigs);
X	while ((px = fgetpwent(pf)) != 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			char	salt[4];	/* Password encryption salt */
X
X			randomstring(salt, sizeof(salt));
X			theUser.pw_passwd = crypt(newpwd, salt);
X			px = &theUser;
X		}
X		(void) putpwent(px, tf);
X	}
X	(void) fflush(tf);			/* Force buffers empty */
X	(void) fstat(fileno(tf), &newstat);	/* Get size */
X	(void) fclose(tf);
X	(void) fclose(pf);
X
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	 * This assumption will FAIL when this program does chfn and chsh!!! -
X	 * use line counts.
X	 */
X	if (newstat.st_size < (oldstat.st_size - SLOP))
X		quit(1,
X		"New password file appears to be incomplete - aborting.\n");
X
X	if (rename(passwdfile, savefile) < 0) {
X		perror("Password file save");
X		(void) unlink(passwdtemp);
X		quit(1, "Can't save password file.\n");
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.\n");
X	}
X	if (is_yp_master() == IS)
X		updateyp();
X	(void) sigsetmask(oldsigs);
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		if (auxlockfile[0])
X			(void) unlink(auxlockfile);
X	}
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 *	mklocktemp - Make temp file with exclusive use checking
X *
X *	Returns file descriptor of created file, else exits with error
X */
Xstatic int
Xmklocktemp(name)
Xchar	*name;
X{
X	int	fd;
X
X	fd = open(name, 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	return(fd);
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	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/*
X *	is_yp_master - Figure out whether we are running on the Yellow Pages
X *		master for the password file maps.
X *
X *	Returns:
X *		IS if we are the master
X *		NOT if we are not the master
X *		NONE if there is no master
X */
X#include <netdb.h>
X#ifndef	MAXHOSTNAMLEN
X#define	MAXHOSTNAMLEN 32
X#endif
X
Xis_yp_master()
X{
X	static char	known = 0,	/* We've been here */
X			answer = NOT;	/* ...and this is the answer */
X	char	hostname[MAXHOSTNAMLEN];	/* Our host name */
X	char	*index();
X	struct hostent	*hinfo;
X
X	if (known)
X		return(answer);
X	(void) gethostname(hostname, sizeof(hostname));
X	if (yp_get_default_domain(&ypdomain)) {
X/* 		quit(1, "Cannot get YP domain.\n"); */
X		known++;
X		return(answer = NONE);		/* Assume no YP running */
X	}
X
X	if (yp_master(ypdomain, PASSWD_MAP, &ypmaster)) {
X		known++;
X		return(answer = NONE);		/* Assume no YP running */
X	}
X
X	known++;
X#ifdef	FASTCHECK
X	/*
X	 * Stupid (but fast) hostname check (first component only)
X	 */
X	{
X		char	*p;			/* Scratch */
X
X		if (p = index(ypmaster, '.')) *p = 0;
X		if (p = index(hostname, '.')) *p = 0;
X	}
X#else
X	/*
X	 * Compare my host name and the YP master's host name.
X	 * Use gethostbyname() to return the fully qualified form so that
X	 * a string compare can be done.
X	 */
X	if (index(hostname, '.') == 0) {
X		if ((hinfo = gethostbyname(hostname)) == 0)
X			quit(1, "Cannot get hostinfo for self.\n");
X		(void) strcpy(hostname, hinfo->h_name);
X	}
X	if (index(ypmaster, '.') == 0) {
X		static char	ypmaster_f[MAXHOSTNAMLEN];
X
X		if ((hinfo = gethostbyname(ypmaster)) == 0)
X			quit(1, "Cannot get hostinfo for ypmaster.\n");
X		(void) strcpy(ypmaster_f, hinfo->h_name);
X		ypmaster = ypmaster_f;
X	}
X#endif
X	if (strcmp(ypmaster, hostname) == 0)
X		return(answer = IS);
X	return(answer);
X}
X
X/*
X *	An example sh(1) script to update YP password map
X */
Xchar	*ypcmd =
X	"(PATH=/bin:/usr/bin; export PATH; ypdirs='/var/yp /etc/yp'\n\
X	for d in $ypdirs; do\n\
X		if [ -d $d ]; then\n\
X			cd $d; exec make passwd\n\
X		fi\n\
X	done\n\
X	echo 'passwd: Cannot rebuild YP maps!' 1>&2\n\
X	false) >/dev/null &\n";
X
X/*
X *	updateyp - update local YP maps
X */
Xupdateyp()
X{
X	(void) setuid(geteuid()); 	/* Get all privs */
X			/* (This assumes that we are setuid root) */
X	/*
X	 * This machine is the YP master for passwd - invoke something
X	 * to update the YP maps.
X	 * Super-user can override the default YP updater by setting
X	 * env "YP_UPDATE_PROC" to a command to be run instead.
X	 * The name of the user being changed is piped to stdin of the command.
X	 */
X	if (myuid == 0) {
X		char	*getenv();
X		char	*proc = getenv("YP_UPDATE_PROC");
X
X		if (proc && *proc) {
X			char	cmdbuf[BUFSIZ];
X
X			(void) sprintf(cmdbuf, "/bin/echo '%s' | ( %s )",
X				theUser.pw_name, proc);
X#ifdef	DEBUG
X			printf("updateyp (proc): %s", cmdbuf);
X#endif
X			(void) system(cmdbuf);
X			return;
X		}
X	}
X#ifdef	DEBUG
X	printf("updateyp: %s", ypcmd);
X#endif
X	(void) system(ypcmd);
X}
X
X#ifdef	SECURE_RPC
X/*
X *	reset_secret_key - Reset secret key for secure RPC
X */
Xreset_secret_key(curpwd)
Xchar	*curpwd;
X{
X	char	mynet[MAXNETNAMELEN+1],
X		key[HEXKEYBYTES+1];
X
X	getnetname(mynet);
X	if (!getsecretkey(mynet, key, curpwd))
X		return;		/* Secure RPC not running */
X	if (key[0] = 0)
X		return;		/* No secret key */
X	fprintf(stderr, "Cannot change secure RPC key\n");
X	/*
X	 * Actually I could, but I'd have to steal from Sun source
X	 * code to do it.  Sun wouldn't like that very much, so I won't.
X	 */
X}
X#endif
X/*		End pw_yp.c 		*/
END_OF_FILE
if test 16110 -ne `wc -c <'pw_yp.c'`; then
    echo shar: \"'pw_yp.c'\" unpacked with wrong size!
fi
# end of 'pw_yp.c'
fi
echo shar: End of archive 3 \(of 3\).
cp /dev/null ark3isdone
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			|

