[alt.sources] Shadow Login Suite, version 3

jfh@rpp386.cactus.org (John F Haugh II) (05/16/91)

The next 8 postings are the shadow login suite that I first posted to
the net about 3 years ago, only much bigger and with lots of really
useless features that you'll probably want to use anyway ;-)

What you get is -

	login, passwd, su, newgrp, chsh, chfn, chage, gpasswd, etc

Too many programs to name.  You also get a library that knows about
password files and how to dork around with them in just about every
conceivable manner.

This is the initial beta version of the code.  Please don't bother
to archive it if you aren't interested in working on it - I hope to
get plenty of bug reports and fix plenty of bugs, and I'd hate for
you to have a bad version spinning on your disk.

I'll post some patches as bugs get reported.  Then, some time after
the reports die down, I'll repost the entire thing with to here then
comp.sources.misc.
-- 
John F. Haugh II        | Distribution to  | UUCP: ...!cs.utexas.edu!rpp386!jfh
Ma Bell: (512) 255-8251 | GEnie PROHIBITED :-) |  Domain: jfh@rpp386.cactus.org
"If liberals interpreted the 2nd Amendment the same way they interpret the
 rest of the Constitution, gun ownership would be mandatory."

jfh@rpp386.cactus.org (John F Haugh II) (05/16/91)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	README
#	newgrp.c
#	Makefile
#	config.h
#	pwunconv.c
#	obscure.c
#	age.c
# This archive created: Sun Mar  3 13:27:13 1991
# By:	John F Haugh II (River Parishes Programming, Austin TX)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'README'" '(12725 characters)'
if test -f 'README'
then
	echo shar: "will not over-write existing file 'README'"
else
sed 's/^X//' << \SHAR_EOF > 'README'
XThis is the explanatory document for John F. Haugh II's login replacement,
Xrelease 3.  This document was last updated 11/21/90.
X
XThis software is copyright 1988, 1989, 1990, John F. Haugh II.  All rights
Xreserved.  Use, duplication and disclosure is permitted according to the
Xguidelines listed below.
X
XThis software is being provided as a freely redistributable login clone.
XYou may distribute this software provided you do not charge for other than
Xtransmission costs.  You are free to copy this software provided you
Xdo not restrict the rights of the recipients to further copy this software.
X
XTHIS SOFTWARE IS BEING DISTRIBUTED AS-IS.  THE AUTHORS DISCLAIM ALL
XLIABILITY FOR ANY CONSEQUENCES OF USE.  THE USER IS SOLELY RESPONSIBLE
XFOR THE MAINTENANCE OF THIS SOFTWARE PACKAGE.  THE AUTHORS ARE UNDER NO
XOBLIGATION TO PROVIDE MODIFICATIONS OR IMPROVEMENTS.  THE USER IS
XENCOURAGE TO TAKE ANY AND ALL STEPS NEEDED TO PROTECT AGAINST ACCIDENTAL
XLOSS OF INFORMATION OR MACHINE RESOURCES.
X
XSpecial thanks are due to Chip Rosenthal for his fine testing efforts;
Xto Steve Simmons for his work in porting this code to BSD; and to Bill
XKennedy for his contributions of LaserJet printer time and energies.
XAlso, thanks for Dennis L. Mumaugh for the initial shadow password
Xinformation and to Tony Walton (olapw@olgb1.oliv.co.uk) for the System
XV Release 4 changes.
X
XNew for Release 3:
X	The objects are being combined into libraries to make maintenance
X	easier and to encourage developers to use the modules as the
X	basis for new tools.
X
X	New lint rules have been added to make the code easier to lint.
X
XBegin by reading and editing the config.h file.  All options are selected
Xby using #define's.  A brief description for each available option appears
Xbelow.  You may want to print this file out as it is LONG and you will
Xneed to refer to it while editting config.h.  You will also have to edit
Xthe Makefile.  The possible differences are documented there.  Pay close
Xattention to the install: rule.  DO NOT MAIL ME DIFFERENCES FOR VARIOUS
XINSTALLATION PROBLEMS.  If you must share your experiences, do so on the
Xnet.  Login now runs on about 30 different varieties of UNIX that I have
Xbeen made aware of.
X
XNote that there are MANY options.  As distributed most options are turned
Xon, which produces a really nice package.  This is the system as used on
Xsome of the authors' machines.
X
XDialup Password Files -
X	This option permits individual ports to have an additional
X	password prompted for on a by-shell basis.  /etc/dialups
X	contains a list of dialup ports, d_passwd contains the
X	password for each shell requiring a dialup password.
X
X	Select this option by defining the DIALUP macro.
X
XPort Access Times File -
X	This option permits individual ports to have restrictions
X	based on user name and time of day and week.
X
X	Select this option by defining the PORTTIME macro.
X
XShadow [ unreadable ] Password Files -
X	This option utilizes an alternate, non-readable file to
X	contain the actual encrypted passwords.  This is presumed
X	to increase system security by increasing the difficulty
X	with which system crackers obtain encrypted passwords.
X
X	Select this option by defining the SHADOWPWD macro.
X
XDBM Password Files -
X	This option utilizes the DBM database access routines to
X	increase the performance of user name and ID lookups in the
X	password file.
X
X	Select this option by defining both the DBM and GETPWENT
X	macros.  The FGETPWENT macro must also be defined or the
X	fgetpwent() library routine must be present.
X
XDouble Length Passwords -
X	This option extends the maximum length of a user password
X	to 16 characters from eight.
X
X	Select this option by defining the DOUBLESIZE macro.
X	Credit for this option is due Jonathan Bayer.
X
XObscure Password Testing -
X	This option includes code to test user passwords for
X	complexity.  The programmer is encouraged to edit the
X	file obscure.c to add additional methods for detecting
X	simplistic passwords.
X
X	Select this option by defining the OBSCURE macro.
X
X	Additionally, the PASSLENGTH macro must be defined to
X	control the minimum length for a legal password.
X
XMandatory Password Prompting -
X	This option requires all passwords, including null ones,
X	to be prompted for.  Traditionally an account with a
X	password field of '::' does not require prompting for.
X	This option modifies this behavior to require even
X	null passwords be prompted for.
X
X	Select this option by defining the NOBLANK macro.
X
XPassword Aging Defaults -
X	You may select the default number of days during which a
X	password is valid.  The pwconv command adds aging
X	information to accounts which do not include it already.
X
X	The MINDAYS macro must be defined to be the minimum
X	number of days which must pass before a password may be
X	changed.  The MAXDAYS macro must be defined to be the
X	maximum number of days which a password will remain
X	valid during.
X
X	The WARNDAYS macro controls how many days warning a user
X	is given that their password is about to expire.  The
X	default is 10.
X
XHZ Environmental Variable -
X	This option pre-defines the HZ environmental variable.
X	Certain systems require this variable be defined for
X	system time reporting functions to work properly.
X
X	Select this option by defining the HZ macro to have
X	the desired environmental variable value.
X
XTZ Environmental Variable -
X	This option pre-defines the TZ environmental variable.
X	This provides a default timezone variable for use by
X	various utilities.
X
X	Select this option by defining the TZ macro to have
X	the desired environmental variable value, or the name
X	of the file containing the desired value.
X
XPassword Aging -
X	This option includes code to perform password aging.
X	Password aging is presumed to increase system security
X	by forcing users to change passwords on a regular
X	basis.  The resolution on password age is in weeks for
X	non-shadow password systems and in days otherwise.
X
X	Select this option by defining the AGING macro.
X
XMailbox Checking -
X	This option includes code to check the status of the
X	user's mailbox.  One of three messages are produced
X	depending on the status of the user's mailbox.
X
X	Select this option by defining the MAILCHECK macro.
X
XConsole Restricted Root Logins -
X	This option restricts the port which root may legally
X	login on.  This option presumably increases system
X	security by preventing outside attacks against the root
X	account.
X
X	Select this option by defining the CONSOLE macro to
X	have the desired port name.  If this file is a regular
X	file, it is considered to contain a list of legal port
X	names, one per line.  Note that the port names DO NOT
X	begin with "/dev/" and that a file name would have to
X	be fully qualified.  See config.h for a pair of
X	examples.
X
XRestricted User Logins -
X	This option permits you to specify a file which disables
X	user logins.  This options permits you to keep normal
X	users off of the system while performing maintenance
X	functions.
X
X	Select this option by defining NOLOGINS to be the name
X	of the file to use.
X
XRestricted Use Accounts -
X	This option permits certain accounts to be used for
X	identification purposes only.  This options associates
X	login ID's with UID's, such as for disk space accounting
X	or anonymous FTP accounts.  Passwords for these accounts
X	may only be changed by root.
X
X	Select this option by defining NOUSE to be the string
X	to include in the password file in place of the user's
X	shell.
X
XMessage of the Day Printing -
X	This option causes the message of the day to be
X	printed at login time.
X
X	Select this option by defining the MOTD macro.
X
X	If you wish this feature to be overriden on a per-user
X	basis, define the macro HUSHLOGIN and users may then
X	turn off the /etc/motd message by creating a file
X	'.hushlogin' in their home directories.
X
XLast Login Time Logging -
X	This option causes a record to be made of successful
X	logins in /usr/adm/lastlog.  The format of the
X	structure is defined in lastlog.h.
X
X	Select this option by defining the LASTLOG macro.
X
X	You will need to determine if you system already has
X	a lastlog.h file and use that file if present.
X
XFailed Login Logging -
X	This option causes a record to be kept of the most
X	recent login failure by date and port.  A cummulative
X	count of failures is maintained and compared against
X	an allowable limit.
X
X	Select this option by defining the FAILLOG macro.
X
X	An additional option is provided which will create
X	utmp-like entries for each failed login.  Because of
X	security concerns, only valid account names will be
X	logged.
X
X	Select this option by defining the FTMP macro to be
X	the name of a utmp-like file.  You may control the
X	recording of unknown login names by defining the
X	UNKNOWNS macro.  This prevents possible passwords from
X	being entered into the FTMP file.
X
X	See the file faillog.h and config.h for more details.
X
XTerminal Permissions - 
X	This option allows the terminal modes to be set at
X	login time.  This is particularly useful to disable
X	messages on user's terminals.
X
X	Select this option by defining the TTYPERM macro as
X	having the desired mode.
X
XTerminal Type Setup -
X	This option allows the terminal type to be set at
X	login time.  The environmental variable TERM will be
X	set from the specified terminal to port mapping
X	file.
X
X	Select this option by defining the TTYTYPE macro as
X	having the value of the name of the type to port
X	mapping file.  Credit for this option is due Chip
X	Rosenthal.
X
XFile Size Setting -
X	This option includes code to set the user's ulimit
X	at login time.  Additional code to set the umask and
X	nice value is also included.
X
X	Select this option by defining the QUOTAS macro.
X
XSwitch-User Logging -
X	This option causes su(1) to log attempts to switch
X	users.  Su(1) will log all attempt, giving the old
X	and new user ID's, tty port, and time.  It also
X	indicates if the attempt was successful.
X
X	Select this option by defining the SULOG macro to
X	have the value of the name of the file you want
X	attempts logged to.
X
XConfigurable Editing Keys -
X	This options allows the erase and kill characters to
X	be selected.  A default value is provided.  By default
X	ERASE will be ^H and KILL will be ^U.
X
X	Select this option by defining the ERASECHAR macro
X	to be the desired erase character and the KILLCHAR 
X	macro to be the desired KILL character.
X
XDefault ulimit and umask Values -
X	This option allows you to select the default values
X	for ulimit and umask, allowing you to avoid
X	regenerating your system kernel.  These values may be
X	overriden with appropriate entries in the GECOS field.
X
X	Select the default ulimit by defining the ULIMIT
X	macro, and the default umask by defining the UMASK
X	macro.
X	
X	Warning: These values will not apply to processes
X	executed by /etc/cron or any of their children.
X
XBSD Notes:	Steve Simmons	scs@iti.org
X
XThe full port of the shadow package to BSD is not complete; but some
Xof the issues have been worked out.  These notes describe the current
Xstate of things:
X
XIn order to make use of password aging under BSD, minor changes to
X/usr/include/pwd.h and getpwent() are needed.  These changes are to
Xkeep the password age from messing up the encrypted password when not
Xusing shadow passwords, and involve placing a new field in the password
Xdata structure.  To use this, you should apply the following two patches:
X	pwd.h.patch
X	getpwent.c.patch
Xto the BSD /usr/include/pwd.h and /usr/src/lib/libc/gen/getpwent.c,
Xrespectively.  After applying the patches, rebuild your standard C
Xlibrary with the new getpwent.  Programs which use the old getpwent
Xwill fail on password checking if they do a strcmp rather than a strncmp.
X[ I do not seem to have these two patches.  I have provided an entire
Xgetpwent collection of code which may be useful instead.  This code
Xdoes not support Sun Yellow Pages(tm?), which is a shame. -jfh ]
X
XThese changes are based on BSD4.3, not Tahoe
X
XToDo BSD:
X
XI'm working on this in my copious spare time (hah!); any help would
Xbe appreciated.  If you decide to help, do these independantly rather
Xthan rework BSD code!  Keep it redistributable!
X
XNo dbm functions have been put in place.  Dbm functionality is needed
Xfor both /etc/password and /etc/shadow management.  [ It is now possible
Xto create /etc/passwd.dir and /etc/passwd.pag using the new mkpasswd
Xcommand.  getpwuid and getpwnam both use these files.   Also, the 
Xcommands chfn, chsh, and chage all update the DBM files. -jfh ]
X
XThe BSD GECOS field gets used for lots more stuff than the USG.  At a
Xminimum this functionality should be duplicated under BSD; better is to put
Xit into USG as well; still better would be to make the chfn command for
Xboth systems; best would be site-configurable data to be put into GECOS/chfn.
X[ this is now possible using chfn and the -o option. - jfh ]
SHAR_EOF
if test 12725 -ne "`wc -c < 'README'`"
then
	echo shar: "error transmitting 'README'" '(should have been 12725 characters)'
fi
fi
echo shar: "extracting 'newgrp.c'" '(8576 characters)'
if test -f 'newgrp.c'
then
	echo shar: "will not over-write existing file 'newgrp.c'"
else
sed 's/^X//' << \SHAR_EOF > 'newgrp.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <sys/types.h>
X#include <string.h>
X#include <stdio.h>
X#include <grp.h>
X#include "pwd.h"
X#include <termio.h>
X#ifdef SYS3
X#include <sys/ioctl.h>
X#endif
X#include "config.h"
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)newgrp.c	3.3	12:30:58	12/12/90";
X#endif
X
X#ifdef	NGROUPS
Xint	ngroups;
Xgid_t	groups[NGROUPS];
X#endif
X
Xstruct	passwd	*pwd;
Xstruct	passwd	*getpwuid();
Xstruct	passwd	*getpwnam();
X
X#ifdef	SHADOWPWD
X#include "shadow.h"
Xstruct	spwd	*spwd;
Xstruct	spwd	*getspnam();
X#endif
Xstruct	group	*grp;
Xstruct	group	*getgrgid();
Xstruct	group	*getgrnam();
X
Xchar	*getlogin();
Xchar	*crypt();
Xvoid	shell();
X
Xchar	*name;
Xchar	*group;
Xint	gid;
X
Xchar	*Prog;
Xchar	prog[BUFSIZ];
Xchar	base[BUFSIZ];
Xchar	passwd[BUFSIZ];
Xchar	*cpasswd;
Xchar	*salt;
X
X#ifndef	MAXENV
X#define	MAXENV	64
X#endif
X
Xchar	*newenvp[MAXENV];
Xint	newenvc = 0;
Xint	maxenv = MAXENV;
X
X/*
X * usage - print command usage message
X */
X
Xusage ()
X{
X	fprintf (stderr, "usage: newgrp [ - ] [ group ]\n");
X	exit (1);
X}
X
X/*
X * newgrp - change the invokers current real and effective group id
X */
X
Xmain (argc, argv, envp)
Xint	argc;
Xchar	**argv;
Xchar	**envp;
X{
X	int	initflag = 0;
X	int	needspasswd = 0;
X	int	i;
X	char	*cp;
X#ifdef	DOUBLESIZE
X	int	longpass;
X#endif
X
X	/*
X	 * let me parse the command line first.  the only legal flag
X	 * is a "-", which indicates the shell is to perform the same
X	 * initialization it does at login time.  the remaining
X	 * optional argument is the name of a new group.  if it isn't
X	 * present i just use the login group id of this user.
X	 */
X
X	if (argc > 1 && strcmp (argv[1], "-") == 0) {
X		initflag = 1;
X		argc--; argv++;
X	}
X	if (argc > 2)
X		usage ();
X
X#ifdef	NGROUPS
X
X	/*
X	 * get the current users groupset.  the new group will be
X	 * added to the concurrent groupset if there is room, otherwise
X	 * you get a nasty message but at least your real and effective
X	 * group id's are set.
X	 */
X
X	ngroups = getgroups (groups);
X#endif
X
X	/*
X	 * save my name for error messages and save my real gid incase
X	 * of errors.  if there is an error i have to exec a new login
X	 * shell for the user since her old shell won't have fork'd to
X	 * create the process.
X	 */
X
X	Prog = argv[0];
X	gid = getgid ();
X
X	/*
X	 * now i get to determine my current name.  i do this to validate
X	 * my access to the requested group.  the validation works like
X	 * this -
X	 *	1) get the name associated with my current user id
X	 *	2) get my login name, as told by getlogin().
X	 *	3) if they match, my name is the login name
X	 *	4) if they don't match, my name is the name in the
X	 *	   password file.
X	 *
X	 * this isn't perfect, but it works more often then not.
X	 */
X
X	pwd = getpwuid (getuid ());
X
X	if (! (name = getlogin ()) || strcmp (name, pwd->pw_name) != 0)
X		name = pwd->pw_name;
X
X	if (! (pwd = getpwnam (name))) {
X		fprintf (stderr, "unknown user: %s\n", name);
X		exit (1);
X	}
X
X	/*
X	 * now we determine the name of the new group which she wishes
X	 * to become a member of.  the password file entry for her
X	 * current user id has been gotten.  if there is no optional
X	 * group argument she will have her real and effective group id
X	 * set to the value from her password file entry.  otherwise
X	 * we validate her access to the specified group.
X	 */
X
X	if (argv[1] != (char *) 0) {
X
X		/*
X		 * start by getting the entry for the requested group.
X		 */
X
X		if (! (grp = getgrnam (group = argv[1]))) {
X			fprintf (stderr, "unknown group: %s\n", group);
X			goto failure;
X		}
X
X		/*
X		 * see if she is a member of this group.
X		 */
X
X		for (i = 0;grp->gr_mem[i];i++)
X			if (strcmp (name, grp->gr_mem[i]) == 0)
X				break;
X
X		/*
X		 * if she isn't a member, she needs to provide the
X		 * group password.  if there is no group password, she
X		 * will be denied access anyway.
X		 */
X
X		if (grp->gr_mem[i] == (char *) 0)
X			needspasswd = 1;
X#ifdef	SHADOWPWD
X
X		/*
X		 * if she does not have either a shadowed password,
X		 * or a regular password, and the group has a password,
X		 * she needs to give the group password.
X		 */
X
X		if (spwd = getspnam (name)) {
X			if (spwd->sp_pwdp[0] == '\0' && grp->gr_passwd[0])
X				needspasswd = 1;
X		} else {
X			if (pwd->pw_passwd[0] == '\0' && grp->gr_passwd[0])
X				needspasswd = 1;
X		}
X#else
X
X		/*
X		 * if she does not have a regular password she will have
X		 * to give the group password, if one exists.
X		 */
X
X		if (pwd->pw_passwd[0] == '\0' && grp->gr_passwd[0])
X			needspasswd = 1;
X#endif
X	} else {
X
X		/*
X		 * get the group file entry for her login group id.
X		 * the entry must exist, simply to be annoying.
X		 */
X
X		if (! (grp = getgrgid (pwd->pw_gid))) {
X			fprintf (stderr, "unknown gid: %d\n", pwd->pw_gid);
X			goto failure;
X		}
X	}
X
X	/*
X	 * now i see about letting her into the group she requested.
X	 * if she is the root user, i'll let her in without having to
X	 * prompt for the password.  otherwise i ask for a password
X	 * if she flunked one of the tests above.  note that she
X	 * won't have to provide the password to her login group even
X	 * if she isn't listed as a member.
X	 */
X
X	if (getuid () != 0 && needspasswd) {
X		passwd[0] = '\0';
X
X		if (grp->gr_passwd[0]) {
X
X		/*
X		 * get the password from her, and set the salt for
X		 * the decryption from the group file.
X		 */
X
X			if (! (cp = getpass ("Password:")))
X				goto failure;
X
X			strcpy (passwd, cp);
X			salt = grp->gr_passwd;
X		} else {
X
X		/*
X		 * there is no password, print out "Sorry" and give up
X		 */
X
X			fputs ("Sorry\n", stderr);
X			goto failure;
X		}
X
X		/*
X		 * encrypt the key she gave us using the salt from
X		 * the password in the group file.  the result of
X		 * this encryption must match the previously
X		 * encrypted value in the file.
X		 */
X
X		cpasswd = pw_encrypt (passwd, salt);
X
X		if (strcmp (cpasswd, grp->gr_passwd) != 0) {
X			fputs ("Sorry\n", stderr);
X			goto failure;
X		}
X	}
X
X	/*
X	 * all successful validations pass through this point.  the
X	 * group id will be set, and the group added to the concurrent
X	 * groupset.
X	 */
X
X	gid = grp->gr_gid;
X#ifdef	NGROUPS
X
X	/*
X	 * i am going to try to add her new group id to her concurrent
X	 * group set.  if the group id is already present i'll just
X	 * skip this part.  if the group doesn't fit, i'll complain
X	 * loudly and skip this part ...
X	 */
X
X	for (i = 0;i < ngroups;i++) {
X		if (gid == groups[i])
X			break;
X	}
X	if (i == ngroups) {
X		if (ngroups == NGROUPS) {
X			fprintf (stderr, "too many groups\n");
X		} else {
X			groups[ngroups++] = gid;
X			if (setgroups (ngroups, groups)) {
X				fprintf (stderr, "%s: ", Prog);
X				perror ("unable to set groups");
X			}
X		}
X	}
X#endif
X
X	/*
X	 * this is where all failures land.  the group id will not
X	 * have been set, so the setgid() below will set me to the
X	 * original group id i had when i was invoked.
X	 */
X
Xfailure:
X
X	/*
X	 * i set her group id either to the value she requested, or
X	 * to the original value.  i have to go back to the original
X	 * because she no longer has a shell running.
X	 */
X
X	if (setgid (gid))
X		perror ("setgid");
X
X	if (setuid (getuid ()))
X		perror ("setuid");
X
X	/*
X	 * i have to get the pathname of her login shell.  as a favor
X	 * i'll try her environment for a $SHELL value first, and
X	 * then try the password file entry.
X	 */
X
X	if (! initflag && (cp = getenv ("SHELL")))
X		strncpy (prog, cp, sizeof prog);
X	else if (pwd->pw_shell && pwd->pw_shell[0])
X		strncpy (prog, pwd->pw_shell, sizeof prog);
X	else
X		strcpy (prog, "/bin/sh");
X
X	/*
X	 * now i try to find the basename of the login shell.  this
X	 * will become argv[0] of the spawned command.
X	 */
X
X	if (cp = strrchr (prog, '/'))
X		cp++;
X	else
X		cp = prog;
X
X	/*
X	 * to have the shell perform login processing i will set the
X	 * first character in the first argument to a "-".
X	 */
X
X	if (initflag)
X		strcat (strcpy (base, "-"), cp);
X	else
X		strcpy (base, cp);
X
X#ifdef	SHADOWPWD
X	endspent ();
X#endif
X	endpwent ();
X	endgrent ();
X
X	/*
X	 * switch back to her home directory if i am doing login
X	 * initialization.
X	 */
X
X	if (initflag) {
X		chdir (pwd->pw_dir);
X		while (*envp) {
X			if (strncmp (*envp, "PATH=", 5) == 0 ||
X					strncmp (*envp, "HOME=", 5) == 0 ||
X					strncmp (*envp, "SHELL=", 6) == 0)
X				addenv (*envp);
X
X			envp++;
X		}
X	} else {
X		while (*envp)
X			addenv (*envp++);
X	}
X
X	/*
X	 * exec the login shell and go away.
X	 */
X
X	shell (prog, base);
X	/*NOTREACHED*/
X}
SHAR_EOF
if test 8576 -ne "`wc -c < 'newgrp.c'`"
then
	echo shar: "error transmitting 'newgrp.c'" '(should have been 8576 characters)'
fi
fi
echo shar: "extracting 'Makefile'" '(11315 characters)'
if test -f 'Makefile'
then
	echo shar: "will not over-write existing file 'Makefile'"
else
sed 's/^X//' << \SHAR_EOF > 'Makefile'
X#
X# Copyright 1988,1989,1990, John F. Haugh II
X# All rights reserved.
X#
X# Permission is granted to copy and create derivative works for any
X# non-commercial purpose, provided this copyright notice is preserved
X# in all copies of source code, or included in human readable form
X# and conspicuously displayed on all copies of object code or
X# distribution media.
X#
X#	%W%	%U%  - Shadow password system
X#
X#	%W%	%U%	%G%
X#
XSHELL = /bin/sh
X
X#
X# Set this flag to decide what level of code "get" returns.
X# The base USENET release was release 1.  It is no longer supported.
X# The version with the utilities added was release 2.
X# The unreleased version with database-like file access is release 3.
XRELEASE = 3
XGFLAGS = -t -r$(RELEASE)
X
X# Define the directory login is copied to.  BE VERY CAREFUL!!!
X# LOGINDIR = /bin
XLOGINDIR = /etc
X
X# Pick your favorite C compiler and tags command
XCC = cc
XTAGS = ctags
X
X# OS.  Currently only BSD and USG are defined.  If you don't use BSD,
X# USG (System V) is assumed.
XOS = -DUSG
X# OS = -DBSD
X
X# Do you have to do ranlib?  Sorry to hear that ...
XRANLIB = ranlib
X# RANLIB = echo
X
X# Flags for SCO Xenix/386
XCFLAGS = -O -M3 -g $(OS)
XLIBS = -lcrypt -lndbm
X# LIBS = -lcrypt -ldbm
XLDFLAGS = -M3 -g
XLTFLAGS = 
X# This should be Slibsec.a for small model, or Llibsec.a for
X# large model or whatever.  MUST AGREE WITH CFLAGS!!!
XLIBSEC = Slibsec.a
X
X# Flags for normal machines
X# CFLAGS = -O -g $(OS)
X# LIBS =
X# LDFLAGS = -g
X# LIBSEC = libsec.a
X
X# Names for root user and group, and bin user and group
XRUID = root
XRGID = root
XBUID = bin
XBGID = bin
X
X# Rules for .L (lint) files.
X.SUFFIXES: .L
XLINT = lint
XLINTFLAGS = $(OS) -Dlint
X
X.c.L:
X	$(LINT) -u $(LINTFLAGS) $*.c > $*.L
X
XLOBJS = lmain.o login.o env.o valid.o setup.o shell.o age.o \
X	utmp.o sub.o mail.o motd.o log.o ttytype.o failure.o
X
XLSRCS = lmain.c login.c env.c valid.c setup.c shell.c age.c \
X	utmp.c sub.c mail.c motd.c log.c ttytype.c failure.c
X
XSOBJS = smain.o env.o entry.o susetup.o shell.o \
X	sub.o mail.o motd.o sulog.o age.o
X
XSSRCS = smain.c env.c entry.c setup.c shell.c \
X	pwent.c sub.c mail.c motd.c sulog.c shadow.c age.c pwpack.c rad64.c
X
XPOBJS = passwd.o obscure.o
XPSRCS = passwd.c obscure.c
X
XGPSRCS = gpmain.c
X
XGPOBJS = gpmain.o
X
XPWOBJS = pwconv.o
X
XPWSRCS = pwconv.c pwent.c shadow.c pwpack.c rad64.c
X
XPWUNOBJS = pwunconv.o
X
XPWUNSRCS = pwunconv.c pwent.c shadow.c pwpack.c rad64.c
X
XSULOGOBJS = sulogin.o entry.o env.o age.o setup.o \
X	valid.o shell.o
X
XSULOGSRCS = sulogin.c entry.c env.c age.c pwent.c setup.c \
X	shadow.c shell.c valid.c pwpack.c
X
XMKPWDOBJS = mkpasswd.o
X
XMKPWDSRCS = mkpasswd.c
X
XNGSRCS = newgrp.c env.c shell.c
X
XNGOBJS = newgrp.o env.o shell.o
X
XCHFNSRCS = chfn.c fields.c
XCHFNOBJS = chfn.o fields.o
XCHSHSRCS = chsh.c fields.c
XCHSHOBJS = chsh.o fields.o
XCHAGEOBJS = chage.o fields.o
XCHAGESRCS = chage.c fields.c
XCHPASSOBJS = chpasswd.o
XCHPASSSRCS = chpasswd.c
XDPSRCS = dpmain.c
XDPOBJS = dpmain.o
X
XALLSRCS = age.c dialchk.c dialup.c entry.c env.c lmain.c log.c login.c mail.c \
X	motd.c obscure.c passwd.c pwconv.c pwent.c pwunconv.c getpass.c \
X	setup.c shadow.c shell.c smain.c sub.c sulog.c sulogin.c ttytype.c \
X	utmp.c valid.c port.c newgrp.c gpmain.c grent.c mkpasswd.c pwpack.c \
X	chfn.c chsh.c chage.c rad64.c encrypt.c chpasswd.c shadowio.c pwio.c \
X	newusers.c groupio.c fields.c pwdbm.c grpack.c grdbm.c sppack.c \
X	spdbm.c dpmain.c gshadow.c gsdbm.c gspack.c sgroupio.c
X
XFILES1 = README newgrp.c Makefile config.h pwunconv.c obscure.c age.c
X
XFILES2 = passwd.c port.c lmain.c mkpasswd.c sulogin.c pwpack.c dialup.c \
X	sulog.c getpass.c
X
XFILES3 = chfn.c chsh.c smain.c faillog.c pwconv.c failure.c utmp.c shadow.c
X
XFILES4 = gpmain.c chage.c pwent.c valid.c setup.c entry.c ttytype.c port.h
X
XFILES5 = pwio.c encrypt.c chpasswd.c newusers.c rad64.c dialchk.c faillog.h \
X	pwdbm.c grdbm.c gshadow.c sppack.c
X
XFILES6 = gspack.c spdbm.c lastlog.h shell.c login.c sub.c dpmain.c mail.c \
X	env.c pwd.h grpack.c shadow.h log.c grent.c motd.c dialup.h fields.c \
X	gsdbm.c
X
XFILES7 = groupio.c shadowio.c sgroupio.c
X
XMAN_1 = chage.1 chfn.1 chsh.1 login.1 passwd.1 su.1
XMAN_3 = shadow.3
XMAN_4 = faillog.4 passwd.4 porttime.4 shadow.4
XMAN_8 = faillog.8 pwconv.8 pwunconv.8 sulogin.8
X
XDOCS = $(MAN_1) $(MAN_3) $(MAN_4) $(MAN_8)
X
XBINS = su login pwconv pwunconv passwd sulogin faillog newgrp gpasswd \
X	mkpasswd chfn chsh chage chpasswd newusers dpasswd id
X
Xall:	$(BINS) $(DOCS)
X
X.PRECIOUS: libshadow.a
Xlibshadow.a: \
X	libshadow.a(dialchk.o) \
X	libshadow.a(dialup.o) \
X	libshadow.a(encrypt.o) \
X	libshadow.a(getpass.o) \
X	libshadow.a(grdbm.o) \
X	libshadow.a(grent.o) \
X	libshadow.a(groupio.o) \
X	libshadow.a(grpack.o) \
X	libshadow.a(gshadow.o) \
X	libshadow.a(gsdbm.o) \
X	libshadow.a(gspack.o) \
X	libshadow.a(sgroupio.o) \
X	libshadow.a(port.o) \
X	libshadow.a(pwdbm.o) \
X	libshadow.a(pwent.o) \
X	libshadow.a(pwio.o) \
X	libshadow.a(pwpack.o) \
X	libshadow.a(rad64.o) \
X	libshadow.a(spdbm.o) \
X	libshadow.a(shadow.o) \
X	libshadow.a(shadowio.o) \
X	libshadow.a(sppack.o)
X	$(RANLIB) libshadow.a
X
Xlibsec: $(LIBSEC)(shadow.o)
X	$(RANLIB) $(LIBSEC)
X
Xinstall: all
X	strip $(BINS)
X	cp login $(LOGINDIR)/login
X	cp mkpasswd pwconv pwunconv sulogin chpasswd newusers /etc
X	cp su passwd gpasswd dpasswd faillog newgrp chfn chsh id /bin
X	cp dialup.h shadow.h pwd.h /usr/include
X	chown $(RUID) $(LOGINDIR)/login /etc/pwconv /etc/pwunconv /etc/sulogin \
X		/bin/su /bin/passwd /bin/gpasswd /bin/newgrp /etc/mkpasswd \
X		/bin/dpasswd /bin/chsh /bin/chfn /bin/chage
X	chgrp $(RGID) $(LOGINDIR)/login /etc/pwconv /etc/pwunconv /etc/sulogin \
X		/bin/su /bin/passwd /bin/gpasswd /bin/newgrp /etc/mkpasswd \
X		/bin/dpasswd /bin/chsh /bin/chfn /bin/chage
X	chown $(BUID) /bin/faillog /bin/id /usr/include/shadow.h \
X		/usr/include/dialup.h /usr/include/pwd.h
X	chgrp $(BGID) /bin/faillog /bin/id /usr/include/shadow.h \
X		/usr/include/dialup.h /usr/include/pwd.h
X	chmod 700 /etc/pwconv /etc/pwunconv /etc/sulogin /etc/mkpasswd \
X		/etc/chpasswd /etc/newusers /bin/dpasswd /bin/chage
X	chmod 4711 $(LOGINDIR)/login /bin/su /bin/passwd /bin/gpasswd \
X		/bin/newgrp /bin/chfn /bin/chsh
X	chmod 711 /bin/faillog /bin/id
X	chmod 444 /usr/include/shadow.h /usr/include/dialup.h \
X		/usr/include/pwd.h
X
Xlint:	su.lint login.lint pwconv.lint pwunconv.lint passwd.lint sulogin.lint \
X	faillog.lint newgrp.lint gpasswd.lint mkpasswd.lint chfn.lint \
X	chsh.lint chage.lint dpasswd.lint $(ALLSRCS:.c=.L)
X
Xtags:	$(ALLSRCS)
X	$(TAGS) $(ALLSRCS)
X
XREADME:	s.README
X	get -t -r$(RELEASE) s.README
X	
X$(DOCS):
X	get -t -r$(RELEASE) s.$@
X
Xlogin:	$(LOBJS) libshadow.a
X	$(CC) -o login $(LDFLAGS) $(LOBJS) libshadow.a $(LIBS)
X
Xlogin.lint: $(LSRCS)
X	$(LINT) $(LINTFLAGS) $(LSRCS) > login.lint
X
Xsu:	$(SOBJS) libshadow.a
X	$(CC) -o su $(LDFLAGS) $(SOBJS) libshadow.a $(LIBS)
X
Xsu.lint:	$(SSRCS)
X	$(LINT) $(LINTFLAGS) -DSU $(SSRCS) > su.lint
X
Xpasswd:	$(POBJS) libshadow.a
X	$(CC) -o passwd $(LDFLAGS) $(POBJS) libshadow.a $(LIBS)
X
Xpasswd.lint: $(PSRCS)
X	$(LINT) $(LINTFLAGS) -DPASSWD $(PSRCS) > passwd.lint
X
Xgpasswd: $(GPOBJS) libshadow.a
X	$(CC) -o gpasswd $(LDFLAGS) $(GPOBJS) libshadow.a $(LIBS)
X
Xgpasswd.lint: $(GPSRCS)
X	$(LINT) $(LINTFLAGS) $(GPSRCS) > gpasswd.lint
X
Xdpasswd: $(DPOBJS) libshadow.a
X	$(CC) -o dpasswd $(LDFLAGS) $(DPOBJS) libshadow.a $(LIBS)
X
Xdpasswd.lint: $(DPSRCS)
X	$(LINT) $(LINTFLAGS) $(DPSRCS) > dpasswd.lint
X
Xpwconv:	$(PWOBJS) libshadow.a config.h
X	$(CC) -o pwconv $(LDFLAGS) $(PWOBJS) libshadow.a $(LIBS)
X
Xpwconv.lint: $(PWSRCS) config.h
X	$(LINT) $(LINTFLAGS) -DPASSWD $(PWSRCS) > pwconv.lint
X
Xpwunconv: $(PWUNOBJS) libshadow.a config.h
X	$(CC) -o pwunconv $(LDFLAGS) $(PWUNOBJS) libshadow.a $(LIBS)
X
Xpwunconv.lint: $(PWUNSRCS)
X	$(LINT) $(LINTFLAGS) -DPASSWD $(PWUNSRCS) > pwunconv.lint
X
Xsulogin: $(SULOGOBJS) libshadow.a
X	$(CC) -o sulogin $(LDFLAGS) $(SULOGOBJS) libshadow.a $(LIBS)
X
Xsulogin.lint: $(SULOGSRCS)
X	$(LINT) $(LINTFLAGS) $(SULOGSRCS) > sulogin.lint
X
Xfaillog: faillog.o
X	$(CC) -o faillog $(LDFLAGS) faillog.o $(LIBS)
X
Xfaillog.lint: faillog.c faillog.h config.h
X	$(LINT) $(LINTFLAGS) faillog.c > faillog.lint
X
Xmkpasswd: $(MKPWDOBJS) libshadow.a
X	$(CC) -o mkpasswd $(LDFLAGS) $(MKPWDOBJS) libshadow.a $(LIBS)
X
Xmkpasswd.lint: $(MKPWDSRCS)
X	$(LINT) $(LINTFLAGS) $(MKPWDSRCS) > mkpasswd.lint
X
Xnewgrp: $(NGOBJS) libshadow.a
X	$(CC) -o newgrp $(LDFLAGS) $(NGOBJS) libshadow.a $(LIBS)
X
Xnewgrp.lint: $(NGSRCS)
X	$(LINT) $(LINTFLAGS) $(NGSRCS) > newgrp.lint
X
Xchfn:	$(CHFNOBJS) libshadow.a
X	$(CC) -o chfn $(LDFLAGS) $(CHFNOBJS) libshadow.a $(LIBS)
X
Xchfn.lint:	$(CHFNSRCS)
X	$(LINT) $(LINTFLAGS) $(CHFNSRCS) > chfn.lint
X
Xchsh:	$(CHSHOBJS) libshadow.a
X	$(CC) -o chsh $(LDFLAGS) $(CHSHOBJS) libshadow.a $(LIBS)
X
Xchsh.lint: $(CHSHSRCS)
X	$(LINT) $(LINTFLAGS) $(CHSHSRCS) > chsh.lint
X
Xchage:	$(CHAGEOBJS) libshadow.a
X	$(CC) -o chage $(LDFLAGS) $(CHAGEOBJS) libshadow.a $(LIBS)
X
Xchage.lint: $(CHAGESRCS)
X	$(LINT) $(LINTFLAGS) -DPASSWD $(CHAGESRCS) > chage.lint
X
Xchpasswd: $(CHPASSOBJS) libshadow.a
X	$(CC) -o chpasswd $(LDFLAGS) $(CHPASSOBJS) libshadow.a $(LIBS)
X
Xchpasswd.lint: $(CHPASSSRCS)
X	$(LINT) $(LINTFLAGS) $(CHPASSSRCS) > chpasswd.lint
X
Xnewusers: newusers.o libshadow.a
X	$(CC) -o newusers $(LDFLAGS) newusers.o libshadow.a $(LIBS)
X
Xnewusers.lint: newusers.c
X	$(LINT) $(LINTFLAGS) newusers.c > newusers.lint
X	
Xid: id.o libshadow.a
X	$(CC) -o id $(LDFLAGS) id.o libshadow.a $(LIBS)
X
Xid.lint: id.c
X	$(LINT) $(LINTFLAGS) id.c > id.lint
X
Xsulog.o: config.h
X
Xsusetup.c: setup.c
X	cp setup.c susetup.c
X
Xsusetup.o: config.h setup.c pwd.h
X	$(CC) -c $(CFLAGS) -DSU susetup.c
X
Xpasswd.o: config.h shadow.h pwd.h
Xlmain.o: config.h lastlog.h faillog.h pwd.h
Xsmain.o: config.h lastlog.h pwd.h shadow.h
Xsub.o: pwd.h
Xsetup.o: config.h pwd.h
X
Xutmp.o: config.h
X
Xmail.o: config.h
X
Xmotd.o: config.h
X
Xage.o: config.h pwd.h
X
Xlog.o: config.h lastlog.h pwd.h
X
Xshell.o: config.h
X
Xentry.o: config.h shadow.h pwd.h
X
Xshadow.o: shadow.h config.h
Xshadowio.o: shadow.h
Xgrent.o: config.h shadow.h
Xsgroupio.o: shadow.h
Xdialup.o: dialup.h
Xdialchk.o: dialup.h config.h
Xpwdbm.o: config.h pwd.h
Xpwpack.o: config.h pwd.h
Xpwent.o: config.h pwd.h
Xpwio.o: pwd.h
Xgetpass.o: config.h
Xencrypt.o: config.h
Xport.o: port.h
X
Xvalid.o: config.h pwd.h
X
Xfailure.o: faillog.h config.h
X
Xfaillog.o: faillog.h config.h pwd.h
X
Xnewgrp.o: config.h shadow.h pwd.h
X
Xmkpasswd.o: config.h shadow.h pwd.h
X
Xgpmain.o: config.h pwd.h
X
Xchfn.o: config.h pwd.h
X
Xchsh.o: config.h pwd.h
X
Xchage.o: config.h shadow.h pwd.h
X
Xpwconv.o: config.h shadow.h
X
Xpwunconv.o: config.h shadow.h pwd.h
X
Xchpasswd.o: config.h shadow.h pwd.h
Xid.o: pwd.h
Xnewusers.o: config.h shadow.h pwd.h
Xdpmain.o: dialup.h
X
Xclean:
X	-rm -f *.o a.out core npasswd nshadow *.pag *.dir
X
Xclobber: clean
X	-rm -f $(BINS) *.lint *.L susetup.c libshadow.a
X
Xnuke:	clobber
X	-for file in * ; do \
X		if [ -f s.$$file -a ! -f p.$$file ] ; then \
X			rm -f $$file ;\
X		fi ;\
X	done
X
Xshar:	login.sh.1 login.sh.2 login.sh.3 login.sh.4 login.sh.5 login.sh.6 \
X	login.sh.7 login.sh.8
X
Xlogin.sh.1: $(FILES1) Makefile
X	shar -a $(FILES1) > login.sh.1
X
Xlogin.sh.2: $(FILES2) Makefile
X	shar -a $(FILES2) > login.sh.2
X
Xlogin.sh.3: $(FILES3) Makefile
X	shar -a $(FILES3) > login.sh.3
X
Xlogin.sh.4: $(FILES4) Makefile
X	shar -a $(FILES4) > login.sh.4
X
Xlogin.sh.5: $(FILES5) Makefile
X	shar -a $(FILES5) > login.sh.5
X
Xlogin.sh.6: $(FILES6) Makefile
X	shar -a $(FILES6) > login.sh.6
X
Xlogin.sh.7: $(FILES7) Makefile
X	shar -a $(FILES7) > login.sh.7
X
Xlogin.sh.8: $(DOCS) Makefile
X	shar -a $(DOCS) > login.sh.8
SHAR_EOF
if test 11315 -ne "`wc -c < 'Makefile'`"
then
	echo shar: "error transmitting 'Makefile'" '(should have been 11315 characters)'
fi
fi
echo shar: "extracting 'config.h'" '(6820 characters)'
if test -f 'config.h'
then
	echo shar: "will not over-write existing file 'config.h'"
else
sed 's/^X//' << \SHAR_EOF > 'config.h'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X/*
X * Configuration file for login.
X *
X *	@(#)config.h	3.4	14:43:20	12/18/90
X */
X
X/*
X * Define DIALUP to use dialup password files.  Define PORTTIME
X * to use the port time restriction file, see port.h for more
X * information.
X */
X
X#define	DIALUP
X#define	PORTTIME
X
X/*
X * Define SHADOWPWD to use shadow [ unreadable ] password file.
X * Release 3 has a requirement that SHADOWPWD always be defined.
X * Define AUTOSHADOW to have root always copy sp_pwdp to pw_passwd
X * for getpwuid() and getpwnam().  This provides compatibility for
X * privileged applications which are shadow-ignorant.  YOU ARE
X * ENCOURAGED TO NOT USE THIS OPTION UNLESS ABSOLUTELY NECESSARY.
X */
X
X#define	SHADOWPWD
X#undef	AUTOSHADOW
X
X/*
X * Define DOUBLESIZE to use 16 character passwords
X */
X
X#define DOUBLESIZE
X
X/*
X * Define OBSCURE to include hard password testing code.
X */
X
X#define	OBSCURE
X
X/*
X * Define PASSLENGTH to be shortest legal password
X */
X
X#define	PASSLENGTH	5
X
X/*
X * Define NOBLANK if you want all passwords prompted for, including
X * empty ones.
X
X#undef	NOBLANK
X
X/*
X * Define MAXDAYS to be the default maximum number of days a password
X * is valid for when converting to shadow passwords.  Define MINDAYS
X * to be the minimum number of days before a password may be changed.
X * See pwconv.c for more details.
X */
X
X#define	MAXDAYS	10000
X#define	MINDAYS	0
X
X/*
X * Define NDEBUG for production versions
X */
X
X#define	NDEBUG
X
X/*
X * Define HZ if login must set HZ value
X */
X
X#define	HZ	"HZ=50"
X
X/*
X * Define TZ if login must set timezone
X *
X * The first example sets the variable directly.  The
X * second example names a file which is read to determine
X * the proper value.  The file consists of a single line
X * of the form 'TZ=zone-name'
X */
X
X/* #define	TZ	"TZ=CST6CDT" */
X#define	TZ	"/etc/tzname"
X
X/*
X * Define the default PATH and SUPATH here.  PATH is for non-privileged
X * users, SUPATH is for root.  The first pair are for real trusting
X * systems, the second pair are for the paranoid ...
X */
X
X/* #define	PATH	"PATH=:/bin:/usr/bin"	*/
X/* #define	SUPATH	"PATH=:/bin:/usr/bin:/etc" */
X#define	PATH	"PATH=/bin:/usr/bin"
X#define	SUPATH	"PATH=/bin:/usr/bin:/etc"
X
X/*
X * Define the mailbox directory
X */
X
X#define	MAILDIR	"/usr/spool/mail/"
X
X/*
X * Define AGING if you want the password aging checks made.
X * Define WARNAGE to be the number of days notice a user receives
X * of a soon to expire password.  Release 3 has a requirement that
X * AGING always be defined.
X */
X
X#define	AGING
X#define	WARNAGE	10
X
X/*
X * Define MAILCHECK if you want the mailbox checked for new mail
X *
X * One of two messages are printed - `You have new mail.' or
X * `You have mail.'.
X */
X
X#define	MAILCHECK
X
X/*
X * Define CONSOLE if you want ROOT restricted to a particular terminal.
X *
X * Use the name of the tty line if you only want a single line, or use
X * the name of the file containing the permissible ports if you wish to
X * allow root logins on more than one port.
X */
X
X/* #define	CONSOLE	"console"	/* root on /dev/console only */
X#define	CONSOLE	"/etc/consoles"		/* check /etc/consoles for a list */
X
X/*
X * Define NOLOGINS if you want to be able to deny non-root users logins.
X * Logins will not be permitted if this file exists.
X */
X
X#define	NOLOGINS	"/etc/nologin"
X
X/*
X * Define NOUSE if you want to be able to declare accounts which can't
X * be logged into.  Define NOLOGIN if you want it to be an su-only account.
X */
X
X#define	NOUSE	"NOUSE"
X#define	NOLOGIN	"NOLOGIN"
X
X/*
X * Define MOTD if you want the message of the day (/etc/motd) printed
X * at login time.
X */
X
X#define	MOTD
X
X/*
X * Define HUSHLOGIN if you want the code added to avoid printing the
X * motd if a file $HOME/.hushlogin exists.  This obviously only matters
X * if any of MOTD, MAILCHECK or LASTLOG are #define'd.
X */
X
X#define	HUSHLOGIN
X
X/*
X * Define LASTLOG if you want a record made of logins in /usr/adm/lastlog.
X */
X
X#define	LASTLOG
X
X/*
X * Define FAILLOG if you want a record make of failed logins in
X * /usr/adm/faillog.  See faillog.h for more details.  See fail(1L)
X * for even still more details ...  Also, define FTMP to record utmp
X * style records for failed logins.  FTMP is the name of a utmp-like
X * file.  You can use who(1) instead of faillog(L), which is an
X * advantage.  Define UNKNOWNS if you do want unknown user names
X * recorded.  This can be a security hole since passwords are often
X * entered mistakenly as user names.
X */
X
X#define	FAILLOG
X#define	FTMP	"/etc/ftmp"
X#define	UNKNOWNS
X
X/*
X * Define TTYPERM to be the initial terminal permissions.  Defining
X * as 0600 will not allow messages, 0622 will.
X */
X
X#define	TTYPERM	0600
X
X/*
X * Define TTYTYPE to the be name of the port to terminal type
X * mapping file.  This is used to set the environmental variable
X * "TERM" to the correct terminal type.
X */
X
X#define	TTYTYPE	"/etc/ttytype"
X
X/*
X * Define QUOTAS if you want the code added in setup.c to support
X * file ulimit and nice [ and umask as well ] setting from the password
X * file.
X */
X
X#define	QUOTAS
X
X/*
X * Pick your version of DBM.  If you define either DBM or NDBM, you
X * must define GETPWENT.  If you define NDBM you must define GETGRENT
X * as well.
X */
X
X/* #define	DBM /* */
X#define	NDBM	/* */
X
X/*
X * Define file name for sulog.  If SULOG is not defined, there will be
X * no logging.  This is NOT a good idea ...  We also define other file
X * names.
X */
X
X#define	SULOG	"/usr/adm/sulog"
X#define	SUCON	"/dev/console"
X#define	PWDFILE	"/etc/passwd"
X#define	OPWDFILE "/etc/passwd-"
X#define	NPWDFILE "/etc/npasswd"
X#define	OSHADOW "/etc/shadow-"
X#define	NSHADOW "/etc/nshadow"
X#define	GRPFILE	"/etc/group"
X#define	OGRPFILE "/etc/group-"
X#define	NGRPFILE "/etc/ngroup"
X
X/*
X * Define PWDLOCK to be a locking semaphore for updating the password
X * file.  GRPLOCK is the same for the group file.
X */
X
X#define	PWDLOCK	"/etc/passwd.lock"
X#define	GRPLOCK "/etc/group.lock"
X#define	SPWLOCK "/etc/shadow.lock"
X
X/*
X * Wierd stuff follows ...
X *
X *	The following macros exist solely to override stuff ...
X *	You will probably want to change their values to suit your
X *	fancy.
X */
X
X#define	ERASECHAR	'\b'
X#define	KILLCHAR	'\025'
X#define	UMASK		022
X
X#define	ULIMIT	(1L<<20) /* Define if your UNIX supports ulimit() */
X#define	FGETPWENT	/* Define if library does not include FGETPWENT */
X#define	GETPWENT	/* Define if you want my GETPWENT(3) routines */
X#define	GETGRENT	/* Define if you want my GETGRENT(3) routines */
X#define	NEED_AL64	/* Define if library does not include a64l() */
X
X/*
X *	These macros control the contents of <pwd.h>.
X */
X
X#define	ATT_AGE		/* the pw_age field exists */
X#define	ATT_COMMENT	/* the pw_comment field exists */
X#undef	BSD_QUOTA	/* the pw_quota field exists */
SHAR_EOF
if test 6820 -ne "`wc -c < 'config.h'`"
then
	echo shar: "error transmitting 'config.h'" '(should have been 6820 characters)'
fi
fi
echo shar: "extracting 'pwunconv.c'" '(3162 characters)'
if test -f 'pwunconv.c'
then
	echo shar: "will not over-write existing file 'pwunconv.c'"
else
sed 's/^X//' << \SHAR_EOF > 'pwunconv.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X *
X * pwunconv - restore old password file from shadow password file.
X *
X *	Pwunconv copies the password file information from the shadow
X *	password file, merging entries from an optional existing shadow
X *	file.
X *
X *	The new password file is left in npasswd.  There is no new
X *	shadow file.  Password aging information is translated where
X *	possible.
X */
X
X#include <sys/types.h>
X#include <stdio.h>
X#include <fcntl.h>
X#include "pwd.h"
X#include "config.h"
X#include "shadow.h"
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)pwunconv.c	3.2	12:31:26	12/12/90";
X#endif
X
X#ifdef	ITI_AGING
X#define	WEEK	(7L*24L*3600L)
X#else
X#define	WEEK	(7)
X#endif
X
Xchar	buf[BUFSIZ];
Xchar	*l64a ();
X
Xint	main ()
X{
X	struct	passwd	*pw;
X	struct	passwd	*sgetpwent ();
X	FILE	*pwd;
X	FILE	*npwd;
X	struct	spwd	*spwd;
X	int	fd;
X	char	newage[5];
X
X	if (! (pwd = fopen (PWDFILE, "r"))) {
X		perror (PWDFILE);
X		return (1);
X	}
X	unlink ("npasswd");
X	if ((fd = open ("npasswd", O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0 ||
X			! (npwd = fdopen (fd, "w"))) {
X		perror ("npasswd");
X		return (1);
X	}
X	while (fgets (buf, BUFSIZ, pwd) == buf) {
X		buf[strlen (buf) - 1] = '\0'; /* remove '\n' character */
X
X		if (buf[0] == '#') {	/* comment line */
X			(void) fprintf (npwd, "%s\n", buf);
X			continue;
X		}
X		if (! (pw = sgetpwent (buf))) { /* copy bad lines verbatim */
X			(void) fprintf (npwd, "%s\n", buf);
X			continue;
X		}
X		setspent ();		/* rewind shadow file */
X
X		if (! (spwd = getspnam (pw->pw_name))) {
X			(void) fprintf (npwd, "%s\n", buf);
X			continue;
X		}
X		pw->pw_passwd = spwd->sp_pwdp;
X
X	/*
X	 * Password aging works differently in the two different systems.
X	 * With shadow password files you apparently must have some aging
X	 * information.  The maxweeks or minweeks may not map exactly.
X	 * In pwconv we set max == 10000, which is about 30 years.  Here
X	 * we have to undo that kludge.  So, if maxdays == 10000, no aging
X	 * information is put into the new file.  Otherwise, the days are
X	 * converted to weeks and so on.
X	 */
X
X#ifdef	ATT_AGE
X		if (spwd->sp_max > (63*WEEK) && spwd->sp_max < 10000)
X			spwd->sp_max = (63*WEEK); /* 10000 is infinity */
X
X		if (spwd->sp_min >= 0 && spwd->sp_min <= 63*7 &&
X				spwd->sp_max >= 0 && spwd->sp_max <= 63*7) {
X			if (spwd->sp_lstchg == -1)
X				spwd->sp_lstchg = 0;
X
X			spwd->sp_max /= WEEK;	/* turn it into weeks */
X			spwd->sp_min /= WEEK;
X			spwd->sp_lstchg /= WEEK;
X
X			strncpy (newage, l64a (spwd->sp_lstchg * (64L*64L) +
X				  spwd->sp_min * (64L) + spwd->sp_max), 5);
X			pw->pw_age = newage;
X		} else
X			pw->pw_age = "";
X#endif	/* ATT_AGE */
X		if (putpwent (pw, npwd)) {
X			perror (stderr, "pwunconv: write error");
X			exit (1);
X		}
X	}
X	endspent ();
X
X	if (ferror (npwd)) {
X		perror ("pwunconv");
X		(void) unlink ("npasswd");
X	}
X	(void) fclose (npwd);
X	(void) fclose (pwd);
X	return (0);
X}
SHAR_EOF
if test 3162 -ne "`wc -c < 'pwunconv.c'`"
then
	echo shar: "error transmitting 'pwunconv.c'" '(should have been 3162 characters)'
fi
fi
echo shar: "extracting 'obscure.c'" '(3031 characters)'
if test -f 'obscure.c'
then
	echo shar: "will not over-write existing file 'obscure.c'"
else
sed 's/^X//' << \SHAR_EOF > 'obscure.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <ctype.h>
X#ifndef	BSD
X#include <string.h>
X#include <memory.h>
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X#include "config.h"
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)obscure.c	3.1	11:31:35	12/19/90";
X#endif
X
X/*
X * Obscure - see if password is obscure enough.
X *
X *	The programmer is encouraged to add as much complexity to this
X *	routine as desired.  Included are some of my favorite ways to
X *	check passwords.
X */
X
Xint	obscure (old, new)
Xchar	*old;
Xchar	*new;
X{
X#ifdef	OBSCURE
X	int	i;
X	char	oldmono[32];
X	char	newmono[32];
X	char	wrapped[64];
X#endif
X	if (old[0] == '\0')
X		return (1);
X
X	if (strlen (new) < PASSLENGTH) { /* too short */
X		printf ("Too short.  ");
X		return (0);
X	}
X#ifdef	OBSCURE
X	for (i = 0;new[i];i++)
X		newmono[i] = tolower (new[i]);
X
X	for (i = 0;old[i];i++)
X		oldmono[i] = tolower (old[i]); 
X
X	if (strcmp (new, old) == 0) {	/* the same */
X		printf ("No Change.  ");
X		return (0);
X	}
X	if (palindrome (newmono, oldmono))	/* a palindrome */
X		return (0);
X
X	if (strcmp (newmono, oldmono) == 0) {	/* case shifted */
X		printf ("Case changes only.  ");
X		return (0);
X	}
X	if (similiar (newmono, oldmono))	/* jumbled version */
X		return (0);
X
X	if (simple (new, old))			/* keyspace size */
X		return (0);
X
X	strcpy (wrapped, oldmono);
X	strcat (wrapped, oldmono);
X	if (strstr (wrapped, newmono)) {
X		printf ("Rotated.  ");
X		return (0);
X	}
X#endif
X	return (1);
X}
X
X#ifdef	OBSCURE
X
X/*
X * can't be a palindrome - like `R A D A R' or `M A D A M'
X */
X
Xint	palindrome (old, new)
Xchar	*old;
Xchar	*new;
X{
X	int	i, j;
X
X	i = strlen (new);
X
X	for (j = 0;j < i;j++)
X		if (new[i - j - 1] != new[j])
X			return (0);
X
X	printf ("A palindrome.  ");
X	return (1);
X}
X
X/*
X * more than half of the characters are different ones.
X */
X
Xint	similiar (old, new)
Xchar	*old;
Xchar	*new;
X{
X	int	i, j;
X	char	*strchr ();
X
X	for (i = j = 0;new[i] && old[i];i++)
X		if (strchr (new, tolower (old[i])))
X			j++;
X
X	if (i >= j * 2)
X		return (0);
X
X	printf ("Too similiar.  ");
X	return (1);
X}
X
X/*
X * a nice mix of characters.
X */
X
Xint	simple (old, new)
Xchar	*old;
Xchar	*new;
X{
X	int	digits = 0;
X	int	uppers = 0;
X	int	lowers = 0;
X	int	others = 0;
X	int	size;
X	int	i;
X	double	complexity;
X
X	for (i = 0;new[i];i++) {
X		if (isdigit (new[i]))
X			digits++;
X		else if (isupper (new[i]))
X			uppers++;
X		else if (islower (new[i]))
X			lowers++;
X		else
X			others++;
X	}
X
X	/*
X	 * The scam is this - a password of only one character type
X	 * must be 8 letters long.  Two types, 7, and so on.
X	 */
X
X	size = 9;
X	if (digits) size--;
X	if (uppers) size--;
X	if (lowers) size--;
X	if (others) size--;
X
X	if (size <= i)
X		return 0;
X
X	printf ("Too Simple.  ");
X	return 1;
X}
X#endif
SHAR_EOF
if test 3031 -ne "`wc -c < 'obscure.c'`"
then
	echo shar: "error transmitting 'obscure.c'" '(should have been 3031 characters)'
fi
fi
echo shar: "extracting 'age.c'" '(6179 characters)'
if test -f 'age.c'
then
	echo shar: "will not over-write existing file 'age.c'"
else
sed 's/^X//' << \SHAR_EOF > 'age.c'
X/*
X * Copyright 1989, 1990, 1991, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <sys/types.h>
X#include <stdio.h>
X#include <errno.h>
X#include "config.h"
X#include "pwd.h"
X#include "shadow.h"
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)age.c	3.3	07:52:49	1/30/91";
X#endif
X
X#define	DAY	(24L*3600L)
X#ifdef	ITI_AGING
X#define	SCALE	(DAY)
X#else
X#define	SCALE	(1)
X#endif
X
X#ifndef	WARNAGE
X#define	WARNAGE	10
X#endif
X
Xextern	time_t	time ();
Xextern	char	*strdup();
X
X/*
X * pwd_to_spwd - create entries for new spwd structure
X *
X *	pwd_to_spwd() creates a new (struct spwd) containing the
X *	information in the pointed-to (struct passwd).
X */
X
Xstatic struct spwd *
Xpwd_to_spwd (pw)
Xstruct	passwd	*pw;
X{
X	static	struct	spwd	tspwd;
X	struct	spwd	*sp = &tspwd;
X	time_t	t;
X
X	/*
X	 * Nice, easy parts first.  The name and passwd map directly
X	 * from the old password structure to the new one.
X	 */
X
X	sp->sp_namp = strdup (pw->pw_name);
X	sp->sp_pwdp = strdup (pw->pw_passwd);
X#ifdef	ATT_AGE
X
X	/*
X	 * AT&T-style password aging maps the sp_min, sp_max, and
X	 * sp_lstchg information from the pw_age field, which appears
X	 * after the encrypted password.
X	 */
X
X	if (pw->pw_age[0]) {
X		t = (c64i (pw->pw_age[0]) * 7) * SCALE;
X		sp->sp_max = t;
X
X		if (pw->pw_age[1]) {
X			t = (c64i (pw->pw_age[1]) * 7) * SCALE;
X			sp->sp_min = t;
X		} else
X			sp->sp_min = (10000L) * SCALE;
X
X		if (pw->pw_age[1] && pw->pw_age[2]) {
X			t = (a64l (pw->pw_age + 2) * 7) * SCALE;
X			sp->sp_lstchg = t;
X		} else
X			sp->sp_lstchg = time ((time_t *) 0) / (DAY/SCALE);
X	} else {
X		sp->sp_min = 0;
X		sp->sp_max = (10000L * SCALE);
X		sp->sp_lstchg = time ((time_t *) 0) / (DAY/SCALE);
X	}
X#else	/* !ATT_AGE */
X	/*
X	 * BSD does not use the pw_age field and has no aging information
X	 * anywheres.  The default values are used to initialize the
X	 * fields which are in the missing pw_age field;
X	 */
X
X	sp->sp_min = 0;
X	sp->sp_max = (10000L * SCALE);
X	sp->sp_lstchg = time ((time_t *) 0) / (DAY/SCALE);
X#endif	/* ATT_AGE */
X
X	/*
X	 * These fields have no corresponding information in the password
X	 * file.  They are set to uninitialized values.
X	 */
X
X	sp->sp_warn = -1;
X	sp->sp_inact = -1;
X	sp->sp_expire = -1;
X	sp->sp_flag = -1;
X}
X
X/*
X * isexpired - determine if account is expired yet
X *
X *	isexpired calculates the expiration date based on the
X *	password expiration criteria.
X */
X
Xint
Xisexpired (pw, sp)
Xstruct	passwd	*pw;
Xstruct	spwd	*sp;
X{
X	long	clock;
X
X	clock = time ((time_t *) 0) / (DAY/SCALE);
X
X	/*
X	 * Quick and easy - there is an expired account field
X	 * along with an inactive account field.  Do the expired
X	 * one first since it is worse.
X	 */
X
X	if (sp->sp_expire > 0 && sp->sp_expire < clock)
X		return 3;
X
X	if (sp->sp_inact > 0 && sp->sp_lstchg > 0 && sp->sp_max > 0 &&
X			sp->sp_inact + sp->sp_lstchg + sp->sp_max < clock)
X		return 2;
X
X	/*
X	 * The last and max fields must be present for an account
X	 * to have an expired password.  A maximum of >10000 days
X	 * is considered to be infinite.
X	 */
X
X	if (sp->sp_lstchg == -1 ||
X			sp->sp_max == -1 || sp->sp_max >= (10000L*SCALE))
X		return 0;
X
X	/*
X	 * Calculate today's day and the day on which the password
X	 * is going to expire.  If that date has already passed,
X	 * the password has expired.
X	 */
X
X	if (sp->sp_lstchg + sp->sp_max < clock)
X		return 1;
X
X	return 0;
X}
X
X/*
X * expire - force password change if password expired
X *
X *	expire() calls /bin/passwd to change the user's password
X *	if it has expired.
X */
X
Xint
Xexpire (pw, sp)
Xstruct	passwd	*pw;
Xstruct	spwd	*sp;
X{
X	int	status;
X	int	child;
X	int	pid;
X
X	if (! sp)
X		sp = pwd_to_spwd (pw);
X
X	/*
X	 * See if the user's password has expired, and if so
X	 * force them to change their password.
X	 */
X
X	switch (status = isexpired (pw, sp)) {
X		case 0:
X			return 0;
X		case 1:
X			printf ("Your password has expired.");
X			break;
X		case 2:
X			printf ("Your password is inactive.");
X			break;
X		case 3:
X			printf ("Your login has expired.");
X			break;
X	}
X
X	/*
X	 * Setting the maximum valid period to less than the minimum
X	 * valid period means that the minimum period will never
X	 * occur while the password is valid, so the user can never
X	 * change that password.
X	 */
X
X	if (status > 1 || sp->sp_max < sp->sp_min) {
X		puts ("  Contact the system administrator.\n");
X		exit (1);
X	}
X	puts ("  Choose a new one.\n");
X	fflush (stdout);
X
X	/*
X	 * Close all the files so that unauthorized access won't
X	 * occur.  This needs to be done anyway because those files
X	 * might become stale after "passwd" is executed.
X	 */
X
X	endspent ();
X	endpwent ();
X	endsgent ();
X	endgrent ();
X
X	/*
X	 * Execute the /bin/passwd command.  The exit status will be
X	 * examined to see what the result is.  If there are any
X	 * errors the routine will exit.  This forces the user to
X	 * change their password before being able to use the account.
X	 */
X
X	if ((pid = fork ()) == 0) {
X		execl ("/bin/passwd", "passwd", pw->pw_name, (char *) 0);
X		puts ("Can't execute /bin/passwd");
X		exit (errno);
X	} else if (pid == -1) {
X		perror ("passwd");
X		exit (errno);
X	}
X	while ((child = wait (&status)) != pid && child != -1)
X		;
X
X	if (child == pid && status == 0)
X		return 1;
X
X	exit (1);
X}
X
X/*
X * agecheck - see if warning is needed for password expiration
X *
X *	agecheck sees how many days until the user's password is going
X *	to expire and warns the user of the pending password expiration.
X */
X
Xvoid
Xagecheck (pw, sp)
Xstruct	passwd	*pw;
Xstruct	spwd	*sp;
X{
X	long	clock = time ((long *) 0) / (DAY/SCALE);
X	long	remain;
X
X	if (! sp)
X		sp = pwd_to_spwd (pw);
X
X	/*
X	 * The last, max, and warn fields must be supported or the
X	 * warning period cannot be calculated.
X	 */
X
X	if (sp->sp_lstchg == -1 || sp->sp_max == -1 || sp->sp_warn == -1)
X		return;
X
X	if ((remain = (sp->sp_lstchg + sp->sp_max) - clock) <= sp->sp_warn) {
X		remain /= SCALE;
X		if (remain >= 0)
X			printf ("Your password will expire in %d %s.\n",
X				remain, remain == 1 ? "day":"days");
X	}
X}
SHAR_EOF
if test 6179 -ne "`wc -c < 'age.c'`"
then
	echo shar: "error transmitting 'age.c'" '(should have been 6179 characters)'
fi
fi
exit 0
#	End of shell archive
-- 
John F. Haugh II        | Distribution to  | UUCP: ...!cs.utexas.edu!rpp386!jfh
Ma Bell: (512) 255-8251 | GEnie PROHIBITED :-) |  Domain: jfh@rpp386.cactus.org
"If liberals interpreted the 2nd Amendment the same way they interpret the
 rest of the Constitution, gun ownership would be mandatory."

jfh@rpp386.cactus.org (John F Haugh II) (05/16/91)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	passwd.c
#	port.c
#	lmain.c
#	mkpasswd.c
#	sulogin.c
#	pwpack.c
#	dialup.c
#	sulog.c
#	getpass.c
# This archive created: Sun Mar  3 13:27:16 1991
# By:	John F Haugh II (River Parishes Programming, Austin TX)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'passwd.c'" '(17138 characters)'
if test -f 'passwd.c'
then
	echo shar: "will not over-write existing file 'passwd.c'"
else
sed 's/^X//' << \SHAR_EOF > 'passwd.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <sys/types.h>
X#include <time.h>
X#include <stdio.h>
X#include <fcntl.h>
X#include <signal.h>
X#include <syslog.h>
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)passwd.c	3.1	09:00:47	2/8/91";
X#endif
X
X/*
X * Set up some BSD defines so that all the BSD ifdef's are
X * kept right here 
X */
X
X#ifndef	BSD
X#include <string.h>
X#include <memory.h>
X#define	bzero(a,n)	memset(a, 0, n)
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X
X#include "config.h"
X#include "pwd.h"
X#include "lastlog.h"
X#include "shadow.h"
X
X/*
X * Password aging constants
X *
X *	DAY - seconds in a day
X *	WEEK - seconds in a week
X *	SCALE - convert from clock to aging units
X */
X
X#define	DAY	(24L*3600L)
X#define	WEEK	(7L*DAY)
X
X#ifdef	ITI_AGING
X#define	SCALE	(1)
X#else
X#define	SCALE	DAY
X#endif
X
X/*
X * Global variables
X */
X
Xchar	name[32];		/* The user's name */
Xchar	*Prog;			/* Program name */
Xint	amroot;			/* The real UID was 0 */
X
X/*
X * External identifiers
X */
X
Xextern	char	*getpass();
Xextern	char	*pw_encrypt();
Xextern	char	*getlogin();
Xextern	int	optind;		/* Index into argv[] for current option */
Xextern	char	*optarg;	/* Pointer to current option value */
X#ifdef	NDBM
Xextern	int	sp_dbm_mode;
Xextern	int	pw_dbm_mode;
X#endif
X
X/*
X * #defines for messages.  This facilities foreign language conversion
X * since all messages are defined right here.
X */
X
X#define	USAGE		"usage: %s [ -f | -s ] [ name ]\n"
X#define	ADMUSAGE \
X	"       %s [ -x max ] [ -n min ] [ -w warn ] [ -i inact ] name\n"
X#define	ADMUSAGE2 \
X	"       %s { -l | -d | -S } name\n"
X#define	OLDPASS		"Old Password:"
X#define	NEWPASSMSG \
X"Enter the new password (minimum of 5 characters)\n\
XPlease use a combination of upper and lower case letters and numbers.\n"
X#define NEWPASS		"New Password:"
X#define	NEWPASS2	"Re-enter new password:"
X#define	WRONGPWD	"Incorrect password for %s.\n"
X#define	WRONGPWD2	"incorrect password for `%s'\n"
X#define	NOMATCH		"They don't match; try again.\n"
X#define	CANTCHANGE	"The password for %s cannot be changed.\n"
X#define	CANTCHANGE2	"password locked for `%s'\n"
X#define	TOOSOON		"Sorry, the password for %s cannot be changed yet.\n"
X#define	TOOSOON2	"now < sp_min for `%s'\n"
X#define	EXECFAILED	"%s: Cannot execute %s"
X#define	EXECFAILED2	"cannot execute %s\n"
X#define	WHOAREYOU	"%s: Cannot determine you user name.\n"
X#define	UNKUSER		"%s: Unknown user %s\n"
X#define	NOPERM		"You may not change the password for %s.\n"
X#define	NOPERM2		"can't change pwd for `%s'\n"
X#define	UNCHANGED	"The password for %s is unchanged.\n"
X#define	SPWDBUSY	"Cannot lock the password file; try again later.\n"
X#define	SPWDBUSY2	"can't lock /etc/shadow\n"
X#define	OPNERROR	"Cannot open the password file.\n"
X#define	OPNERROR2	"can't open /etc/shadow\n"
X#define	UPDERROR	"Error updating the password entry.\n"
X#define	UPDERROR2	"error updating shadow entry\n"
X#define	DBMERROR	"Error updating the DBM password entry.\n"
X#define	DBMERROR2	"error updating DBM shadow entry.\n"
X#define	NOTROOT		"Cannot change ID to root.\n"
X#define	NOTROOT2	"can't setuid(0).\n"
X#define	CLSERROR	"Cannot commit shadow file changes.\n"
X#define	CLSERROR2	"can't rewrite /etc/shadow.\n"
X#define	UNLKERROR	"Cannot unlock the shadow file.\n"
X#define	UNLKERROR2	"can't unlock /etc/shadow.\n"
X#define	TRYAGAIN	"Try again.\n"
X#define	CHGPASSWD	"changed password for `%s'\n"
X
X/*
X * usage - print command usage and exit
X */
X
Xvoid
Xusage ()
X{
X	fprintf (stderr, USAGE, Prog);
X	if (amroot) {
X		fprintf (stderr, ADMUSAGE, Prog);
X		fprintf (stderr, ADMUSAGE2, Prog);
X	}
X	exit (1);
X}
X
X/*
X * new_password - validate old password and replace with new
X */
X
Xint
Xnew_password (pw, sp)
Xstruct	passwd	*pw;
Xstruct	spwd	*sp;
X{
X	char	*clear;		/* Pointer to clear text */
X	char	*cipher;	/* Pointer to cipher text */
X	char	*cp;		/* Pointer to getpass() response */
X	char	orig[BUFSIZ];	/* Original password */
X	char	pass[BUFSIZ];	/* New password */
X	int	i;		/* Counter for retries */
X
X	/*
X	 * Authenticate the user.  The user will be prompted for their
X	 * own password.
X	 */
X
X	if (! amroot && sp->sp_pwdp[0]) {
X		bzero (orig, sizeof orig);
X
X		if (! (clear = getpass (OLDPASS)))
X			return -1;
X
X		cipher = pw_encrypt (clear, sp->sp_pwdp);
X		if (strcmp (cipher, sp->sp_pwdp) != 0) {
X			sleep (1);
X			fprintf (stderr, WRONGPWD, sp->sp_namp);
X			syslog (LOG_WARN, WRONGPWD2, sp->sp_namp);
X			return -1;
X		}
X		strcpy (orig, clear);
X	}
X
X	/*
X	 * Get the new password.  The user is prompted for the new password
X	 * and has three tries to get it right.  The password will be tested
X	 * for strength, unless it is the root user.  This provides an escape
X	 * for initial login passwords.
X	 */
X
X	printf (NEWPASSMSG);
X	for (i = 0;i < 3;i++) {
X		if (! (cp = getpass (NEWPASS)))
X			return -1;
X		else
X			strcpy (pass, cp);
X
X		if (! amroot && ! obscure (orig, pass)) {
X			printf (TRYAGAIN);
X			continue;
X		}
X		if (! (cp = getpass (NEWPASS2)))
X			return -1;
X
X		if (strcmp (cp, pass))
X			fprintf (stderr, NOMATCH);
X		else
X			break;
X	}
X	if (i == 3)
X		return -1;
X
X	/*
X	 * Encrypt the password.  The new password is encrypted and
X	 * the shadow password structure updated to reflect the change.
X	 */
X
X	sp->sp_pwdp = pw_encrypt (pass, (char *) 0);
X	sp->sp_lstchg = time ((time_t *) 0) / SCALE;
X
X	return 0;
X}
X
X/*
X * check_password - test a password to see if it can be changed
X *
X *	check_password() sees if the invoker has permission to change the
X *	password for the given user.
X */
X
Xvoid
Xcheck_password (pw, sp)
Xstruct	passwd	*pw;
Xstruct	spwd	*sp;
X{
X	time_t	now = time ((time_t *) 0) / SCALE;
X
X	/*
X	 * Root can change any password any time.
X	 */
X
X	if (amroot)
X		return;
X
X	/*
X	 * Expired accounts cannot be changed ever.  Passwords
X	 * which are locked may not be changed.  Passwords where
X	 * min > max may not be changed.  Passwords which have
X	 * been inactive too long cannot be changed.
X	 */
X
X	if ((sp->sp_expire > 0 && now >= sp->sp_expire) ||
X	    (sp->sp_inact >= 0 && sp->sp_max >= 0 &&
X		now >= (sp->sp_lstchg + sp->sp_inact + sp->sp_max)) ||
X			strcmp (sp->sp_pwdp, "!") == 0 ||
X			sp->sp_min > sp->sp_max) {
X		fprintf (stderr, CANTCHANGE, sp->sp_namp);
X		syslog (LOG_WARN, CANTCHANGE2, sp->sp_namp);
X		exit (1);
X	}
X
X	/*
X	 * Passwords may only be changed after sp_min time is up.
X	 */
X
X	if (sp->sp_min >= 0 && now < (sp->sp_lstchg + sp->sp_min)) {
X		fprintf (stderr, TOOSOON, sp->sp_namp);
X		syslog (LOG_WARN, TOOSOON2, sp->sp_namp);
X		exit (1);
X	}
X}
X
X/*
X * pwd_to_spwd - create entries for new spwd structure
X *
X *	pwd_to_spwd() creates a new (struct spwd) containing the
X *	information in the pointed-to (struct passwd).
X */
X
Xvoid
Xpwd_to_spwd (pw, sp)
Xstruct	passwd	*pw;
Xstruct	spwd	*sp;
X{
X	time_t	t;
X
X	/*
X	 * Nice, easy parts first.  The name and passwd map directly
X	 * from the old password structure to the new one.
X	 */
X
X	sp->sp_namp = strdup (pw->pw_name);
X	sp->sp_pwdp = strdup (pw->pw_passwd);
X#ifdef	ATT_AGE
X
X	/*
X	 * AT&T-style password aging maps the sp_min, sp_max, and
X	 * sp_lstchg information from the pw_age field, which appears
X	 * after the encrypted password.
X	 */
X
X	if (pw->pw_age[0]) {
X		t = (c64i (pw->pw_age[0]) * WEEK) / SCALE;
X		sp->sp_max = t;
X
X		if (pw->pw_age[1]) {
X			t = (c64i (pw->pw_age[1]) * WEEK) / SCALE;
X			sp->sp_min = t;
X		} else
X			sp->sp_min = (10000L * DAY) / SCALE;
X
X		if (pw->pw_age[1] && pw->pw_age[2]) {
X			t = (a64l (pw->pw_age + 2) * WEEK) / SCALE;
X			sp->sp_lstchg = t;
X		} else
X			sp->sp_lstchg = time ((time_t *) 0) / SCALE;
X	} else {
X		sp->sp_min = 0;
X		sp->sp_max = (10000L * DAY) / SCALE;
X		sp->sp_lstchg = time ((time_t *) 0) / SCALE;
X	}
X#else
X	/*
X	 * BSD does not use the pw_age field and has no aging information
X	 * anywheres.  The default values are used to initialize the
X	 * fields which are in the missing pw_age field;
X	 */
X
X	sp->sp_min = 0;
X	sp->sp_max = (10000L * DAY) / SCALE;
X	sp->sp_lstchg = time ((time_t *) 0) / SCALE;
X#endif
X
X	/*
X	 * These fields have no corresponding information in the password
X	 * file.  They are set to uninitialized values.
X	 */
X
X	sp->sp_warn = -1;
X	sp->sp_inact = -1;
X	sp->sp_expire = -1;
X	sp->sp_flag = -1;
X}
X
X/*
X * print_status - print current password status
X */
X
Xvoid
Xprint_status (sp)
Xstruct	spwd	*sp;
X{
X	struct	tm	*tm;
X	time_t	time;
X
X	time = sp->sp_lstchg * SCALE;
X	tm = gmtime (&time);
X
X	printf ("%s ", sp->sp_namp);
X	printf ("%s ",
X		sp->sp_pwdp[0] ? (sp->sp_pwdp[0] == '!' ? "L":"P"):"NP");
X	printf ("%02.2d/%02.2d/%02.2d ",
X		tm->tm_mon + 1, tm->tm_mday, tm->tm_year % 100);
X	printf ("%d %d %d %d\n",
X		(sp->sp_min * SCALE) / DAY, (sp->sp_max * SCALE) / DAY,
X		(sp->sp_warn * SCALE) / DAY, (sp->sp_inact * SCALE) / DAY);
X}
X
X/*
X * passwd - change a user's password file information
X *
X *	This command controls the password file and commands which are
X * 	used to modify it.
X *
X *	The valid options are
X *
X *	-l	lock the named account (*)
X *	-d	delete the password for the named account (*)
X *	-x #	set sp_max to # days (*)
X *	-n #	set sp_min to # days (*)
X *	-w #	set sp_warn to # days (*)
X *	-i #	set sp_inact to # days (*)
X *	-S	show password status of named account (*)
X *	-g	execute gpasswd command to interpret flags
X *	-f	execute chfn command to interpret flags
X *	-s	execute chsh command to interpret flags
X *
X *	(*) requires root permission to execute.
X *
X *	All of the time fields are entered in days and converted to the
X * 	appropriate internal format.  For finer resolute the chage
X *	command must be used.
X */
X
Xint
Xmain (argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	char	buf[BUFSIZ];		/* I/O buffer for messages, etc.      */
X	char	*cp;			/* Miscellaneous character pointing   */
X	time_t	min;			/* Minimum days before change         */
X	time_t	max;			/* Maximum days until change          */
X	time_t	warn;			/* Warning days before change         */
X	time_t	inact;			/* Days without change before locked  */
X	int	i;			/* Loop control variable              */
X	int	flag;			/* Current option to process          */
X	int	lflg = 0;		/* -l - lock account option           */
X	int	dflg = 0;		/* -d - delete password option        */
X	int	xflg = 0;		/* -x - set maximum days              */
X	int	nflg = 0;		/* -n - set minimum days              */
X	int	wflg = 0;		/* -w - set warning days              */
X	int	iflg = 0;		/* -i - set inactive days             */
X	int	Sflg = 0;		/* -S - show password status          */
X	struct	passwd	*pw;		/* Password file entry for user       */
X	struct	spwd	*sp;		/* Shadow file entry for user         */
X	struct	spwd	tspwd;		/* New shadow file entry if none      */
X
X	/*
X	 * The program behaves differently when executed by root
X	 * than when executed by a normal user.
X	 */
X
X	amroot = getuid () == 0;
X#ifdef	NDBM
X	sp_dbm_mode = O_RDWR;
X	pw_dbm_mode = O_RDWR;
X#endif
X
X	/*
X	 * Get the program name.  The program name is used as a
X	 * prefix to most error messages.  It is also used as input
X	 * to the openlog() function for error logging.
X	 */
X
X	if (Prog = strrchr (argv[0], '/'))
X		Prog++;
X	else
X		Prog = argv[0];
X
X	openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
X
X	/*
X	 * Start with the flags which cause another command to be
X	 * executed.  The effective UID will be set back to the
X	 * real UID and the new command executed with the flags
X	 */
X
X	if (argc > 1 && argv[1][0] == '-' && strchr ("gfs", argv[1][1])) {
X		setuid (getuid ());
X		switch (argv[1][1]) {
X			case 'g':
X				argv[1] = "gpasswd";
X				execv ("/bin/gpasswd", &argv[1]);
X				break;
X			case 'f':
X				argv[1] = "chfn";
X				execv ("/bin/chfn", &argv[1]);
X				break;
X			case 's':
X				argv[1] = "chsh";
X				execv ("/bin/chsh", &argv[1]);
X				break;
X			default:
X				usage ();
X		}
X		sprintf (buf, EXECFAILED, Prog, argv[1]);
X		perror (buf);
X		syslog (LOG_CRIT, EXECFAILED2, argv[1]);
X		exit (1);
X	}
X
X	/* 
X	 * The remaining arguments will be processed one by one and
X	 * executed by this command.  The name is the last argument
X	 * if it does not begin with a "-", otherwise the name is
X	 * determined from the environment and must agree with the
X	 * real UID.  Also, the UID will be checked for any commands
X	 * which are restricted to root only.
X	 */
X
X	while ((flag = getopt (argc, argv, "ldx:n:w:i:S")) != EOF) {
X		switch (flag) {
X			case 'x':
X				max = strtol (optarg, &cp, 10);
X				if (*cp || getuid ())
X					usage ();
X
X				xflg++;
X				break;
X			case 'n':
X				min = strtol (optarg, &cp, 10);
X				if (*cp || getuid ())
X					usage ();
X
X				nflg++;
X				break;
X			case 'w':
X				warn = strtol (optarg, &cp, 10);
X				if (*cp || getuid ())
X					usage ();
X
X				wflg++;
X				break;
X			case 'i':
X				inact = strtol (optarg, &cp, 10);
X				if (*cp || getuid ())
X					usage ();
X
X				iflg++;
X				break;
X			case 'S':
X				if (getuid ())
X					usage ();
X
X				Sflg++;
X				break;
X			case 'd':
X				dflg++;
X				break;
X			case 'l':
X				lflg++;
X				break;
X			default:
X				usage ();
X		}
X	}
X
X	/*
X	 * If any of the flags were given, a user name must be supplied
X	 * on the command line.  Only an unadorned command line doesn't
X	 * require the user's name be given.  Also, on -x, -n, -m, and
X	 * -i may appear with each other.  -d, -l and -S must appear alone.
X	 */
X
X	if ((dflg || lflg || xflg || nflg ||
X				wflg || iflg || Sflg) && optind >= argc)
X		usage ();
X
X	if ((dflg + lflg + (xflg || nflg || wflg || iflg) + Sflg) > 1)
X		usage ();
X
X	/*
X	 * Now I have to get the user name.  The name will be gotten 
X	 * from the command line if possible.  Otherwise it is figured
X	 * out from the environment.
X	 */
X
X	if (optind < argc) {
X		strncpy (name, argv[optind], sizeof name);
X		name[sizeof name - 1] = '\0';
X	} else if (cp = getlogin ()) {
X		strncpy (name, cp, sizeof name);
X		name[sizeof name - 1] = '\0';
X	} else {
X		fprintf (stderr, WHOAREYOU, Prog);
X		exit (1);
X	}
X
X	/*
X	 * Now I have a name, let's see if the UID for the name
X	 * matches the current real UID.
X	 */
X
X	if (! (pw = getpwnam (name))) {
X		fprintf (stderr, UNKUSER, Prog, name);
X		exit (1);
X	}
X	if (! amroot && pw->pw_uid != getuid ()) {
X		fprintf (stderr, NOPERM, name);
X		syslog (LOG_WARN, NOPERM2, name);
X		exit (1);
X	}
X
X	/*
X	 * The user name is valid, so let's get the shadow file
X	 * entry.
X	 */
X
X	if (! (sp = getspnam (name)))
X		pwd_to_spwd (pw, sp = &tspwd);
X
X	/*
X	 * Save the shadow entry off to the side so it doesn't
X	 * get changed by any of the following code.
X	 */
X
X	if (sp != &tspwd) {
X		tspwd = *sp;
X		sp = &tspwd;
X	}
X	tspwd.sp_namp = strdup (sp->sp_namp);
X	tspwd.sp_pwdp = strdup (sp->sp_pwdp);
X
X	if (Sflg) {
X		print_status (sp);
X		exit (0);
X	}
X
X	/*
X	 * If there are no other flags, just change the password.
X	 */
X
X	if (! (dflg || lflg || xflg || nflg || wflg || iflg)) {
X
X		/*
X		 * See if the user is permitted to change the password.
X		 * Otherwise, go ahead and set a new password.
X		 */
X
X		check_password (pw, sp);
X
X		if (new_password (pw, sp)) {
X			fprintf (stderr, UNCHANGED, name);
X			exit (1);
X		}
X	}
X
X	/*
X	 * The other options are incredibly simple.  Just modify the
X	 * field in the shadow file entry.
X	 */
X
X	if (dflg)			/* Set password to blank */
X		sp->sp_pwdp = "";
X
X	if (lflg)			/* Set password to "locked" value */
X		sp->sp_pwdp = "!";
X
X	if (xflg)
X		sp->sp_max = (max * DAY) / SCALE;
X
X	if (nflg)
X		sp->sp_min = (min * DAY) / SCALE;
X
X	if (wflg)
X		sp->sp_warn = (warn * DAY) / SCALE;
X
X	if (iflg)
X		sp->sp_inact = (inact * DAY) / SCALE;
X
X	/*
X	 * Before going any further, raise the ulimit to prevent
X	 * colliding into a lowered ulimit, and set the real UID
X	 * to root to protect against unexpected signals.  Any
X	 * keyboard signals are set to be ignored.
X	 */
X
X	ulimit (2, 30000);
X	if (setuid (0)) {
X		fprintf (stderr, NOTROOT);
X		syslog (LOG_ERR, NOTROOT2);
X		exit (1);
X	}
X	signal (SIGHUP, SIG_IGN);
X	signal (SIGINT, SIG_IGN);
X	signal (SIGQUIT, SIG_IGN);
X#ifdef	SIGTSTP
X	signal (SIGTSTP, SIG_IGN);
X#endif
X
X	/*
X	 * The shadow entry is now ready to be committed back to
X	 * the shadow file.  Get a lock on the file and open it.
X	 */
X
X	for (i = 0;i < 30;i++)
X		if (spw_lock ())
X			break;
X
X	if (i == 30) {
X		fprintf (stderr, SPWDBUSY);
X		syslog (LOG_WARN, SPWDBUSY2);
X		exit (1);
X	}
X	if (! spw_open (O_RDWR)) {
X		fprintf (stderr, OPNERROR);
X		syslog (LOG_ERR, OPNERROR2);
X		(void) spw_unlock ();
X		exit (1);
X	}
X
X	/*
X	 * Update the shadow file entry.  If there is a DBM file,
X	 * update that entry as well.
X	 */
X
X	if (! spw_update (sp)) {
X		fprintf (stderr, UPDERROR);
X		syslog (LOG_ERR, UPDERROR2);
X		(void) spw_unlock ();
X		exit (1);
X	}
X#ifdef	NDBM
X	if (access ("/etc/shadow.pag", 0) == 0 && ! sp_dbm_update (sp)) {
X		fprintf (stderr, DBMERROR);
X		syslog (LOG_ERR, DBMERROR2);
X		(void) spw_unlock ();
X		exit (1);
X	}
X#endif
X
X	/*
X	 * Changes have all been made, so commit them and unlock the
X	 * file.
X	 */
X
X	if (! spw_close ()) {
X		fprintf (stderr, CLSERROR);
X		syslog (LOG_ERR, CLSERROR2);
X		(void) spw_unlock ();
X		exit (1);
X	}
X	if (! spw_unlock ()) {
X		fprintf (stderr, UNLKERROR);
X		syslog (LOG_ERR, UNLKERROR2);
X		exit (1);
X	}
X	syslog (LOG_INFO, CHGPASSWD, name);
X	exit (0);
X}
SHAR_EOF
if test 17138 -ne "`wc -c < 'passwd.c'`"
then
	echo shar: "error transmitting 'passwd.c'" '(should have been 17138 characters)'
fi
fi
echo shar: "extracting 'port.c'" '(8978 characters)'
if test -f 'port.c'
then
	echo shar: "will not over-write existing file 'port.c'"
else
sed 's/^X//' << \SHAR_EOF > 'port.c'
X/*
X * Copyright 1989, 1990, 1991, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <stdio.h>
X#include <time.h>
X#include <sys/types.h>
X#include <ctype.h>
X#include <errno.h>
X#ifndef	BSD
X#include <string.h>
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X#include "port.h"
X
X#ifndef	lint
Xstatic	char	_sccsid[] = "@(#)port.c	3.1	08:59:32	2/8/91";
X#endif
X
Xextern	int	errno;
X
Xstatic	FILE	*ports;
X
X/*
X * setttyent - open /etc/porttime file or rewind
X *
X *	the /etc/porttime file is rewound if already open, or
X *	opened for reading.
X */
X
Xvoid
Xsetttyent ()
X{
X	if (ports)
X		rewind (ports);
X	else 
X		ports = fopen (PORTS, "r");
X}
X
X/*
X * endttyent - close the /etc/porttime file
X *
X *	the /etc/porttime file is closed and the ports variable set
X *	to NULL to indicate that the /etc/porttime file is no longer
X *	open.
X */
X
Xvoid
Xendttyent ()
X{
X	if (ports)
X		fclose (ports);
X
X	ports = (FILE *) 0;
X}
X
X/*
X * getttyent - read a single entry from /etc/porttime
X *
X *	the next line in /etc/porttime is converted to a (struct port)
X *	and a pointer to a static (struct port) is returned to the
X *	invoker.  NULL is returned on either EOF or error.  errno is
X *	set to EINVAL on error to distinguish the two conditions.
X */
X
Xstruct port *
Xgetttyent ()
X{
X	static	struct	port	port;	/* static struct to point to         */
X	static	char	buf[BUFSIZ];	/* some space for stuff              */
X	static	char	*ttys[PORT_TTY+1]; /* some pointers to tty names     */
X	static	char	*users[PORT_IDS+1]; /* some pointers to user ids     */
X	static	struct	pt_time	times[PORT_TIMES+1]; /* time ranges          */
X	char	*cp;			/* pointer into line                 */
X	int	time;			/* scratch time of day               */
X	int	i, j;
X	int	saveerr = errno;	/* errno value on entry              */
X
X	/*
X	 * If the ports file is not open, open the file.  Do not rewind
X	 * since we want to search from the beginning each time.
X	 */
X
X	if (! ports)
X		setttyent ();
X
X	if (! ports) {
X		errno = saveerr;
X		return 0;
X	}
X
X	/*
X	 * Common point for beginning a new line -
X	 *
X	 *	- read a line, and NUL terminate
X	 *	- skip lines which begin with '#'
X	 *	- parse off the tty names
X	 *	- parse off a list of user names
X	 *	- parse off a list of days and times
X	 */
X
Xagain:
X
X	/*
X	 * Get the next line and remove the last character, which
X	 * is a '\n'.  Lines which begin with '#' are all ignored.
X	 */
X
X	if (fgets (buf, BUFSIZ, ports) == 0) {
X		errno = saveerr;
X		return 0;
X	}
X	if (buf[0] == '#')
X		goto again;
X
X	/*
X	 * Get the name of the TTY device.  It is the first colon
X	 * separated field, and is the name of the TTY with no
X	 * leading "/dev".  The entry '*' is used to specify all
X	 * TTY devices.
X	 */
X
X	buf[strlen (buf) - 1] = 0;
X
X	port.pt_names = ttys;
X	for (cp = buf, j = 0;j < PORT_TTY;j++) {
X		port.pt_names[j] = cp;
X		while (*cp && *cp != ':' && *cp != ',')
X			cp++;
X
X		if (! *cp)
X			goto again;	/* line format error */
X
X		if (*cp == ':')		/* end of tty name list */
X			break;
X
X		if (*cp == ',')		/* end of current tty name */
X			*cp++ = '\0';
X	}
X	*cp++ = 0;
X	port.pt_names[j + 1] = (char *) 0;
X
X	/*
X	 * Get the list of user names.  It is the second colon
X	 * separated field, and is a comma separated list of user
X	 * names.  The entry '*' is used to specify all usernames.
X	 * The last entry in the list is a (char *) 0 pointer.
X	 */
X
X	if (*cp != ':') {
X		port.pt_users = users;
X		port.pt_users[0] = cp;
X
X		for (j = 1;*cp != ':';cp++) {
X			if (*cp == ',' && j < PORT_IDS) {
X				*cp++ = 0;
X				port.pt_users[j++] = cp;
X			}
X		}
X		port.pt_users[j] = 0;
X	} else
X		port.pt_users = 0;
X
X	if (*cp != ':')
X		goto again;
X
X	*cp++ = 0;
X
X	/*
X	 * Get the list of valid times.  The times field is the third
X	 * colon separated field and is a list of days of the week and
X	 * times during which this port may be used by this user.  The
X	 * valid days are 'Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', and 'Sa'.
X	 *
X	 * In addition, the value 'Al' represents all 7 days, and 'Wk'
X	 * represents the 5 weekdays.
X	 *
X	 * Times are given as HHMM-HHMM.  The ending time may be before
X	 * the starting time.  Days are presumed to wrap at 0000.
X	 */
X
X	if (*cp == '\0') {
X		port.pt_times = 0;
X		return &port;
X	}
X
X	port.pt_times = times;
X
X	/*
X	 * Get the next comma separated entry
X	 */
X
X	for (j = 0;*cp && j < PORT_TIMES;j++) {
X
X		/*
X		 * Start off with no days of the week
X		 */
X
X		port.pt_times[j].t_days = 0;
X
X		/*
X		 * Check each two letter sequence to see if it is
X		 * one of the abbreviations for the days of the
X		 * week or the other two values.
X		 */
X
X		for (i = 0;cp[i] && cp[i + 1] && isalpha (cp[i]);i += 2) {
X			switch ((cp[i] << 8) | (cp[i + 1])) {
X				case ('S' << 8) | 'u':
X					port.pt_times[j].t_days |= 01;
X					break;
X				case ('M' << 8) | 'o':
X					port.pt_times[j].t_days |= 02;
X					break;
X				case ('T' << 8) | 'u':
X					port.pt_times[j].t_days |= 04;
X					break;
X				case ('W' << 8) | 'e':
X					port.pt_times[j].t_days |= 010;
X					break;
X				case ('T' << 8) | 'h':
X					port.pt_times[j].t_days |= 020;
X					break;
X				case ('F' << 8) | 'r':
X					port.pt_times[j].t_days |= 040;
X					break;
X				case ('S' << 8) | 'a':
X					port.pt_times[j].t_days |= 0100;
X					break;
X				case ('W' << 8) | 'k':
X					port.pt_times[j].t_days |= 076;
X					break;
X				case ('A' << 8) | 'l':
X					port.pt_times[j].t_days |= 0177;
X					break;
X				default:
X					errno = EINVAL;
X					return 0;
X			}
X		}
X
X		/*
X		 * The default is 'Al' if no days were seen.
X		 */
X
X		if (i == 0)
X			port.pt_times[j].t_days = 0177;
X
X		/*
X		 * The start and end times are separated from each
X		 * other by a '-'.  The times are four digit numbers
X		 * representing the times of day.
X		 */
X
X		for (time = 0;cp[i] && isdigit (cp[i]);i++)
X			time = time * 10 + cp[i] - '0';
X
X		if (cp[i] != '-' || time > 2400 || time % 100 > 59)
X			goto again;
X		port.pt_times[j].t_start = time;
X		cp = cp + i + 1;
X
X		for (time = i = 0;cp[i] && isdigit (cp[i]);i++)
X			time = time * 10 + cp[i] - '0';
X
X		if ((cp[i] != ',' && cp[i]) || time > 2400 || time % 100 > 59)
X			goto again;
X
X		port.pt_times[j].t_end = time;
X		cp = cp + i + 1;
X	}
X
X	/*
X	 * The end of the list is indicated by a pair of -1's for the
X	 * start and end times.
X	 */
X
X	port.pt_times[j].t_start = port.pt_times[j].t_end = -1;
X
X	return &port;
X}
X
X/*
X * getttyuser - get ports information for user and tty
X *
X *	getttyuser() searches the ports file for an entry with a TTY
X *	and user field both of which match the supplied TTY and
X *	user name.  The file is searched from the beginning, so the
X *	entries are treated as an ordered list.
X */
X
Xstruct port *
Xgetttyuser (tty, user)
Xchar	*tty;
Xchar	*user;
X{
X	int	i, j;
X	struct	port	*port;
X
X	setttyent ();
X
X	while (port = getttyent ()) {
X		if (port->pt_names == 0 || port->pt_users == 0)
X			continue;
X
X		for (i = 0;port->pt_names[i];i++)
X			if (strcmp (port->pt_names[i], tty) == 0 ||
X					strcmp (port->pt_names[i], "*") == 0)
X				break;
X
X		if (port->pt_names[i] == 0)
X			continue;
X
X		for (j = 0;port->pt_users[j];j++)
X			if (strcmp (user, port->pt_users[j]) == 0 ||
X					strcmp (port->pt_users[j], "*") == 0)
X				break;
X
X		if (port->pt_users[j] != 0)
X			break;
X	}
X	endttyent ();
X	return port;
X}
X
X/*
X * isttytime - tell if a given user may login at a particular time
X *
X *	isttytime searches the ports file for an entry which matches
X *	the user name and TTY given.
X */
X
Xint
Xisttytime (id, port, clock)
Xchar	*id;
Xchar	*port;
Xlong	clock;
X{
X	int	i;
X	int	time;
X	struct	port	*pp;
X	struct	tm	*tm,
X			*localtime();
X
X	/*
X	 * Try to find a matching entry for this user.  Default to
X	 * letting the user in - there are pleny of ways to have an
X	 * entry to match all users.
X	 */
X
X	if (! (pp = getttyuser (port, id)))
X		return 1;
X
X	/*
X	 * The entry is there, but has not time entries - don't
X	 * ever let them login.
X	 */
X
X	if (pp->pt_times == 0)
X		return 0;
X
X	/*
X	 * The current time is converted to HHMM format for
X	 * comparision against the time values in the TTY entry.
X	 */
X
X	tm = localtime (&clock);
X	time = tm->tm_hour * 100 + tm->tm_min;
X
X	/*
X	 * Each time entry is compared against the current
X	 * time.  For entries with the start after the end time,
X	 * the comparision is made so that the time is between
X	 * midnight and either the start or end time.
X	 */
X
X	for (i = 0;pp->pt_times[i].t_start != -1;i++) {
X		if (! (pp->pt_times[i].t_days & PORT_DAY(tm->tm_wday)))
X			continue;
X
X		if (pp->pt_times[i].t_start <= pp->pt_times[i].t_end) {
X			if (time >= pp->pt_times[i].t_start &&
X					time <= pp->pt_times[i].t_end)
X				return 1;
X		} else {
X			if (time >= pp->pt_times[i].t_start ||
X					time <= pp->pt_times[i].t_end)
X				return 1;
X		}
X	}
X
X	/*
X	 * No matching time entry was found, user shouldn't
X	 * be let in right now.
X	 */
X
X	return 0;
X}
SHAR_EOF
if test 8978 -ne "`wc -c < 'port.c'`"
then
	echo shar: "error transmitting 'port.c'" '(should have been 8978 characters)'
fi
fi
echo shar: "extracting 'lmain.c'" '(13146 characters)'
if test -f 'lmain.c'
then
	echo shar: "will not over-write existing file 'lmain.c'"
else
sed 's/^X//' << \SHAR_EOF > 'lmain.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <stdio.h>
X#include "pwd.h"
X#include <utmp.h>
X#include <time.h>
X#include <signal.h>
X#include <syslog.h>
X#ifndef	BSD
X#include <string.h>
X#include <memory.h>
X#define	bzero(a,n)	memset(a, 0, n);
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X#ifndef	BSD
X#include <termio.h>
X#else
X#include <sgtty.h>
X#endif
X#include "config.h"
X#include "lastlog.h"
X#include "faillog.h"
X#include "shadow.h"
X
X#ifndef	lint
Xstatic	char	sccsid[] = "%W%	%U%	%G%";
X#endif
X
X#ifndef	ERASECHAR
X#define	ERASECHAR	'\b'		/* backspace */
X#endif
X
X#ifndef	KILLCHAR
X#define	KILLCHAR	'\025'		/* control U */
X#endif
X
X#ifdef	UT_HOST
Xchar	host[BUFSIZ];
X#endif
X#ifdef	HUSHLOGIN
Xint	hushed;
X#endif
X
Xstruct	passwd	pwent;
Xstruct	utmp	utent;
Xstruct	lastlog	lastlog;
Xint	pflg;
Xint	rflg;
Xint	fflg;
Xint	hflg;
X#ifndef	BSD
Xstruct	termio	termio;
X#endif
X
X#ifndef	MAXENV
X#define	MAXENV	64
X#endif
X
X/*
X * Global variables.
X */
X
Xchar	*newenvp[MAXENV];
Xchar	*Prog;
Xint	newenvc = 0;
Xint	maxenv = MAXENV;
X
X/*
X * External identifiers.
X */
X
Xextern	char	*getenv ();
Xextern	char	*getpass ();
Xextern	void	checkutmp ();
Xextern	void	addenv ();
Xextern	void	setenv ();
Xextern	unsigned alarm ();
Xextern	void	login ();
Xextern	void	entry ();
Xextern	void	setutmp ();
Xextern	void	subsystem ();
Xextern	void	log ();
Xextern	void	setup ();
Xextern	int	expire ();
Xextern	void	motd ();
Xextern	void	mailcheck ();
Xextern	void	shell ();
Xextern	long	a64l ();
Xextern	int	c64i ();
Xextern	int	optind;
Xextern	char	*optarg;
Xextern	char	**environ;
X
X#ifdef	TZ
XFILE	*tzfile;
Xchar	tzbuf[32] = TZ;
X#endif
X
X#ifndef	ALARM
X#define	ALARM	60
X#endif
X
X#ifndef	RETRIES
X#define	RETRIES	3
X#endif
X
X#ifdef	FAILLOG
Xstruct	faillog	faillog;
X#endif
X
X#ifdef	FTMP
Xstruct	utmp	failent;
X#endif
X
X#ifndef	UMASK
X#define	UMASK	0
X#endif
X
X#ifndef	ULIMIT
X#define	ULIMIT	(1L<<21)
X#endif
X
X#define	NO_SHADOW	"no shadow password for `%s' on `%s'\n"
X#define	BAD_PASSWD	"invalid password for `%s' on `%s'\n"
X#define	BAD_DIALUP	"invalid dialup password for `%s' on `%s'\n"
X#define	BAD_TIME	"invalid login time for `%s' on `%s'\n"
X#define	BAD_ROOT_LOGIN	"ILLEGAL ROOT LOGIN ON TTY `%s'\n"
X#define	ROOT_LOGIN	"ROOT LOGIN ON TTY `%s'\n"
X#define	FAILURE_CNT	"exceeded failure limit for `%s' on `%s'\n"
X#define	NOT_A_TTY	"not a tty\n"
X#define	NOT_ROOT	"-r or -f flag and not ROOT on `%s'\n"
X
X/*
X * usage - print login command usage and exit
X */
X
Xusage ()
X{
X	fprintf (stderr, "usage: login [ -p ] [ name ]\n");
X#ifdef	UT_HOST
X	fprintf (stderr, "       login -r name\n");
X	fprintf (stderr, "       login [ -p ] -f name [ -h host ]\n");
X#else
X	fprintf (stderr, "       login [ -p ] -f name\n");
X#endif
X	exit (1);
X}
X
X/*
X * login - create a new login session for a user
X *
X *	login is typically called by getty as the second step of a
X *	new user session.  getty is responsible for setting the line
X *	characteristics to a reasonable set of values and getting
X *	the name of the user to be logged in.  login may also be
X *	called to create a new user session on a pty for a variety
X *	of reasons, such as X servers or network logins.
X *
X *	the flags which login supports are
X *	
X *	-p - preserve the environment
X *	-r - perform autologin protocol for rlogin
X *	-f - do not perform authentication, user is preauthenticated
X *	-h - the name of the remote host
X */
X
Xint
Xmain (argc, argv, envp)
Xint	argc;
Xchar	**argv;
Xchar	**envp;
X{
X	char	name[32];
X	char	pass[32];
X	char	hush[BUFSIZ];
X	char	tty[BUFSIZ];
X	int	retries;
X	int	failed;
X	int	flag;
X	int	subroot = 0;
X	char	*cp;
X	struct	passwd	*pwd;
X	struct	spwd	*spwd;
X	struct	spwd	*getspnam();
X#ifdef	CONSOLE
X	int	conflag;
X	char	console[BUFSIZ];
X	FILE	*fp;
X	struct	stat	statbuf;
X#endif	/* CONSOLE */
X
X	/*
X	 * Some quick initialization.
X	 */
X
X	name[0] = '\0';
X
X	/*
X	 * Get the utmp file entry and get the tty name from it.  The
X	 * current process ID must match the process ID in the utmp
X	 * file if there are no additional flags on the command line.
X	 */
X
X	checkutmp (argc > 1 && argv[1][0] != '-');
X	strncpy (tty, utent.ut_line, sizeof tty);
X	tty[sizeof tty - 1] = '\0';
X
X	if (Prog = strrchr (argv[0], '/'))
X		Prog++;
X	else
X		Prog = argv[0];
X
X#ifdef	UT_HOST
X	while ((flag = getopt (argc, argv, "pr:f:h:")) != EOF)
X#else
X	while ((flag = getopt (argc, argv, "pf:")) != EOF)
X#endif
X	{
X		switch (flag) {
X			case 'p': pflg++;
X				break;
X			case 'f': fflg++;
X				strncpy (name, optarg, sizeof name);
X				break;
X#ifdef	UT_HOST
X			case 'r': rflg++;
X				strncpy (name, optarg, sizeof name);
X				break;
X			case 'h': hflg++;
X				strncpy (host, optarg, sizeof host);
X				strncpy (utmp.ut_host, host,
X							sizeof utmp.ut_host);
X				break;
X#endif
X			default:
X				usage ();
X		}
X	}
X
X	/*
X	 * The -r option is not valid with any other flags
X	 */
X
X	if (rflg && (hflg || fflg || pflg))
X		usage ();
X
X	openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
X
X	/*
X	 * The -r and -f flags both require the real UID to be
X	 * zero.  No authentication may be required for these
X	 * flags, so the user must already be root.
X	 */
X
X	if ((rflg || fflg) && getuid () != 0)
X		exit (1);		/* only root can use -r or -f */
X
X	if (! isatty (0) || ! isatty (1) || ! isatty (2))
X		exit (1);		/* must be a terminal */
X
X#ifndef	BSD
X	(void) ioctl (0, TCGETA, &termio); /* get terminal characteristics */
X
X	/*
X	 * Add your favorite terminal modes here ...
X	 */
X
X	termio.c_lflag |= ISIG;
X
X	termio.c_cc[VERASE] = ERASECHAR;
X	termio.c_cc[VKILL] = KILLCHAR;
X	(void) ioctl (0, TCSETAF, &termio); /* set erase and kill characters */
X#endif	/* !BSD */
X	umask (UMASK);			/* set the default umask */
X	ulimit (2, (long) ULIMIT);	/* set the default ulimit */
X
X	/*
X	 * The entire environment will be preserved if the -p flag
X	 * is used.
X	 */
X
X	if (pflg)
X		while (*envp)		/* add inherited environment, */
X			addenv (*envp++); /* some variables change later */
X
X#ifdef	TZ
X	if (! pflg) {
X		if (tzbuf[0] == '/') {
X			if ((tzfile = fopen (tzbuf, "r")) != (FILE *) 0) {
X				if (fgets (tzbuf, sizeof tzbuf, tzfile)) {
X					tzbuf[strlen (tzbuf) - 1] = '\0';
X					addenv (tzbuf);
X				}
X				fclose (tzfile);
X			}
X		} else {
X			addenv (tzbuf);
X		}
X	}
X#endif	/* TZ */
X#ifdef	HZ
X	if (! pflg)
X		addenv (HZ);		/* set the default $HZ, if one */
X#endif	/* HZ */
X	if (optind < argc) {	/* now set command line variables */
X		if (optind + 1 < argc)
X			setenv (argc - optind - 1, &argv[optind + 1]);
X
X		(void) strncpy (name, argv[optind], sizeof name);
X	}
Xtop:
X	(void) alarm (ALARM);		/* only allow ALARM sec. for login */
X
X	retries = RETRIES;
X	while (1) {	/* repeatedly get login/password pairs */
X		pass[0] = '\0';
X
X		if (! name[0]) {	/* need to get a login id */
X			if (subroot)
X				exit (1);
X
X			rflg = fflg = 0;
X			login (name);
X			continue;
X		}
X		if (! (pwd = getpwnam (name)))
X			pwent.pw_name = (char *) 0;
X		else
X			pwent = *pwd;
X
X		if (pwent.pw_name) {
X			if (! (spwd = getspnam (name)))
X				syslog (LOG_WARN, NO_SHADOW, name, tty);
X			else
X				pwent.pw_passwd = spwd->sp_pwdp;
X			failed = 0;	/* hasn't failed validation yet */
X		} else
X			failed = 1;	/* will never pass validation */
X
X		/*
X		 * The -r and -f flags provide a name which has already
X		 * been authenticated by some server.
X		 */
X
X		if (pwent.pw_name && (rflg || fflg))
X			goto have_name;
X
X	/*
X	 * Get the user's password.  One will only be prompted for
X	 * if the pw_passwd (or sp_passwd) field is non-blank.  It
X	 * will then be checked against the password entry, along
X	 * with other options which prevent logins.
X	 */
X		cp = 0;
X		if ((! pwent.pw_name || (strlen (pwent.pw_passwd) > 0))
X				&& ! (cp = getpass ("Password:")))
X			continue;
X
X		if (cp)
X			strncpy (pass, cp, sizeof pass);
X
X		if (! valid (pass, &pwent)) { /* check encrypted passwords */
X			syslog (LOG_WARN, BAD_PASSWD, name, tty);
X			failed = 1;
X		}
X		bzero (pass, sizeof pass);
X
X		/*
X		 * This is the point where password-authenticated users
X		 * wind up.  If you reach this far, your password has
X		 * been authenticated and so on.
X		 */
X
Xhave_name:
X#ifdef	DIALUP
X		alarm (30);
X		if (pwent.pw_name && ! dialcheck (tty,
X				pwent.pw_shell[0] ? pwent.pw_shell:"/bin/sh")) {
X			syslog (LOG_WARN, BAD_DIALUP, name, tty);
X			failed = 1;
X		}
X#endif	/* DIALUP */
X#ifdef	PORTTIME
X		if (pwent.pw_name &&
X			! isttytime (pwent.pw_name, tty, time ((time_t *) 0))) {
X			syslog (LOG_WARN, BAD_TIME, name, tty);
X			failed = 1;
X		}
X#endif	/* PORTTIME */
X#ifdef	CONSOLE
X		if (! failed && pwent.pw_name &&
X			pwent.pw_uid == 0 && stat (CONSOLE, &statbuf) == 0) {
X			if ((statbuf.st_mode & S_IFMT) == S_IFREG) {
X				fp = fopen (CONSOLE, "r");
X				while (fp && fgets (console, BUFSIZ, fp)
X						== console) {
X					console[strlen (console) - 1] = '\0';
X					if (! strcmp (console, tty))
X						break;
X				}
X				if (! fp || feof (fp))
X					failed = 1;
X
X				fclose (fp);
X			} else {
X				if (strcmp (CONSOLE, tty))
X					failed = 1;
X			}
X			if (failed)
X				syslog (LOG_CRIT, BAD_ROOT_LOGIN, tty);
X		}
X#endif	/* CONSOLE */
X#ifdef	FAILLOG
X		if (pwent.pw_name &&
X			! failcheck (pwent.pw_uid, &faillog, failed)) {
X			syslog (LOG_CRIT, FAILURE_CNT, name, tty);
X			failed = 1;
X		}
X#endif	/* FAILLOG */
X		if (! failed)
X			break;
X
X		puts ("Login incorrect");
X		if (rflg || fflg)
X			exit (1);
X#ifdef	FAILLOG
X		if (pwent.pw_name)	/* don't log non-existent users */
X			failure (pwent.pw_uid, tty, &faillog);
X#endif	/* FAILLOG */
X#ifdef FTMP
X		failent = utent;
X
X		if (pwent.pw_name)
X			strncpy (failent.ut_name,
X				pwent.pw_name, sizeof failent.ut_name);
X		else
X#ifdef	UNKNOWNS
X			strcpy (failent.ut_name, name);
X#else	/* !UNKNOWNS */
X			strcpy (failent.ut_name, "UNKNOWN");
X#endif	/* UNKNOWNS */
X		time (&failent.ut_time);
X		failent.ut_type = USER_PROCESS;
X
X		failtmp (&failent);
X#endif /* FTMP */
X		if (--retries <= 0)	/* only allow so many failures */
X			exit (1);
X
X		bzero (name, sizeof name);
X		bzero (pass, sizeof pass);
X	}
X	(void) alarm (0);		/* turn off alarm clock */
X#ifdef	NOLOGINS
X	/*
X	 * Check to see if system is turned off for non-root users.
X	 * This would be useful to prevent users from logging in
X	 * during system maintenance.
X	 */
X
X	if (pwent.pw_uid != 0 && access (NOLOGINS, 0) == 0) {
X		FILE	*fp;
X		int	c;
X
X		if (fp = fopen (NOLOGINS, "r")) {
X			while ((c = getc (fp)) != EOF) {
X				if (c == '\n')
X					putchar ('\r');
X
X				putchar (c);
X			}
X			fflush (stdout);
X			fclose (fp);
X		} else
X			printf ("\r\nSystem closed for routine maintenance\n");
X
X		exit (0);
X	}
X#endif	/* NOLOGINS */
X	environ = newenvp;		/* make new environment active */
X
X	if (getenv ("IFS"))		/* don't export user IFS ... */
X		addenv ("IFS= \t\n");	/* ... instead, set a safe IFS */
X
X	setutmp (name, tty);		/* make entry in utmp & wtmp files */
X	if (pwent.pw_shell[0] == '*') {	/* subsystem root */
X		subsystem (&pwent);	/* figure out what to execute */
X		subroot++;		/* say i was here again */
X		endpwent ();		/* close all of the file which were */
X		endgrent ();		/* open in the original rooted file */
X		endspent ();		/* system.  they will be re-opened */
X		endsgent ();		/* in the new rooted file system */
X		goto top;		/* go do all this all over again */
X	}
X
X#ifdef	LASTLOG
X	log ();				/* give last login and log this one */
X#endif	/* LASTLOG */
X	setup (&pwent);			/* set UID, GID, HOME, etc ... */
X#ifdef	AGING
X	if (spwd) {			/* check for age of password */
X		if (expire (&pwent, spwd)) {
X			spwd = getspnam (name);
X			pwd = getpwnam (name);
X			pwent = *pwd;
X		}
X	}
X#ifdef	ATT_AGE
X	else if (pwent.pw_age && pwent.pw_age[0]) {
X		if (expire (&pwent, (void *) 0)) {
X			pwd = getpwnam (name);
X			pwent = *pwd;
X		}
X	}
X#endif	/* ATT_AGE */
X#endif	/* AGING */
X#ifdef	HUSHLOGIN
X	sprintf (hush, "%s/.hushlogin", pwent.pw_dir, 0);
X	hushed = access (hush, 0) == 0;
X#endif	/* HUSHLOGIN */
X#ifdef	MOTD
X	if (! hushed)
X		motd ();		/* print the message of the day */
X#endif
X#ifdef	FAILLOG
X	if (faillog.fail_cnt != 0)
X		failprint (pwent.pw_uid, &faillog);
X#endif	/* FAILLOG */
X#ifdef	LASTLOG
X	if (lastlog.ll_time != 0 && ! hushed)
X		printf ("Last login: %.19s on %s\n",
X			ctime (&lastlog.ll_time), lastlog.ll_line);
X#endif	/* LASTLOG */
X#ifdef	AGING
X	if (! hushed)
X		agecheck (&pwent, spwd);
X#endif	/* AGING */
X#ifdef	MAILCHECK
X	if (! hushed)
X		mailcheck ();		/* report on the status of mail */
X#endif	/* MAILCHECK */
X#ifdef	TTYTYPE
X	if (! pflg)
X		ttytype (tty);
X#endif	/* TTYTYPE */
X	signal (SIGINT, SIG_DFL);	/* default interrupt signal */
X	signal (SIGQUIT, SIG_DFL);	/* default quit signal */
X	signal (SIGTERM, SIG_DFL);	/* default terminate signal */
X	signal (SIGALRM, SIG_DFL);	/* default alarm signal */
X
X	endpwent ();			/* stop access to password file */
X	endgrent ();			/* stop access to group file */
X	endspent ();			/* stop access to shadow passwd file */
X	endsgent ();			/* stop access to shadow group file */
X
X	if (pwent.pw_uid == 0)
X		syslog (LOG_INFO, ROOT_LOGIN, tty);
X
X	shell (pwent.pw_shell, (char *) 0); /* exec the shell finally. */
X	/*NOTREACHED*/
X}
SHAR_EOF
if test 13146 -ne "`wc -c < 'lmain.c'`"
then
	echo shar: "error transmitting 'lmain.c'" '(should have been 13146 characters)'
fi
fi
echo shar: "extracting 'mkpasswd.c'" '(8207 characters)'
if test -f 'mkpasswd.c'
then
	echo shar: "will not over-write existing file 'mkpasswd.c'"
else
sed 's/^X//' << \SHAR_EOF > 'mkpasswd.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include "config.h"
X#include <stdio.h>
X#include <fcntl.h>
X#include "pwd.h"
X#ifdef	BSD
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#else
X#include <string.h>
X#endif
X
X#if !defined(DBM) && !defined(NDBM)
XWhat you are trying to do?  You have to have a DBM of some kind ...
X#endif
X
X#ifdef	DBM
X#include <dbm.h>
X#endif
X#ifdef	NDBM
X#include <ndbm.h>
X#include <grp.h>
X#include "shadow.h"
X
XDBM	*pw_dbm;
XDBM	*gr_dbm;
XDBM	*sp_dbm;
XDBM	*sgr_dbm;
X#endif
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)mkpasswd.c	3.4	11:29:08	12/19/90";
Xstatic	char	copyright[] = "Copyright 1990, John F. Haugh II";
X#endif
X
Xchar	*CANT_OPEN =	"%s: cannot open file %s\n";
Xchar	*CANT_OVERWRITE = "%s: cannot overwrite file %s\n";
Xchar	*CANT_CREATE =	"%s: cannot create %s\n";
Xchar	*DBM_OPEN_ERR =	"%s: cannot open DBM files for %s\n";
Xchar	*PARSE_ERR =	"%s: error parsing line\n\"%s\"\n";
Xchar	*LINE_TOO_LONG = "%s: the beginning with \"%.16s ...\" is too long\n";
Xchar	*ADD_REC =	"adding record for name \"%s\"\n";
Xchar	*ADD_REC_ERR =	"%s: error adding record for \"%s\"\n";
Xchar	*INFO =		"added %d entries, longest was %d\n";
X#ifdef	NDBM
Xchar	*USAGE =	"Usage: %s [ -vf ] [ -p|g|sp|sg ] file\n";
X#else
Xchar	*USAGE =	"Usage: %s [ -vf ] file\n";
X#endif
X
Xchar	*Progname;
Xint	vflg = 0;
Xint	fflg = 0;
X#ifdef	NDBM
Xint	gflg = 0;
Xint	sflg = 0;
Xint	pflg = 0;
X#endif
X
Xvoid	usage();
X
Xextern	char	*malloc();
Xextern	struct	passwd	*sgetpwent();
X#ifdef	NDBM
Xextern	struct	group	*sgetgrent();
Xextern	struct	spwd	*sgetspent();
Xextern	struct	sgrp	*sgetsgent();
X#endif
X
X/*
X * mkpasswd - create DBM files for /etc/passwd-like input file
X *
X * mkpasswd takes an an argument the name of a file in /etc/passwd format
X * and creates a DBM file keyed by user ID and name.  The output files have
X * the same name as the input file, with .dir and .pag appended.
X *
X * if NDBM is defined this command will also create look-aside files for
X * /etc/group, /etc/shadow, and /etc/gshadow.
X */
X
Xint
Xmain (argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	extern	int	optind;
X	extern	char	*optarg;
X	FILE	*fp;			/* File pointer for input file        */
X	char	*file;			/* Name of input file                 */
X	char	*dir;			/* Name of .dir file                  */
X	char	*pag;			/* Name of .pag file                  */
X	char	*cp;			/* Temporary character pointer        */
X	int	flag;			/* Flag for command line option       */
X	int	fd;			/* File descriptor of open DBM file   */
X	int	cnt = 0;		/* Number of entries in database      */
X	int	longest = 0;		/* Longest entry in database          */
X	int	len;			/* Length of input line               */
X	int	errors = 0;		/* Count of errors processing file    */
X	char	buf[BUFSIZ*8];		/* Input line from file               */
X	struct	passwd	*passwd;	/* Pointer to password file entry     */
X#ifdef	NDBM
X	struct	group	*group;		/* Pointer to group file entry        */
X	struct	spwd	*shadow;	/* Pointer to shadow passwd entry     */
X	struct	sgrp	*gshadow;	/* Pointer to shadow group entry      */
X	DBM	*dbm;			/* Pointer to new NDBM files          */
X#endif
X
X	/*
X	 * Figure out what my name is.  I will use this later ...
X	 */
X
X	if (Progname = strrchr (argv[0], '/'))
X		Progname++;
X	else
X		Progname = argv[0];
X
X	/*
X	 * Figure out what the flags might be ...
X	 */
X
X#ifdef	NDBM
X	while ((flag = getopt (argc, argv, "fvpgs")) != EOF)
X#else
X	while ((flag = getopt (argc, argv, "fv")) != EOF)
X#endif
X	{
X		switch (flag) {
X			case 'v':
X				vflg++;
X				break;
X			case 'f':
X				fflg++;
X				break;
X#ifdef	NDBM
X			case 'g':
X				gflg++;
X				if (pflg)
X					usage ();
X
X				break;
X			case 's':
X				sflg++;
X				break;
X			case 'p':
X				pflg++;
X				if (gflg)
X					usage ();
X
X				break;
X#endif
X			default:
X				usage ();
X		}
X	}
X
X	/*
X	 * Backwards compatibility fix for -p flag ...
X	 */
X
X	if (! sflg && ! gflg)
X		pflg++;
X
X	/*
X	 * The last and only remaining argument must be the file name
X	 */
X
X	if (argc - 1 != optind)
X		usage ();
X
X	file = argv[optind];
X
X	if (! (fp = fopen (file, "r"))) {
X		fprintf (stderr, CANT_OPEN, Progname, file);
X		exit (1);
X	}
X
X	/*
X	 * Make the filenames for the two DBM files.
X	 */
X
X	dir = malloc (strlen (file) + 5);	/* space for .dir file */
X	strcat (strcpy (dir, file), ".dir");
X
X	pag = malloc (strlen (file) + 5);	/* space for .pag file */
X	strcat (strcpy (pag, file), ".pag");
X
X	/*
X	 * Remove existing files if requested.
X	 */
X
X	if (fflg) {
X		(void) unlink (dir);
X		(void) unlink (pag);
X	}
X
X	/*
X	 * Create the two DBM files - it is an error for these files
X	 * to have existed already.
X	 */
X
X	if (access (dir, 0) == 0) {
X		fprintf (stderr, CANT_OVERWRITE, Progname, dir);
X		exit (1);
X	}
X	if (access (pag, 0) == 0) {
X		fprintf (stderr, CANT_OVERWRITE, Progname, pag);
X		exit (1);
X	}
X
X#ifdef	NDBM
X	if (sflg)
X		umask (077);
X	else
X#endif
X	umask (0);
X#ifdef	DBM
X	if ((fd = open (dir, O_RDWR|O_CREAT|O_EXCL, 0644)) == -1) {
X		fprintf (stderr, CANT_CREATE, Progname, dir);
X		exit (1);
X	} else
X		close (fd);
X
X	if ((fd = open (pag, O_RDWR|O_CREAT|O_EXCL, 0644)) == -1) {
X		fprintf (stderr, CANT_CREATE, Progname, pag);
X		unlink (dir);
X		exit (1);
X	} else
X		close (fd);
X#endif
X
X	/*
X	 * Now the DBM database gets initialized
X	 */
X
X#ifdef	DBM
X	if (dbminit (file) == -1) {
X		fprintf (stderr, DBM_OPEN_ERR, Progname, file);
X		exit (1);
X	}
X#endif
X#ifdef	NDBM
X	if (! (dbm = dbm_open (file, O_RDWR|O_CREAT, 0644))) {
X		fprintf (stderr, DBM_OPEN_ERR, Progname, file);
X		exit (1);
X	}
X	if (gflg) {
X		if (sflg)
X			sgr_dbm = dbm;
X		else
X			gr_dbm = dbm;
X	} else {
X		if (sflg)
X			sp_dbm = dbm;
X		else
X			pw_dbm = dbm;
X	}
X#endif
X
X	/*
X	 * Read every line in the password file and convert it into a
X	 * data structure to be put in the DBM database files.
X	 */
X
X#ifdef	NDBM
X	while (fgetsx (buf, BUFSIZ, fp) != NULL)
X#else
X	while (fgets (buf, BUFSIZ, fp) != NULL)
X#endif
X	{
X
X		/*
X		 * Get the next line and strip off the trailing newline
X		 * character.
X		 */
X
X		buf[sizeof buf - 1] = '\0';
X		if (! (cp = strchr (buf, '\n'))) {
X			fprintf (stderr, LINE_TOO_LONG, Progname, buf);
X			exit (1);
X		}
X		*cp = '\0';
X		len = strlen (buf);
X
X		/*
X		 * Parse the password file line into a (struct passwd).
X		 * Erroneous lines cause error messages, but that's
X		 * all.  YP lines are ignored completely.
X		 */
X
X		if (buf[0] == '-' || buf[0] == '+')
X			continue;
X
X#ifdef	DBM
X		if (! (passwd = sgetpwent (buf)))
X#endif
X#ifdef	NDBM
X		if (! (((! sflg && pflg) && (passwd = sgetpwent (buf)))
X			|| ((sflg && pflg) && (shadow = sgetspent (buf)))
X			|| ((! sflg && gflg) && (group = sgetgrent (buf)))
X			|| ((sflg && gflg) && (gshadow = sgetsgent (buf)))))
X#endif
X		{
X			fprintf (stderr, PARSE_ERR, Progname, buf);
X			errors++;
X			continue;
X		}
X#ifdef	DBM
X		if (vflg)
X			printf (ADD_REC, passwd->pw_name);
X
X		if (! pw_dbm_update (passwd))
X			fprintf (stderr, ADD_REC_ERR,
X				Progname, passwd->pw_name);
X#endif
X#ifdef	NDBM
X		if (vflg) {
X			if (!sflg && pflg) printf (ADD_REC, passwd->pw_name);
X			if (sflg && pflg) printf (ADD_REC, shadow->sp_namp);
X			if (!sflg && gflg) printf (ADD_REC, group->gr_name);
X			if (sflg && gflg) printf (ADD_REC, gshadow->sg_name);
X		}
X		if (! sflg && pflg && ! pw_dbm_update (passwd))
X			fprintf (stderr, ADD_REC_ERR,
X				Progname, passwd->pw_name);
X
X		if (sflg && pflg && ! sp_dbm_update (shadow))
X			fprintf (stderr, ADD_REC_ERR,
X				Progname, shadow->sp_namp);
X
X		if (! sflg && gflg && ! gr_dbm_update (group))
X			fprintf (stderr, ADD_REC_ERR,
X				Progname, group->gr_name);
X
X		if (sflg && gflg && ! sgr_dbm_update (gshadow))
X			fprintf (stderr, ADD_REC_ERR,
X				Progname, gshadow->sg_name);
X#endif
X
X		/*
X		 * Update the longest record and record count
X		 */
X
X		if (len > longest)
X			longest = len;
X		cnt++;
X	}
X
X	/*
X	 * Tell the user how things went ...
X	 */
X
X	if (vflg)
X		printf (INFO, cnt, longest);
X
X	exit (errors);
X	/*NOTREACHED*/
X}
X
X/*
X * usage - print error message and exit
X */
X
Xvoid
Xusage ()
X{
X	fprintf (stderr, USAGE, Progname);
X	exit (1);
X	/*NOTREACHED*/
X}
SHAR_EOF
if test 8207 -ne "`wc -c < 'mkpasswd.c'`"
then
	echo shar: "error transmitting 'mkpasswd.c'" '(should have been 8207 characters)'
fi
fi
echo shar: "extracting 'sulogin.c'" '(3501 characters)'
if test -f 'sulogin.c'
then
	echo shar: "will not over-write existing file 'sulogin.c'"
else
sed 's/^X//' << \SHAR_EOF > 'sulogin.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <sys/types.h>
X#include <stdio.h>
X#include "pwd.h"
X#include <utmp.h>
X#ifdef	BSD
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#else
X#include <string.h>
X#include <memory.h>
X#endif
X#include "config.h"
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)sulogin.c	3.3	12:31:35	12/12/90";
X#endif
X
Xchar	name[BUFSIZ];
Xchar	pass[BUFSIZ];
Xchar	home[BUFSIZ];
Xchar	prog[BUFSIZ];
Xchar	mail[BUFSIZ];
X
Xstruct	passwd	pwent;
Xstruct	utmp	utent;
X
X#ifdef	TZ
XFILE	*tzfile;
Xchar	tzbuf[BUFSIZ] = TZ;
X#endif
X
X#ifndef	MAXENV
X#define	MAXENV	64
X#endif
X
Xchar	*newenvp[MAXENV];
Xint	newenvc = 0;
Xint	maxenv = MAXENV;
Xextern	char	**environ;
X
X#ifndef	ALARM
X#define	ALARM	60
X#endif
X
X#ifndef	RETRIES
X#define	RETRIES	3
X#endif
X
Xint	main (argc, argv, envp)
Xint	argc;
Xchar	**argv;
Xchar	**envp;
X{
X	char	*getenv ();
X	char	*ttyname ();
X	char	*cp;
X
X	if (access (PWDFILE, 0) == -1) { /* must be a password file! */
X		printf ("No password file\n");
X		exit (1);
X	}
X#ifdef	NDEBUG
X	if (getppid () != 1)		/* parent must be INIT */
X		exit (1);
X#endif
X	if (! isatty (0) || ! isatty (1) || ! isatty (2))
X		exit (1);		/* must be a terminal */
X
X	while (*envp)			/* add inherited environment, */
X		addenv (*envp++);	/* some variables change later */
X
X#ifdef	TZ
X	if (tzbuf[0] == '/') {
X		if ((tzfile = fopen (tzbuf, "r")) != (FILE *) 0) {
X			if (fgets (tzbuf, sizeof tzbuf, tzfile)) {
X				tzbuf[strlen (tzbuf) - 1] = '\0';
X				addenv (tzbuf);
X			}
X			fclose (tzfile);
X		}
X	} else {
X		addenv (tzbuf);
X	}
X#endif	/* TZ */
X#ifdef	HZ
X	addenv (HZ);			/* set the default $HZ, if one */
X#endif	/* HZ */
X	(void) strcpy (name, "root");	/* KLUDGE!!! */
X
X	alarm (ALARM);		/* only wait so long ... */
X	while (1) {		/* repeatedly get login/password pairs */
X		entry (name, &pwent);	/* get entry from password file */
X		if (pwent.pw_name == (char *) 0) {
X			printf ("No password entry for 'root'\n");
X			exit (1);
X		}
X
X	/*
X	 * Here we prompt for the root password, or if no password is
X	 * given we just exit.
X	 */
X
X					/* get a password for root */
X		if (! (cp = getpass ("Type control-d for normal startup,\n\
X(or give root password for system maintenance):")))
X			exit (0);
X		else
X			strcpy (pass, cp);
X
X		if (valid (pass, &pwent)) /* check encrypted passwords ... */
X			break;		/* ... encrypted passwords matched */
X
X		puts ("Login incorrect");
X	}
X	alarm (0);
X	environ = newenvp;		/* make new environment active */
X
X	puts ("Entering System Maintenance Mode");
X
X	/*
X	 * Normally there would be a utmp entry for login to mung on
X	 * to get the tty name, date, etc. from.  We don't need all that
X	 * stuff because we won't update the utmp or wtmp files.  BUT!,
X	 * we do need the tty name so we can set the permissions and
X	 * ownership.
X	 */
X
X	if (cp = ttyname (0)) {		/* found entry in /dev/ */
X		if (strrchr (cp, '/') != (char *) 0)
X			strcpy (utent.ut_line, strrchr (cp, '/') + 1);
X		else
X			strcpy (utent.ut_line, cp);
X	}
X	if (getenv ("IFS"))		/* don't export user IFS ... */
X		addenv ("IFS= \t\n");	/* ... instead, set a safe IFS */
X
X	setup (&pwent);			/* set UID, GID, HOME, etc ... */
X
X	shell (pwent.pw_shell, (char *) 0); /* exec the shell finally. */
X	/*NOTREACHED*/
X}
SHAR_EOF
if test 3501 -ne "`wc -c < 'sulogin.c'`"
then
	echo shar: "error transmitting 'sulogin.c'" '(should have been 3501 characters)'
fi
fi
echo shar: "extracting 'pwpack.c'" '(2994 characters)'
if test -f 'pwpack.c'
then
	echo shar: "will not over-write existing file 'pwpack.c'"
else
sed 's/^X//' << \SHAR_EOF > 'pwpack.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <stdio.h>
X#include "pwd.h"
X#ifdef	BSD
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#else
X#include <string.h>
X#endif
X#include "config.h"
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)pwpack.c	3.3	12:31:23	12/12/90";
X#endif
X
X/*
X * pw_pack - convert a (struct pwd) to a packed record
X */
X
Xint
Xpw_pack (passwd, buf)
Xstruct	passwd	*passwd;
Xchar	*buf;
X{
X	char	*cp;
X
X	cp = buf;
X	strcpy (cp, passwd->pw_name);
X	cp += strlen (cp) + 1;
X
X	strcpy (cp, passwd->pw_passwd);
X#ifdef	ATT_AGE
X	if (passwd->pw_age[0]) {
X		*cp++ = ',';
X		strcat (cp, passwd->pw_age);
X	}
X#endif
X	cp += strlen (cp) + 1;
X
X	memcpy (cp, (void *) &passwd->pw_uid, sizeof passwd->pw_uid);
X	cp += sizeof passwd->pw_uid;
X
X	memcpy (cp, (void *) &passwd->pw_gid, sizeof passwd->pw_gid);
X	cp += sizeof passwd->pw_gid;
X#ifdef	BSD_QUOTAS
X	memcpy (cp, (void *) &passwd->pw_quota, sizeof passwd->pw_quota);
X	cp += sizeof passwd->pw_quota;
X#endif
X#ifdef	ATT_COMMENT
X	if (passwd->pw_comment) {
X		strcpy (cp, passwd->pw_comment);
X		cp += strlen (cp) + 1;
X	} else
X		*cp++ = '\0';
X#endif
X	strcpy (cp, passwd->pw_gecos);
X	cp += strlen (cp) + 1;
X
X	strcpy (cp, passwd->pw_dir);
X	cp += strlen (cp) + 1;
X
X	strcpy (cp, passwd->pw_shell);
X		cp += strlen (cp) + 1;
X
X	return cp - buf;
X}
X
X/*
X * pw_unpack - convert a packed (struct pwd) record to a (struct pwd)
X */
X
Xint
Xpw_unpack (buf, len, passwd)
Xchar	*buf;
Xint	len;
Xstruct	passwd	*passwd;
X{
X	char	*org = buf;
X	char	*cp;
X
X	memset ((void *) passwd, 0, sizeof *passwd);
X
X	passwd->pw_name = buf;
X	buf += strlen (buf) + 1;
X	if (buf - org > len)
X		return -1;
X
X	passwd->pw_passwd = buf;
X	buf += strlen (buf) + 1;
X	if (buf - org > len)
X		return -1;
X
X#ifdef	ATT_AGE
X	if (cp = strchr (passwd->pw_passwd, ',')) {
X		*cp++ = '\0';
X		passwd->pw_age = cp;
X	} else
X		passwd->pw_age = "";
X#endif
X
X	memcpy ((void *) &passwd->pw_uid, (void *) buf, sizeof passwd->pw_uid);
X	buf += sizeof passwd->pw_uid;
X	if (buf - org > len)
X		return -1;
X
X	memcpy ((void *) &passwd->pw_gid, (void *) buf, sizeof passwd->pw_gid);
X	buf += sizeof passwd->pw_gid;
X	if (buf - org > len)
X		return -1;
X
X#ifdef	BSD_QUOTAS
X	memcpy ((void *) &passwd->pw_quota, (void *) buf,
X		sizeof passwd->pw_quota);
X	buf += sizeof passwd->pw_quota;
X	if (buf - org > len)
X		return -1;
X#endif
X#ifdef	ATT_COMMENT
X	passwd->pw_comment = buf;
X	buf += strlen (buf) + 1;
X	if (buf - org > len)
X		return -1;
X#endif
X	passwd->pw_gecos = buf;
X	buf += strlen (buf) + 1;
X	if (buf - org > len)
X		return -1;
X
X	passwd->pw_dir = buf;
X	buf += strlen (buf) + 1;
X	if (buf - org > len)
X		return -1;
X
X	passwd->pw_shell = buf;
X	buf += strlen (buf) + 1;
X	if (buf - org > len)
X		return -1;
X
X	return 0;
X}
SHAR_EOF
if test 2994 -ne "`wc -c < 'pwpack.c'`"
then
	echo shar: "error transmitting 'pwpack.c'" '(should have been 2994 characters)'
fi
fi
echo shar: "extracting 'dialup.c'" '(2631 characters)'
if test -f 'dialup.c'
then
	echo shar: "will not over-write existing file 'dialup.c'"
else
sed 's/^X//' << \SHAR_EOF > 'dialup.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <stdio.h>
X#ifndef	BSD
X#include <string.h>
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X#include "dialup.h"
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)dialup.c	3.3	19:44:20	12/10/90";
X#endif
X
Xstatic	FILE	*dialpwd;
X
Xvoid
Xsetduent ()
X{
X	if (dialpwd)
X		rewind (dialpwd);
X	else
X		dialpwd = fopen (DIALPWD, "r");
X}
X
Xvoid
Xendduent ()
X{
X	if (dialpwd)
X		fclose (dialpwd);
X
X	dialpwd = (FILE *) 0;
X}
X
Xstruct dialup *
Xfgetduent (fp)
XFILE	*fp;
X{
X	static	struct	dialup	dialup;	/* static structure to point to */
X	static	char	shell[64];	/* some space for a login shell */
X	static	char	passwd[16];	/* some space for dialup password */
X	char	buf[BUFSIZ];
X	char	*cp;
X
X	if (! fp)
X		return 0;
X
X	if (! fp || feof (fp))
X		return ((struct dialup *) 0);
X
X	while (fgets (buf, BUFSIZ, fp) == buf && buf[0] == '#')
X		;
X
X	if (feof (fp))
X		return ((struct dialup *) 0);
X
X	cp = strchr (buf, ':');
X	if (cp - buf > sizeof shell)	/* something is fishy ... */
X		return ((struct dialup *) 0);
X
X	(void) strncpy (shell, buf, cp - buf);
X	shell[cp - buf] = '\0';
X
X	if (strlen (cp + 1) > sizeof passwd) /* something is REALLY fishy */
X		return ((struct dialup *) 0);
X
X	(void) strcpy (passwd, cp + 1);
X	passwd[strlen (passwd) - 1] = '\0';
X	if (cp = strchr (passwd, ':'))
X		*cp = '\0';
X
X	dialup.du_shell = shell;
X	dialup.du_passwd = passwd;
X
X	return (&dialup);
X}
X
Xstruct dialup *
Xgetduent ()
X{
X	if (! dialpwd)
X		setduent ();
X
X	return fgetduent (dialpwd);
X}
X
Xstruct	dialup	*getdushell (shell)
Xchar	*shell;
X{
X	struct	dialup	*dialup;
X
X	while (dialup = getduent ()) {
X		if (strcmp (shell, dialup->du_shell) == 0)
X			return (dialup);
X
X		if (strcmp (dialup->du_shell, "*") == 0)
X			return (dialup);
X	}
X	return ((struct dialup *) 0);
X}
X
Xint	isadialup (tty)
Xchar	*tty;
X{
X	FILE	*fp;
X	char	buf[BUFSIZ];
X	int	dialup = 0;
X
X	if (! (fp = fopen (DIALUPS, "r")))
X		return (0);
X
X	while (fgets (buf, BUFSIZ, fp) == buf) {
X		if (buf[0] == '#')
X			continue;
X
X		buf[strlen (buf) - 1] = '\0';
X
X		if (strcmp (buf, tty) == 0) {
X			dialup = 1;
X			break;
X		}
X	}
X	fclose (fp);
X
X	return (dialup);
X}
X
Xint
Xputduent (dial, fp)
Xstruct	dialup	*dial;
XFILE	*fp;
X{
X	if (! fp || ! dial)
X		return -1;
X
X	if (fprintf (fp, "%s:%s\n", dial->du_shell, dial->du_passwd) == EOF)
X		return -1;
X
X	return 0;
X}
SHAR_EOF
if test 2631 -ne "`wc -c < 'dialup.c'`"
then
	echo shar: "error transmitting 'dialup.c'" '(should have been 2631 characters)'
fi
fi
echo shar: "extracting 'sulog.c'" '(1168 characters)'
if test -f 'sulog.c'
then
	echo shar: "will not over-write existing file 'sulog.c'"
else
sed 's/^X//' << \SHAR_EOF > 'sulog.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X#include <sys/types.h>
X#include <stdio.h>
X#include <time.h>
X#ifndef	BSD
X#include <string.h>
X#include <memory.h>
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X#include "config.h"
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)sulog.c	3.1	09:22:38	11/21/90";
X#endif
X
Xextern	char	name[];
Xextern	char	oldname[];
X
Xtime_t	time ();
X
Xvoid	sulog (success)
Xint	success;
X{
X#ifdef	SULOG
X	char	*tty;
X	char	*cp;
X	char	*ttyname ();
X	time_t	clock;
X	struct	tm	*tm;
X	struct	tm	*localtime ();
X	FILE	*fp;
X
X	if ((fp = fopen (SULOG, "a+")) == (FILE *) 0)
X		return;			/* can't open or create logfile */
X
X	(void) time (&clock);
X	tm = localtime (&clock);
X
X	if (isatty (0) && (cp = ttyname (0))) {
X		if (tty = strrchr (cp, '/'))
X			tty++;
X		else
X			tty = cp;
X	} else
X		tty = "???";
X
X	(void) fprintf (fp, "SU %.02d/%0.2d %.02d:%.02d %c %.6s %s-%s\n",
X		tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
X		success ? '+':'-', tty, oldname, name);
X
X	fflush (fp);
X	fclose (fp);
X#endif
X}
SHAR_EOF
if test 1168 -ne "`wc -c < 'sulog.c'`"
then
	echo shar: "error transmitting 'sulog.c'" '(should have been 1168 characters)'
fi
fi
echo shar: "extracting 'getpass.c'" '(3390 characters)'
if test -f 'getpass.c'
then
	echo shar: "will not over-write existing file 'getpass.c'"
else
sed 's/^X//' << \SHAR_EOF > 'getpass.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <sys/signal.h>
X#include <stdio.h>
X#include "config.h"
X
X#ifdef	BSD
X#include <sgtty.h>
X#include <strings.h>
X#else
X#include <termio.h>
X#include <string.h>
X#endif
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)getpass.c	3.2	08:19:03	2/5/91";
X#endif
X
X/*
X * limits.h may be kind enough to specify the length of a prompted
X * for password.
X */
X
X#if defined(__STDC__) || defined(_POSIX_SOURCE)
X#include <limits.h>
X#endif
X
X/*
X * This is really a giant mess.  On the one hand, it would be nice
X * if PASS_MAX were real big so that DOUBLESIZE isn't needed.  But
X * if it is defined we must honor it because some idiot might use
X * this in a routine expecting some standard behavior.
X */
X
X#ifndef	PASS_MAX
X#ifdef	DOUBLESIZE
X#define	PASS_MAX	16
X#else
X#define	PASS_MAX	8
X#endif
X#endif
X
X#ifdef	BSD
X#define	STTY(fd,termio)	stty(fd, termio)
X#define	GTTY(fd,termio) gtty(fd, termio)
X#define	TERMIO	struct	sgttyb
X#define	INDEX	index
X#else
X#define	STTY(fd,termio) ioctl(fd, TCSETA, termio)
X#define	GTTY(fd,termio) ioctl(fd, TCGETA, termio)
X#define	TERMIO	struct	termio
X#define	INDEX	strchr
X#endif
X
Xstatic	int	sig_caught;
X
Xstatic void
Xsig_catch ()
X{
X	sig_caught = 1;
X}
X
Xchar *
Xgetpass (prompt)
Xchar	*prompt;
X{
X	static	char	input[PASS_MAX+1];
X	char	*return_value = 0;
X	char	*cp;
X	FILE	*fp;
X	int	tty_opened = 0;
X	void	(*old_signal)();
X	TERMIO	new_modes;
X	TERMIO	old_modes;
X
X	/*
X	 * set a flag so the SIGINT signal can be re-sent if it
X	 * is caught
X	 */
X
X	sig_caught = 0;
X
X	/*
X	 * if /dev/tty can't be opened, getpass() needs to read
X	 * from stdin instead.
X	 */
X
X	if ((fp = fopen ("/dev/tty", "r")) == 0) {
X		fp = stdin;
X		setbuf (fp, (char *) 0);
X	} else {
X		tty_opened = 1;
X	}
X
X	/*
X	 * the current tty modes must be saved so they can be
X	 * restored later on.  echo will be turned off, except
X	 * for the newline character (BSD has to punt on this)
X	 */
X
X	if (GTTY (fileno (fp), &new_modes))
X		return 0;
X
X	old_modes = new_modes;
X	old_signal = signal (SIGINT, sig_catch);
X
X#ifdef	BSD
X	new_modes.sg_flags &= ~ECHO ;
X#else
X	new_modes.c_lflag &= ~(ECHO|ECHOE|ECHOK);
X	new_modes.c_lflag |= ECHONL;
X#endif
X
X	if (STTY (fileno (fp), &new_modes))
X		goto out;
X
X	/*
X	 * the prompt is output, and the response read without
X	 * echoing.  the trailing newline must be removed.  if
X	 * the fgets() returns an error, a NULL pointer is
X	 * returned.
X	 */
X
X	if (fputs (prompt, stdout) == EOF)
X		goto out;
X
X	if (fgets (input, sizeof input, fp) == input) {
X		if (cp = INDEX (input, '\n'))
X			*cp = '\0';
X		else
X			input[sizeof input - 1] = '\0';
X
X		return_value = input;
X#ifdef	BSD
X		putc ('\n', stdout);
X#endif
X	}
Xout:
X	/*
X	 * the old SIGINT handler is restored after the tty
X	 * modes.  then /dev/tty is closed if it was opened in
X	 * the beginning.  finally, if a signal was caught it
X	 * is sent to this process for normal processing.
X	 */
X
X	if (STTY (fileno (fp), &old_modes))
X		return_value = 0;
X
X	signal (SIGINT, old_signal);
X
X	if (tty_opened)
X		fclose (fp);
X
X	if (sig_caught) {
X		kill (getpid (), SIGINT);
X		return_value = 0;
X	}
X	return return_value;
X}
SHAR_EOF
if test 3390 -ne "`wc -c < 'getpass.c'`"
then
	echo shar: "error transmitting 'getpass.c'" '(should have been 3390 characters)'
fi
fi
exit 0
#	End of shell archive
-- 
John F. Haugh II        | Distribution to  | UUCP: ...!cs.utexas.edu!rpp386!jfh
Ma Bell: (512) 255-8251 | GEnie PROHIBITED :-) |  Domain: jfh@rpp386.cactus.org
"If liberals interpreted the 2nd Amendment the same way they interpret the
 rest of the Constitution, gun ownership would be mandatory."

jfh@rpp386.cactus.org (John F Haugh II) (05/16/91)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	chfn.c
#	chsh.c
#	smain.c
#	faillog.c
#	pwconv.c
#	failure.c
#	utmp.c
#	shadow.c
# This archive created: Sun Mar  3 13:27:20 1991
# By:	John F Haugh II (River Parishes Programming, Austin TX)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'chfn.c'" '(11610 characters)'
if test -f 'chfn.c'
then
	echo shar: "will not over-write existing file 'chfn.c'"
else
sed 's/^X//' << \SHAR_EOF > 'chfn.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <sys/types.h>
X#include <syslog.h>
X#include <stdio.h>
X#include <fcntl.h>
X#include <signal.h>
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)chfn.c	3.4	11:23:40	12/19/90";
X#endif
X
X/*
X * Set up some BSD defines so that all the BSD ifdef's are
X * kept right here 
X */
X
X#ifndef	BSD
X#include <string.h>
X#include <memory.h>
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X
X#include "config.h"
X#include "pwd.h"
X
X/*
X * Global variables.
X */
X
Xchar	*Progname;
Xchar	user[BUFSIZ];
Xchar	fullnm[BUFSIZ];
Xchar	roomno[BUFSIZ];
Xchar	workph[BUFSIZ];
Xchar	homeph[BUFSIZ];
Xchar	slop[BUFSIZ];
Xint	amroot;
X
X/*
X * External identifiers
X */
X
Xextern	int	optind;
Xextern	char	*optarg;
Xextern	struct	passwd	*getpwuid ();
Xextern	struct	passwd	*getpwnam ();
Xextern	char	*getlogin ();
X#ifdef	NDBM
Xextern	int	pw_dbm_mode;
X#endif
X
X/*
X * #defines for messages.  This facilities foreign language conversion
X * since all messages are defined right here.
X */
X
X#define	USAGE \
X"Usage: %s [ -f full_name ] [ -r room_no ] [ -w work_ph ] [ -h home_ph ]\n"
X#define	ADMUSAGE \
X"Usage: %s [ -f full_name ] [ -r room_no ] [ -w work_ph ]\n\
X       [ -h home_ph ] [ -o other ] [ user ]\n"
X#define	NOPERM		"%s: Permission denied.\n"
X#define	WHOAREYOU	"%s: Cannot determine you user name.\n"
X#define	INVALID_NAME	"%s: invalid name: \"%s\"\n"
X#define	INVALID_ROOM	"%s: invalid room number: \"%s\"\n"
X#define	INVALID_WORKPH	"%s: invalid work phone: \"%s\"\n"
X#define	INVALID_HOMEPH	"%s: invalid home phone: \"%s\"\n"
X#define	INVALID_OTHER	"%s: \"%s\" contains illegal characters\n"
X#define	INVALID_FIELDS	"%s: fields too long\n"
X#define	NEWFIELDSMSG	"Changing the user information for %s\n"
X#define	NEWFIELDSMSG2 \
X"Enter the new value, or press return for the default\n\n"
X#define	NEWNAME		"Full Name"
X#define	NEWROOM		"Room Number"
X#define	NEWWORKPHONE	"Work Phone"
X#define	NEWHOMEPHONE	"Home Phone"
X#define	NEWSLOP		"Other"
X#define	UNKUSER		"%s: Unknown user %s\n"
X#define	PWDBUSY		"Cannot lock the password file; try again later.\n"
X#define	PWDBUSY2	"can't lock /etc/passwd\n"
X#define	OPNERROR	"Cannot open the password file.\n"
X#define	OPNERROR2	"can't open /etc/passwd\n"
X#define	UPDERROR	"Error updating the password entry.\n"
X#define	UPDERROR2	"error updating passwd entry\n"
X#define	DBMERROR	"Error updating the DBM password entry.\n"
X#define	DBMERROR2	"error updating DBM passwd entry.\n"
X#define	NOTROOT		"Cannot change ID to root.\n"
X#define	NOTROOT2	"can't setuid(0).\n"
X#define	CLSERROR	"Cannot commit password file changes.\n"
X#define	CLSERROR2	"can't rewrite /etc/passwd.\n"
X#define	UNLKERROR	"Cannot unlock the password file.\n"
X#define	UNLKERROR2	"can't unlock /etc/passwd.\n"
X#define	CHGGECOS	"changed user `%s' information.\n"
X
X/*
X * usage - print command line syntax and exit
X */
X
Xvoid
Xusage ()
X{
X	fprintf (stderr, amroot ? USAGE:ADMUSAGE, Progname);
X	exit (1);
X}
X
X/*
X * new_fields - change the user's GECOS information interactively
X *
X * prompt the user for each of the four fields and fill in the fields
X * from the user's response, or leave alone if nothing was entered.
X */
X
Xnew_fields ()
X{
X	printf (NEWFIELDSMSG2);
X
X	change_field (fullnm, NEWNAME);
X	change_field (roomno, NEWROOM);
X	change_field (workph, NEWWORKPHONE);
X	change_field (homeph, NEWHOMEPHONE);
X
X	if (amroot)
X		change_field (slop, NEWSLOP);
X}
X
X/*
X * copy_field - get the next field from the gecos field
X *
X * copy_field copies the next field from the gecos field, returning a
X * pointer to the field which follows, or NULL if there are no more
X * fields.
X */
X
Xchar *
Xcopy_field (in, out, extra)
Xchar	*in;			/* the current GECOS field */
Xchar	*out;			/* where to copy the field to */
Xchar	*extra;			/* fields with '=' get copied here */
X{
X	char	*cp;
X
X	while (in) {
X		if (cp = strchr (in, ','))
X			*cp++ = '\0';
X
X		if (! strchr (in, '='))
X			break;
X
X		if (extra) {
X			if (extra[0])
X				strcat (extra, ",");
X
X			strcat (extra, in);
X		}
X		in = cp;
X	}
X	if (in && out)
X		strcpy (out, in);
X
X	return cp;
X}
X
X/*
X * chfn - change a user's password file information
X *
X *	This command controls the GECOS field information in the
X *	password file entry.
X *
X *	The valid options are
X *
X *	-f	full name
X *	-r	room number
X *	-w	work phone number
X *	-h	home phone number
X *	-o	other information (*)
X *
X *	(*) requires root permission to execute.
X */
X
Xint
Xmain (argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	char	*cp;			/* temporary character pointer       */
X	struct	passwd	*pw;		/* password file entry               */
X	struct	passwd	pwent;		/* modified password file entry      */
X	char	old_gecos[BUFSIZ];	/* buffer for old GECOS fields       */
X	char	new_gecos[BUFSIZ];	/* buffer for new GECOS fields       */
X	int	flag;			/* flag currently being processed    */
X	int	fflg = 0;		/* -f - set full name                */
X	int	rflg = 0;		/* -r - set room number              */
X	int	wflg = 0;		/* -w - set work phone number        */
X	int	hflg = 0;		/* -h - set home phone number        */
X	int	oflg = 0;		/* -o - set other information        */
X	int	i;			/* loop control variable             */
X
X	/*
X	 * This command behaves different for root and non-root
X	 * users.
X	 */
X
X	amroot = getuid () == 0;
X#ifdef	NDBM
X	pw_dbm_mode = O_RDWR;
X#endif
X
X	/*
X	 * Get the program name.  The program name is used as a
X	 * prefix to most error messages.  It is also used as input
X	 * to the openlog() function for error logging.
X	 */
X
X	if (Progname = strrchr (argv[0], '/'))
X		Progname++;
X	else
X		Progname = argv[0];
X
X	openlog (Progname, LOG_PID, LOG_AUTH);
X
X	/* 
X	 * The remaining arguments will be processed one by one and
X	 * executed by this command.  The name is the last argument
X	 * if it does not begin with a "-", otherwise the name is
X	 * determined from the environment and must agree with the
X	 * real UID.  Also, the UID will be checked for any commands
X	 * which are restricted to root only.
X	 */
X
X	while ((flag = getopt (argc, argv, "f:r:w:h:o:")) != EOF) {
X		switch (flag) {
X			case 'f':
X				fflg++;
X				strcpy (fullnm, optarg);
X				break;
X			case 'r':
X				rflg++;
X				strcpy (roomno, optarg);
X				break;
X			case 'w':
X				wflg++;
X				strcpy (workph, optarg);
X				break;
X			case 'h':
X				hflg++;
X				strcpy (homeph, optarg);
X				break;
X			case 'o':
X				if (amroot) {
X					oflg++;
X					strcpy (slop, optarg);
X					break;
X				}
X				fprintf (stderr, NOPERM, Progname);
X				exit (1);
X			default:
X				usage ();
X		}
X	}
X
X	/*
X	 * Get the name of the user to check.  It is either
X	 * the command line name, or the name getlogin()
X	 * returns.
X	 */
X
X	if (optind < argc) {
X		strncpy (user, argv[optind], sizeof user);
X		pw = getpwnam (user);
X	} else if (cp = getlogin ()) {
X		strncpy (user, cp, sizeof user);
X		pw = getpwnam (user);
X	} else {
X		fprintf (stderr, WHOAREYOU, Progname);
X		exit (1);
X	}
X
X	/*
X	 * Make certain there was a password entry for the
X	 * user.
X	 */
X
X	if (! pw) {
X		fprintf (stderr, UNKUSER, Progname, user);
X		exit (1);
X	}
X
X	/*
X	 * Non-privileged users are only allowed to change the
X	 * shell if the UID of the user matches the current
X	 * real UID.
X	 */
X
X	if (! amroot && pw->pw_uid != getuid ()) {
X		fprintf (stderr, NOPERM, Progname);
X		exit (1);
X	}
X
X	/*
X	 * Make a copy of the user's password file entry so it
X	 * can be modified without worrying about it be modified
X	 * elsewhere.
X	 */
X
X	pwent = *pw;
X	pwent.pw_name = strdup (pw->pw_name);
X	pwent.pw_passwd = strdup (pw->pw_passwd);
X#ifdef	ATT_AGE
X	pwent.pw_age = strdup (pw->pw_age);
X#endif
X#ifdef	ATT_COMMENT
X	pwent.pw_comment = strdup (pw->pw_comment);
X#endif
X	pwent.pw_dir = strdup (pw->pw_dir);
X	pwent.pw_shell = strdup (pw->pw_shell);
X
X	/*
X	 * Now get the full name.  It is the first comma separated field
X	 * in the GECOS field.
X	 */
X
X	strcpy (old_gecos, pw->pw_gecos);
X	cp = copy_field (old_gecos, fflg ? (char *) 0:fullnm, slop);
X
X	/*
X	 * Now get the room number.  It is the next comma separated field,
X	 * if there is indeed one.
X	 */
X
X	if (cp)
X		cp = copy_field (cp, rflg ? (char *) 0:roomno, slop);
X
X	/*
X	 * Now get the work phone number.  It is the third field.
X	 */
X
X	if (cp)
X		cp = copy_field (cp, wflg ? (char *) 0:workph, slop);
X
X	/*
X	 * Now get the home phone number.  It is the fourth field.
X	 */
X
X	if (cp)
X		cp = copy_field (cp, hflg ? (char *) 0:homeph, slop);
X
X	/*
X	 * Anything left over is "slop".
X	 */
X
X	if (cp) {
X		if (slop[0])
X			strcat (slop, ",");
X
X		strcat (slop, cp);
X	}
X
X	/*
X	 * If none of the fields were changed from the command line,
X	 * let the user interactively change them.
X	 */
X
X	if (! fflg && ! rflg && ! wflg && ! hflg && ! oflg) {
X		printf (NEWFIELDSMSG, user);
X		new_fields ();
X	}
X
X	/*
X	 * Check all of the fields for valid information
X	 */
X
X	if (valid_field (fullnm, ":,=")) {
X		fprintf (stderr, INVALID_NAME, Progname, fullnm);
X		exit (1);
X	}
X	if (valid_field (roomno, ":,=")) {
X		fprintf (stderr, INVALID_ROOM, Progname, roomno);
X		exit (1);
X	}
X	if (valid_field (workph, ":,=")) {
X		fprintf (stderr, INVALID_WORKPH, Progname, workph);
X		exit (1);
X	}
X	if (valid_field (homeph, ":,=")) {
X		fprintf (stderr, INVALID_HOMEPH, Progname, homeph);
X		exit (1);
X	}
X	if (valid_field (slop, ":")) {
X		fprintf (stderr, INVALID_OTHER, Progname, slop);
X		exit (1);
X	}
X
X	/*
X	 * Build the new GECOS field by plastering all the pieces together,
X	 * if they will fit ...
X	 */
X
X	if (strlen (fullnm) + strlen (roomno) + strlen (workph) +
X			strlen (homeph) + strlen (slop) > 80) {
X		fprintf (stderr, INVALID_FIELDS, Progname);
X		exit (1);
X	}
X	sprintf (new_gecos, "%s,%s,%s,%s", fullnm, roomno, workph, homeph);
X	if (slop[0]) {
X		strcat (new_gecos, ",");
X		strcat (new_gecos, slop);
X	}
X	pwent.pw_gecos = new_gecos;
X	pw = &pwent;
X
X	/*
X	 * Before going any further, raise the ulimit to prevent
X	 * colliding into a lowered ulimit, and set the real UID
X	 * to root to protect against unexpected signals.  Any
X	 * keyboard signals are set to be ignored.
X	 */
X
X	ulimit (2, 30000);
X	if (setuid (0)) {
X		fprintf (stderr, NOTROOT);
X		syslog (LOG_ERR, NOTROOT2);
X		exit (1);
X	}
X	signal (SIGHUP, SIG_IGN);
X	signal (SIGINT, SIG_IGN);
X	signal (SIGQUIT, SIG_IGN);
X#ifdef	SIGTSTP
X	signal (SIGTSTP, SIG_IGN);
X#endif
X
X	/*
X	 * The passwd entry is now ready to be committed back to
X	 * the password file.  Get a lock on the file and open it.
X	 */
X
X	for (i = 0;i < 30;i++)
X		if (pw_lock ())
X			break;
X
X	if (i == 30) {
X		fprintf (stderr, PWDBUSY);
X		syslog (LOG_WARN, PWDBUSY2);
X		exit (1);
X	}
X	if (! pw_open (O_RDWR)) {
X		fprintf (stderr, OPNERROR);
X		syslog (LOG_ERR, OPNERROR2);
X		(void) pw_unlock ();
X		exit (1);
X	}
X
X	/*
X	 * Update the passwd file entry.  If there is a DBM file,
X	 * update that entry as well.
X	 */
X
X	if (! pw_update (pw)) {
X		fprintf (stderr, UPDERROR);
X		syslog (LOG_ERR, UPDERROR2);
X		(void) pw_unlock ();
X		exit (1);
X	}
X#if defined(DBM) || defined(NDBM)
X	if (access ("/etc/passwd.pag", 0) == 0 && ! pw_dbm_update (pw)) {
X		fprintf (stderr, DBMERROR);
X		syslog (LOG_ERR, DBMERROR2);
X		(void) pw_unlock ();
X		exit (1);
X	}
X#endif
X
X	/*
X	 * Changes have all been made, so commit them and unlock the
X	 * file.
X	 */
X
X	if (! pw_close ()) {
X		fprintf (stderr, CLSERROR);
X		syslog (LOG_ERR, CLSERROR2);
X		(void) pw_unlock ();
X		exit (1);
X	}
X	if (! pw_unlock ()) {
X		fprintf (stderr, UNLKERROR);
X		syslog (LOG_ERR, UNLKERROR2);
X		exit (1);
X	}
X	syslog (LOG_INFO, CHGGECOS, user);
X	closelog ();
X	exit (0);
X}
SHAR_EOF
if test 11610 -ne "`wc -c < 'chfn.c'`"
then
	echo shar: "error transmitting 'chfn.c'" '(should have been 11610 characters)'
fi
fi
echo shar: "extracting 'chsh.c'" '(9361 characters)'
if test -f 'chsh.c'
then
	echo shar: "will not over-write existing file 'chsh.c'"
else
sed 's/^X//' << \SHAR_EOF > 'chsh.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <sys/types.h>
X#include <syslog.h>
X#include <stdio.h>
X#include <fcntl.h>
X#include <signal.h>
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)chsh.c	3.3	11:23:29	12/19/90";
X#endif
X
X/*
X * Set up some BSD defines so that all the BSD ifdef's are
X * kept right here 
X */
X
X#ifndef	BSD
X#include <string.h>
X#include <memory.h>
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X
X#include "config.h"
X#include "pwd.h"
X
X/*
X * Global variables.
X */
X
Xchar	*Progname;			/* Program name */
Xint	amroot;				/* Real UID is root */
Xchar	loginsh[BUFSIZ];		/* Name of new login shell */
X
X/*
X * External identifiers
X */
X
Xextern	struct	passwd	*getpwuid ();
Xextern	struct	passwd	*getpwnam ();
Xextern	int	optind;
Xextern	char	*optarg;
Xextern	char	*getlogin ();
X#ifdef	NDBM
Xextern	int	pw_dbm_mode;
X#endif
X
X/*
X * #defines for messages.  This facilities foreign language conversion
X * since all messages are defined right here.
X */
X
X#define	USAGE		"Usage: %s [ -s shell ] [ name ]\n"
X#define	WHOAREYOU	"%s: Cannot determine you user name.\n"
X#define	UNKUSER		"%s: Unknown user %s\n"
X#define	NOPERM		"You may not change the shell for %s.\n"
X#define	NOPERM2		"can't change shell for `%s'\n"
X#define	NEWSHELLMSG	"Changing the login shell for %s\n"
X#define	NEWSHELL	"Login Shell"
X#define	NEWSHELLMSG2 \
X	"Enter the new value, or press return for the default\n\n"
X#define	BADSHELL	"%s is an invalid shell.\n"
X#define	BADFIELD	"%s: Invalid entry: %s\n"
X#define	PWDBUSY		"Cannot lock the password file; try again later.\n"
X#define	PWDBUSY2	"can't lock /etc/passwd\n"
X#define	OPNERROR	"Cannot open the password file.\n"
X#define	OPNERROR2	"can't open /etc/passwd\n"
X#define	UPDERROR	"Error updating the password entry.\n"
X#define	UPDERROR2	"error updating passwd entry\n"
X#define	DBMERROR	"Error updating the DBM password entry.\n"
X#define	DBMERROR2	"error updating DBM passwd entry.\n"
X#define	NOTROOT		"Cannot change ID to root.\n"
X#define	NOTROOT2	"can't setuid(0).\n"
X#define	CLSERROR	"Cannot commit password file changes.\n"
X#define	CLSERROR2	"can't rewrite /etc/passwd.\n"
X#define	UNLKERROR	"Cannot unlock the password file.\n"
X#define	UNLKERROR2	"can't unlock /etc/passwd.\n"
X#define	CHGSHELL	"changed user `%s' shell to `%s'\n"
X
X/*
X * usage - print command line syntax and exit
X */
X
Xvoid
Xusage ()
X{
X	fprintf (stderr, USAGE, Progname);
X	exit (1);
X}
X
X/*
X * new_fields - change the user's login shell information interactively
X *
X * prompt the user for the login shell and change it according to the
X * response, or leave it alone if nothing was entered.
X */
X
Xnew_fields ()
X{
X	printf (NEWSHELLMSG2);
X	change_field (loginsh, NEWSHELL);
X}
X
X/*
X * check_shell - see if the user's login shell is listed in /etc/shells
X *
X * The /etc/shells file is read for valid names of login shells.  If the
X * /etc/shells file does not exist the user cannot set any shell unless
X * they are root.
X */
X
Xcheck_shell (shell)
Xchar	*shell;
X{
X	char	buf[BUFSIZ];
X	char	*cp;
X	int	found = 0;
X	FILE	*fp;
X
X	if (amroot)
X		return 1;
X
X	if ((fp = fopen ("/etc/shells", "r")) == (FILE *) 0)
X		return 0;
X
X	while (fgets (buf, BUFSIZ, fp) && ! found) {
X		if (cp = strrchr (buf, '\n'))
X			*cp = '\0';
X
X		if (strcmp (buf, shell) == 0)
X			found = 1;
X	}
X	fclose (fp);
X
X	return found;
X}
X
X/*
X * restricted_shell - return true if the named shell begins with 'r' or 'R'
X *
X * If the first letter of the filename is 'r' or 'R', the shell is
X * considered to be restricted.
X */
X
Xint
Xrestricted_shell (shell)
Xchar	*shell;
X{
X	char	*cp;
X
X	if (cp = strrchr (shell, '/'))
X		cp++;
X	else
X		cp = shell;
X
X	return *cp == 'r' || *cp == 'R';
X}
X
X/*
X * chsh - this command controls changes to the user's shell
X *
X *	The only suppoerted option is -s which permits the
X *	the login shell to be set from the command line.
X */
X
Xint
Xmain (argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	char	user[BUFSIZ];		/* User name                         */
X	int	flag;			/* Current command line flag         */
X	int	sflg = 0;		/* -s - set shell from command line  */
X	int	i;			/* Loop control variable             */
X	char	*cp;			/* Miscellaneous character pointer   */
X	struct	passwd	*pw;		/* Password entry from /etc/passwd   */
X	struct	passwd	pwent;		/* New password entry                */
X
X	/*
X	 * This command behaves different for root and non-root
X	 * users.
X	 */
X
X	amroot = getuid () == 0;
X#ifdef	NDBM
X	pw_dbm_mode = O_RDWR;
X#endif
X
X	/*
X	 * Get the program name.  The program name is used as a
X	 * prefix to most error messages.  It is also used as input
X	 * to the openlog() function for error logging.
X	 */
X
X	if (Progname = strrchr (argv[0], '/'))
X		Progname++;
X	else
X		Progname = argv[0];
X
X	openlog (Progname, LOG_PID, LOG_AUTH);
X
X	/*
X	 * There is only one option, but use getopt() anyway to
X	 * keep things consistent.
X	 */
X
X	while ((flag = getopt (argc, argv, "s:")) != EOF) {
X		switch (flag) {
X			case 's':
X				sflg++;
X				strcpy (loginsh, optarg);
X				break;
X			default:
X				usage ();
X		}
X	}
X
X	/*
X	 * There should be only one remaining argument at most
X	 * and it should be the user's name.
X	 */
X
X	if (argc > optind + 1)
X		usage ();
X
X	/*
X	 * Get the name of the user to check.  It is either
X	 * the command line name, or the name getlogin()
X	 * returns.
X	 */
X
X	if (optind < argc) {
X		strncpy (user, argv[optind], sizeof user);
X		pw = getpwnam (user);
X	} else if (cp = getlogin ()) {
X		strncpy (user, cp, sizeof user);
X		pw = getpwnam (user);
X	} else {
X		fprintf (stderr, WHOAREYOU, Progname);
X		exit (1);
X	}
X
X	/*
X	 * Make certain there was a password entry for the
X	 * user.
X	 */
X
X	if (! pw) {
X		fprintf (stderr, UNKUSER, Progname, user);
X		exit (1);
X	}
X
X	/*
X	 * Non-privileged users are only allowed to change the
X	 * shell if the UID of the user matches the current
X	 * real UID.
X	 */
X
X	if (! amroot && pw->pw_uid != getuid ()) {
X		fprintf (stderr, NOPERM, user);
X		syslog (LOG_WARN, NOPERM2, user);
X		exit (1);
X	}
X
X	/*
X	 * Non-privileged users are only allowed to change the
X	 * shell if it is not a restricted one.
X	 */
X
X	if (! amroot && restricted_shell (pw->pw_shell)) {
X		fprintf (stderr, NOPERM, user);
X		syslog (LOG_WARN, NOPERM2, user);
X		exit (1);
X	}
X
X	/*
X	 * Make a copy of the user's password file entry so it
X	 * can be modified without worrying about it be modified
X	 * elsewhere.
X	 */
X
X	pwent = *pw;
X	pwent.pw_name = strdup (pw->pw_name);
X	pwent.pw_passwd = strdup (pw->pw_passwd);
X#ifdef	ATT_AGE
X	pwent.pw_age = strdup (pw->pw_age);
X#endif
X#ifdef	ATT_COMMENT
X	pwent.pw_comment = strdup (pw->pw_comment);
X#endif
X	pwent.pw_dir = strdup (pw->pw_dir);
X	pwent.pw_gecos = strdup (pw->pw_gecos);
X
X	/*
X	 * Now get the login shell.  Either get it from the password
X	 * file, or use the value from the command line.
X	 */
X
X	if (! sflg)
X		strcpy (loginsh, pw->pw_shell);
X
X	/*
X	 * If the login shell was not set on the command line,
X	 * let the user interactively change it.
X	 */
X
X	if (! sflg) {
X		printf (NEWSHELLMSG, user);
X		new_fields ();
X	}
X
X	/*
X	 * Check all of the fields for valid information.  The shell
X	 * field may not contain any illegal characters.  Non-privileged
X	 * users are restricted to using the shells in /etc/shells.
X	 */
X
X	if (valid_field (loginsh, ":,=")) {
X		fprintf (stderr, BADFIELD, Progname, loginsh);
X		exit (1);
X	}
X	if (! check_shell (loginsh)) {
X		fprintf (stderr, BADSHELL, loginsh);
X		exit (1);
X	}
X	pwent.pw_shell = loginsh;
X	pw = &pwent;
X
X	/*
X	 * Before going any further, raise the ulimit to prevent
X	 * colliding into a lowered ulimit, and set the real UID
X	 * to root to protect against unexpected signals.  Any
X	 * keyboard signals are set to be ignored.
X	 */
X
X	ulimit (2, 30000);
X	if (setuid (0)) {
X		fprintf (stderr, NOTROOT);
X		syslog (LOG_ERR, NOTROOT2);
X		exit (1);
X	}
X	signal (SIGHUP, SIG_IGN);
X	signal (SIGINT, SIG_IGN);
X	signal (SIGQUIT, SIG_IGN);
X#ifdef	SIGTSTP
X	signal (SIGTSTP, SIG_IGN);
X#endif
X
X	/*
X	 * The passwd entry is now ready to be committed back to
X	 * the password file.  Get a lock on the file and open it.
X	 */
X
X	for (i = 0;i < 30;i++)
X		if (pw_lock ())
X			break;
X
X	if (i == 30) {
X		fprintf (stderr, PWDBUSY);
X		syslog (LOG_WARN, PWDBUSY2);
X		exit (1);
X	}
X	if (! pw_open (O_RDWR)) {
X		fprintf (stderr, OPNERROR);
X		syslog (LOG_ERR, OPNERROR2);
X		(void) pw_unlock ();
X		exit (1);
X	}
X
X	/*
X	 * Update the passwd file entry.  If there is a DBM file,
X	 * update that entry as well.
X	 */
X
X	if (! pw_update (pw)) {
X		fprintf (stderr, UPDERROR);
X		syslog (LOG_ERR, UPDERROR2);
X		(void) pw_unlock ();
X		exit (1);
X	}
X#if defined(DBM) || defined(NDBM)
X	if (access ("/etc/passwd.pag", 0) == 0 && ! pw_dbm_update (pw)) {
X		fprintf (stderr, DBMERROR);
X		syslog (LOG_ERR, DBMERROR2);
X		(void) pw_unlock ();
X		exit (1);
X	}
X#endif
X
X	/*
X	 * Changes have all been made, so commit them and unlock the
X	 * file.
X	 */
X
X	if (! pw_close ()) {
X		fprintf (stderr, CLSERROR);
X		syslog (LOG_ERR, CLSERROR2);
X		(void) pw_unlock ();
X		exit (1);
X	}
X	if (! pw_unlock ()) {
X		fprintf (stderr, UNLKERROR);
X		syslog (LOG_ERR, UNLKERROR2);
X		exit (1);
X	}
X	syslog (LOG_INFO, CHGSHELL, user, pwent.pw_shell);
X	closelog ();
X	exit (0);
X}
SHAR_EOF
if test 9361 -ne "`wc -c < 'chsh.c'`"
then
	echo shar: "error transmitting 'chsh.c'" '(should have been 9361 characters)'
fi
fi
echo shar: "extracting 'smain.c'" '(9944 characters)'
if test -f 'smain.c'
then
	echo shar: "will not over-write existing file 'smain.c'"
else
sed 's/^X//' << \SHAR_EOF > 'smain.c'
X/*
X * Copyright 1989, 1990, 1991, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <sys/types.h>
X#include <stdio.h>
X
X#ifndef	lint
Xstatic	char	sccsid[] = "%W%	%U%	%G%";
X#endif
X
X/*
X * Set up some BSD defines so that all the BSD ifdef's are
X * kept right here 
X */
X
X#ifndef	BSD
X#include <string.h>
X#include <memory.h>
X#define	bzero(a,n)	memset(a, 0, n)
X#include <termio.h>
X#else
X#include <strings.h>
X#include <sgtty.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X
X#include <signal.h>
X#include <syslog.h>
X#include "config.h"
X#include "lastlog.h"
X#include "pwd.h"
X#include "shadow.h"
X
X/*
X * Password aging constants
X *
X *	DAY - seconds in a day
X *	WEEK - seconds in a week
X *	SCALE - convert from clock to aging units
X */
X
X#define	DAY	(24L*3600L)
X#define	WEEK	(7L*DAY)
X
X#ifdef	ITI_AGING
X#define	SCALE	(1)
X#else
X#define	SCALE	DAY
X#endif
X
X/*
X * Assorted #defines to control su's behavior
X */
X
X#ifndef	MAXENV
X#define	MAXENV	128
X#endif
X
X#ifndef	PATH
X#define	PATH	":/bin:/usr/bin"
X#endif
X
X#ifndef	SUPATH
X#define	SUPATH	":/bin:/usr/bin:/etc"
X#endif
X
X/*
X * Global variables
X */
X
X#ifdef	HUSHLOGIN
Xchar	hush[BUFSIZ];
Xint	hushed;
X#endif
X
Xchar	name[BUFSIZ];
Xchar	pass[BUFSIZ];
Xchar	home[BUFSIZ];
Xchar	prog[BUFSIZ];
Xchar	mail[BUFSIZ];
Xchar	oldname[BUFSIZ];
Xchar	*newenvp[MAXENV];
Xchar	*Prog;
Xint	newenvc = 0;
Xint	maxenv = MAXENV;
Xstruct	passwd	pwent;
X
X#ifdef	TZ
XFILE	*tzfile;
Xchar	tzbuf[16] = TZ;
X#endif
X
X/*
X * External identifiers
X */
X
Xextern	void	addenv ();
Xextern	void	entry ();
Xextern	void	sulog ();
Xextern	void	subsystem ();
Xextern	void	setup ();
Xextern	void	motd ();
Xextern	void	mailcheck ();
Xextern	void	shell ();
Xextern	char	*ttyname ();
Xextern	char	*getenv ();
Xextern	char	*getpass ();
Xextern	struct	passwd	*getpwuid ();
Xextern	struct	passwd	*getpwnam ();
Xextern	struct	spwd	*getspnam ();
Xextern	char	**environ;
X
X/*
X * die - set or reset termio modes.
X *
X *	die() is called before processing begins.  signal() is then
X *	called with die() as the signal handler.  If signal later
X *	calls die() with a signal number, the terminal modes are
X *	then reset.
X */
X
Xvoid	die (killed)
Xint	killed;
X{
X#ifdef	BSD
X	static	struct	sgtty	sgtty;
X
X	if (killed)
X		stty (0, &sgtty);
X	else
X		gtty (0, &sgtty);
X#else
X	static	struct	termio	sgtty;
X
X	if (killed)
X		ioctl (0, TCSETA, &sgtty);
X	else
X		ioctl (0, TCGETA, &sgtty);
X#endif
X	if (killed) {
X		closelog ();
X		exit (killed);
X	}
X}
X
X/*
X * su - switch user id
X *
X *	su changes the user's ids to the values for the specified user.
X *	if no new user name is specified, "root" is used by default.
X *
X *	The only valid option is a "-" character, which is interpreted
X *	as requiring a new login session to be simulated.
X *
X *	Any additional arguments are passed to the user's shell.  In
X *	particular, the argument "-c" will cause the next argument to
X *	be interpreted as a command by the common shell programs.
X */
X
Xint	main (argc, argv, envp)
Xint	argc;
Xchar	**argv;
Xchar	**envp;
X{
X	void	(*oldsig)();
X	char	*cp;
X	char	*tty = 0;		/* Name of tty SU is run from        */
X	int	doshell = 0;
X	int	fakelogin = 0;
X	int	amroot = 0;
X	struct	passwd	*pw = 0;
X	struct	spwd	*spwd = 0;
X
X	/*
X	 * Get the program name.  The program name is used as a
X	 * prefix to most error messages.  It is also used as input
X	 * to the openlog() function for error logging.
X	 */
X
X	if (Prog = strrchr (argv[0], '/'))
X		Prog++;
X	else
X		Prog = argv[0];
X
X	openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
X
X	/*
X	 * Get the tty name.  Entries will be logged indicating that
X	 * the user tried to change to the named new user from the
X	 * current terminal.
X	 */
X
X	if (isatty (0) && (cp = ttyname (0))) {
X		if (strncmp (cp, "/dev/", 5) == 0)
X			tty = cp + 5;
X		else
X			tty = cp;
X	} else
X		tty = "???";
X
X	/*
X	 * Process the command line arguments. 
X	 */
X
X	argc--; argv++;			/* shift out command name */
X
X	if (argc > 0 && argv[0][0] == '-' && argv[0][1] == '\0') {
X		fakelogin = 1;
X		argc--; argv++;		/* shift ... */
X	}
X
X	/*
X	 * If a new login is being set up, the old environment will
X	 * be ignored and a new one created later on.
X	 */
X
X	if (! fakelogin)
X		while (*envp)
X			addenv (*envp++);
X
X#ifdef	TZ
X
X	/*
X	 * The timezone will be reset to the login value if required.
X	 */
X
X	if (fakelogin) {
X		if (tzbuf[0] == '/') {
X			if ((tzfile = fopen (tzbuf, "r")) != (FILE *) 0) {
X				if (fgets (tzbuf, sizeof tzbuf, tzfile)) {
X					tzbuf[strlen (tzbuf) - 1] = '\0';
X					addenv (tzbuf);
X				}
X				fclose (tzfile);
X			}
X		} else {
X			addenv (tzbuf);
X		}
X	}
X#endif	/* TZ */
X#ifdef	HZ
X
X	/*
X	 * The clock frequency will be reset to the login value if required
X	 */
X
X	if (fakelogin)
X		addenv (HZ);		/* set the default $HZ, if one */
X#endif	/* HZ */
X
X	/*
X	 * The next argument must be either a user ID, or some flag to
X	 * a subshell.  Pretty sticky since you can't have an argument
X	 * which doesn't start with a "-" unless you specify the new user
X	 * name.  Any remaining arguments will be passed to the user's
X	 * login shell.
X	 */
X
X	if (argc > 0 && argv[0][0] != '-') {
X		(void) strcpy (name, argv[0]);	/* use this login id */
X		argc--; argv++;		/* shift ... */
X	}
X	if (! name[0]) 			/* use default user ID */
X		(void) strcpy (name, "root");
X
X	doshell = argc == 0;		/* any arguments remaining? */
X
X	/*
X	 * Get the user's real name.  The current UID is used to determine
X	 * who has executed su.  That user ID must exist.
X	 */
X
X	if (pw = getpwuid (getuid ()))	/* need old user name */
X		(void) strcpy (oldname, pw->pw_name);
X	else {				/* user ID MUST exist */ 
X		syslog (LOG_CRIT, "Unknown UID: %d\n", getuid ());
X		goto failure;
X	}
X	amroot = getuid () == 0;	/* currently am super user */
X
Xtop:
X	/*
X	 * This is the common point for validating a user whose name
X	 * is known.  It will be reached either by normal processing,
X	 * or if the user is to be logged into a subsystem root.
X	 *
X	 * The password file entries for the user is gotten and the
X	 * accont validated.
X	 */
X
X	if (pw = getpwnam (name)) {
X		if (spwd = getspnam (name))
X			pw->pw_passwd = spwd->sp_pwdp;
X	} else {
X		(void) fprintf (stderr, "Unknown id: %s\n", name);
X		closelog ();
X		exit (1);
X	}
X	pwent = *pw;
X
X#ifdef	NOLOGIN
X
X	/*
X	 * See if the account is usable for anything but login.
X	 */
X
X	if (strcmp (pwent.pw_shell, NOLOGIN) == 0)
X		pwent.pw_shell = getenv ("SHELL");
X#endif	/* NOLOGIN */
X
X	/*
X	 * Set the default shell.
X	 */
X
X	if (pwent.pw_shell == 0 || pwent.pw_shell[0] == '\0')
X		pwent.pw_shell = "/bin/sh";
X
X	/*
X	 * Set up a signal handler in case the user types QUIT.
X	 */
X
X	die (0);
X	oldsig = signal (SIGQUIT, die);
X
X	/*
X	 * Get the password from the invoker
X	 */
X
X	if (! amroot && pwent.pw_passwd[0]) {
X		if (! (cp = getpass ("Password:"))) {
X			syslog (pwent.pw_uid ? LOG_WARN:LOG_CRIT,
X				"Unable to get password for %s\n", name);
X			goto failure;
X		} else
X			strncpy (pass, cp, sizeof pass);
X	} else
X		bzero (pass, sizeof pass);
X
X	/*
X	 * check encrypted passwords ...
X	 */
X
X	if (! amroot && ((pass[0] != '\0' || pwent.pw_passwd[0] != '\0') &&
X			strcmp (pwent.pw_passwd,
X				pw_encrypt (pass, pwent.pw_passwd)) != 0)) {
X		syslog (pwent.pw_uid ? LOG_WARN:LOG_CRIT,
X			"Invalid password for %s\n", name);
Xfailure:	sulog (0);		/* log failed attempt */
X		syslog (pwent.pw_uid ? LOG_INFO:LOG_CRIT,
X			"- %s %s-%s\n", tty ? tty:"???",
X			oldname[0] ? oldname:"???", name[0] ? name:"???");
X		puts ("Sorry.");
X		closelog ();
X		exit (1);
X	}
X	signal (SIGQUIT, oldsig);
X
X	/*
X	 * Check to see if the account is expired.  root gets to
X	 * ignore any expired accounts, but normal users can't become
X	 * a user with an expired password.
X	 */
X
X	if (! amroot) {
X		if (spwd) {
X			if (isexpired (&pwent, spwd)) {
X				syslog (pwent.pw_uid ? LOG_WARN:LOG_CRIT,
X					"Expired account %s\n", name);
X				goto failure;
X			}
X		}
X#ifdef	ATT_AGE
X		else if (pwent.pw_age[0] &&
X				isexpired (&pwent, (struct spwd *) 0)) {
X			syslog (pwent.pw_uid ? LOG_WARN:LOG_CRIT,
X				"Expired account %s\n", name);
X			goto failure;
X		}
X#endif	/* ATT_AGE */
X	}
X	if (pwent.pw_uid == 0)
X		addenv (SUPATH);
X	else
X		addenv (PATH);
X
X	environ = newenvp;		/* make new environment active */
X
X	if (getenv ("IFS"))		/* don't export user IFS ... */
X		addenv ("IFS= \t\n");	/* ... instead, set a safe IFS */
X
X	if (doshell && pwent.pw_shell[0] == '*') { /* subsystem root required */
X		subsystem (&pwent);	/* figure out what to execute */
X		endpwent ();
X		endspent ();
X		goto top;
X	}
X
X	sulog (1);			/* save SU information */
X	syslog (LOG_INFO, "+ %s %s-%s\n", tty ? tty:"???",
X		oldname[0] ? oldname:"???", name[0] ? name:"???");
X
X	if (fakelogin)
X		setup (&pwent);		/* set UID, GID, HOME, etc ... */
X	else {
X		if (setgid (pwent.pw_gid) || setuid (pwent.pw_uid))  {
X			perror ("Can't set ID");
X			syslog (LOG_CRIT, "Unable to set uid = %d, gid = %d\n",
X				pwent.pw_uid, pwent.pw_gid);
X			closelog ();
X			exit (1);
X		}
X	}
X	if (! doshell) {		/* execute arguments as command */
X		if (cp = getenv ("SHELL"))
X			pwent.pw_shell = cp;
X		argv[-1] = pwent.pw_shell;
X		(void) execv (pwent.pw_shell, &argv[-1]);
X		(void) fprintf (stderr, "No shell\n");
X		syslog (LOG_WARN, "Cannot execute %s\n", pwent.pw_shell);
X		closelog ();
X		exit (1);
X	}
X	if (fakelogin) {
X#ifdef	HUSHLOGIN
X		sprintf (hush, "%s/.hushlogin", pwent.pw_dir);
X		hushed = access (hush, 0) != -1;
X#endif	/* HUSHLOGIN */
X#ifdef	MOTD
X		motd ();		/* print the message of the day */
X#endif	/* MOTD */
X#ifdef	MAILCHECK
X		if (! hushed)
X			mailcheck ();	/* report on the status of mail */
X#endif	/* MAILCHECK */
X		shell (pwent.pw_shell, "-su"); /* exec the shell finally. */
X	} else {
X		if (cp = strrchr (pwent.pw_shell, '/'))
X			cp++;
X		else
X			cp = pwent.pw_shell;
X
X		shell (pwent.pw_shell, cp);
X	}
X	syslog (LOG_WARN, "Cannot execute %s\n", pwent.pw_shell);
X
X	/*NOTREACHED*/
X}
SHAR_EOF
if test 9944 -ne "`wc -c < 'smain.c'`"
then
	echo shar: "error transmitting 'smain.c'" '(should have been 9944 characters)'
fi
fi
echo shar: "extracting 'faillog.c'" '(5024 characters)'
if test -f 'faillog.c'
then
	echo shar: "will not over-write existing file 'faillog.c'"
else
sed 's/^X//' << \SHAR_EOF > 'faillog.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <stdio.h>
X#include "pwd.h"
X#include <time.h>
X#ifndef	BSD
X#include <string.h>
X#include <memory.h>
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X#include "config.h"
X#include "faillog.h"
X
X#ifndef	lint
Xstatic	char	_sccsid[] = "@(#)faillog.c	3.1	12:30:41	12/12/90";
X#endif
X
XFILE	*fail;		/* failure file stream */
Xoff_t	user;		/* one single user, specified on command line */
Xint	days;		/* number of days to consider for print command */
Xtime_t	seconds;	/* that number of days in seconds */
Xint	max;		/* maximum failure count for fail_max */
X
Xint	mflg;		/* set fail_max for a given user */
Xint	rflg;		/* reset fail_cnt for user or all user's */
Xint	uflg;		/* set if user is a valid user id */
Xint	tflg;		/* print is restricted to most recent days */
Xstruct	faillog	faillog; /* scratch structure to play with ... */
Xstruct	stat	statbuf; /* fstat buffer for file size */
X
Xextern	int	optind;
Xextern	char	*optarg;
Xextern	char	*asctime ();
Xextern	struct	passwd	*getpwuid ();
Xextern	struct	passwd	*getpwnam ();
Xextern	struct	passwd	*getpwent ();
Xextern	struct	tm	*localtime ();
X
X#define	DAY	(24L*3600L)
X#define	NOW	(time ((time_t *) 0))
X
Xmain (argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	char	*mode;
X	int	uid = 0;
X	int	c;
X	struct	passwd	*pwent;
X
X	if (getuid () == 0)	/* only root can update anything */
X		mode = "r+";
X	else			/* all others can only look */
X		mode = "r";
X
X	if ((fail = fopen (FAILFILE, mode)) == (FILE *) 0) {
X		perror (FAILFILE);
X		exit (1);
X	}
X	while ((c = getopt (argc, argv, "m:pru:t:")) != EOF) {
X		switch (c) {
X			case 'm':
X				max = atoi (optarg);
X				setmax ();
X				break;
X			case 'p':
X				print ();
X				break;
X			case 'r':
X				reset ();
X				break;
X			case 'u':
X				pwent = getpwnam (optarg);
X				if (! pwent) {
X					fprintf (stderr, "Unknown User: %s\n", optarg);
X					exit (1);
X				}
X				uflg++;
X				user = pwent->pw_uid;
X				break;
X			case 't':
X				days = atoi (optarg);
X				seconds = days * DAY;
X				tflg++;
X				break;
X		}
X	}
X	fclose (fail);
X	exit (0);
X}
X
Xprint ()
X{
X	int	uid;
X	off_t	offset;
X
X	if (uflg) {
X		offset = user * sizeof faillog;
X		fstat (fileno (fail), &statbuf);
X		if (offset >= statbuf.st_size)
X			return;
X
X		fseek (fail, (off_t) user * sizeof faillog, 0);
X		if (fread ((char *) &faillog, sizeof faillog, 1, fail) == 1)
X			print_one (&faillog, user);
X		else
X			perror (FAILFILE);
X	} else {
X		for (uid = 0;
X			fread ((char *) &faillog, sizeof faillog, 1, fail) == 1;
X				uid++) {
X
X			if (faillog.fail_cnt == 0)
X				continue;
X
X			if (tflg && NOW - faillog.fail_time > seconds)
X				continue;
X
X			print_one (&faillog, uid);
X		}
X	}
X}
X
Xprint_one (faillog, uid)
Xstruct	faillog	*faillog;
X{
X	static	int	once;
X	char	*cp;
X	struct	tm	*tm;
X	struct	passwd	*pwent;
X
X	if (! once) {
X		printf ("Username        Failures    Maximum     Latest\n");
X		once++;
X	}
X	pwent = getpwuid (uid);
X	tm = localtime (&faillog->fail_time);
X	cp = asctime (tm);
X	cp[24] = '\0';
X
X	if (pwent) {
X		printf ("%-16s    %4d       %4d",
X			pwent->pw_name, faillog->fail_cnt, faillog->fail_max);
X		if (faillog->fail_time)
X			printf ("     %s on %s\n", cp, faillog->fail_line);
X		else
X			putchar ('\n');
X	}
X}
X
Xreset ()
X{
X	int	uid = 0;
X
X	if (uflg)
X		reset_one (user);
X	else
X		for (uid = 0;reset_one (uid);uid++)
X			;
X}
X
Xreset_one (uid)
Xint	uid;
X{
X	off_t	offset;
X
X	offset = uid * sizeof faillog;
X	fstat (fileno (fail), &statbuf);
X	if (offset >= statbuf.st_size)
X		return (0);
X
X	if (fseek (fail, offset, 0) != 0) {
X		perror (FAILFILE);
X		return (0);
X	}
X	if (fread ((char *) &faillog, sizeof faillog, 1, fail) != 1) {
X		if (! feof (fail))
X			perror (FAILFILE);
X
X		return (0);
X	}
X	if (faillog.fail_cnt == 0)
X		return (1);	/* don't fill in no holes ... */
X
X	faillog.fail_cnt = 0;
X
X	if (fseek (fail, offset, 0) == 0
X		&& fwrite ((char *) &faillog, sizeof faillog, 1, fail) == 1) {
X		fflush (fail);
X		return (1);
X	} else {
X		perror (FAILFILE);
X	}
X	return (0);
X}
X
Xsetmax ()
X{
X	int	uid = 0;
X	struct	passwd	*pwent;
X
X	if (uflg) {
X		setmax_one (user);
X	} else {
X		setpwent ();
X		while (pwent = getpwent ())
X			setmax_one (pwent->pw_uid);
X	}
X}
X
Xsetmax_one (uid)
Xint	uid;
X{
X	off_t	offset;
X
X	offset = uid * sizeof faillog;
X
X	if (fseek (fail, offset, 0) != 0) {
X		perror (FAILFILE);
X		return;
X	}
X	if (fread ((char *) &faillog, sizeof faillog, 1, fail) != 1) {
X		if (! feof (fail))
X			perror (FAILFILE);
X	} else {
X#ifndef	BSD
X		memset ((char *) &faillog, '\0', sizeof faillog);
X#else
X		bzero ((char *) &faillog, sizeof faillog);
X#endif
X	}
X	faillog.fail_max = max;
X
X	if (fseek (fail, offset, 0) == 0
X		&& fwrite ((char *) &faillog, sizeof faillog, 1, fail) == 1)
X		fflush (fail);
X	else
X		perror (FAILFILE);
X}
SHAR_EOF
if test 5024 -ne "`wc -c < 'faillog.c'`"
then
	echo shar: "error transmitting 'faillog.c'" '(should have been 5024 characters)'
fi
fi
echo shar: "extracting 'pwconv.c'" '(4442 characters)'
if test -f 'pwconv.c'
then
	echo shar: "will not over-write existing file 'pwconv.c'"
else
sed 's/^X//' << \SHAR_EOF > 'pwconv.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X *
X * pwconv - convert and update shadow password files
X *
X *	Pwconv copies the old password file information to a new shadow
X *	password file, merging entries from an optional existing shadow
X *	file.
X *
X *	The new password file is left in npasswd, the new shadow file is
X *	left in nshadow.  Existing shadow entries are copied as is.
X *	New entries are created with passwords which expire in MAXDAYS days,
X *	with a last changed date of today, unless password aging
X *	information was already present.  Likewise, the minimum number of
X *	days before which the password may be changed is controlled by
X *	MINDAYS.  The number of warning days is set to WARNAGE if that
X *	macro exists.  Entries with blank passwordsare not copied to the
X *	shadow file at all.
X */
X
X#include <sys/types.h>
X#include <stdio.h>
X#include <fcntl.h>
X#include "pwd.h"
X#ifndef	BSD
X#include <string.h>
X#else
X#define	strchr	index
X#define	strrchr	rindex
X#include <strings.h>
X#endif
X#include "config.h"
X#include "shadow.h"
X
X#ifndef	lint
Xstatic	char	_sccsid[] = "@(#)pwconv.c	3.2	12:31:11	12/12/90";
X#endif
X
Xchar	buf[BUFSIZ];
X
Xlong	time ();
Xlong	a64l ();
X
Xint	main ()
X{
X	long	today;
X	struct	passwd	*pw;
X	struct	passwd	*sgetpwent ();
X	FILE	*pwd;
X	FILE	*npwd;
X	FILE	*shadow;
X	struct	spwd	*spwd;
X	struct	spwd	tspwd;
X	int	fd;
X	char	*cp;
X
X	if (! (pwd = fopen (PWDFILE, "r"))) {
X		perror (PWDFILE);
X		exit (1);
X	}
X	unlink ("npasswd");
X	if ((fd = open ("npasswd", O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0 ||
X			! (npwd = fdopen (fd, "w"))) {
X		perror ("npasswd");
X		exit (1);
X	}
X	unlink  ("nshadow");
X	if ((fd = open ("nshadow", O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0 ||
X			! (shadow = fdopen (fd, "w"))) {
X		perror ("nshadow");
X		(void) unlink ("npasswd");
X		(void) unlink ("nshadow");
X		exit (1);
X	}
X
X	(void) time (&today);
X	today /= (24L * 60L * 60L);
X
X	while (fgets (buf, BUFSIZ, pwd) == buf) {
X		if (cp = strrchr (buf, '\n'))
X			*cp = '\0';
X
X		if (buf[0] == '#') {	/* comment line */
X			(void) fprintf (npwd, "%s\n", buf);
X			continue;
X		}
X		if (! (pw = sgetpwent (buf))) { /* copy bad lines verbatim */
X			(void) fprintf (npwd, "%s\n", buf);
X			continue;
X		}
X		if (pw->pw_passwd[0] == '\0') { /* no password, skip */
X			(void) fprintf (npwd, "%s\n", buf);
X			continue;
X		}
X		setspent ();		/* rewind old shadow file */
X
X		if (spwd = getspnam (pw->pw_name)) {
X			if (putspent (spwd, shadow)) { /* copy old entry */
X				perror ("nshadow");
X				goto error;
X			}
X		} else {		/* need a new entry. */
X			tspwd.sp_namp = pw->pw_name;
X			tspwd.sp_pwdp = pw->pw_passwd;
X			pw->pw_passwd = "x";
X#ifdef	ATT_AGE
X			if (pw->pw_age) { /* copy old password age stuff */
X				if (strlen (pw->pw_age) >= 2) {
X					tspwd.sp_min = c64i (pw->pw_age[1]);
X					tspwd.sp_max = c64i (pw->pw_age[0]);
X				} else {
X					tspwd.sp_min = tspwd.sp_max = -1;
X				}
X				if (strlen (pw->pw_age) == 4)
X					tspwd.sp_lstchg = a64l (&pw->pw_age[2]);
X				else
X					tspwd.sp_lstchg = -1;
X
X				/*
X				 * Convert weeks to days
X				 */
X
X				if (tspwd.sp_min != -1)
X					tspwd.sp_min *= 7;
X
X				if (tspwd.sp_max != -1)
X					tspwd.sp_max *= 7;
X
X				if (tspwd.sp_lstchg != -1)
X					tspwd.sp_lstchg *= 7;
X			} else
X#endif	/* ATT_AGE */
X			{	/* fake up new password age stuff */
X				tspwd.sp_max = MAXDAYS;
X				tspwd.sp_min = MINDAYS;
X				tspwd.sp_lstchg = today;
X			}
X#ifdef	WARNAGE
X			tspwd.sp_warn = WARNAGE;
X			tspwd.sp_inact = tspwd.sp_expire = tspwd.sp_flag = -1;
X#else
X			tspwd.sp_warn = tspwd.sp_inact = tspwd.sp_expire =
X				tspwd.sp_flag = -1;
X#endif
X			if (putspent (&tspwd, shadow)) { /* output entry */
X				perror ("nshadow");
X				goto error;
X			}
X		}
X		(void) fprintf (npwd, "%s:%s:%d:%d:%s:%s:",
X				pw->pw_name, pw->pw_passwd,
X				pw->pw_uid, pw->pw_gid,
X				pw->pw_gecos, pw->pw_dir);
X
X		if (fprintf (npwd, "%s\n",
X				pw->pw_shell ? pw->pw_shell:"") == EOF) {
X			perror ("npasswd");
X			goto error;
X		}
X	}
X	endspent ();
X
X	if (ferror (npwd) || ferror (shadow)) {
X		perror ("pwconv");
Xerror:
X		(void) unlink ("npasswd");
X		(void) unlink ("nshadow");
X		exit (1);
X	}
X	(void) fclose (pwd);
X	(void) fclose (npwd);
X	(void) fclose (shadow);
X
X	exit (0);
X}
SHAR_EOF
if test 4442 -ne "`wc -c < 'pwconv.c'`"
then
	echo shar: "error transmitting 'pwconv.c'" '(should have been 4442 characters)'
fi
fi
echo shar: "extracting 'failure.c'" '(2948 characters)'
if test -f 'failure.c'
then
	echo shar: "will not over-write existing file 'failure.c'"
else
sed 's/^X//' << \SHAR_EOF > 'failure.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X#include <sys/types.h>
X#include <fcntl.h>
X#include <time.h>
X#ifndef	BSD
X#include <string.h>
X#include <memory.h>
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X#include "faillog.h"
X#include "config.h"
X
X#ifdef	FTMP
X#include <utmp.h>
X#endif
X
X#ifndef	lint
Xstatic	char	_sccsid[] = "@(#)failure.c	2.3	19:23:48	7/29/90";
X#endif
X
X#ifdef	FAILLOG
X
X#define	DAY	(24L*3600L)
X#define	YEAR	(365L*DAY)
X#define	NOW	(time ((time_t *) 0))
X
Xextern	struct	tm	*localtime ();
Xextern	char	*asctime ();
Xextern	void	failprint ();
X
X/*
X * failure - make failure entry
X */
X
Xvoid
Xfailure (uid, tty, faillog)
Xint	uid;
Xchar	*tty;
Xstruct	faillog	*faillog;
X{
X	int	fd;
X
X	if ((fd = open (FAILFILE, O_RDWR)) < 0)
X		return;
X
X	lseek (fd, (off_t) (sizeof *faillog) * uid, 0);
X	if (read (fd, (char *) faillog, sizeof *faillog)
X			!= sizeof *faillog)
X#ifndef	BSD
X		memset ((void *) faillog, '\0', sizeof *faillog);
X#else
X		bzero ((char *) faillog, sizeof *faillog);
X#endif
X
X	if (faillog->fail_max == 0 || faillog->fail_cnt < faillog->fail_max)
X		faillog->fail_cnt++;
X
X	strncpy (faillog->fail_line, tty, sizeof faillog->fail_line);
X	faillog->fail_time = time ((time_t *) 0);
X
X	lseek (fd, (off_t) (sizeof *faillog) * uid, 0);
X	write (fd, (char *) faillog, sizeof *faillog);
X	close (fd);
X}
X
X/*
X * failcheck - check for failures > allowable
X *
X * failcheck() is called AFTER the password has been validated.
X */
X
Xint
Xfailcheck (uid, faillog, failed)
Xint	uid;
Xstruct	faillog	*faillog;
X{
X	int	fd;
X	int	okay = 1;
X	struct	faillog	fail;
X
X	if ((fd = open (FAILFILE, O_RDWR)) < 0)
X		return (1);
X
X	lseek (fd, (off_t) (sizeof *faillog) * uid, 0);
X	if (read (fd, (char *) faillog, sizeof *faillog) == sizeof *faillog) {
X		if (faillog->fail_max != 0
X				&& faillog->fail_cnt >= faillog->fail_max)
X			okay = 0;
X	}
X	if (!failed && okay) {
X		fail = *faillog;
X		fail.fail_cnt = 0;
X
X		lseek (fd, (off_t) sizeof fail * uid, 0);
X		write (fd, (char *) &fail, sizeof fail);
X	}
X	close (fd);
X
X	return (okay);
X}
X
X/*
X * failprint - print line of failure information
X */
X
Xvoid
Xfailprint (uid, fail)
Xstruct	faillog	*fail;
X{
X	int	fd;
X	struct	tm	*tp;
X	char	*lasttime;
X
X	if (fail->fail_cnt == 0)
X		return;
X
X	tp = localtime (&fail->fail_time);
X	lasttime = asctime (tp);
X	lasttime[24] = '\0';
X
X	if (NOW - fail->fail_time < YEAR)
X		lasttime[19] = '\0';
X	if (NOW - fail->fail_time < DAY)
X		lasttime = lasttime + 11;
X
X	if (*lasttime == ' ')
X		lasttime++;
X
X	printf ("%d %s since last login.  Last was %s on %s.\n",
X		fail->fail_cnt, fail->fail_cnt > 1 ? "failures":"failure",
X		lasttime, fail->fail_line);
X}
X#endif
X
X#ifdef	FTMP
X
Xvoid
Xfailtmp (failent)
Xstruct	utmp	*failent;
X{
X	int	fd;
X
X	if ((fd = open (FTMP, O_WRONLY|O_APPEND)) == -1)
X		return;
X
X	write (fd, (char *) failent, sizeof *failent);
X	close (fd);
X}
X#endif
SHAR_EOF
if test 2948 -ne "`wc -c < 'failure.c'`"
then
	echo shar: "error transmitting 'failure.c'" '(should have been 2948 characters)'
fi
fi
echo shar: "extracting 'utmp.c'" '(2985 characters)'
if test -f 'utmp.c'
then
	echo shar: "will not over-write existing file 'utmp.c'"
else
sed 's/^X//' << \SHAR_EOF > 'utmp.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <sys/types.h>
X#include <utmp.h>
X#include <fcntl.h>
X#ifndef	BSD
X#include <string.h>
X#include <memory.h>
X#define	bzero(a,n)	memset(a, 0, n)
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X#include <stdio.h>
X#include "config.h"
X
X#ifndef	lint
Xstatic	char	sccsid[] = "%W%	%U%	%G%";
X#endif
X
Xextern	struct	utmp	utent;
Xextern	char	name[];
X
Xextern	struct	utmp	*getutent();
Xextern	void	setutent();
Xextern	void	endutent();
Xextern	time_t	time();
Xextern	char	*ttyname();
X
X#define	NO_UTENT \
X	"No utmp entry.  You must exec \"login\" from the lowest level \"sh\""
X
X/*
X * checkutmp - see if utmp file is correct for this process
X *
X *	System V is very picky about the contents of the utmp file
X *	and requires that a slot for the current process exist.
X *	The utmp file is scanned for an entry with the same process
X *	ID.  If no entry exists the process exits with a message.
X */
X
Xvoid
Xcheckutmp (picky)
Xint	picky;
X{
X	struct	utmp	*ut;
X	char	*line;
X#ifndef	NDEBUG
X	int	pid = getppid ();
X#else
X	int	pid = getpid ();
X#endif
X	setutent ();
X
X#ifndef	BSD
X	if (picky) {
X		while (ut = getutent ())
X			if (ut->ut_pid == pid)
X				break;
X
X		if (ut)
X			utent = *ut;
X
X		endutent ();
X
X		if (ut && utent.ut_pid == pid)
X			return;
X
X		puts (NO_UTENT);
X		exit (1);
X	} else {
X		line = ttyname (0);
X		if (strncmp (line, "/dev/", 5) == 0)
X			line += 5;
X
X		strncpy (utent.ut_line, line, sizeof utent.ut_line);
X		if (ut = getutline (&utent))
X			strncpy (utent.ut_id, ut->ut_id, sizeof ut->ut_id);
X
X		strcpy (utent.ut_user, "LOGIN");
X		utent.ut_pid = getpid ();
X		utent.ut_type = LOGIN_PROCESS;
X		time (&utent.ut_time);
X	}
X#endif
X}
X
X/*
X * setutmp - put a USER_PROCESS entry in the utmp file
X *
X *	setutmp changes the type of the current utmp entry to
X *	USER_PROCESS.  the wtmp file will be updated as well.
X */
X
Xvoid
Xsetutmp (name, line)
Xchar	*name;
Xchar	*line;
X{
X	FILE	*wtmp;
X	struct	utmp	utent;
X	int	fd;
X	int	i;
X	int	found = 0;
X
X	if (! (fd = open ("/etc/utmp", O_RDWR)))
X		return;
X
X	while (! found && read (fd, &utent, sizeof utent) == sizeof utent) {
X		if (! strncmp (line, utent.ut_line, sizeof utent.ut_line))
X			found++;
X	}
X	if (! found) {
X		bzero (&utent, sizeof utent);
X		strncpy (utent.ut_line, line, sizeof utent.ut_line);
X	}
X	(void) strncpy (utent.ut_user, name, sizeof utent.ut_user);
X#ifndef	BSD
X	utent.ut_type = USER_PROCESS;
X	utent.ut_pid = getpid ();
X#endif
X	(void) time (&utent.ut_time);
X
X	if (found)
X		lseek (fd, (long) - sizeof utent, 1);
X
X	write (fd, &utent, sizeof utent);
X	close (fd);
X
X	if ((wtmp = fopen (WTMP_FILE, "a+"))) {
X		fwrite (&utent, sizeof utent, 1, wtmp);
X		fclose (wtmp);
X	}
X}
SHAR_EOF
if test 2985 -ne "`wc -c < 'utmp.c'`"
then
	echo shar: "error transmitting 'utmp.c'" '(should have been 2985 characters)'
fi
fi
echo shar: "extracting 'shadow.c'" '(5862 characters)'
if test -f 'shadow.c'
then
	echo shar: "will not over-write existing file 'shadow.c'"
else
sed 's/^X//' << \SHAR_EOF > 'shadow.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include "shadow.h"
X#include "config.h"
X#include <stdio.h>
X
X#ifndef	BSD
X#include <string.h>
X#include <memory.h>
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X
X#ifdef	NDBM
X#include <ndbm.h>
X#include <fcntl.h>
XDBM	*sp_dbm;
Xint	sp_dbm_mode = -1;
Xstatic	int	dbmopened;
Xstatic	int	dbmerror;
X#endif
X
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)shadow.c	3.8	07:57:47	2/8/91";
X#endif
X
Xstatic	FILE	*shadow;
Xstatic	char	spwbuf[BUFSIZ];
Xstatic	struct	spwd	spwd;
X
X#define	FIELDS	9
X#define	OFIELDS	5
X
Xvoid
Xsetspent ()
X{
X	int	mode;
X
X	if (shadow)
X		rewind (shadow);
X	else
X		shadow = fopen (SHADOW, "r");
X
X	/*
X	 * Attempt to open the DBM files if they have never been opened
X	 * and an error has never been returned.
X	 */
X
X#ifdef NDBM
X	if (! dbmerror && ! dbmopened) {
X		int	mode;
X		char	dbmfiles[BUFSIZ];
X
X		strcpy (dbmfiles, SHADOW);
X		strcat (dbmfiles, ".pag");
X
X		if (sp_dbm_mode == -1)
X			mode = O_RDWR;
X		else
X			mode = (sp_dbm_mode == O_RDWR) ? O_RDWR:O_RDONLY;
X
X		if (! (sp_dbm = dbm_open (SHADOW, mode, 0)))
X			dbmerror = 1;
X		else
X			dbmopened = 1;
X	}
X#endif
X}
X
Xvoid
Xendspent ()
X{
X	if (shadow)
X		(void) fclose (shadow);
X
X	shadow = (FILE *) 0;
X#ifdef	NDBM
X	if (dbmopened && sp_dbm) {
X		dbm_close (sp_dbm);
X		sp_dbm = 0;
X	}
X	dbmopened = 0;
X	dbmerror = 0;
X#endif
X}
X
Xstruct spwd *
Xsgetspent (string)
Xchar	*string;
X{
X	char	*fields[FIELDS];
X	char	*cp;
X	char	*cpp;
X	int	atoi ();
X	long	atol ();
X	int	i;
X
X	strncpy (spwbuf, string, BUFSIZ-1);
X	spwbuf[BUFSIZ-1] = '\0';
X
X	if (cp = strrchr (spwbuf, '\n'))
X		*cp = '\0';
X
X	for (cp = spwbuf, i = 0;*cp && i < FIELDS;i++) {
X		fields[i] = cp;
X		while (*cp && *cp != ':')
X			cp++;
X
X		if (*cp)
X			*cp++ = '\0';
X	}
X	if (i == (FIELDS-1))
X		fields[i++] = cp;
X
X	if (*cp || (i != FIELDS && i != OFIELDS))
X		return 0;
X
X	spwd.sp_namp = fields[0];
X	spwd.sp_pwdp = fields[1];
X
X	if ((spwd.sp_lstchg = strtol (fields[2], &cpp, 10)) == 0 && *cpp)
X		return 0;
X	else if (fields[2][0] == '\0')
X		spwd.sp_lstchg = -1;
X
X	if ((spwd.sp_min = strtol (fields[3], &cpp, 10)) == 0 && *cpp)
X		return 0;
X	else if (fields[3][0] == '\0')
X		spwd.sp_min = -1;
X
X	if ((spwd.sp_max = strtol (fields[4], &cpp, 10)) == 0 && *cpp)
X		return 0;
X	else if (fields[4][0] == '\0')
X		spwd.sp_max = -1;
X
X	if (i == OFIELDS) {
X		spwd.sp_warn = spwd.sp_inact = spwd.sp_expire =
X			spwd.sp_flag = -1;
X
X		return &spwd;
X	}
X	if ((spwd.sp_warn = strtol (fields[5], &cpp, 10)) == 0 && *cpp)
X		return 0;
X	else if (fields[5][0] == '\0')
X		spwd.sp_warn = -1;
X
X	if ((spwd.sp_inact = strtol (fields[6], &cpp, 10)) == 0 && *cpp)
X		return 0;
X	else if (fields[6][0] == '\0')
X		spwd.sp_inact = -1;
X
X	if ((spwd.sp_expire = strtol (fields[7], &cpp, 10)) == 0 && *cpp)
X		return 0;
X	else if (fields[7][0] == '\0')
X		spwd.sp_expire = -1;
X
X	if ((spwd.sp_flag = strtol (fields[8], &cpp, 10)) == 0 && *cpp)
X		return 0;
X	else if (fields[8][0] == '\0')
X		spwd.sp_flag = -1;
X
X	return (&spwd);
X}
X
Xstruct spwd
X*fgetspent (fp)
XFILE	*fp;
X{
X	char	buf[BUFSIZ];
X
X	if (! fp)
X		return (0);
X
X	if (fgets (buf, BUFSIZ, fp) == (char *) 0)
X		return (0);
X
X	return sgetspent (buf);
X}
X
Xstruct spwd
X*getspent ()
X{
X	if (! shadow)
X		setspent ();
X
X	return (fgetspent (shadow));
X}
X
Xstruct spwd
X*getspnam (name)
Xchar	*name;
X{
X	struct	spwd	*sp;
X#ifdef NDBM
X	datum	key;
X	datum	content;
X#endif
X
X	setspent ();
X
X#ifdef NDBM
X
X	/*
X	 * If the DBM file are now open, create a key for this UID and
X	 * try to fetch the entry from the database.  A matching record
X	 * will be unpacked into a static structure and returned to
X	 * the user.
X	 */
X
X	if (dbmopened) {
X		key.dsize = strlen (name);
X		key.dptr = name;
X
X		content = dbm_fetch (sp_dbm, key);
X		if (content.dptr != 0) {
X			memcpy (spwbuf, content.dptr, content.dsize);
X			spw_unpack (spwbuf, content.dsize, &spwd);
X			return &spwd;
X		}
X	}
X#endif
X	while ((sp = getspent ()) != (struct spwd *) 0) {
X		if (strcmp (name, sp->sp_namp) == 0)
X			return (sp);
X	}
X	return (0);
X}
X
Xint
Xputspent (sp, fp)
Xstruct	spwd	*sp;
XFILE	*fp;
X{
X	int	errors = 0;
X
X	if (! fp || ! sp)
X		return -1;
X
X	if (fprintf (fp, "%s:%s:", sp->sp_namp, sp->sp_pwdp) < 0)
X		errors++;
X
X	if (sp->sp_lstchg != -1) {
X		if (fprintf (fp, "%ld:", sp->sp_lstchg) < 0)
X			errors++;
X	} else if (putc (':', fp) == EOF)
X		errors++;
X
X	if (sp->sp_min != -1) {
X		if (fprintf (fp, "%ld:", sp->sp_min) < 0)
X			errors++;
X	} else if (putc (':', fp) == EOF)
X		errors++;
X
X	if (sp->sp_max != -1) {
X		if (fprintf (fp, "%ld", sp->sp_max) < 0)
X			errors++;
X	}
X
X	/*
X	 * See if the structure has any of the SVR4 fields in
X	 * it.  If none of those fields have any data there is
X	 * no reason to write them out since they will be filled
X	 * in the same way when they are read back in.  Otherwise
X	 * there is at least one SVR4 field that must be output.
X	 */
X
X	if (sp->sp_warn == -1 && sp->sp_inact == -1 &&
X			sp->sp_expire == -1 && sp->sp_flag == -1) {
X		if (putc ('\n', fp) == EOF || errors)
X			return -1;
X		else
X			return 0;
X	} else if (putc (':', fp) == EOF)
X		errors++;
X
X	if (sp->sp_warn != -1) {
X		if (fprintf (fp, "%ld:", sp->sp_warn) < 0)
X			errors++;
X	} else if (putc (':', fp) == EOF)
X		errors++;
X
X	if (sp->sp_inact != -1) {
X		if (fprintf (fp, "%ld:", sp->sp_inact) < 0)
X			errors++;
X	} else if (putc (':', fp) == EOF)
X		errors++;
X
X	if (sp->sp_expire != -1) {
X		if (fprintf (fp, "%ld:", sp->sp_expire) < 0)
X			errors++;
X	} else if (putc (':', fp) == EOF)
X		errors++;
X
X	if (sp->sp_flag != -1) {
X		if (fprintf (fp, "%ld", sp->sp_flag) < 0)
X			errors++;
X	}
X	if (putc ('\n', fp) == EOF)
X		errors++;
X
X	if (errors)
X		return -1;
X	else
X		return 0;
X}
SHAR_EOF
if test 5862 -ne "`wc -c < 'shadow.c'`"
then
	echo shar: "error transmitting 'shadow.c'" '(should have been 5862 characters)'
fi
fi
exit 0
#	End of shell archive
-- 
John F. Haugh II        | Distribution to  | UUCP: ...!cs.utexas.edu!rpp386!jfh
Ma Bell: (512) 255-8251 | GEnie PROHIBITED :-) |  Domain: jfh@rpp386.cactus.org
"If liberals interpreted the 2nd Amendment the same way they interpret the
 rest of the Constitution, gun ownership would be mandatory."

jfh@rpp386.cactus.org (John F Haugh II) (05/16/91)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	gpmain.c
#	chage.c
#	pwent.c
#	valid.c
#	setup.c
#	entry.c
#	ttytype.c
#	port.h
# This archive created: Sun Mar  3 13:27:23 1991
# By:	John F Haugh II (River Parishes Programming, Austin TX)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'gpmain.c'" '(9448 characters)'
if test -f 'gpmain.c'
then
	echo shar: "will not over-write existing file 'gpmain.c'"
else
sed 's/^X//' << \SHAR_EOF > 'gpmain.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <sys/types.h>
X#include <stdio.h>
X#include "pwd.h"
X#include <grp.h>
X#include <fcntl.h>
X#include <signal.h>
X#include <errno.h>
X#ifndef	BSD
X#include <termio.h>
X#ifdef SYS3
X#include <sys/ioctl.h>
X#endif
X#include <string.h>
X#ifndef	SYS3
X#include <memory.h>
X#endif
X#else
X#include <sgtty.h>
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X#include "config.h"
X
X#ifndef	PASSLENGTH
X#define	PASSLENGTH	5
X#endif
X
X#ifndef	lint
Xstatic	char	_sccsid[] = "@(#)gpmain.c	3.4	12:30:43	12/12/90";
X#endif
X
Xchar	name[BUFSIZ];
Xchar	pass[BUFSIZ];
Xchar	pass2[BUFSIZ];
X
Xstruct	group	grent;
X
Xchar	*Prog;
Xchar	*user;
Xchar	*group;
Xint	aflg;
Xint	dflg;
Xint	rflg;
Xint	Rflg;
X
X#ifndef	RETRIES
X#define	RETRIES	3
X#endif
X
Xextern	char	*l64a ();
Xextern	char	*crypt ();
Xextern	char	*pw_encrypt ();
Xextern	int	errno;
Xextern	long	a64l ();
Xextern	void	entry ();
Xextern	time_t	time ();
X
X/*
X * usage - display usage message
X */
X
Xvoid
Xusage ()
X{
X	fprintf (stderr, "usage: %s [ -r|R ] group\n", Prog);
X	fprintf (stderr, "       %s [ -a user ] group\n", Prog);
X	fprintf (stderr, "       %s [ -d user ] group\n", Prog);
X	exit (1);
X}
X
X/*
X * add_list - add a member to a list of group members
X *
X *	the array of member names is searched for the new member
X *	name, and if not present it is added to a freshly allocated
X *	list of users.
X */
X
Xchar **
Xadd_list (list, member)
Xchar	**list;
Xchar	*member;
X{
X	int	i;
X	int	found = 0;
X	char	**tmp;
X
X	for (i = 0;!found && list[i] != (char *) 0;i++)
X		if (strcmp (list[i], member) == 0)
X			found++;
X
X	tmp = (char **) malloc ((i + 2) * sizeof member);
X
X	for (i = 0;list[i] != (char *) 0;i++)
X		tmp[i] = list[i];
X
X	if (! found)
X		tmp[i++] = strdup (member);
X
X	tmp[i] = (char *) 0;
X	return tmp;
X}
X
X/*
X * del_list - delete a group member from a list of members
X *
X *	del_list searches a list of group members, copying the
X *	members which do not match "member" to a newly allocated
X *	list.
X */
X
Xchar **
Xdel_list (list, member)
Xchar	**list;
Xchar	*member;
X{
X	int	i, j;
X	char	**tmp;
X
X	for (j = i = 0;list[i] != (char *) 0;i++)
X		if (strcmp (list[i], member))
X			j++;
X
X	tmp = (char **) malloc ((j + 1) * sizeof member);
X
X	for (j = i = 0;list[i] != (char *) 0;i++)
X		if (strcmp (list[i], member) != 0)
X			tmp[j++] = list[i];
X
X	tmp[j] = (char *) 0;
X
X	return tmp;
X}
X
Xint
Xmain (argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	extern	int	optind;
X	extern	char	*optarg;
X	int	flag;
X	int	i;
X	void	die ();
X	char	*cp;
X	char	*getlogin ();
X	int	amroot;
X	int	retries;
X	int	ruid = getuid();
X	struct	group	*gr;
X	struct	group	*getgrnam ();
X	struct	group	*sgetgrent ();
X	struct	passwd	*pw;
X	struct	passwd	*getpwuid ();
X	struct	passwd	*getpwnam ();
X
X	/*
X	 * Make a note of whether or not this command was invoked
X	 * by root.  This will be used to bypass certain checks
X	 * later on.  Also, set the real user ID to match the
X	 * effective user ID.  This will prevent the invoker from
X	 * issuing signals which would interfer with this command.
X	 */
X
X	amroot = getuid () == 0;
X	setuid (geteuid ());
X	Prog = argv[0];
X	setbuf (stdout, (char *) 0);
X	setbuf (stderr, (char *) 0);
X
X	while ((flag = getopt (argc, argv, "a:d:grR")) != EOF) {
X		switch (flag) {
X			case 'a':	/* add a user */
X				aflg++;
X				user = optarg;
X				break;
X			case 'd':	/* delete a user */
X				dflg++;
X				user = optarg;
X				break;
X			case 'g':	/* no-op from normal password */
X				break;
X			case 'r':	/* remove group password */
X				rflg++;
X				break;
X			case 'R':	/* restrict group password */
X				Rflg++;
X				break;
X			default:
X				usage ();
X		}
X	}
X
X	/*
X	 * Make sure exclusive flags are exclusive
X	 */
X
X	if (aflg + dflg + rflg + Rflg > 1)
X		usage ();
X
X	/*
X	 * Unless the mode is -a, -d or -r, the input and output must
X	 * both be a tty.  The typical keyboard signals are caught
X	 * so the termio modes can be restored.
X	 */
X
X	if (! aflg && ! dflg && ! rflg && ! Rflg) {
X		if (! isatty (0) || ! isatty (1))
X			exit (1);
X
X		die (0);			/* save tty modes */
X
X		signal (SIGHUP, die);
X		signal (SIGINT, die);
X		signal (SIGQUIT, die);
X		signal (SIGTERM, die);
X	}
X
X	/*
X	 * Determine the name of the user that invoked this command.
X	 * This is really hit or miss because there are so many ways
X	 * that command can be executed and so many ways to trip up
X	 * the routines that report the user name.
X	 */
X
X	if ((cp = getlogin ()) && (pw = getpwnam (cp)) && pw->pw_uid == ruid) {
X					/* need user name */
X		(void) strcpy (name, cp);
X	} else if (pw = getpwuid (ruid)) /* get it from password file */
X		strcpy (name, pw->pw_name);
X	else {				/* can't find user name! */
X		fprintf (stderr, "Who are you?\n");
X		exit (1);
X	}
X	if (! (pw = getpwnam (name)))
X		goto failure;		/* can't get my name ... */
X		
X	/*
X	 * Get the name of the group that is being affected.  The group
X	 * entry will be completely replicated so it may be modified
X	 * later on.
X	 */
X
X	if (! (group = argv[optind]))
X		usage ();
X
X	if (! (gr = getgrnam (group))) {
X		fprintf (stderr, "unknown group: %s\n", group);
X		exit (1);
X	}
X	grent = *gr;
X	grent.gr_name = strdup (gr->gr_name);
X	grent.gr_passwd = strdup (gr->gr_passwd);
X
X	for (i = 0;gr->gr_mem[i];i++)
X		;
X	grent.gr_mem = (char **) malloc ((i + 1) * sizeof (char *));
X	for (i = 0;gr->gr_mem[i];i++)
X		grent.gr_mem[i] = strdup (gr->gr_mem[i]);
X	grent.gr_mem[i] = (char *) 0;
X
X	/*
X	 * The policy for changing a group is that 1) you must be root
X	 * or 2) you must be the first listed member of the group.  The
X	 * first listed member of a group can do anything to that group
X	 * that the root user can.
X	 */
X
X	if (! amroot) {
X		if (grent.gr_mem[0] == (char *) 0)
X			goto failure;
X
X		if (strcmp (grent.gr_mem[0], name) != 0)
X			goto failure;
X	}
X
X	/*
X	 * Removing a password is straight forward.  Just set the
X	 * password field to a "".
X	 */
X
X	if (rflg) {
X		grent.gr_passwd = "";
X		goto output;
X	} else if (Rflg) {
X		grent.gr_passwd = "!";
X		goto output;
X	}
X
X	/*
X	 * Adding a member to a member list is pretty straightforward
X	 * as well.  Call the appropriate routine and split.
X	 */
X
X	if (aflg) {
X		if (getpwnam (user) == (struct passwd *) 0) {
X			fprintf (stderr, "%s: unknown user %s\n", Prog, user);
X			exit (1);
X		}
X		printf ("Adding user %s to group %s\n", user, group);
X		grent.gr_mem = add_list (grent.gr_mem, user);
X		goto output;
X	}
X
X	/*
X	 * Removing a member from the member list is the same deal
X	 * as adding one, except the routine is different.
X	 */
X
X	if (dflg) {
X		for (i = 0;grent.gr_mem[i];i++)
X			if (strcmp (user, grent.gr_mem[i]) == 0)
X				break;
X
X		if (grent.gr_mem[i] == (char *) 0) {
X			fprintf (stderr, "%s: unknown member %s\n", Prog, user);
X			exit (1);
X		}
X		printf ("Removing user %s from group %s\n", user, group);
X		grent.gr_mem = del_list (grent.gr_mem, user);
X		goto output;
X	}
X
X	/*
X	 * A new password is to be entered and it must be encrypted,
X	 * etc.  The password will be prompted for twice, and both
X	 * entries must be identical.  There is no need to validate
X	 * the old password since the invoker is either the group
X	 * owner, or root.
X	 */
X
X	printf ("Changing the password for group %s\n", group);
X
X	for (retries = 0;retries < RETRIES;retries++) {
X		if (! (cp = getpass ("New Password:")))
X			exit (1);
X		else
X			strcpy (pass, cp);
X
X		if (! (cp = getpass ("Re-enter new password:")))
X			exit (1);
X		else
X			strcpy (pass2, cp);
X
X		if (strcmp (pass, pass2) == 0)
X			break;
X
X		if (retries + 1 < RETRIES)
X			puts ("They don't match; try again");
X	}
X	if (retries == RETRIES) {
X		fprintf (stderr, "%s: Try again later\n", Prog);
X		exit (1);
X	}
X	grent.gr_passwd = pw_encrypt (pass, (char *) 0);
X
X	/*
X	 * This is the common arrival point to output the new group
X	 * file.  The freshly crafted entry is in allocated space.
X	 * The group file will be locked and opened for writing.  The
X	 * new entry will be output, etc.
X	 */
X
Xoutput:
X	signal (SIGHUP, SIG_IGN);
X	signal (SIGINT, SIG_IGN);
X	signal (SIGQUIT, SIG_IGN);
X
X	if (! gr_lock ()) {
X		fprintf (stderr, "%s: can't get lock\n", Prog);
X		exit (1);
X	}
X	if (! gr_open (O_RDWR)) {
X		fprintf (stderr, "%s: can't open file\n", Prog);
X		exit (1);
X	}
X	if (! gr_update (&grent)) {
X		fprintf (stderr, "%s: can't update entry\n", Prog);
X		exit (1);
X	}
X	if (! gr_close ()) {
X		fprintf (stderr, "%s: can't re-write file\n", Prog);
X		exit (1);
X	}
X	if (! gr_unlock ()) {
X		fprintf (stderr, "%s: can't unlock file\n", Prog);
X		exit (1);
X	}
X#ifdef	NDBM
X	if (! gr_dbm_update (&grent)) {
X		fprintf (stderr, "%s: can't update DBM files\n", Prog);
X		exit (1);
X	}
X#endif
X	exit (0);
X	/*NOTREACHED*/
X
Xfailure:
X	fprintf (stderr, "Permission denied.\n");
X	exit (1);
X	/*NOTREACHED*/
X}
X
X/*
X * die - set or reset termio modes.
X *
X *	die() is called before processing begins.  signal() is then
X *	called with die() as the signal handler.  If signal later
X *	calls die() with a signal number, the terminal modes are
X *	then reset.
X */
X
Xvoid	die (killed)
Xint	killed;
X{
X#ifdef	BSD
X	static	struct	sgtty	sgtty;
X
X	if (killed)
X		stty (0, &sgtty);
X	else
X		gtty (0, &sgtty);
X#else
X	static	struct	termio	sgtty;
X
X	if (killed)
X		ioctl (0, TCSETA, &sgtty);
X	else
X		ioctl (0, TCGETA, &sgtty);
X#endif
X	if (killed) {
X		putchar ('\n');
X		fflush (stdout);
X		exit (killed);
X	}
X}
SHAR_EOF
if test 9448 -ne "`wc -c < 'gpmain.c'`"
then
	echo shar: "error transmitting 'gpmain.c'" '(should have been 9448 characters)'
fi
fi
echo shar: "extracting 'chage.c'" '(15243 characters)'
if test -f 'chage.c'
then
	echo shar: "will not over-write existing file 'chage.c'"
else
sed 's/^X//' << \SHAR_EOF > 'chage.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <sys/types.h>
X#include <syslog.h>
X#include <stdio.h>
X#include <fcntl.h>
X#include <signal.h>
X#include <ctype.h>
X#include <time.h>
X
X#ifndef	lint
Xstatic	char	sccsid[] = "%W%	%U%	%G%";
X#endif
X
X/*
X * Set up some BSD defines so that all the BSD ifdef's are
X * kept right here 
X */
X
X#ifndef	BSD
X#include <string.h>
X#include <memory.h>
X#define	bzero(a,n)	memset(a, 0, n)
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X
X#include "config.h"
X#include "pwd.h"
X#include "shadow.h"
X
X/*
X * Global variables
X */
X
Xchar	*Prog;
Xlong	mindays;
Xlong	maxdays;
Xlong	lastday;
Xlong	warndays;
Xlong	inactdays;
Xlong	expdays;
Xvoid	cleanup();
X
X/*
X * External identifiers
X */
X
Xextern	long	a64l();
Xextern	int	pw_lock(), pw_open(),
X		pw_unlock(), pw_close(),
X		pw_update();
Xextern	struct	passwd	*pw_locate();
Xextern	int	spw_lock(), spw_open(),
X		spw_unlock(), spw_close(),
X		spw_update();
Xextern	struct	spwd	*spw_locate();
Xextern	int	optind;
Xextern	char	*optarg;
Xextern	char	*getlogin ();
X#ifdef	NDBM
Xextern	int	pw_dbm_mode;
Xextern	int	sp_dbm_mode;
X#endif
X
X/*
X * Password aging constants
X *
X *	DAY - seconds in a day
X *	WEEK - seconds in a week
X *	SCALE - convert from clock to aging units
X */
X
X#define	DAY	(24L*3600L)
X#define	WEEK	(7*DAY)
X
X#ifdef	ITI_AGING
X#define	SCALE	(1)
X#else
X#define	SCALE	(DAY)
X#endif
X
X/*
X * days and juldays are used to compute the number of days in the
X * current month, and the cummulative number of days in the preceding
X * months.  they are declared so that january is 1, not 0.
X */
X
Xstatic	short	days[13] = { 0,
X	31,	28,	31,	30,	31,	30,	/* JAN - JUN */
X	31,	31,	30,	31,	30,	31 };	/* JUL - DEC */
X
Xstatic	short	juldays[13] = { 0,
X	0,	31,	59,	90,	120,	151,	/* JAN - JUN */
X	181,	212,	243,	273,	304,	334 };	/* JUL - DEC */
X
X/*
X * #defines for messages.  This facilities foreign language conversion
X * since all messages are defined right here.
X */
X
X#define	USAGE \
X"Usage: %s [ -l ] [ -m min_days ] [ -M max_days ] [ -W warn ]\n\
X       [ -I inactive ] [ -E expire ] [ -d last_day ] user\n"
X#define	DBMERROR	"Error updating the DBM password entry.\n"
X#define	DBMERROR2	"error updating DBM shadow entry.\n"
X
X/*
X * usage - print command line syntax and exit
X */
X
Xvoid
Xusage ()
X{
X	fprintf (stderr, USAGE, Prog);
X	exit (1);
X}
X
X/*
X * strtoday - compute the number of days since 1970.
X *
X * the total number of days prior to the current date is
X * computed.  january 1, 1970 is used as the origin with
X * it having a day number of 0.  the gmtime() routine is
X * used to prevent confusion regarding time zones.
X */
X
Xlong
Xstrtoday (str)
Xchar	*str;
X{
X	char	slop[2];
X	int	month;
X	int	day;
X	int	year;
X	long	total;
X
X	/*
X	 * start by separating the month, day and year.  this is
X	 * a chauvanistic program - it only takes date input in
X	 * the standard USA format.
X	 */
X
X	if (sscanf (str, "%d/%d/%d%c", &month, &day, &year, slop) != 3)
X		return -1;
X
X	/*
X	 * the month, day of the month, and year are checked for
X	 * correctness and the year adjusted so it falls between
X	 * 1970 and 2069.
X	 */
X
X	if (month < 1 || month > 12)
X		return -1;
X
X	if (day < 1)
X		return -1;
X
X	if ((month != 2 || (year % 4) != 0) && day > days[month])
X		return -1;
X	else if ((month == 2 && (year % 4) == 0) && day > 29)
X		return -1;
X
X	if (year < 0)
X		return -1;
X	else if (year < 69)
X		year += 2000;
X	else if (year < 99)
X		year += 1900;
X
X	if (year < 1970 || year > 2069)
X		return -1;
X
X	/*
X	 * the total number of days is the total number of days in all
X	 * the whole years, plus the number of leap days, plus the
X	 * number of days in the whole months preceding, plus the number
X	 * of days so far in the month.
X	 */
X
X	total = ((year - 1970) * 365) + (((year + 1) - 1970) / 4);
X	total += juldays[month] + (month > 2 && (year % 4) == 0 ? 1:0);
X	total += day - 1;
X
X	return total;
X}
X
X/*
X * new_fields - change the user's password aging information interactively.
X *
X * prompt the user for all of the password age values.  set the fields
X * from the user's response, or leave alone if nothing was entered.  the
X * value (-1) is used to indicate the field should be removed if possible.
X * any other negative value is an error.  very large positive values will
X * be handled elsewhere.
X */
X
Xint
Xnew_fields ()
X{
X	char	buf[BUFSIZ];
X	char	*cp;
X	long	value;
X	struct	tm	*tp;
X
X	printf ("Enter the new value, or press return for the default\n\n");
X
X	sprintf (buf, "%ld", mindays);
X	change_field (buf, "Minimum Password Age");
X	if (((mindays = strtol (buf, &cp, 10)) == 0 && *cp) || mindays < -1)
X		return 0;
X
X	sprintf (buf, "%ld", maxdays);
X	change_field (buf, "Maximum Password Age");
X	if (((maxdays = strtol (buf, &cp, 10)) == 0 && *cp) || maxdays < -1)
X		return 0;
X
X	value = lastday * SCALE;
X	tp = gmtime (&value);
X	sprintf (buf, "%02d/%02d/%02d",
X		tp->tm_mon + 1, tp->tm_mday, tp->tm_year);
X	change_field (buf, "Last Password Change (MM/DD/YY)");
X	if (strcmp (buf, "12/31/69") == 0)
X		lastday = -1;
X	else if ((lastday = strtoday (buf)) == -1)
X		return 0;
X
X	sprintf (buf, "%ld", warndays);
X	change_field (buf, "Password Expiration Warning");
X	if (((warndays = strtol (buf, &cp, 10)) == 0 && *cp) || warndays < -1)
X		return 0;
X
X	sprintf (buf, "%ld", inactdays);
X	change_field (buf, "Password Inactive");
X	if (((inactdays = strtol (buf, &cp, 10)) == 0 && *cp) || inactdays < -1)
X		return 0;
X
X	value = expdays * SCALE;
X	tp = gmtime (&value);
X	sprintf (buf, "%02d/%02d/%02d",
X		tp->tm_mon + 1, tp->tm_mday, tp->tm_year);
X	change_field (buf, "Account Expiration Date (MM/DD/YY)");
X	if (strcmp (buf, "12/31/69") == 0)
X		expdays = -1;
X	else if ((expdays = strtoday (buf)) == -1)
X		return 0;
X
X	return 1;
X}
X
X/*
X * list_fields - display the current values of the expiration fields
X *
X * display the password age information from the password fields.  date
X * values will be displayed as a calendar date, or the word "Never" if
X * the date is 1/1/70, which is day number 0.
X */
X
Xvoid
Xlist_fields ()
X{
X	struct	tm	*tp;
X	char	*cp;
X	long	changed;
X	long	expires;
X
X	/*
X	 * Start with the easy numbers - the number of days before the
X	 * password can be changed, the number of days after which the
X	 * password must be chaged, the number of days before the
X	 * password expires that the user is told, and the number of
X	 * days after the password expires that the account becomes
X	 * unusable.
X	 */
X
X	printf ("Minimum:\t%d\n", mindays);
X	printf ("Maximum:\t%d\n", maxdays);
X	printf ("Warning:\t%d\n", warndays);
X	printf ("Inactive:\t%d\n", inactdays);
X
X	/*
X	 * The "last change" date is either "Never" or the date the
X	 * password was last modified.  The date is the number of
X	 * days since 1/1/1970.
X	 */
X
X	printf ("Last Change:\t\t");
X	if (changed <= 0) {
X		printf ("Never\n");
X	} else {
X		changed = lastday * SCALE;
X		tp = gmtime (&changed);
X		cp = asctime (tp);
X		printf ("%6.6s, %4.4s\n", cp + 4, cp + 20);
X	}
X
X	/*
X	 * The password expiration date is determined from the last
X	 * change date plus the number of days the password is valid
X	 * for.
X	 */
X
X	printf ("Password Expires:\t");
X	if (changed <= 0 || maxdays >= 10000*(DAY/SCALE) || maxdays <= 0) {
X		printf ("Never\n");
X	} else {
X		expires = changed + maxdays * SCALE;
X		tp = gmtime (&expires);
X		cp = asctime (tp);
X		printf ("%6.6s, %4.4s\n", cp + 4, cp + 20);
X	}
X
X	/*
X	 * The account becomes inactive if the password is expired
X	 * for more than "inactdays".  The expiration date is calculated
X	 * and the number of inactive days is added.  The resulting date
X	 * is when the active will be disabled.
X	 */
X
X	printf ("Password Inactive:\t");
X	if (changed <= 0 || inactdays <= 0 ||
X			maxdays >= 10000*(DAY/SCALE) || maxdays <= 0) {
X		printf ("Never\n");
X	} else {
X		expires = changed + (maxdays + inactdays) * SCALE;
X		tp = gmtime (&expires);
X		cp = asctime (tp);
X		printf ("%6.6s, %4.4s\n", cp + 4, cp + 20);
X	}
X
X	/*
X	 * The account will expire on the given date regardless of the
X	 * password expiring or not.
X	 */
X
X	printf ("Account Expires:\t");
X	if (expdays <= 0) {
X		printf ("Never\n");
X	} else {
X		expires = expdays * SCALE;
X		tp = gmtime (&expires);
X		cp = asctime (tp);
X		printf ("%6.6s, %4.4s\n", cp + 4, cp + 20);
X	}
X}
X
X/*
X * chage - change a user's password aging information
X *
X *	This command controls the password aging information.
X *
X *	The valid options are
X *
X *	-m	minimum number of days before password change (*)
X *	-M	maximim number of days before password change (*)
X *	-d	last password change date (*)
X *	-l	last password change date
X *	-W	expiration warning days (*)
X *	-I	password inactive after expiration (*)
X *	-E	account expiration date (*)
X *
X *	(*) requires root permission to execute.
X *
X *	All of the time fields are entered in the internal format
X *	which is either seconds or days.
X */
X
Xint
Xmain (argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	int	flag;
X	int	lflg;
X	int	mflg;
X	int	Mflg;
X	int	dflg;
X	int	Wflg;
X	int	Iflg;
X	int	Eflg;
X	int	ruid = getuid ();
X	struct	passwd	*pw;
X	struct	passwd	pwent;
X	struct	spwd	*sp;
X	struct	spwd	spwd;
X	char	name[BUFSIZ];
X
X	/*
X	 * Get the program name so that error messages can use it.
X	 */
X
X	if (Prog = strrchr (argv[0], '/'))
X		Prog++;
X	else
X		Prog = argv[0];
X
X	openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
X#ifdef	NDBM
X	sp_dbm_mode = O_RDWR;
X	pw_dbm_mode = O_RDWR;
X#endif
X
X	/*
X	 * Parse the flags.  The difference between password file
X	 * formats includes the number of fields, and whether the
X	 * dates are entered as days or weeks.  Shadow password
X	 * file info =must= be entered in days, while regular
X	 * password file info =must= be entered in weeks.
X	 */
X
X	while ((flag = getopt (argc, argv, "lm:M:W:I:E:d:")) != EOF) {
X		switch (flag) {
X			case 'l':
X				lflg++;
X				break;
X			case 'm':
X				mflg++;
X				mindays = strtol (optarg, 0, 10);
X				break;
X			case 'M':
X				Mflg++;
X				maxdays = strtol (optarg, 0, 10);
X				break;
X			case 'd':
X				dflg++;
X				lastday = strtol (optarg, 0, 10);
X				break;
X			case 'W':
X				Wflg++;
X				warndays = strtol (optarg, 0, 10);
X				break;
X			case 'I':
X				Iflg++;
X				inactdays = strtol (optarg, 0, 10);
X				break;
X			case 'E':
X				Eflg++;
X				expdays = strtol (optarg, 0, 10);
X				break;
X			default:
X				usage ();
X		}
X	}
X
X	/*
X	 * Make certain the flags do not conflict and that there is
X	 * a user name on the command line.
X	 */
X
X	if (argc != optind + 1)
X		usage ();
X
X	if (lflg && (mflg || Mflg || dflg || Wflg || Iflg || Eflg)) {
X		fprintf (stderr, "%s: do not include \"l\" with other flags\n",
X			Prog);
X		usage ();
X	}
X
X	/*
X	 * An unprivileged user can ask for their own aging information,
X	 * but only root can change it, or list another user's aging
X	 * information.
X	 */
X
X	if (ruid != 0 && ! lflg) {
X		fprintf (stderr, "%s: permission denied\n", Prog);
X		exit (1);
X	}
X
X	/*
X	 * Lock and open the password file.  This loads all of the
X	 * password file entries into memory.  Then we get a pointer
X	 * to the password file entry for the requested user.
X	 */
X
X	if (! pw_lock ()) {
X		fprintf (stderr, "%s: can't lock password file\n", Prog);
X		exit (1);
X	}
X	if (! pw_open (ruid != 0 || lflg ? O_RDONLY:O_RDWR)) {
X		fprintf (stderr, "%s: can't open password file\n", Prog);
X		cleanup (1);
X		exit (1);
X	}
X	if (! (pw = pw_locate (argv[optind]))) {
X		fprintf (stderr, "%s: unknown user: %s\n", Prog, argv[optind]);
X		cleanup (1);
X		exit (1);
X	}
X
X	/*
X	 * For shadow password files we have to lock the file and
X	 * read in the entries as was done for the password file.
X	 * The user entries does not have to exist in this case;
X	 * a new entry will be created for this user if one does
X	 * not exist already.
X	 */
X
X	if (! spw_lock ()) {
X		fprintf (stderr, "%s: can't lock shadow file\n", Prog);
X		cleanup (1);
X		exit (1);
X	}
X	if (! spw_open ((ruid != 0 || lflg) ? O_RDONLY:O_RDWR)) {
X		fprintf (stderr, "%s: can't open shadow file\n", Prog);
X		cleanup (2);
X		exit (1);
X	}
X	if (sp = spw_locate (argv[optind]))
X		spwd = *sp;
X
X	strcpy (name, pw->pw_name);
X	pwent = *pw;
X
X	/*
X	 * Set the fields that aren't being set from the command line
X	 * from the password file.
X	 */
X
X	if (sp) {
X		if (! Mflg)
X			maxdays = spwd.sp_max;
X		if (! mflg)
X			mindays = spwd.sp_min;
X		if (! dflg)
X			lastday = spwd.sp_lstchg;
X		if (! Wflg)
X			warndays = spwd.sp_warn;
X		if (! Iflg)
X			inactdays = spwd.sp_inact;
X		if (! Eflg)
X			expdays = spwd.sp_expire;
X	} else
X#ifdef	ATT_AGE
X	{
X		if (pwent.pw_age && strlen (pwent.pw_age) >= 2) {
X			if (! Mflg)
X				maxdays = c64i (pwent.pw_age[0]) * (WEEK/SCALE);
X			if (! mflg)
X				mindays = c64i (pwent.pw_age[1]) * (WEEK/SCALE);
X			if (! dflg && strlen (pwent.pw_age) == 4)
X				lastday = a64l (pwent.pw_age+2) * (WEEK/SCALE);
X		} else {
X			mindays = 0;
X			maxdays = 10000L * (DAY/SCALE);
X			lastday = -1;
X		}
X		warndays = inactdays = expdays = -1;
X	}
X#endif
X
X	/*
X	 * Print out the expiration fields if the user has
X	 * requested the list option.
X	 */
X
X	if (lflg) {
X		if (ruid != 0 && ruid != pw->pw_uid) {
X			fprintf (stderr, "%s: permission denied\n", Prog);
X			exit (1);
X		}
X		list_fields ();
X		cleanup (2);
X		exit (0);
X	}
X
X	/*
X	 * If none of the fields were changed from the command line,
X	 * let the user interactively change them.
X	 */
X
X	if (! mflg && ! Mflg && ! dflg && ! Wflg && ! Iflg && ! Eflg) {
X		printf ("Changing the aging information for %s\n", name);
X		if (! new_fields ()) {
X			fprintf (stderr, "%s: error changing fields\n", Prog);
X			cleanup (2);
X			exit (1);
X		}
X	}
X
X	/*
X	 * There was no shadow entry.  The new entry will have the
X	 * encrypted password transferred from the normal password
X	 * file along with the aging information.
X	 */
X
X	if (sp == 0) {
X		sp = &spwd;
X		bzero (&spwd, sizeof spwd);
X
X		sp->sp_namp = pw->pw_name;
X		sp->sp_pwdp = pw->pw_passwd;
X		sp->sp_flag = -1;
X
X		pwent.pw_passwd = "!";
X#ifdef	ATT_AGE
X		pwent.pw_age = "";
X#endif
X		if (! pw_update (&pwent)) {
X			fprintf (stderr, "%s: can't update password file\n",
X				Prog);
X			cleanup (2);
X			exit (1);
X		}
X#if defined(DBM) || defined(NDBM)
X		(void) pw_dbm_update (&pwent);
X#endif
X	}
X
X	/*
X	 * Copy the fields back to the shadow file entry and
X	 * write the modified entry back to the shadow file.
X	 * Closing the shadow and password files will commit
X	 * any changes that have been made.
X	 */
X
X	sp->sp_max = maxdays;
X	sp->sp_min = mindays;
X	sp->sp_lstchg = lastday;
X	sp->sp_warn = warndays;
X	sp->sp_inact = inactdays;
X	sp->sp_expire = expdays;
X
X	if (! spw_update (sp)) {
X		fprintf (stderr, "%s: can't update shadow file\n", Prog);
X		cleanup (2);
X		exit (1);
X	}
X#ifdef	NDBM
X	if (access ("/etc/shadow.pag", 0) == 0 && ! sp_dbm_update (sp)) {
X		fprintf (stderr, DBMERROR);
X		syslog (LOG_ERR, DBMERROR2);
X		(void) spw_unlock ();
X		exit (1);
X	}
X#endif	/* NDBM */
X	if (! spw_close ()) {
X		fprintf (stderr, "%s: can't rewrite shadow file\n", Prog);
X		cleanup (2);
X		exit (1);
X	}
X	(void) pw_close ();
X	cleanup (2);
X	exit (0);
X	/*NOTREACHED*/
X}
X
X/*
X * cleanup - unlock any locked password files
X */
X
Xvoid
Xcleanup (state)
Xint	state;
X{
X	switch (state) {
X		case 2:
X			spw_unlock ();
X		case 1:
X			pw_unlock ();
X		case 0:
X			break;
X	}
X}
SHAR_EOF
if test 15243 -ne "`wc -c < 'chage.c'`"
then
	echo shar: "error transmitting 'chage.c'" '(should have been 15243 characters)'
fi
fi
echo shar: "extracting 'pwent.c'" '(9715 characters)'
if test -f 'pwent.c'
then
	echo shar: "will not over-write existing file 'pwent.c'"
else
sed 's/^X//' << \SHAR_EOF > 'pwent.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <stdio.h>
X#include "pwd.h"
X#ifdef	BSD
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#else
X#include <string.h>
X#endif
X#include "config.h"
X
X/*
X * If AUTOSHADOW is enable, the getpwnam and getpwuid calls will
X * fill in the pw_passwd and pw_age fields from the passwd and
X * shadow files.
X */
X
X#if defined(AUTOSHADOW) && !defined(SHADOWPWD)
X#undef	AUTOSHADOW
X#endif
X#ifdef	AUTOSHADOW
X#include "shadow.h"
X#endif
X
X/*
X * If DBM or NDBM is enabled, the getpwnam and getpwuid calls will
X * go to the database files to look for the requested entries.
X */
X
X#ifdef	DBM
X#include <dbm.h>
X#endif
X#ifdef	NDBM
X#include <ndbm.h>
X#include <fcntl.h>
XDBM	*pw_dbm;
Xint	pw_dbm_mode = -1;
X#endif
X
X/*
X * ITI-style aging uses time_t's as the time fields, while
X * AT&T-style aging uses long numbers of days.
X */
X
X#ifdef	ITI_AGING
X#define	WEEK	(7L*24L*3600L)
X#else
X#define	WEEK	7
X#endif
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)pwent.c	3.5	12:53:57	12/19/90";
X#endif
X
X#define	SBUFSIZ	64
X#define	NFIELDS	7
X
Xstatic	FILE	*pwdfp;
Xstatic	char	pwdbuf[BUFSIZ];
Xstatic	char	*pwdfile = PWDFILE;
X#if defined(DBM) || defined(NDBM)
Xstatic	int	dbmopened;
Xstatic	int	dbmerror;
X#endif
Xstatic	char	*pwdfields[NFIELDS];
Xstatic	struct	passwd	pwent;
X
X#if defined(AUTOSHADOW) && defined(ATT_AGE)
X/*
X * sptopwage - convert shadow ages to AT&T-style pw_age ages
X *
X *	sptopwage() converts the values in the shadow password
X *	entry to the format used in the old-style password
X *	entry.
X */
X
Xstatic char *
Xsptopwage (spwd)
Xstruct	spwd	*spwd;
X{
X	static	char	age[5];
X	long	min;
X	long	max;
X	long	last;
X
X	if ((min = (spwd->sp_min / WEEK)) < 0)
X		min = 0;
X	else if (min >= 64)
X		min = 63;
X
X	if ((max = (spwd->sp_max / WEEK)) < 0)
X		max = 0;
X	else if (max >= 64)
X		max = 63;
X
X	if ((last = (spwd->sp_lstchg / WEEK)) < 0)
X		last = 0;
X	else if (last >= 4096)
X		last = 4095;
X
X	age[0] = i64c (max);
X	age[1] = i64c (min);
X	age[2] = i64c (last % 64);
X	age[3] = i64c (last / 64);
X	age[4] = '\0';
X	return age;
X}
X#endif
X
X/*
X * sgetpwent - convert a string to a (struct passwd)
X *
X * sgetpwent() parses a string into the parts required for a password
X * structure.  Strict checking is made for the UID and GID fields and
X * presence of the correct number of colons.  Any failing tests result
X * in a NULL pointer being returned.
X */
X
Xstruct passwd *
Xsgetpwent (buf)
Xchar	*buf;
X{
X	int	i;
X	char	*cp;
X
X	/*
X	 * Copy the string to a static buffer so the pointers into
X	 * the password structure remain valid.
X	 */
X
X	strncpy (pwdbuf, buf, BUFSIZ);
X	pwdbuf[BUFSIZ-1] = '\0';
X
X	/*
X	 * Save a pointer to the start of each colon separated
X	 * field.  The fields are converted into NUL terminated strings.
X	 */
X
X	for (cp = pwdbuf, i = 0;i < NFIELDS && cp;i++) {
X		pwdfields[i] = cp;
X		if (cp = strchr (cp, ':'))
X			*cp++ = 0;
X	}
X
X	/*
X	 * There must be exactly NFIELDS colon separated fields or
X	 * the entry is invalid.  Also, the UID and GID must be non-blank.
X	 */
X
X	if (i != NFIELDS || *pwdfields[2] == '\0' || *pwdfields[3] == '\0')
X		return 0;
X
X	/*
X	 * Each of the fields is converted the appropriate data type
X	 * and the result assigned to the password structure.  If the
X	 * UID or GID does not convert to an integer value, a NULL
X	 * pointer is returned.
X	 */
X
X	pwent.pw_name = pwdfields[0];
X	pwent.pw_passwd = pwdfields[1];
X	if ((pwent.pw_uid = strtol (pwdfields[2], &cp, 10)) == 0 && *cp)
X		return 0;
X
X	if ((pwent.pw_gid = strtol (pwdfields[3], &cp, 10)) == 0 && *cp)
X		return 0;
X#ifdef	ATT_AGE
X	if (cp = strchr (pwent.pw_passwd, ',')) {
X		pwent.pw_age = cp + 1;
X		*cp = '\0';
X	} else
X		pwent.pw_age = "";
X#endif
X	pwent.pw_gecos = pwdfields[4];
X#ifdef	ATT_COMMENT
X	pwent.pw_comment = "";
X#endif
X	pwent.pw_dir = pwdfields[5];
X	pwent.pw_shell = pwdfields[6];
X
X	return (&pwent);
X}
X
X/*
X * fgetpwent - get a password file entry from a stream
X *
X * fgetpwent() reads the next line from a password file formatted stream
X * and returns a pointer to the password structure for that line.
X */
X
Xstruct passwd *
Xfgetpwent (fp)
XFILE	*fp;
X{
X	char	buf[BUFSIZ];
X
X	while (fgets (buf, BUFSIZ, fp) != (char *) 0) {
X		buf[strlen (buf) - 1] = '\0';
X		return (sgetpwent (buf));
X	}
X	return 0;
X}
X
X/*
X * endpwent - close a password file
X *
X * endpwent() closes the password file if open.  if autoshadowing is
X * enabled the system must also end access to the shadow files since
X * the user is probably unaware it was ever accessed.
X */
X
Xint
Xendpwent ()
X{
X	if (pwdfp)
X		if (fclose (pwdfp))
X			return -1;
X
X	pwdfp = 0;
X#ifdef	NDBM
X	if (dbmopened && pw_dbm) {
X		dbm_close (pw_dbm);
X		dbmopened = 0;
X		dbmerror = 0;
X		pw_dbm = 0;
X	}
X#endif
X#ifdef	AUTOSHADOW
X	endspent ();
X#endif
X	return 0;
X}
X
X/*
X * getpwent - get a password entry from the password file
X *
X * getpwent() opens the password file, if not already opened, and reads
X * a single entry.  NULL is returned if any errors are encountered reading
X * the password file.
X */
X
Xstruct passwd *
Xgetpwent ()
X{
X	if (! pwdfp && setpwent ())
X		return 0;
X
X	return fgetpwent (pwdfp);
X}
X
X/*
X * getpwuid - locate the password entry for a given UID
X *
X * getpwuid() locates the first password file entry for the given UID.
X * If there is a valid DBM file, the DBM files are queried first for
X * the entry.  Otherwise, a linear search is begun of the password file
X * searching for an entry which matches the provided UID.
X */
X
Xstruct passwd *
Xgetpwuid (uid)
Xint	uid;
X{
X	struct	passwd	*pwd;
X#if defined(DBM) || defined(NDBM)
X	datum	key;
X	datum	content;
X#endif
X#ifdef	AUTOSHADOW
X	struct	spwd	*spwd;
X#endif
X
X	if (setpwent ())
X		return 0;
X
X#if defined(DBM) || defined(NDBM)
X
X	/*
X	 * If the DBM file are now open, create a key for this UID and
X	 * try to fetch the entry from the database.  A matching record
X	 * will be unpacked into a static structure and returned to
X	 * the user.
X	 */
X
X	if (dbmopened) {
X		pwent.pw_uid = uid;
X		key.dsize = sizeof pwent.pw_uid;
X		key.dptr = (char *) &pwent.pw_uid;
X#ifdef	DBM
X		content = fetch (key);
X#endif
X#ifdef	NDBM
X		content = dbm_fetch (pw_dbm, key);
X#endif
X		if (content.dptr != 0) {
X			memcpy (pwdbuf, content.dptr, content.dsize);
X			pw_unpack (pwdbuf, content.dsize, &pwent);
X#ifdef	AUTOSHADOW
X			if (spwd = getspnam (pwent.pw_name)) {
X				pwent.pw_passwd = spwd->sp_pwdp;
X#ifdef	ATT_AGE
X				pwent.pw_age = sptopwage (spwd);
X#endif
X			}
X#endif
X			return &pwent;
X		}
X	}
X#endif
X	/*
X	 * Search for an entry which matches the UID.  Return the
X	 * entry when a match is found.
X	 */
X
X	while (pwd = getpwent ())
X		if (pwd->pw_uid == uid)
X			break;
X
X#ifdef	AUTOSHADOW
X	if (pwd && (spwd = getspnam (pwd->pw_name))) {
X		pwd->pw_passwd = spwd->sp_pwdp;
X#ifdef	ATT_AGE
X		pwd->pw_age = sptopwage (spwd);
X#endif
X	}
X#endif
X	return pwd;
X}
X
X/*
X * getpwnam - locate the password entry for a given name
X *
X * getpwnam() locates the first password file entry for the given name.
X * If there is a valid DBM file, the DBM files are queried first for
X * the entry.  Otherwise, a linear search is begun of the password file
X * searching for an entry which matches the provided name.
X */
X
Xstruct passwd *
Xgetpwnam (name)
Xchar	*name;
X{
X	struct	passwd	*pwd;
X#if defined(DBM) || defined(NDBM)
X	datum	key;
X	datum	content;
X#endif
X#ifdef	AUTOSHADOW
X	struct	spwd	*spwd;
X#endif
X
X	if (setpwent ())
X		return 0;
X
X#if defined(DBM) || defined(NDBM)
X
X	/*
X	 * If the DBM file are now open, create a key for this UID and
X	 * try to fetch the entry from the database.  A matching record
X	 * will be unpacked into a static structure and returned to
X	 * the user.
X	 */
X
X	if (dbmopened) {
X		key.dsize = strlen (name);
X		key.dptr = name;
X#ifdef	DBM
X		content = fetch (key);
X#endif
X#ifdef	NDBM
X		content = dbm_fetch (pw_dbm, key);
X#endif
X		if (content.dptr != 0) {
X			memcpy (pwdbuf, content.dptr, content.dsize);
X			pw_unpack (pwdbuf, content.dsize, &pwent);
X#ifdef	AUTOSHADOW
X			if (spwd = getspnam (pwent.pw_name)) {
X				pwent.pw_passwd = spwd->sp_pwdp;
X#ifdef	ATT_AGE
X				pwent.pw_age = sptopwage (spwd);
X#endif
X			}
X#endif
X			return &pwent;
X		}
X	}
X#endif
X	/*
X	 * Search for an entry which matches the name.  Return the
X	 * entry when a match is found.
X	 */
X
X	while (pwd = getpwent ())
X		if (strcmp (pwd->pw_name, name) == 0)
X			break;
X
X#ifdef	AUTOSHADOW
X	if (pwd && (spwd = getspnam (pwd->pw_name))) {
X		pwd->pw_passwd = spwd->sp_pwdp;
X#ifdef	ATT_AGE
X		pwd->pw_age = sptopwage (spwd);
X#endif
X	}
X#endif
X	return pwd;
X}
X
X/*
X * setpwent - open the password file
X *
X * setpwent() opens the system password file, and the DBM password files
X * if they are present.  The system password file is rewound if it was
X * open already.
X */
X
Xint
Xsetpwent ()
X{
X#ifdef	NDBM
X	int	mode;
X#endif
X
X	if (! pwdfp) {
X		if (! (pwdfp = fopen (pwdfile, "r")))
X			return -1;
X	} else {
X		if (fseek (pwdfp, 0L, 0) != 0)
X			return -1;
X	}
X
X	/*
X	 * Attempt to open the DBM files if they have never been opened
X	 * and an error has never been returned.
X	 */
X
X#if defined (DBM) || defined (NDBM)
X	if (! dbmerror && ! dbmopened) {
X		char	dbmfiles[BUFSIZ];
X
X		strcpy (dbmfiles, pwdfile);
X		strcat (dbmfiles, ".pag");
X#ifdef	NDBM
X		if (pw_dbm_mode == -1)
X			mode = O_RDONLY;
X		else
X			mode = (pw_dbm_mode == O_RDONLY ||
X				pw_dbm_mode == O_RDWR) ? pw_dbm_mode:O_RDONLY;
X#endif
X#ifdef	DBM
X		if (access (dbmfiles, 0) || dbminit (pwdfile))
X#endif
X#ifdef	NDBM
X		if (access (dbmfiles, 0) ||
X			(! (pw_dbm = dbm_open (pwdfile, mode, 0))))
X#endif
X			dbmerror = 1;
X		else
X			dbmopened = 1;
X	}
X#endif
X	return 0;
X}
SHAR_EOF
if test 9715 -ne "`wc -c < 'pwent.c'`"
then
	echo shar: "error transmitting 'pwent.c'" '(should have been 9715 characters)'
fi
fi
echo shar: "extracting 'valid.c'" '(2336 characters)'
if test -f 'valid.c'
then
	echo shar: "will not over-write existing file 'valid.c'"
else
sed 's/^X//' << \SHAR_EOF > 'valid.c'
X/*
X * Copyright 1989, 1990, 1991, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <stdio.h>
X#include "pwd.h"
X#ifdef	BSD
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#else
X#include <string.h>
X#include <memory.h>
X#endif
X#include "config.h"
X
X#ifndef	lint
Xstatic	char	_sccsid[] = "@(#)valid.c	3.3	08:00:20	2/6/91";
X#endif
X
X/*
X * valid - compare encrypted passwords
X *
X *	Valid() compares the DES encrypted password from the password file
X *	against the password which the user has entered after it has been
X *	encrypted using the same salt as the original.  Entries which do
X *	not have a password file entry have a NULL pw_name field and this
X *	is used to indicate that a dummy salt must be used to encrypt the
X *	password anyway.
X */
X
Xint	valid (password, entry)
Xchar	*password;
Xstruct	passwd	*entry;
X{
X	char	*encrypt;
X	char	*salt;
X	char	*pw_encrypt ();
X	char	*shell;
X
X	/*
X	 * Start with blank or empty password entries.  Always encrypt
X	 * a password if no such user exists.  Only if the ID exists and
X	 * the password is really empty do you return quickly.  This
X	 * routine is meant to waste CPU time.
X	 */
X
X	if (entry->pw_name && ! entry->pw_passwd[0]) {
X		if (! password[0])
X			return (1);	/* user entered nothing */
X		else
X			return (0);	/* user entered something! */
X	}
X
X	/*
X	 * If there is no entry then we need a salt to use.
X	 */
X
X	if (entry->pw_name == (char *) 0 || entry->pw_passwd[0] == '\0')
X		salt = "xx";
X	else
X		salt = entry->pw_passwd;
X
X	/*
X	 * Now, perform the encryption using the salt from before on
X	 * the users input.  Since we always encrypt the string, it
X	 * should be very difficult to determine if the user exists by
X	 * looking at execution time.
X	 */
X
X	encrypt = pw_encrypt (password, salt);
X
X	/*
X	 * One last time we must deal with there being no password file
X	 * entry for the user.  We use the pw_passwd == NULL idiom to
X	 * cause non-existent users to not be validated.
X	 */
X
X	if (entry->pw_name && strcmp (encrypt, entry->pw_passwd) == 0)
X		return (1);
X	else
X		return (0);
X}
SHAR_EOF
if test 2336 -ne "`wc -c < 'valid.c'`"
then
	echo shar: "error transmitting 'valid.c'" '(should have been 2336 characters)'
fi
fi
echo shar: "extracting 'setup.c'" '(3560 characters)'
if test -f 'setup.c'
then
	echo shar: "will not over-write existing file 'setup.c'"
else
sed 's/^X//' << \SHAR_EOF > 'setup.c'
X/*
X * Copyright 1989, 1990, 1991, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <sys/types.h>
X#include <stdio.h>
X#include <utmp.h>
X#include <syslog.h>
X
X#ifdef	BSD
X#include <strings.h>
X#define	strchr	index
X#else
X#include <string.h>
X#include <memory.h>
X#endif
X
X#include "config.h"
X#include "pwd.h"
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)setup.c	3.3	07:46:41	2/6/91";
X#endif
X
X#ifndef	PATH
X#define	PATH	"PATH=/bin:/usr/bin"
X#endif
X
X#ifndef	SUPATH
X#define	SUPATH	"PATH=/bin:/usr/bin:/etc"
X#endif
X
X#ifndef	MAILDIR
X#define	MAILDIR	"/usr/spool/mail"
X#endif
X
X#ifndef	TTYPERM
X#define	TTYPERM	0622
X#endif
X
X#ifndef	SU
Xextern	struct	utmp	utent;
X#endif
X
X#ifdef	QUOTAS
Xlong	strtol ();
X#ifdef	ULIMIT
Xlong	ulimit ();
X#endif
X#endif
X
Xvoid	addenv ();
X
X/*
X * setup - initialize login environment
X *
X *	setup() performs the following steps -
X *
X *	set the login tty to be owned by the new user ID with TTYPERM modes
X *	change to the user's home directory
X *	set the process nice, ulimit, and umask from the password file entry
X *	set the group ID to the value from the password file entry
X *	set the user ID to the value from the password file entry
X *	set the HOME, SHELL, MAIL, PATH, and LOGNAME environmental variables
X */
X
Xvoid	setup (info)
Xstruct	passwd	*info;
X{
X	extern	int	errno;
X	char	buf[BUFSIZ];
X#ifndef	SU
X	char	tty[30];
X#endif
X	char	*cp;
X	int	i;
X	long	l;
X
X#ifndef	SU
X	(void) strcat (strcpy (tty, "/dev/"), utent.ut_line);
X	if (chown (tty, info->pw_uid, info->pw_gid) || chmod (tty, TTYPERM)) {
X		(void) sprintf (buf, "Unable to change tty %s", tty);
X		syslog (LOG_WARN, "unable to change tty `%s' for user `%s'",
X			tty, info->pw_name);
X		perror (buf);
X		exit (errno);
X	}
X#endif
X	if (chdir (info->pw_dir) == -1) {
X		(void) sprintf (buf, "Unable to cd to \"%s\"", info->pw_dir);
X		syslog (LOG_WARN, "unable to cd to `%s' for user `%s'",
X			info->pw_dir, info->pw_name);
X		perror (buf);
X		exit (errno);
X	}
X#ifdef	QUOTAS
X	for (cp = info->pw_gecos;cp != (char *) 0;cp = strchr (cp, ',')) {
X		if (*cp == ',')
X			cp++;
X
X		if (strncmp (cp, "pri=", 4) == 0) {
X			i = atoi (cp + 4);
X			if (i >= -20 && i <= 20)
X				(void) nice (i);
X
X			continue;
X		}
X#ifdef	ULIMIT
X		if (strncmp (cp, "ulimit=", 7) == 0) {
X			l = strtol (cp + 7, (char **) 0, 10);
X			(void) ulimit (2, l);
X
X			continue;
X		}
X#endif
X		if (strncmp (cp, "umask=", 6) == 0) {
X			i = strtol (cp + 6, (char **) 0, 8) & 0777;
X			(void) umask (i);
X
X			continue;
X		}
X	}
X#endif
X	if (setgid (info->pw_gid) == -1) {
X		puts ("Bad group id");
X		syslog (LOG_WARN, "bad group ID `%d' for user `%s'",
X			info->pw_gid, info->pw_name);
X		exit (errno);
X	}
X#ifndef	BSD
X	if (setuid (info->pw_uid))
X#else
X	if (setreuid (info->pw_uid, info->pw_uid))
X#endif
X	{
X		puts ("Bad user id");
X		syslog (LOG_WARN, "bad user ID `%d' for user `%s'",
X			info->pw_uid, info->pw_name);
X		exit (errno);
X	}
X	(void) strcat (strcpy (buf, "HOME="), info->pw_dir);
X	addenv (buf);
X
X	if (info->pw_shell == (char *) 0)
X		info->pw_shell = "/bin/sh";
X
X	(void) strcat (strcpy (buf, "SHELL="), info->pw_shell);
X	addenv (buf);
X
X	if (info->pw_uid == 0)
X		addenv (SUPATH);
X	else
X		addenv (PATH);
X
X	(void) strcat (strcpy (buf, "LOGNAME="), info->pw_name);
X	addenv (buf);
X
X	(void) strcat (strcat (strcpy (buf, "MAIL="), MAILDIR), info->pw_name);
X	addenv (buf);
X}
SHAR_EOF
if test 3560 -ne "`wc -c < 'setup.c'`"
then
	echo shar: "error transmitting 'setup.c'" '(should have been 3560 characters)'
fi
fi
echo shar: "extracting 'entry.c'" '(1946 characters)'
if test -f 'entry.c'
then
	echo shar: "will not over-write existing file 'entry.c'"
else
sed 's/^X//' << \SHAR_EOF > 'entry.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include "pwd.h"
X#ifndef	BSD
X#include <string.h>
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X#include "config.h"
X#ifdef	SHADOWPWD
X#include "shadow.h"
X#endif
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)entry.c	3.2	12:30:39	12/12/90";
X#endif
X
Xstruct	passwd	*fgetpwent ();
X
Xvoid	entry (name, pwent)
Xchar	*name;
Xstruct	passwd	*pwent;
X{
X	struct	passwd	*passwd;
X#ifdef	SHADOWPWD
X	struct	spwd	*spwd;
X	char	*l64a ();
X#endif
X	char	*cp;
X
X	if (! (passwd = getpwnam (name))) {
X		pwent->pw_name = (char *) 0;
X		return;
X	} else  {
X		pwent->pw_name = strdup (passwd->pw_name);
X		pwent->pw_uid = passwd->pw_uid;
X		pwent->pw_gid = passwd->pw_gid;
X#ifdef	ATT_COMMENT
X		pwent->pw_comment = strdup (passwd->pw_comment);
X#endif
X		pwent->pw_gecos = strdup (passwd->pw_gecos);
X		pwent->pw_dir = strdup (passwd->pw_dir);
X		pwent->pw_shell = strdup (passwd->pw_shell);
X#if defined(SHADOWPWD) && !defined(AUTOSHADOW)
X		setspent ();
X		if (spwd = getspnam (name)) {
X			pwent->pw_passwd = strdup (spwd->sp_pwdp);
X#ifdef	ATT_AGE
X			pwent->pw_age = malloc (5);
X
X			if (spwd->sp_max > (63*7))
X				spwd->sp_max = (63*7);
X			if (spwd->sp_min > (63*7))
X				spwd->sp_min = (63*7);
X
X			pwent->pw_age[0] = i64c (spwd->sp_max / 7);
X			pwent->pw_age[1] = i64c (spwd->sp_min / 7);
X
X			cp = l64a (spwd->sp_lstchg / 7);
X			pwent->pw_age[2] = cp[0];
X			pwent->pw_age[3] = cp[1];
X
X			pwent->pw_age[4] = '\0';
X#endif
X			endspent ();
X			return;
X		}
X		endspent ();
X#endif
X		pwent->pw_passwd = strdup (passwd->pw_passwd);
X#ifdef	ATT_AGE
X		pwent->pw_age = strdup (passwd->pw_age);
X#endif
X	}
X}
SHAR_EOF
if test 1946 -ne "`wc -c < 'entry.c'`"
then
	echo shar: "error transmitting 'entry.c'" '(should have been 1946 characters)'
fi
fi
echo shar: "extracting 'ttytype.c'" '(1125 characters)'
if test -f 'ttytype.c'
then
	echo shar: "will not over-write existing file 'ttytype.c'"
else
sed 's/^X//' << \SHAR_EOF > 'ttytype.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X#include <stdio.h>
X#ifndef	BSD
X#include <string.h>
X#include <memory.h>
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X#include "config.h"
X
X#ifdef	TTYTYPE
X#ifndef	lint
Xstatic	char	_sccsid[] = "@(#)ttytype.c	2.2	19:24:24	7/29/90";
X#endif
X
X/*
X * ttytype - set ttytype from port to terminal type mapping database
X */
X
Xvoid	ttytype (line)
Xchar	*line;
X{
X	FILE	*fp;
X	char	buf[BUFSIZ];
X	char	termvar[BUFSIZ];
X	char	*cp;
X	char	*type;
X	char	*port;
X	char	*getenv ();
X
X	if (getenv ("TERM"))
X		return;
X
X	if (! (fp = fopen (TTYTYPE, "r")))
X		return;
X
X	while (fgets (buf, BUFSIZ, fp)) {
X		if (buf[0] == '#')
X			continue;
X
X		if (cp = strchr (buf, '\n'))
X			*cp = '\0';
X
X		if ((type = strtok (buf, " \t"))
X				&& (port = strtok ((char *) 0, " \t"))) {
X			if (strcmp (line, port) == 0)
X				break;
X		}
X	}
X	if (! feof (fp) && ! ferror (fp)) {
X		strcat (strcpy (termvar, "TERM="), type);
X		addenv (termvar);
X	}
X	fclose (fp);
X}
X#endif
SHAR_EOF
if test 1125 -ne "`wc -c < 'ttytype.c'`"
then
	echo shar: "error transmitting 'ttytype.c'" '(should have been 1125 characters)'
fi
fi
echo shar: "extracting 'port.h'" '(1743 characters)'
if test -f 'port.h'
then
	echo shar: "will not over-write existing file 'port.h'"
else
sed 's/^X//' << \SHAR_EOF > 'port.h'
X/*
X * Copyright 1989, 1990, 1991, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X/*
X * port.h - structure of /etc/porttime
X *
X *	@(#)port.h	3.1	08:59:36	2/8/91
X *
X *	Each entry in /etc/porttime consists of a TTY device
X *	name or "*" to indicate all TTY devices, followed by
X *	a list of 1 or more user IDs or "*" to indicate all
X *	user names, followed by a list of zero or more valid
X *	login times.  Login time entries consist of zero or
X *	more day names (Su, Mo, Tu, We, Th, Fr, Sa, Wk, Al)
X *	followed by a pair of time values in HHMM format
X *	separated by a "-".
X */
X
X/*
X * PORTS - Name of system port access time file.
X * PORT_IDS - Allowable number of IDs per entry.
X * PORT_TTY - Allowable number of TTYs per entry.
X * PORT_TIMES - Allowable number of time entries per entry.
X * PORT_DAY - Day of the week to a bit value (0 = Sunday).
X */
X
X#define	PORTS	"/etc/porttime"
X#define	PORT_IDS	64
X#define	PORT_TTY	64
X#define	PORT_TIMES	24
X#define	PORT_DAY(day)	(1<<(day))
X
X/*
X *	pt_names - pointer to array of device names in /dev/
X *	pt_users - pointer to array of applicable user IDs.
X *	pt_times - pointer to list of allowable time periods.
X */
X
Xstruct	port	{
X	char	**pt_names;
X	char	**pt_users;
X	struct	pt_time	*pt_times;
X};
X
X/*
X *	t_days - bit array for each day of the week (0 = Sunday)
X *	t_start - starting time for this entry
X *	t_end - ending time for this entry
X */
X
Xstruct	pt_time	{
X	short	t_days;
X	short	t_start;
X	short	t_end;
X};
SHAR_EOF
if test 1743 -ne "`wc -c < 'port.h'`"
then
	echo shar: "error transmitting 'port.h'" '(should have been 1743 characters)'
fi
fi
exit 0
#	End of shell archive
-- 
John F. Haugh II        | Distribution to  | UUCP: ...!cs.utexas.edu!rpp386!jfh
Ma Bell: (512) 255-8251 | GEnie PROHIBITED :-) |  Domain: jfh@rpp386.cactus.org
"If liberals interpreted the 2nd Amendment the same way they interpret the
 rest of the Constitution, gun ownership would be mandatory."

jfh@rpp386.cactus.org (John F Haugh II) (05/16/91)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	pwio.c
#	encrypt.c
#	chpasswd.c
#	newusers.c
#	rad64.c
#	dialchk.c
#	faillog.h
#	pwdbm.c
#	grdbm.c
#	gshadow.c
#	sppack.c
# This archive created: Sun Mar  3 13:27:28 1991
# By:	John F Haugh II (River Parishes Programming, Austin TX)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'pwio.c'" '(11009 characters)'
if test -f 'pwio.c'
then
	echo shar: "will not over-write existing file 'pwio.c'"
else
sed 's/^X//' << \SHAR_EOF > 'pwio.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X *
X *	This file implements a transaction oriented password database
X *	library.  The password file is updated one entry at a time.
X *	After each transaction the file must be logically closed and
X *	transferred to the existing password file.  The sequence of
X *	events is
X *
X *	pw_lock				-- lock password file
X *	pw_open				-- logically open password file
X *	while transaction to process
X *		pw_(locate,update,remove) -- perform transaction
X *	done
X *	pw_close			-- commit transactions
X *	pw_unlock			-- remove password lock
X */
X
X#include <sys/stat.h>
X#include <fcntl.h>
X#include <errno.h>
X#include "pwd.h"
X#include <stdio.h>
X
X#ifndef lint
Xstatic	char	sccsid[] = "@(#)pwio.c	3.6	12:31:19	12/12/90";
X#endif
X
Xstatic	int	islocked;
Xstatic	int	isopen;
Xstatic	int	open_modes;
Xstatic	FILE	*pwfp;
X
Xstruct	pw_file_entry {
X	char	*pwf_line;
X	int	pwf_changed;
X	struct	passwd	*pwf_entry;
X	struct	pw_file_entry *pwf_next;
X};
X
Xstatic	struct	pw_file_entry	*pwf_head;
Xstatic	struct	pw_file_entry	*pwf_tail;
Xstatic	struct	pw_file_entry	*pwf_cursor;
Xstatic	int	pw_changed;
Xstatic	int	lock_pid;
X
X#define	PW_LOCK	"/etc/passwd.lock"
X#define	PW_TEMP "/etc/pwd.%d"
X#define	PASSWD	"/etc/passwd"
X
Xstatic	char	pw_filename[BUFSIZ] = PASSWD;
X
Xextern	char	*strdup();
Xextern	struct	passwd	*sgetpwent();
X
X/*
X * pw_dup - duplicate a password file entry
X *
X *	pw_dup() accepts a pointer to a password file entry and
X *	returns a pointer to a password file entry in allocated
X *	memory.
X */
X
Xstatic struct passwd *
Xpw_dup (pwent)
Xstruct	passwd	*pwent;
X{
X	struct	passwd	*pw;
X
X	if (! (pw = (struct passwd *) malloc (sizeof *pw)))
X		return 0;
X
X	if ((pw->pw_name = strdup (pwent->pw_name)) == 0 ||
X			(pw->pw_passwd = strdup (pwent->pw_passwd)) == 0 ||
X#ifdef	ATT_AGE
X			(pw->pw_age = strdup (pwent->pw_age)) == 0 ||
X#endif	/* ATT_AGE */
X#ifdef	ATT_COMMENT
X			(pw->pw_comment = strdup (pwent->pw_comment)) == 0 ||
X#endif	/* ATT_COMMENT */
X			(pw->pw_gecos = strdup (pwent->pw_gecos)) == 0 ||
X			(pw->pw_dir = strdup (pwent->pw_dir)) == 0 ||
X			(pw->pw_shell = strdup (pwent->pw_shell)) == 0)
X		return 0;
X
X	pw->pw_uid = pwent->pw_uid;
X	pw->pw_gid = pwent->pw_gid;
X
X	return pw;
X}
X
X/*
X * pw_free - free a dynamically allocated password file entry
X *
X *	pw_free() frees up the memory which was allocated for the
X *	pointed to entry.
X */
X
Xstatic void
Xpw_free (pwent)
Xstruct	passwd	*pwent;
X{
X	free (pwent->pw_name);
X	free (pwent->pw_passwd);
X	free (pwent->pw_gecos);
X	free (pwent->pw_dir);
X	free (pwent->pw_shell);
X}
X
X/*
X * pw_name - change the name of the password file
X */
X
Xint
Xpw_name (name)
Xchar	*name;
X{
X	if (isopen || strlen (name) > (BUFSIZ-10))
X		return -1;
X
X	strcpy (pw_filename, name);
X	return 0;
X}
X
X/*
X * pw_lock - lock a password file
X *
X *	pw_lock() encapsulates the lock operation.  it returns
X *	TRUE or FALSE depending on the password file being
X *	properly locked.  the lock is set by creating a semaphore
X *	file, PW_LOCK.
X */
X
Xint
Xpw_lock ()
X{
X	int	fd;
X	int	pid;
X	int	len;
X	char	file[BUFSIZ];
X	char	buf[32];
X	struct	stat	sb;
X
X	if (islocked)
X		return 1;
X
X	if (strcmp (pw_filename, PASSWD) != 0)
X		return 0;
X
X	/*
X	 * Create a lock file which can be switched into place
X	 */
X
X	sprintf (file, PW_TEMP, lock_pid = getpid ());
X	if ((fd = open (file, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1)
X		return 0;
X
X	sprintf (buf, "%d", lock_pid);
X	if (write (fd, buf, strlen (buf) + 1) != strlen (buf) + 1) {
X		(void) close (fd);
X		(void) unlink (file);
X		return 0;
X	}
X	close (fd);
X
X	/*
X	 * Simple case first -
X	 *	Link fails (in a sane environment ...) if the target
X	 *	exists already.  So we try to switch in a new lock
X	 *	file.  If that succeeds, we assume we have the only
X	 *	valid lock.  Needs work for NFS where this assumption
X	 *	may not hold.  The simple hack is to check the link
X	 *	count on the source file, which should be 2 iff the
X	 *	link =really= worked.
X	 */
X
X	if (link (file, PW_LOCK) == 0) {
X		if (stat (file, &sb) != 0)
X			return 0;
X
X		if (sb.st_nlink != 2)
X			return 0;
X
X		(void) unlink (file);
X		islocked = 1;
X		return 1;
X	}
X
X	/*
X	 * Invalid lock test -
X	 *	Open the lock file and see if the lock is valid.
X	 *	The PID of the lock file is checked, and if the PID
X	 *	is not valid, the lock file is removed.  If the unlink
X	 *	of the lock file fails, it should mean that someone
X	 *	else is executing this code.  They will get success,
X	 *	and we will fail.
X	 */
X
X	if ((fd = open (PW_LOCK, O_RDWR)) == -1 ||
X			(len = read (fd, buf, BUFSIZ)) <= 0) {
X		errno = EINVAL;
X		return 0;
X	}
X	buf[len] = '\0';
X	if ((pid = strtol (buf, (char **) 0, 10)) == 0) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (kill (pid, 0) == 0)  {
X		errno = EEXIST;
X		return 0;
X	}
X	if (unlink (PW_LOCK)) {
X		(void) close (fd);
X		(void) unlink (file);
X
X		return 0;
X	}
X
X	/*
X	 * Re-try lock -
X	 *	The invalid lock has now been removed and I should
X	 *	be able to acquire a lock for myself just fine.  If
X	 *	this fails there will be no retry.  The link count
X	 *	test here makes certain someone executing the previous
X	 *	block of code didn't just remove the lock we just
X	 *	linked to.
X	 */
X
X	if (link (file, PW_LOCK) == 0) {
X		if (stat (file, &sb) != 0)
X			return 0;
X
X		if (sb.st_nlink != 2)
X			return 0;
X
X		(void) unlink (file);
X		islocked = 1;
X		return 1;
X	}
X	(void) unlink (file);
X	return 0;
X}
X
X/*
X * pw_unlock - logically unlock a password file
X *
X *	pw_unlock() removes the lock which was set by an earlier
X *	invocation of pw_lock().
X */
X
Xint
Xpw_unlock ()
X{
X	if (isopen) {
X		open_modes = O_RDONLY;
X		if (! pw_close ())
X			return 0;
X	}
X  	if (islocked) {
X  		islocked = 0;
X		if (lock_pid != getpid ())
X			return 0;
X
X		(void) unlink (PW_LOCK);
X  		return 1;
X	}
X	return 0;
X}
X
X/*
X * pw_open - open a password file
X *
X *	pw_open() encapsulates the open operation.  it returns
X *	TRUE or FALSE depending on the password file being
X *	properly opened.
X */
X
Xint
Xpw_open (mode)
Xint	mode;
X{
X	char	buf[8192];
X	char	*cp;
X	struct	pw_file_entry	*pwf;
X	struct	passwd	*pwent;
X
X	if (isopen || (mode != O_RDONLY && mode != O_RDWR))
X		return 0;
X
X	if (mode != O_RDONLY && ! islocked &&
X			strcmp (pw_filename, PASSWD) == 0)
X		return 0;
X
X	if ((pwfp = fopen (pw_filename, mode == O_RDONLY ? "r":"r+")) == 0)
X		return 0;
X
X	pwf_head = pwf_tail = pwf_cursor = 0;
X	pw_changed = 0;
X
X	while (fgets (buf, sizeof buf, pwfp) != (char *) 0) {
X		if (cp = strrchr (buf, '\n'))
X			*cp = '\0';
X
X		if (! (pwf = (struct pw_file_entry *) malloc (sizeof *pwf)))
X			return 0;
X
X		pwf->pwf_changed = 0;
X		pwf->pwf_line = strdup (buf);
X		if ((pwent = sgetpwent (buf)) && ! (pwent = pw_dup (pwent)))
X			return 0;
X
X		pwf->pwf_entry = pwent;
X
X		if (pwf_head == 0) {
X			pwf_head = pwf_tail = pwf;
X			pwf->pwf_next = 0;
X		} else {
X			pwf_tail->pwf_next = pwf;
X			pwf->pwf_next = 0;
X			pwf_tail = pwf;
X		}
X	}
X	isopen++;
X	open_modes = mode;
X
X	return 1;
X}
X
X/*
X * pw_close - close the password file
X *
X *	pw_close() outputs any modified password file entries and
X *	frees any allocated memory.
X */
X
Xint
Xpw_close ()
X{
X	char	backup[BUFSIZ];
X	int	fd;
X	int	mask;
X	int	c;
X	int	i;
X	int	errors = 0;
X	FILE	*bkfp;
X	struct	pw_file_entry *pwf;
X	struct	pw_file_entry *opwf;
X
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (islocked && lock_pid != getpid ()) {
X		isopen = 0;
X		islocked = 0;
X		errno = EACCES;
X		return 0;
X	}
X	strcpy (backup, pw_filename);
X	strcat (backup, "-");
X
X	if (open_modes == O_RDWR && pw_changed) {
X		mask = umask (022);
X		if ((bkfp = fopen (backup, "w")) == 0) {
X			umask (mask);
X			return 0;
X		}
X		umask (mask);
X
X		rewind (pwfp);
X		while ((c = getc (pwfp)) != EOF) {
X			if (putc (c, bkfp) == EOF) {
X				fclose (bkfp);
X				return 0;
X			}
X		}
X		if (fclose (bkfp))
X			return 0;
X
X		isopen = 0;
X		(void) fclose (pwfp);
X
X		mask = umask (022);
X		if (! (pwfp = fopen (pw_filename, "w"))) {
X			umask (mask);
X			return 0;
X		}
X		umask (mask);
X
X		for (pwf = pwf_head;errors == 0 && pwf;pwf = pwf->pwf_next) {
X			if (pwf->pwf_changed) {
X				if (putpwent (pwf->pwf_entry, pwfp))
X					errors++;
X			} else {
X				if (fputs (pwf->pwf_line, pwfp) == EOF)
X					errors++;
X				if (putc ('\n', pwfp) == EOF)
X					errors++;
X			}
X		}
X		if (fflush (pwfp))
X			errors++;
X
X		if (errors) {
X			unlink (pw_filename);
X			link (backup, pw_filename);
X			unlink (backup);
X			return 0;
X		}
X	}
X	if (fclose (pwfp))
X		return 0;
X
X	pwfp = 0;
X
X	while (pwf_head != 0) {
X		pwf = pwf_head;
X		pwf_head = pwf->pwf_next;
X
X		if (pwf->pwf_entry) {
X			pw_free (pwf->pwf_entry);
X			free (pwf->pwf_entry);
X		}
X		if (pwf->pwf_line)
X			free (pwf->pwf_line);
X
X		free (pwf);
X	}
X	pwf_tail = 0;
X	return 1;
X}
X
Xint
Xpw_update (pwent)
Xstruct	passwd	*pwent;
X{
X	struct	pw_file_entry	*pwf;
X	struct	passwd	*npw;
X
X	if (! isopen || open_modes == O_RDONLY) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (pwf = pwf_head;pwf != 0;pwf = pwf->pwf_next) {
X		if (pwf->pwf_entry == 0)
X			continue;
X
X		if (strcmp (pwent->pw_name, pwf->pwf_entry->pw_name) != 0)
X			continue;
X
X		if (! (npw = pw_dup (pwent)))
X			return 0;
X		else {
X			pw_free (pwf->pwf_entry);
X			*(pwf->pwf_entry) = *npw;
X		}
X		pwf->pwf_changed = 1;
X		pwf_cursor = pwf;
X		return pw_changed = 1;
X	}
X	pwf = (struct pw_file_entry *) malloc (sizeof *pwf);
X	if (! (pwf->pwf_entry = pw_dup (pwent)))
X		return 0;
X
X	pwf->pwf_changed = 1;
X	pwf->pwf_next = 0;
X	pwf->pwf_line = 0;
X
X	if (pwf_tail)
X		pwf_tail->pwf_next = pwf;
X
X	if (! pwf_head)
X		pwf_head = pwf;
X
X	pwf_tail = pwf;
X
X	return pw_changed = 1;
X}
X
Xint
Xpw_remove (name)
Xchar	*name;
X{
X	struct	pw_file_entry	*pwf;
X	struct	pw_file_entry	*opwf;
X
X	if (! isopen || open_modes == O_RDONLY) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (opwf = 0, pwf = pwf_head;pwf != 0;
X			opwf = pwf, pwf = pwf->pwf_next) {
X		if (! pwf->pwf_entry)
X			continue;
X
X		if (strcmp (name, pwf->pwf_entry->pw_name) != 0)
X			continue;
X
X		if (pwf == pwf_cursor)
X			pwf_cursor = opwf;
X
X		if (opwf != 0)
X			opwf->pwf_next = pwf->pwf_next;
X		else
X			pwf_head = pwf->pwf_next;
X
X		if (pwf == pwf_tail)
X			pwf_tail = opwf;
X
X		return pw_changed = 1;
X	}
X	errno = ENOENT;
X	return 0;
X}
X
Xstruct passwd *
Xpw_locate (name)
Xchar	*name;
X{
X	struct	pw_file_entry	*pwf;
X
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (pwf = pwf_head;pwf != 0;pwf = pwf->pwf_next) {
X		if (pwf->pwf_entry == 0)
X			continue;
X
X		if (strcmp (name, pwf->pwf_entry->pw_name) == 0) {
X			pwf_cursor = pwf;
X			return pwf->pwf_entry;
X		}
X	}
X	errno = ENOENT;
X	return 0;
X}
X
Xint
Xpw_rewind ()
X{
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	pwf_cursor = 0;
X	return 1;
X}
X
Xstruct passwd *
Xpw_next ()
X{
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (pwf_cursor == 0)
X		pwf_cursor = pwf_head;
X	else
X		pwf_cursor = pwf_cursor->pwf_next;
X
X	while (pwf_cursor) {
X		if (pwf_cursor->pwf_entry)
X			return pwf_cursor->pwf_entry;
X
X		pwf_cursor = pwf_cursor->pwf_next;
X	}
X	return 0;
X}
SHAR_EOF
if test 11009 -ne "`wc -c < 'pwio.c'`"
then
	echo shar: "error transmitting 'pwio.c'" '(should have been 11009 characters)'
fi
fi
echo shar: "extracting 'encrypt.c'" '(1287 characters)'
if test -f 'encrypt.c'
then
	echo shar: "will not over-write existing file 'encrypt.c'"
else
sed 's/^X//' << \SHAR_EOF > 'encrypt.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <string.h>
X#include "config.h"
X
X#ifndef lint
Xstatic	char	sccsid[] = "@(#)encrypt.c	3.4	19:44:23	12/10/90";
X#endif
X
Xextern	char	*crypt();
X
Xchar *
Xpw_encrypt (clear, salt)
Xchar	*clear;
Xchar	*salt;
X{
X	static	char	cipher[32];
X	static	int	count;
X	char	newsalt[2];
X	char	*cp;
X	long	now;
X
X	/*
X	 * See if a new salt is needed and get a few random
X	 * bits of information.  The amount of randomness is
X	 * probably not all that crucial since the salt only
X	 * serves to thwart a dictionary attack.
X	 */
X
X	if (salt == (char *) 0) {
X		now = time ((long *) 0) + count++;
X		now ^= clock ();
X		now ^= getpid ();
X		now = ((now >> 12) ^ (now)) & 07777;
X		newsalt[0] = i64c ((now >> 6) & 077);
X		newsalt[1] = i64c (now & 077);
X		salt = newsalt;
X	}
X	cp = crypt (clear, salt);
X	strcpy (cipher, cp);
X
X#ifdef	DOUBLESIZE
X	if (strlen (clear) > 8) {
X		cp = crypt (clear + 8, salt);
X		strcat (cipher, cp + 2);
X	}
X#endif
X	return cipher;
X}
SHAR_EOF
if test 1287 -ne "`wc -c < 'encrypt.c'`"
then
	echo shar: "error transmitting 'encrypt.c'" '(should have been 1287 characters)'
fi
fi
echo shar: "extracting 'chpasswd.c'" '(5114 characters)'
if test -f 'chpasswd.c'
then
	echo shar: "will not over-write existing file 'chpasswd.c'"
else
sed 's/^X//' << \SHAR_EOF > 'chpasswd.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X *
X * chpass - update passwords in batch
X *
X *	chpass reads standard input for a list of colon separated
X *	user names and new passwords.  the appropriate password
X *	files are updated to reflect the changes.  because the
X *	changes are made in a batch fashion, the user must run
X *	the mkpasswd command after this command terminates since
X *	no password updates occur until the very end.
X */
X
X#include <stdio.h>
X#include "pwd.h"
X#include <fcntl.h>
X#include <string.h>
X#include "config.h"
X#ifdef	SHADOWPWD
X#include "shadow.h"
X#endif
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)chpasswd.c	3.2	12:30:30	12/12/90";
X#endif
X
Xchar	*Prog;
X
Xextern	char	*pw_encrypt();
X
X/* 
X * If it weren't for the different structures and differences in how
X * certain fields were manipulated, I could just use macros to replace
X * the function calls for the different file formats.  So I make the
X * best of things and just use macros to replace a few of the calls.
X */
X
X#ifdef	SHADOWPWD
X#define	pw_lock		spw_lock
X#define	pw_open		spw_open
X#define	pw_close	spw_close
X#define	pw_unlock	spw_unlock
X#endif
X
X/*
X * usage - display usage message and exit
X */
X
Xusage ()
X{
X	fprintf (stderr, "usage: %s\n", Prog);
X	exit (1);
X}
X
Xmain (argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	char	buf[BUFSIZ];
X	char	*name;
X	char	*newpwd;
X	char	*cp;
X#ifdef	SHADOWPWD
X	struct	spwd	*sp;
X	struct	spwd	newsp;
X	struct	spwd	*spw_locate();
X#else
X	struct	passwd	*pw;
X	struct	passwd	newpw;
X	struct	passwd	*pw_locate();
X	char	newage[5];
X#endif
X	int	errors = 0;
X	int	line = 0;
X	long	now = time ((long *) 0) / (24L*3600L);
X
X	if (Prog = strrchr (argv[0], '/'))
X		Prog++;
X	else
X		Prog = argv[0];
X
X	if (argc != 1)
X		usage ();
X
X	/*
X	 * Lock the password file and open it for reading.  This will
X	 * bring all of the entries into memory where they may be
X	 * updated.
X	 */
X
X	if (! pw_lock ()) {
X		fprintf (stderr, "%s: can't lock password file\n", Prog);
X		exit (1);
X	}
X	if (! pw_open (O_RDWR)) {
X		fprintf (stderr, "%s: can't open password file\n", Prog);
X		exit (1);
X	}
X
X	/*
X	 * Read each line, separating the user name from the password.
X	 * The password entry for each user will be looked up in the
X	 * appropriate file (shadow or passwd) and the password changed.
X	 * For shadow files the last change date is set directly, for
X	 * passwd files the last change date is set in the age only if
X	 * aging information is present.
X	 */
X
X	while (fgets (buf, sizeof buf, stdin) != (char *) 0) {
X		line++;
X		if (cp = strrchr (buf, '\n')) {
X			*cp = '\0';
X		} else {
X			fprintf (stderr, "%s: line %d: line too long\n",
X				Prog, line);
X			errors++;
X			continue;
X		}
X
X		/*
X		 * The username is the first field.  It is separated
X		 * from the password with a ":" character which is
X		 * replaced with a NUL to give the new password.  The
X		 * new password will then be encrypted in the normal
X		 * fashion with a new salt generated.
X		 */
X
X		name = buf;
X		if (cp = strchr (name, ':')) {
X			*cp++ = '\0';
X		} else {
X			fprintf (stderr, "%s: line %d: missing new password\n",
X				Prog, line);
X			errors++;
X			continue;
X		}
X		newpwd = cp;
X		cp = pw_encrypt (newpwd, (char *) 0);
X
X		/*
X		 * Get the password file entry for this user.  The user
X		 * must already exist.
X		 */
X
X#ifdef	SHADOWPWD
X		if (! (sp = spw_locate (name)))
X#else
X		if (! (pw = pw_locate (name)))
X#endif
X		{
X			fprintf (stderr, "%s: line %d: unknown user %s\n",
X				Prog, line, name);
X			errors++;
X			continue;
X		}
X
X		/*
X		 * The freshly encrypted new password is merged into
X		 * the user's password file entry and the last password
X		 * change date is set to the current date.
X		 */
X
X#ifdef	SHADOWPWD
X		newsp = *sp;
X		newsp.sp_pwdp = cp;
X		newsp.sp_lstchg = now;
X#else
X		newpw = *pw;
X		newpw.pw_passwd = cp;
X#ifdef	ATT_AGE
X		if (newpw.pw_age[0]) {
X			strcpy (newage, newpw.pw_age);
X			strcpy (newage + 2, l64a (now / 7));
X			newpw.pw_age = newage;
X		}
X#endif
X#endif
X
X		/* 
X		 * The updated password file entry is then put back
X		 * and will be written to the password file later, after
X		 * all the other entries have been updated as well.
X		 */
X
X#ifdef	SHADOWPWD
X		if (! spw_update (&newsp))
X#else
X		if (! pw_update (&newpw))
X#endif
X		{
X			fprintf (stderr, "%s: line %d: cannot update password entry\n",
X				Prog, line);
X			errors++;
X			continue;
X		}
X	}
X
X	/*
X	 * Any detected errors will cause the entire set of changes
X	 * to be aborted.  Unlocking the password file will cause
X	 * all of the changes to be ignored.  Otherwise the file is
X	 * closed, causing the changes to be written out all at
X	 * once, and then unlocked afterwards.
X	 */
X
X	if (errors) {
X		fprintf ("%s: error detected, changes ignored\n", Prog);
X		pw_unlock ();
X		exit (1);
X	}
X	if (! pw_close ()) {
X		fprintf ("%s: error updating password file\n", Prog);
X		exit (1);
X	}
X	(void) pw_unlock ();
X}
SHAR_EOF
if test 5114 -ne "`wc -c < 'chpasswd.c'`"
then
	echo shar: "error transmitting 'chpasswd.c'" '(should have been 5114 characters)'
fi
fi
echo shar: "extracting 'newusers.c'" '(13360 characters)'
if test -f 'newusers.c'
then
	echo shar: "will not over-write existing file 'newusers.c'"
else
sed 's/^X//' << \SHAR_EOF > 'newusers.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X *
X *	newusers - create users from a batch file
X *
X *	newusers creates a collection of entries in /etc/passwd
X *	and related files by reading a passwd-format file and
X *	adding entries in the related directories.
X */
X
X#include <stdio.h>
X#include "pwd.h"
X#include <grp.h>
X#include <fcntl.h>
X#include <string.h>
X#include "config.h"
X#ifdef	SHADOWPWD
X#include "shadow.h"
X#endif
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)newusers.c	3.2	12:31:02	12/12/90";
X#endif
X
Xchar	*Prog;
X
Xextern	char	*pw_encrypt();
X
Xint	pw_lock(), gr_lock();
Xint	pw_open(), gr_open();
Xstruct	passwd	*pw_locate(), *pw_next();
Xstruct	group	*gr_locate(), *gr_next();
Xint	pw_update(), gr_update();
Xint	pw_close(), gr_close();
Xint	pw_unlock(), gr_unlock();
X
X#ifdef	SHADOWPWD
Xint	spw_lock(), spw_open(), spw_update(), spw_close(), spw_unlock();
Xstruct	spwd	*spw_locate(), *spw_next();
X#endif
X
X#ifndef	MKDIR
X
X/*
X * mkdir - for those of us with no mkdir() system call.
X */
X
Xmkdir (dir, mode)
Xchar	*dir;
Xint	mode;
X{
X	int	mask;
X	int	status;
X	int	pid;
X	int	i;
X	char	buf[BUFSIZ];
X
X	mode = (~mode & 0777);
X	mask = umask (mode);
X	if ((pid = fork ()) == 0) {
X		execl ("/bin/mkdir", "mkdir", dir, (char *) 0);
X		perror ("/bin/mkdir");
X		_exit (1);
X	} else {
X		while ((i = wait (&status)) != pid && i != -1)
X			;
X	}
X	umask (mask);
X	return status;
X}
X#endif
X
X/*
X * usage - display usage message and exit
X */
X
Xusage ()
X{
X	fprintf (stderr, "Usage: %s [ input ]\n", Prog);
X	exit (1);
X}
X
X/*
X * add_group - create a new group or add a user to an existing group
X */
X
Xint
Xadd_group (name, uid, gid, ngid)
Xchar	*name;
Xchar	*uid;
Xchar	*gid;
Xint	*ngid;
X{
X	struct	passwd	*pwd;
X	struct	group	*grp;
X	struct	group	grent;
X	char	*members[2];
X	int	i;
X
X	/*
X	 * Start by seeing if the named group already exists.  This
X	 * will be very easy to deal with if it does.
X	 */
X
X	if (grp = gr_locate (gid)) {
Xadd_member:
X		grent = *grp;
X		*ngid = grent.gr_gid;
X		for (i = 0;grent.gr_mem[i] != (char *) 0;i++)
X			if (strcmp (grent.gr_mem[i], name) == 0)
X				return 0;
X
X		if (! (grent.gr_mem = (char **)
X				malloc (sizeof (char *) * (i + 2)))) {
X			fprintf (stderr, "%s: Out of Memory\n", Prog);
X			return -1;
X		}
X		memcpy (grent.gr_mem, grp->gr_mem, sizeof (char *) * (i + 2));
X		grent.gr_mem[i] = strdup (name);
X		grent.gr_mem[i + 1] = (char *) 0;
X
X		return ! gr_update (&grent);
X	}
X
X	/*
X	 * The group did not exist, so I try to figure out what the
X	 * GID is going to be.  The gid parameter is probably "", meaning
X	 * I figure out the GID from the password file.  I want the UID
X	 * and GID to match, unless the GID is already used.
X	 */
X
X	if (gid[0] == '\0') {
X		i = 100;
X		for (pw_rewind ();pwd = pw_next ();) {
X			if (pwd->pw_uid >= i)
X				i = pwd->pw_uid + 1;
X		}
X		for (gr_rewind ();grp = gr_next ();) {
X			if (grp->gr_gid == i) {
X				i = -1;
X				break;
X			}
X		}
X	} else if (gid[0] >= '0' && gid[0] <= '9') {
X
X	/*
X	 * The GID is a number, which means either this is a brand new
X	 * group, or an existing group.  For existing groups I just add
X	 * myself as a member, just like I did earlier.
X	 */
X
X		i = atoi (gid);
X		for (gr_rewind ();grp = gr_next ();)
X			if (grp->gr_gid == i)
X				goto add_member;
X	} else
X
X	/*
X	 * The last alternative is that the GID is a name which is not
X	 * already the name of an existing group, and I need to figure
X	 * out what group ID that group name is going to have.
X	 */
X
X		i = -1;
X
X	/*
X	 * If I don't have a group ID by now, I'll go get the
X	 * next one.
X	 */
X
X	if (i == -1) {
X		for (i = 100, gr_rewind;grp = gr_next ();)
X			if (grp->gr_gid >= i)
X				i = grp->gr_gid + 1;
X	}
X
X	/*
X	 * Now I have all of the fields required to create the new
X	 * group.
X	 */
X
X	if (gid[0] && (gid[0] <= '0' || gid[0] >= '9'))
X		grent.gr_name = gid;
X	else
X		grent.gr_name = name;
X
X	grent.gr_passwd = "!";
X	grent.gr_gid = i;
X	members[0] = name;
X	members[1] = (char *) 0;
X	grent.gr_mem = members;
X
X	*ngid = grent.gr_gid;
X	return ! gr_update (&grent);
X}
X
X/*
X * add_user - create a new user ID
X */
X
Xadd_user (name, uid, nuid, gid)
Xchar	*name;
Xchar	*uid;
Xint	*nuid;
Xint	gid;
X{
X	struct	passwd	*pwd;
X	struct	passwd	pwent;
X	int	i;
X
X	/*
X	 * The first guess for the UID is either the numerical UID
X	 * that the caller provided, or the next available UID.
X	 */
X
X	if (uid[0] >= '0' && uid[0] <= '9') {
X		i = atoi (uid);
X	} if (uid[0] && (pwd = pw_locate (uid))) {
X		i = pwd->pw_uid;
X	} else {
X		i = 100;
X		for (pw_rewind ();pwd = pw_next ();)
X			if (pwd->pw_uid >= i)
X				i = pwd->pw_uid + 1;
X	}
X
X	/*
X	 * I don't want to fill in the entire password structure
X	 * members JUST YET, since there is still more data to be
X	 * added.  So, I fill in the parts that I have.
X	 */
X
X	pwent.pw_name = name;
X	pwent.pw_passwd = "!";
X#ifdef	ATT_AGE
X	pwent.pw_age = "";
X#endif
X#ifdef	ATT_COMMENT
X	pwent.pw_comment = "";
X#endif
X#ifdef	BSD_QUOTAS
X	pwent.pw_quota = 0;
X#endif
X	pwent.pw_uid = i;
X	pwent.pw_gid = gid;
X	pwent.pw_gecos = "";
X	pwent.pw_dir = "";
X	pwent.pw_shell = "";
X
X	*nuid = i;
X	return ! pw_update (&pwent);
X}
X
X/*
X * add_passwd - add or update the encrypted password
X */
X
Xadd_passwd (pwd, passwd)
Xstruct	passwd	*pwd;
Xchar	*passwd;
X{
X#ifdef	SHADOWPWD
X	struct	spwd	*sp;
X	struct	spwd	spent;
X#endif
X	struct	passwd	*pw;
X	struct	passwd	pwent;
X	static	char	newage[5];
X
X	/*
X	 * In the case of regular password files, this is real
X	 * easy - pwd points to the entry in the password file.
X	 * Shadow files are harder since there are zillions of
X	 * things to do ...
X	 */
X
X#ifndef	SHADOWPWD
X	pwd->pw_passwd = pw_encrypt (passwd, (char *) 0);
X#ifdef	ATT_AGE
X	if (strlen (pwd->pw_age) == 4) {
X		strcpy (newage, pwd->pw_age);
X		strcpy (newage + 2,
X			l64a (time ((long *) 0) / (7L*24L*3600L)));
X		pwd->pw_age = newage;
X	}
X#endif	/* ATT_AGE */
X	return 0;
X#else
X
X	/*
X	 * Do the first and easiest shadow file case.  The user
X	 * already exists in the shadow password file.
X	 */
X
X	if (sp = spw_locate (pwd->pw_name)) {
X		spent = *sp;
X		spent.sp_pwdp = pw_encrypt (passwd, (char *) 0);
X		return ! spw_update (sp);
X	}
X
X	/*
X	 * Pick the next easiest case - the user has an encrypted
X	 * password which isn't equal to "!".  The password was set
X	 * to "!" earlier when the entry was created, so this user
X	 * would have to have had the password set someplace else.
X	 */
X
X	if (strcmp (pwd->pw_passwd, "!") != 0) {
X		pwd->pw_passwd = pw_encrypt (passwd, (char *) 0);
X#ifdef	ATT_AGE
X		if (strlen (pwd->pw_age) == 4) {
X			strcpy (newage, pwd->pw_age);
X			strcpy (newage + 2,
X				l64a (time ((long *) 0) / (7L*24L*3600L)));
X			pwd->pw_age = newage;
X		}
X#endif	/* ATT_AGE */
X		return 0;
X	}
X
X	/*
X	 * Now the really hard case - I need to create an entirely
X	 * shadow password file entry.
X	 */
X
X	spent.sp_namp = pwd->pw_name;
X	spent.sp_pwdp = pw_encrypt (passwd, (char *) 0);
X	spent.sp_lstchg = time ((long *) 0) / (24L*3600L);
X#ifdef	MINDAYS
X	spent.sp_min = MINDAYS;
X#else
X	spent.sp_min = 0;
X#endif
X#ifdef	MAXDAYS
X	spent.sp_max = MAXDAYS;
X#else
X	spent.sp_max = 10000;		/* 10000 is infinity this week */
X#endif
X#ifdef	WARNAGE
X	spent.sp_warn = WARNAGE;
X#else
X	spent.sp_warn = -1;
X#endif
X	spent.sp_inact = -1;
X	spent.sp_expire = -1;
X	spent.sp_flag = -1;
X
X	return ! spw_update (&spent);
X#endif
X}
X
Xmain (argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	char	buf[BUFSIZ];
X	char	*fields[8];
X	int	nfields;
X	char	*name;
X	char	*newpwd;
X	char	*cp;
X#ifdef	SHADOWPWD
X	struct	spwd	*sp;
X	struct	spwd	newsp;
X	struct	spwd	*spw_locate();
X#endif
X	struct	passwd	*pw;
X	struct	passwd	newpw;
X	struct	passwd	*pw_locate();
X	char	newage[5];
X	int	errors = 0;
X	int	line = 0;
X	long	now = time ((long *) 0) / (24L*3600L);
X	int	uid;
X	int	gid;
X	int	i;
X
X	if (Prog = strrchr (argv[0], '/'))
X		Prog++;
X	else
X		Prog = argv[0];
X
X	if (argc > 1 && argv[1][0] == '-')
X		usage ();
X
X	if (argc == 2) {
X		if (! freopen (argv[1], "r", stdin)) {
X			sprintf (buf, "%s: %s", Prog, argv[1]);
X			perror (buf);
X			exit (1);
X		}
X	}
X
X	/*
X	 * Lock the password files and open them for update.  This will
X	 * bring all of the entries into memory where they may be
X	 * searched for an modified, or new entries added.  The password
X	 * file is the key - if it gets locked, assume the others can
X	 * be locked right away.
X	 */
X
X	for (i = 0;i < 30;i++) {
X		if (pw_lock ())
X			break;
X	}
X	if (i == 30) {
X		fprintf (stderr, "%s: can't lock /etc/passwd.\n", Prog);
X		exit (1);
X	}
X#ifdef	SHADOWPWD
X	if (! spw_lock () || ! gr_lock ())
X#else
X	if (! gr_lock ())
X#endif
X	{
X		fprintf (stderr, "%s: can't lock files, try again later\n",
X			Prog);
X		(void) pw_unlock ();
X#ifdef	SHADOWPWD
X		(void) spw_unlock ();
X#endif
X		exit (1);
X	}
X#ifdef	SHADOWPWD
X	if (! pw_open (O_RDWR) || ! spw_open (O_RDWR) || ! gr_open (O_RDWR))
X#else
X	if (! pw_open (O_RDWR) || ! gr_open (O_RDWR))
X#endif
X	{
X		fprintf (stderr, "%s: can't open files\n", Prog);
X		(void) pw_unlock ();
X#ifdef	SHADOWPWD
X		(void) spw_unlock ();
X#endif
X		(void) gr_unlock ();
X		exit (1);
X	}
X
X	/*
X	 * Read each line.  The line has the same format as a password
X	 * file entry, except that certain fields are not contrained to
X	 * be numerical values.  If a group ID is entered which does
X	 * not already exist, an attempt is made to allocate the same
X	 * group ID as the numerical user ID.  Should that fail, the
X	 * next available group ID over 100 is allocated.  The pw_gid
X	 * field will be updated with that value.
X	 */
X
X	while (fgets (buf, sizeof buf, stdin) != (char *) 0) {
X		line++;
X		if (cp = strrchr (buf, '\n')) {
X			*cp = '\0';
X		} else {
X			fprintf (stderr, "%s: line %d: line too long\n",
X				Prog, line);
X			errors++;
X			continue;
X		}
X
X		/*
X		 * Break the string into fields and screw around with
X		 * them.  There MUST be 7 colon separated fields,
X		 * although the values aren't that particular.
X		 */
X
X		for (cp = buf, nfields = 0;nfields < 7;nfields++) {
X			fields[nfields] = cp;
X			if (cp = strchr (cp, ':'))
X				*cp++ = '\0';
X			else
X				break;
X		}
X		if (*cp || nfields != 6) {
X			fprintf (stderr, "%s: line %d: invalid line\n",
X				Prog, line);
X			continue;
X		}
X
X		/*
X		 * Now the fields are processed one by one.  The first
X		 * field to be processed is the group name.  A new
X		 * group will be created if the group name is non-numeric
X		 * and does not already exist.  The named user will be
X		 * the only member.  If there is no named group to be a
X		 * member of, the UID will be figured out and that value
X		 * will be a candidate for a new group, if that group ID
X		 * exists, a whole new group ID will be made up.
X		 */
X		
X		if (! (pw = pw_locate (fields[0])) &&
X			add_group (fields[0], fields[2], fields[3], &gid)) {
X			fprintf (stderr, "%s: %d: can't create GID\n",
X				Prog, line);
X			errors++;
X			continue;
X		}
X
X		/*
X		 * Now we work on the user ID.  It has to be specified
X		 * either as a numerical value, or left blank.  If it
X		 * is a numerical value, that value will be used, otherwise
X		 * the next available user ID is computed and used.  After
X		 * this there will at least be a (struct passwd) for the
X		 * user.
X		 */
X
X		if (! pw && add_user (fields[0], fields[2], &uid, gid)) {
X			fprintf (stderr, "%s: line %d: can't create UID\n",
X				Prog, line);
X			errors++;
X			continue;
X		}
X
X		/*
X		 * The password, gecos field, directory, and shell fields
X		 * all come next.
X		 */
X
X		if (! (pw = pw_locate (fields[0]))) {
X			fprintf (stderr, "%s: line %d: cannot find user %s\n",
X				Prog, line, fields[0]);
X			errors++;
X			continue;
X		}
X		newpw = *pw;
X
X		if (add_passwd (&newpw, fields[1])) {
X			fprintf (stderr, "%s: line %d: can't update password\n",
X				Prog, line);
X			errors++;
X			continue;
X		}
X		if (fields[4][0])
X			newpw.pw_gecos = fields[4];
X
X		if (fields[5][0])
X			newpw.pw_dir = fields[5];
X
X		if (fields[6][0])
X			newpw.pw_shell = fields[6];
X
X		if (newpw.pw_dir[0] && access (newpw.pw_dir, 0)) {
X#ifdef	UMASK
X			if (mkdir (newpw.pw_dir, 0777 & (~UMASK)))
X#else
X			if (mkdir (newpw.pw_dir, 0777))
X#endif
X				fprintf (stderr, "%s: line %d: mkdir failed\n",
X					Prog, line);
X			else if (chown (newpw.pw_dir,
X					newpw.pw_uid, newpw.pw_gid))
X				fprintf (stderr, "%s: line %d: chown failed\n",
X					Prog, line);
X		}
X
X		/*
X		 * Update the password entry with the new changes made.
X		 */
X
X		if (! pw_update (&newpw)) {
X			fprintf (stderr, "%s: line %d: can't update entry\n",
X				Prog, line);
X			errors++;
X			continue;
X		}
X	}
X
X	/*
X	 * Any detected errors will cause the entire set of changes
X	 * to be aborted.  Unlocking the password file will cause
X	 * all of the changes to be ignored.  Otherwise the file is
X	 * closed, causing the changes to be written out all at
X	 * once, and then unlocked afterwards.
X	 */
X
X	if (errors) {
X		fprintf ("%s: error detected, changes ignored\n", Prog);
X		(void) gr_unlock ();
X#ifdef	SHADOWPWD
X		(void) spw_unlock ();
X#endif
X		(void) pw_unlock ();
X		exit (1);
X	}
X#ifdef	SHADOWPWD
X	if (! pw_close () || ! spw_close () || ! gr_close ())
X#else
X	if (! pw_close () || ! gr_close ())
X#endif
X	{
X		fprintf ("%s: error updating files\n", Prog);
X		(void) gr_unlock ();
X#ifdef	SHADOWPWD
X		(void) spw_unlock ();
X#endif
X		(void) pw_unlock ();
X		exit (1);
X	}
X	(void) gr_unlock ();
X#ifdef	SHADOWPWD
X	(void) spw_unlock ();
X#endif
X	(void) pw_unlock ();
X
X	exit (0);
X}
SHAR_EOF
if test 13360 -ne "`wc -c < 'newusers.c'`"
then
	echo shar: "error transmitting 'newusers.c'" '(should have been 13360 characters)'
fi
fi
echo shar: "extracting 'rad64.c'" '(1670 characters)'
if test -f 'rad64.c'
then
	echo shar: "will not over-write existing file 'rad64.c'"
else
sed 's/^X//' << \SHAR_EOF > 'rad64.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)rad64.c	3.2	19:44:25	12/10/90";
X#endif
X
X/*
X * c64i - convert a radix 64 character to an integer
X */
X
Xint	c64i (c)
Xchar	c;
X{
X	if (c == '.')
X		return (0);
X
X	if (c == '/')
X		return (1);
X
X	if (c >= '0' && c <= '9')
X		return (c - '0' + 2);
X
X	if (c >= 'A' && c <= 'Z')
X		return (c - 'A' + 12);
X
X	if (c >= 'a' && c <= 'z')
X		return (c - 'a' + 38);
X	else
X		return (-1);
X}
X
X/*
X * i64c - convert an integer to a radix 64 character
X */
X
Xint	i64c (i)
Xint	i;
X{
X	if (i < 0)
X		return ('.');
X	else if (i > 63)
X		return ('z');
X
X	if (i == 0)
X		return ('.');
X
X	if (i == 1)
X		return ('/');
X
X	if (i >= 2 && i <= 11)
X		return ('0' - 2 + i);
X
X	if (i >= 12 && i <= 37)
X		return ('A' - 12 + i);
X
X	if (i >= 38 && i <= 63)
X		return ('a' - 38 + i);
X
X	return ('\0');
X}
X
X/*
X * l64a - convert a long to a string of radix 64 characters
X */
X
Xchar	*l64a (l)
Xlong	l;
X{
X	static	char	buf[8];
X	int	i = 0;
X
X	if (i < 0L)
X		return ((char *) 0);
X
X	do {
X		buf[i++] = i64c ((int) (l % 64));
X		buf[i] = '\0';
X	} while (l /= 64L, l > 0 && i < 6);
X
X	return (buf);
X}
X
X/*
X * a64l - convert a radix 64 string to a long integer
X */
X
Xlong	a64l (s)
Xchar	*s;
X{
X	int	i;
X	long	value;
X	long	shift = 0;
X
X	for (i = 0, value = 0L;i < 6 && *s;s++) {
X		value += (c64i (*s) << shift);
X		shift += 6;
X	}
X	return (value);
X}
SHAR_EOF
if test 1670 -ne "`wc -c < 'rad64.c'`"
then
	echo shar: "error transmitting 'rad64.c'" '(should have been 1670 characters)'
fi
fi
echo shar: "extracting 'dialchk.c'" '(1328 characters)'
if test -f 'dialchk.c'
then
	echo shar: "will not over-write existing file 'dialchk.c'"
else
sed 's/^X//' << \SHAR_EOF > 'dialchk.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <stdio.h>
X#ifdef	BSD
X#include <strings.h>
X#else
X#include <string.h>
X#endif
X#include "config.h"
X#include "dialup.h"
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)dialchk.c	3.2	19:44:18	12/10/90";
X#endif
X
Xextern	char	*pw_encrypt();
X
X/*
X * Check for dialup password
X *
X *	dialcheck tests to see if tty is listed as being a dialup
X *	line.  If so, a dialup password may be required if the shell
X *	is listed as one which requires a second password.
X */
X
Xint	dialcheck (tty, shell)
Xchar	*tty;
Xchar	*shell;
X{
X	char	*crypt ();
X	char	*getpass ();
X	struct	dialup	*dialup;
X	char	*pass;
X	char	*cp;
X
X	setduent ();
X
X	if (! isadialup (tty)) {
X		endduent ();
X		return (1);
X	}
X	if (! (dialup = getdushell (shell))) {
X		endduent ();
X		return (1);
X	}
X	endduent ();
X
X	if (dialup->du_passwd[0] == '\0')
X		return (1);
X
X	if (! (pass = getpass ("Dialup Password:")))
X		return (0);
X
X	cp = pw_encrypt (pass, dialup->du_passwd);
X	return (strcmp (cp, dialup->du_passwd) == 0);
X}
SHAR_EOF
if test 1328 -ne "`wc -c < 'dialchk.c'`"
then
	echo shar: "error transmitting 'dialchk.c'" '(should have been 1328 characters)'
fi
fi
echo shar: "extracting 'faillog.h'" '(715 characters)'
if test -f 'faillog.h'
then
	echo shar: "will not over-write existing file 'faillog.h'"
else
sed 's/^X//' << \SHAR_EOF > 'faillog.h'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X/*
X * faillog.h - login failure logging file format
X *
X *	@(#)faillog.h	2.2	19:23:46	7/29/90
X *
X * The login failure file is maintained by login(1) and fail(1L)
X * Each record in the file represents a separate UID and the file
X * is indexed in that fashion.
X */
X
X#define	FAILFILE	"/usr/adm/faillog"
X
Xstruct	faillog {
X	short	fail_cnt;	/* failures since last success */
X	short	fail_max;	/* failures before turning account off */
X	char	fail_line[12];	/* last failure occured here */
X	time_t	fail_time;	/* last failure occured then */
X};
SHAR_EOF
if test 715 -ne "`wc -c < 'faillog.h'`"
then
	echo shar: "error transmitting 'faillog.h'" '(should have been 715 characters)'
fi
fi
echo shar: "extracting 'pwdbm.c'" '(1637 characters)'
if test -f 'pwdbm.c'
then
	echo shar: "will not over-write existing file 'pwdbm.c'"
else
sed 's/^X//' << \SHAR_EOF > 'pwdbm.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)pwdbm.c	3.3	12:31:13	12/12/90";
X#endif
X
X#ifdef	BSD
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#else
X#include <string.h>
X#endif
X#include <stdio.h>
X#include "pwd.h"
X#include "config.h"
X
X#ifdef	DBM
X#include <dbm.h>
X#endif
X#ifdef	NDBM
X#include <ndbm.h>
XDBM	*pw_dbm;
X#endif
X
X/*
X * pw_dbm_update
X *
X * Updates the DBM password files, if they exist.
X */
X
Xint
Xpw_dbm_update (pw)
Xstruct	passwd	*pw;
X{
X	datum	key;
X	datum	content;
X	char	data[BUFSIZ];
X	int	len;
X	static	int	once;
X
X	if (! once) {
X#ifdef	NDBM
X		if (! pw_dbm)
X			setpwent ();
X#else
X		setpwent ();
X#endif
X		once++;
X	}
X#ifdef	DBM
X	strcpy (data, PWDFILE);
X	strcat (data, ".pag");
X	if (access (data, 0))
X		return 0;
X#endif
X#ifdef	NDBM
X	if (! pw_dbm)
X		return 0;
X#endif
X	len = pw_pack (pw, data);
X	content.dsize = len;
X	content.dptr = data;
X
X	key.dsize = strlen (pw->pw_name);
X	key.dptr = pw->pw_name;
X#ifdef	DBM
X	if (store (key, content))
X		return 0;
X#endif
X#ifdef	NDBM
X	if (dbm_store (pw_dbm, key, content, DBM_REPLACE))
X		return 0;
X#endif
X
X	key.dsize = sizeof pw->pw_uid;
X	key.dptr = (char *) &pw->pw_uid;
X#ifdef	DBM
X	if (store (key, content))
X		return 0;
X#endif
X#ifdef	NDBM
X	if (dbm_store (pw_dbm, key, content, DBM_REPLACE))
X		return 0;
X#endif
X	return 1;
X}
SHAR_EOF
if test 1637 -ne "`wc -c < 'pwdbm.c'`"
then
	echo shar: "error transmitting 'pwdbm.c'" '(should have been 1637 characters)'
fi
fi
echo shar: "extracting 'grdbm.c'" '(2117 characters)'
if test -f 'grdbm.c'
then
	echo shar: "will not over-write existing file 'grdbm.c'"
else
sed 's/^X//' << \SHAR_EOF > 'grdbm.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)grdbm.c	3.1	08:11:52	11/21/90";
X#endif
X
X#include <string.h>
X#include <stdio.h>
X#include <grp.h>
X#include "config.h"
X
X#ifdef	NDBM
X#include <ndbm.h>
XDBM	*gr_dbm;
X
X#define	GRP_FRAG	256
X
X/*
X * gr_dbm_update
X *
X * Updates the DBM password files, if they exist.
X */
X
Xint
Xgr_dbm_update (gr)
Xstruct	group	*gr;
X{
X	datum	key;
X	datum	content;
X	char	data[BUFSIZ*8];
X	char	grpkey[60];
X	char	*cp;
X	int	len;
X	int	i;
X	int	cnt;
X	static	int	once;
X
X	if (! once) {
X		if (! gr_dbm)
X			setgrent ();
X
X		once++;
X	}
X	if (! gr_dbm)
X		return 0;
X
X	len = gr_pack (gr, data);
X
X	if (len <= GRP_FRAG) {
X		content.dsize = len;
X		content.dptr = data;
X
X		key.dsize = strlen (gr->gr_name);
X		key.dptr = gr->gr_name;
X		if (dbm_store (gr_dbm, key, content, DBM_REPLACE))
X			return 0;
X
X		key.dsize = sizeof gr->gr_gid;
X		key.dptr = (char *) &gr->gr_gid;
X		if (dbm_store (gr_dbm, key, content, DBM_REPLACE))
X			return 0;
X
X	} else {
X		content.dsize = sizeof cnt;
X		content.dptr = (char *) &cnt;
X		cnt = (len + (GRP_FRAG-1)) / GRP_FRAG;
X
X		key.dsize = strlen (gr->gr_name);
X		key.dptr = gr->gr_name;
X		if (dbm_store (gr_dbm, key, content, DBM_REPLACE))
X			return 0;
X
X		key.dsize = sizeof gr->gr_gid;
X		key.dptr = (char *) &gr->gr_gid;
X		if (dbm_store (gr_dbm, key, content, DBM_REPLACE))
X			return 0;
X
X		for (cp = data, i = 0;i < cnt;i++) {
X			content.dsize = len > GRP_FRAG ? GRP_FRAG:len;
X			len -= content.dsize;
X			content.dptr = cp;
X			cp += content.dsize;
X
X			key.dsize = sizeof i + strlen (gr->gr_name);
X			key.dptr = grpkey;
X			memcpy (grpkey, &i, sizeof i);
X			strcpy (grpkey + sizeof i, gr->gr_name);
X			if (dbm_store (gr_dbm, key, content, DBM_REPLACE))
X				return 0;
X
X			key.dsize = sizeof i + sizeof gr->gr_gid;
X			key.dptr = grpkey;
X			memcpy (grpkey, &i, sizeof i);
X			memcpy (grpkey + sizeof i, &gr->gr_gid,
X				sizeof gr->gr_gid);
X			if (dbm_store (gr_dbm, key, content, DBM_REPLACE))
X				return 0;
X		}
X	}
X	return 1;
X}
X#endif
SHAR_EOF
if test 2117 -ne "`wc -c < 'grdbm.c'`"
then
	echo shar: "error transmitting 'grdbm.c'" '(should have been 2117 characters)'
fi
fi
echo shar: "extracting 'gshadow.c'" '(4652 characters)'
if test -f 'gshadow.c'
then
	echo shar: "will not over-write existing file 'gshadow.c'"
else
sed 's/^X//' << \SHAR_EOF > 'gshadow.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include "shadow.h"
X#include "config.h"
X#include <stdio.h>
X#ifndef	BSD
X#include <string.h>
X#include <memory.h>
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X
X#ifdef	NDBM
X#include <ndbm.h>
X#include <fcntl.h>
XDBM	*sg_dbm;
Xint	sg_dbm_mode;
Xstatic	int	dbmopened;
Xstatic	int	dbmerror;
X#endif
X
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)gshadow.c	3.3	11:25:55	12/19/90";
X#endif
X
X#define	MAXMEM	1024
X
Xstatic	FILE	*shadow;
Xstatic	char	*sgrpfile = "/etc/gshadow";
Xstatic	char	sgrbuf[BUFSIZ*4];
Xstatic	char	*members[MAXMEM+1];
Xstatic	char	*admins[MAXMEM+1];
Xstatic	struct	sgrp	sgroup;
X
Xextern	char	*fgetsx();
Xextern	int	fputsx();
X
X#define	FIELDS	4
X
Xstatic char **
Xlist (s, l)
Xchar	*s;
Xchar	**l;
X{
X	int	nmembers = 0;
X
X	while (*s) {
X		l[nmembers++] = s;
X		if (s = strchr (s, ','))
X			*s++ = '\0';
X	}
X	l[nmembers] = (char *) 0;
X	return l;
X}
X
Xvoid
Xsetsgent ()
X{
X#ifdef	NDBM
X	int	mode;
X#endif	/* NDBM */
X
X	if (shadow)
X		rewind (shadow);
X	else
X		shadow = fopen (GSHADOW, "r");
X
X	/*
X	 * Attempt to open the DBM files if they have never been opened
X	 * and an error has never been returned.
X	 */
X
X#ifdef NDBM
X	if (! dbmerror && ! dbmopened) {
X		char	dbmfiles[BUFSIZ];
X
X		strcpy (dbmfiles, sgrpfile);
X		strcat (dbmfiles, ".pag");
X		if (sg_dbm_mode != -1)
X			mode = O_RDONLY;
X		else
X			mode = (sg_dbm_mode == O_RDONLY ||
X				sg_dbm_mode == O_RDWR) ? sg_dbm_mode:O_RDONLY;
X
X		if (access (dbmfiles, 0) ||
X			(! (sg_dbm = dbm_open (sgrpfile, mode, 0))))
X			dbmerror = 1;
X		else
X			dbmopened = 1;
X	}
X#endif	/* NDBM */
X}
X
Xvoid
Xendsgent ()
X{
X	if (shadow)
X		(void) fclose (shadow);
X
X	shadow = (FILE *) 0;
X#ifdef	NDBM
X	if (dbmopened && sg_dbm) {
X		dbm_close (sg_dbm);
X		dbmopened = 0;
X		sg_dbm = 0;
X	}
X#endif
X}
X
Xstruct sgrp *
Xsgetsgent (string)
Xchar	*string;
X{
X	char	*fields[FIELDS];
X	char	*cp;
X	char	*cpp;
X	int	atoi ();
X	long	atol ();
X	int	i;
X
X	strncpy (sgrbuf, string, sizeof sgrbuf - 1);
X	sgrbuf[sizeof sgrbuf - 1] = '\0';
X
X	if (cp = strrchr (sgrbuf, '\n'))
X		*cp = '\0';
X
X	for (cp = sgrbuf, i = 0;*cp && i < FIELDS;i++) {
X		fields[i] = cp;
X		while (*cp && *cp != ':')
X			cp++;
X
X		if (*cp)
X			*cp++ = '\0';
X	}
X	if (*cp || i != FIELDS)
X		return 0;
X
X	sgroup.sg_name = fields[0];
X	sgroup.sg_passwd = fields[1];
X	sgroup.sg_adm = list (fields[2], admins);
X	sgroup.sg_mem = list (fields[3], members);
X
X	return &sgroup;
X}
X
Xstruct sgrp
X*fgetsgent (fp)
XFILE	*fp;
X{
X	char	buf[sizeof sgrbuf];
X
X	if (! fp)
X		return (0);
X
X	if (fgetsx (buf, sizeof buf, fp) == (char *) 0)
X		return (0);
X
X	return sgetsgent (buf);
X}
X
Xstruct sgrp
X*getsgent ()
X{
X	if (! shadow)
X		setsgent ();
X
X	return (fgetsgent (shadow));
X}
X
Xstruct sgrp *
Xgetsgnam (name)
Xchar	*name;
X{
X	struct	sgrp	*sgrp;
X#ifdef NDBM
X	datum	key;
X	datum	content;
X#endif
X
X	setsgent ();
X
X#ifdef NDBM
X
X	/*
X	 * If the DBM file are now open, create a key for this group and
X	 * try to fetch the entry from the database.  A matching record
X	 * will be unpacked into a static structure and returned to
X	 * the user.
X	 */
X
X	if (dbmopened) {
X		key.dsize = strlen (name);
X		key.dptr = name;
X
X		content = dbm_fetch (sg_dbm, key);
X		if (content.dptr != 0) {
X			memcpy (sgrbuf, content.dptr, content.dsize);
X			sgroup.sg_mem = members;
X			sgroup.sg_adm = admins;
X			sgr_unpack (sgrbuf, content.dsize, &sgroup);
X			return &sgroup;
X		}
X	}
X#endif
X	while ((sgrp = getsgent ()) != (struct sgrp *) 0) {
X		if (strcmp (name, sgrp->sg_name) == 0)
X			return (sgrp);
X	}
X	return (0);
X}
X
Xint
Xputsgent (sgrp, fp)
Xstruct	sgrp	*sgrp;
XFILE	*fp;
X{
X	char	buf[sizeof sgrbuf];
X	char	*cp = buf;
X	int	errors = 0;
X	int	i;
X
X	if (! fp || ! sgrp)
X		return -1;
X
X	/*
X	 * Copy the group name and passwd.
X	 */
X
X	strcpy (cp, sgrp->sg_name);
X	cp += strlen (cp);
X	*cp++ = ':';
X
X	strcpy (cp, sgrp->sg_passwd);
X	cp += strlen (cp);
X	*cp++ = ':';
X
X	/*
X	 * Copy the administrators, separating each from the other
X	 * with a ",".
X	 */
X
X	for (i = 0;sgrp->sg_adm[i];i++) {
X		if (i > 0)
X			*cp++ = ',';
X
X		strcpy (cp, sgrp->sg_adm[i]);
X		cp += strlen (cp);
X	}
X	*cp++ = ':';
X
X	/*
X	 * Now do likewise with the group members.
X	 */
X
X	for (i = 0;sgrp->sg_mem[i];i++) {
X		if (i > 0)
X			*cp++ = ',';
X
X		strcpy (cp, sgrp->sg_mem[i]);
X		cp += strlen (cp);
X	}
X	*cp++ = '\n';
X	*cp = '\0';
X
X	/*
X	 * Output using the function which understands the line
X	 * continuation conventions.
X	 */
X
X	return fputsx (buf, fp);
X}
SHAR_EOF
if test 4652 -ne "`wc -c < 'gshadow.c'`"
then
	echo shar: "error transmitting 'gshadow.c'" '(should have been 4652 characters)'
fi
fi
echo shar: "extracting 'sppack.c'" '(2142 characters)'
if test -f 'sppack.c'
then
	echo shar: "will not over-write existing file 'sppack.c'"
else
sed 's/^X//' << \SHAR_EOF > 'sppack.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X *
X * Duplication is permitted for non-commercial [ profit making ]
X * purposes provided this and other copyright notices remain
X * intact.
X */
X
X#include <stdio.h>
X#ifdef	BSD
X#include <strings.h>
X#else
X#include <string.h>
X#endif
X
X#include "shadow.h"
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)sppack.c	3.1	08:16:27	11/21/90";
X#endif
X
Xint	spw_pack (spwd, buf)
Xstruct	spwd	*spwd;
Xchar	*buf;
X{
X	char	*cp;
X
X	cp = buf;
X	strcpy (cp, spwd->sp_namp);
X	cp += strlen (cp) + 1;
X
X	strcpy (cp, spwd->sp_pwdp);
X	cp += strlen (cp) + 1;
X
X	memcpy (cp, &spwd->sp_min, sizeof spwd->sp_min);
X	cp += sizeof spwd->sp_min;
X
X	memcpy (cp, &spwd->sp_max, sizeof spwd->sp_max);
X	cp += sizeof spwd->sp_max;
X
X	memcpy (cp, &spwd->sp_lstchg, sizeof spwd->sp_lstchg);
X	cp += sizeof spwd->sp_lstchg;
X
X	memcpy (cp, &spwd->sp_warn, sizeof spwd->sp_warn);
X	cp += sizeof spwd->sp_warn;
X
X	memcpy (cp, &spwd->sp_inact, sizeof spwd->sp_inact);
X	cp += sizeof spwd->sp_inact;
X
X	memcpy (cp, &spwd->sp_expire, sizeof spwd->sp_expire);
X	cp += sizeof spwd->sp_expire;
X
X	memcpy (cp, &spwd->sp_flag, sizeof spwd->sp_flag);
X	cp += sizeof spwd->sp_flag;
X
X	return cp - buf;
X}
X
Xint	spw_unpack (buf, len, spwd)
Xchar	*buf;
Xint	len;
Xstruct	spwd	*spwd;
X{
X	char	*org = buf;
X	char	*cp;
X
X	spwd->sp_namp = buf;
X	buf += strlen (buf) + 1;
X
X	spwd->sp_pwdp = buf;
X	buf += strlen (buf) + 1;
X
X	memcpy (&spwd->sp_min, buf, sizeof spwd->sp_min);
X	buf += sizeof spwd->sp_min;
X
X	memcpy (&spwd->sp_max, buf, sizeof spwd->sp_max);
X	buf += sizeof spwd->sp_max;
X
X	memcpy (&spwd->sp_lstchg, buf, sizeof spwd->sp_lstchg);
X	buf += sizeof spwd->sp_lstchg;
X
X	memcpy (&spwd->sp_warn, buf, sizeof spwd->sp_warn);
X	buf += sizeof spwd->sp_warn;
X
X	memcpy (&spwd->sp_inact, buf, sizeof spwd->sp_inact);
X	buf += sizeof spwd->sp_inact;
X
X	memcpy (&spwd->sp_expire, buf, sizeof spwd->sp_expire);
X	buf += sizeof spwd->sp_expire;
X
X	memcpy (&spwd->sp_flag, buf, sizeof spwd->sp_flag);
X	buf += sizeof spwd->sp_flag;
X
X	if (buf - org > len)
X		return -1;
X
X	return 0;
X}
SHAR_EOF
if test 2142 -ne "`wc -c < 'sppack.c'`"
then
	echo shar: "error transmitting 'sppack.c'" '(should have been 2142 characters)'
fi
fi
exit 0
#	End of shell archive
-- 
John F. Haugh II        | Distribution to  | UUCP: ...!cs.utexas.edu!rpp386!jfh
Ma Bell: (512) 255-8251 | GEnie PROHIBITED :-) |  Domain: jfh@rpp386.cactus.org
"If liberals interpreted the 2nd Amendment the same way they interpret the
 rest of the Constitution, gun ownership would be mandatory."

jfh@rpp386.cactus.org (John F Haugh II) (05/16/91)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	gspack.c
#	spdbm.c
#	lastlog.h
#	shell.c
#	login.c
#	sub.c
#	dpmain.c
#	mail.c
#	env.c
#	pwd.h
#	grpack.c
#	shadow.h
#	log.c
#	grent.c
#	motd.c
#	dialup.h
#	fields.c
#	gsdbm.c
# This archive created: Sun Mar  3 13:27:34 1991
# By:	John F Haugh II (River Parishes Programming, Austin TX)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'gspack.c'" '(2890 characters)'
if test -f 'gspack.c'
then
	echo shar: "will not over-write existing file 'gspack.c'"
else
sed 's/^X//' << \SHAR_EOF > 'gspack.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <stdio.h>
X#include "shadow.h"
X#ifdef	BSD
X#include <strings.h>
X#else
X#include <string.h>
X#endif
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)gspack.c	3.1	09:13:50	12/13/90";
X#endif
X
X/*
X * sgr_pack - convert a shadow group structure to a packed
X *	      shadow group record
X *
X *	sgr_pack takes the shadow group structure and packs
X *	the components in a record.  this record will be
X *	unpacked later by sgr_unpack.
X */
X
Xint
Xsgr_pack (sgrp, buf)
Xstruct	sgrp	*sgrp;
Xchar	*buf;
X{
X	char	*cp;
X	int	i;
X
X	/*
X	 * The name and password are both easy - append each string
X	 * to the buffer.  These are always the first two strings
X	 * in a record.
X	 */
X
X	cp = buf;
X	strcpy (cp, sgrp->sg_name);
X	cp += strlen (cp) + 1;
X
X	strcpy (cp, sgrp->sg_passwd);
X	cp += strlen (cp) + 1;
X
X	/*
X	 * The arrays of administrators and members are slightly
X	 * harder.  Each element is appended as a string, with a
X	 * final '\0' appended to serve as a blank string.  The
X	 * number of elements is not known in advance, so the
X	 * entire collection of administrators must be scanned to
X	 * find the start of the members.
X	 */
X
X	for (i = 0;sgrp->sg_adm[i];i++) {
X		strcpy (cp, sgrp->sg_adm[i]);
X		cp += strlen (cp) + 1;
X	}
X	*cp++ = '\0';
X
X	for (i = 0;sgrp->sg_mem[i];i++) {
X		strcpy (cp, sgrp->sg_mem[i]);
X		cp += strlen (cp) + 1;
X	}
X	*cp++ = '\0';
X
X	return cp - buf;
X}
X
X/*
X * sgr_unpack - convert a packed shadow group record to an
X *	        unpacked record
X *
X *	sgr_unpack converts a record which was packed by sgr_pack
X *	into the normal shadow group structure format.
X */
X
Xint
Xsgr_unpack (buf, len, sgrp)
Xchar	*buf;
Xint	len;
Xstruct	sgrp	*sgrp;
X{
X	char	*org = buf;
X	int	i;
X
X	/*
X	 * The name and password are both easy - they are the first
X	 * two strings in the record.
X	 */
X
X	sgrp->sg_name = buf;
X	buf += strlen (buf) + 1;
X	if (buf - org > len)
X		return -1;
X
X	sgrp->sg_passwd = buf;
X	buf += strlen (buf) + 1;
X	if (buf - org > len)
X		return -1;
X
X	/*
X	 * The administrators and members are slightly more difficult.
X	 * The arrays are lists of strings.  Each list is terminated
X	 * by a string of length zero.  This string is detected by
X	 * looking for an initial character of '\0'.
X	 */
X
X	for (i = 0;*buf && i < 1024;i++) {
X		sgrp->sg_adm[i] = buf;
X		buf += strlen (buf) + 1;
X
X		if (buf - org > len)
X			return -1;
X	}
X	sgrp->sg_adm[i] = (char *) 0;
X	if (! *buf)
X		buf++;
X
X	for (i = 0;*buf && i < 1024;i++) {
X		sgrp->sg_mem[i] = buf;
X		buf += strlen (buf) + 1;
X
X		if (buf - org > len)
X			return -1;
X	}
X	sgrp->sg_mem[i] = (char *) 0;
X
X	return 0;
X}
SHAR_EOF
if test 2890 -ne "`wc -c < 'gspack.c'`"
then
	echo shar: "error transmitting 'gspack.c'" '(should have been 2890 characters)'
fi
fi
echo shar: "extracting 'spdbm.c'" '(933 characters)'
if test -f 'spdbm.c'
then
	echo shar: "will not over-write existing file 'spdbm.c'"
else
sed 's/^X//' << \SHAR_EOF > 'spdbm.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)spdbm.c	3.1	08:16:16	11/21/90";
X#endif
X
X#include <string.h>
X#include <stdio.h>
X#include "config.h"
X#include "shadow.h"
X
X#ifdef	NDBM
X#include <ndbm.h>
XDBM	*sp_dbm;
X
X/*
X * sp_dbm_update
X *
X * Updates the DBM password files, if they exist.
X */
X
Xint
Xsp_dbm_update (sp)
Xstruct	spwd	*sp;
X{
X	datum	key;
X	datum	content;
X	char	data[BUFSIZ];
X	char	spwdkey[60];
X	char	*cp;
X	int	len;
X	int	i;
X	int	cnt;
X	static	int	once;
X
X	if (! once) {
X		if (! sp_dbm)
X			setspent ();
X
X		once++;
X	}
X	if (! sp_dbm)
X		return 0;
X
X	len = spw_pack (sp, data);
X
X	content.dsize = len;
X	content.dptr = data;
X
X	key.dsize = strlen (sp->sp_namp);
X	key.dptr = sp->sp_namp;
X	if (dbm_store (sp_dbm, key, content, DBM_REPLACE))
X		return 0;
X
X	return 1;
X}
X#endif
SHAR_EOF
if test 933 -ne "`wc -c < 'spdbm.c'`"
then
	echo shar: "error transmitting 'spdbm.c'" '(should have been 933 characters)'
fi
fi
echo shar: "extracting 'lastlog.h'" '(511 characters)'
if test -f 'lastlog.h'
then
	echo shar: "will not over-write existing file 'lastlog.h'"
else
sed 's/^X//' << \SHAR_EOF > 'lastlog.h'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X/*
X * lastlog.h - structure of lastlog file
X *
X *	@(#)lastlog.h	2.3	19:23:49	7/29/90
X *
X *	This file defines a lastlog file structure which should be sufficient
X *	to hold the information required by login.  It should only be used if
X *	there is no real lastlog.h file.
X */
X
Xstruct	lastlog	{
X	time_t	ll_time;
X	char	ll_line[8];
X};
SHAR_EOF
if test 511 -ne "`wc -c < 'lastlog.h'`"
then
	echo shar: "error transmitting 'lastlog.h'" '(should have been 511 characters)'
fi
fi
echo shar: "extracting 'shell.c'" '(2527 characters)'
if test -f 'shell.c'
then
	echo shar: "will not over-write existing file 'shell.c'"
else
sed 's/^X//' << \SHAR_EOF > 'shell.c'
X/*
X * Copyright 1989, 1990, 1991, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X#include <stdio.h>
X#include <errno.h>
X#ifndef	BSD
X#include <string.h>
X#include <memory.h>
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X#include "config.h"
X
X#ifndef	lint
Xstatic	char	_sccsid[] = "@(#)shell.c	3.2	07:55:08	2/6/91";
X#endif
X
Xextern	char	*newenvp[];
X
X/*
X * shell - execute the named program
X *
X *	shell begins by trying to figure out what argv[0] is going to
X *	be for the named process.  The user may pass in that argument,
X *	or it will be the last pathname component of the file with a
X *	'-' prepended.  The first attempt is to just execute the named
X *	file.  If the errno comes back "ENOEXEC", the file is assumed
X *	at first glance to be a shell script.  The first two characters
X *	must be "#!", in which case "/bin/sh" is executed to process
X *	the file.  If all that fails, give up in disgust ...
X */
X
Xvoid	shell (file, arg)
Xchar	*file;
Xchar	*arg;
X{
X	char	arg0[BUFSIZ];
X	FILE	*fp;
X	char	*path;
X	int	err;
X
X	if (file == (char *) 0)
X		exit (1);
X
X	/*
X	 * The argv[0]'th entry is usually the path name, but
X	 * for various reasons the invoker may want to override
X	 * that.  So, we determine the 0'th entry only if they
X	 * don't want to tell us what it is themselves.
X	 */
X
X	if (arg == (char *) 0) {
X		if (path = strrchr (file, '/'))
X			path++;
X		else
X			path = file;
X
X		(void) strcpy (arg0 + 1, path);
X		arg0[0] = '-';
X		arg = arg0;
X	}
X#ifndef	NDEBUG
X	printf ("Executing shell %s\n", file);
X#endif
X
X	/*
X	 * First we try the direct approach.  The system should be
X	 * able to figure out what we are up to without too much
X	 * grief.
X	 */
X
X	execle (file, arg, (char *) 0, newenvp);
X	err = errno;
X
X	/*
X	 * It is perfectly OK to have a shell script for a login
X	 * shell, and this code attempts to support that.  It
X	 * relies on the standard shell being able to make sense
X	 * of the "#!" magic number.
X	 */
X
X	if (err == ENOEXEC) {
X		if (fp = fopen (file, "r")) {
X			if (getc (fp) == '#' && getc (fp) == '!') {
X				fclose (fp);
X				execle ("/bin/sh", "sh",
X					file, (char *) 0, newenvp);
X				err = errno;
X			} else {
X				fclose (fp);
X			}
X		}
X	}
X
X	/*
X	 * Obviously something is really wrong - I can't figure out
X	 * how to execute this stupid shell, so I might as well give
X	 * up in disgust ...
X	 */
X
X	sprintf (arg0, "Cannot execute %s", file);
X	errno = err;
X	perror (arg0);
X	exit (err);
X}
SHAR_EOF
if test 2527 -ne "`wc -c < 'shell.c'`"
then
	echo shar: "error transmitting 'shell.c'" '(should have been 2527 characters)'
fi
fi
echo shar: "extracting 'login.c'" '(1164 characters)'
if test -f 'login.c'
then
	echo shar: "will not over-write existing file 'login.c'"
else
sed 's/^X//' << \SHAR_EOF > 'login.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#ifndef	BSD
X#include <string.h>
X#include <memory.h>
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)login.c	2.3	19:23:55	7/29/90";
X#endif
X
Xvoid	setenv ();
X
Xvoid	login (name)
Xchar	*name;
X{
X	char	buf[BUFSIZ];
X	char	*envp[32];
X	int	envc;
X	char	*cp;
X	int	i;
X
X#ifndef	BSD
X	(void) memset (buf, '\0', sizeof buf);
X#else
X	bzero (buf, sizeof buf);
X#endif
X	fputs ("login: ", stdout);
X
X	if (fgets (buf, BUFSIZ, stdin) != buf)
X		exit (1);
X
X	buf[strlen (buf) - 1] = '\0';	/* remove \n [ must be there ] */
X
X	for (cp = buf;*cp == ' ' || *cp == '\t';cp++)
X		;
X
X	for (i = 0;i < BUFSIZ - 1 && isgraph (*cp);name[i++] = *cp++)
X		;
X
X	if (*cp)
X		cp++;
X
X	name[i] = '\0';
X
X	if (*cp != '\0') {		/* process new variables */
X		for (envc = 0;envc < 32;envc++) {
X			envp[envc] = strtok (envc == 0 ? cp:(char *) 0, " \t,");
X
X			if (envp[envc] == (char *) 0)
X				break;
X		}
X		setenv (envc, envp);
X	}
X}
SHAR_EOF
if test 1164 -ne "`wc -c < 'login.c'`"
then
	echo shar: "error transmitting 'login.c'" '(should have been 1164 characters)'
fi
fi
echo shar: "extracting 'sub.c'" '(1538 characters)'
if test -f 'sub.c'
then
	echo shar: "will not over-write existing file 'sub.c'"
else
sed 's/^X//' << \SHAR_EOF > 'sub.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <sys/types.h>
X#include <syslog.h>
X
X#include "pwd.h"
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)sub.c	3.2	08:22:12	1/30/91";
X#endif
X
X#define	BAD_SUBROOT	"Invalid root directory \"%s\"\n"
X#define	BAD_SUBROOT2	"invalid root `%s' for user `%s'\n"
X#define	NO_SUBROOT	"Can't change root directory to \"%s\"\n"
X#define	NO_SUBROOT2	"no subsystem root `%s' for user `%s'\n"
X
X/*
X * subsystem - change to subsystem root
X *
X *	A subsystem login is indicated by the presense of a "*" as
X *	the first character of the login shell.  The given home
X *	directory will be used as the root of a new filesystem which
X *	the user is actually logged into.
X */
X
Xvoid	subsystem (pw)
Xstruct	passwd	*pw;
X{
X	/*
X	 * The new root directory must begin with a "/" character.
X	 */
X
X	if (pw->pw_dir[0] != '/') {
X		printf (BAD_SUBROOT, pw->pw_dir);
X		syslog (LOG_WARN, BAD_SUBROOT2, pw->pw_dir, pw->pw_name);
X		exit (1);
X	}
X
X	/*
X	 * The directory must be accessible and the current process
X	 * must be able to change into it.
X	 */
X
X	if (chdir (pw->pw_dir) || chroot (pw->pw_dir)) {
X		printf (NO_SUBROOT, pw->pw_dir);
X		syslog (LOG_WARN, NO_SUBROOT2, pw->pw_dir, pw->pw_name);
X		exit (1);
X	}
X}
SHAR_EOF
if test 1538 -ne "`wc -c < 'sub.c'`"
then
	echo shar: "error transmitting 'sub.c'" '(should have been 1538 characters)'
fi
fi
echo shar: "extracting 'dpmain.c'" '(2895 characters)'
if test -f 'dpmain.c'
then
	echo shar: "will not over-write existing file 'dpmain.c'"
else
sed 's/^X//' << \SHAR_EOF > 'dpmain.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <stdio.h>
X#include <fcntl.h>
X#ifdef	BSD
X#include <strings.h>
X#else
X#include <string.h>
X#endif
X#include "dialup.h"
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)dpmain.c	3.2	12:30:37	12/12/90";
X#endif
X
X#define	DTMP	"/etc/d_passwd.tmp"
X
X/*
X * Prompts and messages go here.
X */
X
X#define	PASS1	"Shell password:"
X#define	PASS2	"re-enter Shell password:"
X#define	NOMATCH	"%s: Passwords do not match, try again.\n"
X#define	NOFOUND	"%s: Shell %s not found.\n"
X
Xint	aflg;
Xint	dflg;
Xchar	*Prog;
X
Xextern	char	*pw_encrypt();
Xextern	char	*getpass();
X
Xusage ()
X{
X	fprintf (stderr, "Usage: %s -a|c|d shell\n", Prog);
X	exit (1);
X}
X
Xmain (argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	struct	dialup	*dial;
X	struct	dialup	dent;
X	struct	stat	sb;
X	FILE	*fp;
X	char	*shell;
X	char	*cp;
X	char	pass[BUFSIZ];
X	int	fd;
X	int	found = 0;
X	int	opt;
X	extern	int	optind;
X	extern	char	*optarg;
X
X	if (Prog = strrchr (argv[0], '/'))
X		Prog++;
X	else
X		Prog = argv[0];
X
X	while ((opt = getopt (argc, argv, "a:d:")) != EOF) {
X		switch (opt) {
X			case 'a':
X				aflg++;
X				shell = optarg;
X				break;
X			case 'd':
X				dflg++;
X				shell = optarg;
X				break;
X			default:
X				usage ();
X		}
X	}
X	if (aflg + dflg != 1)
X		usage ();
X
X	if (aflg) {
X		dent.du_shell = shell;
X		dent.du_passwd = "";
X
X		if (! (cp = getpass (PASS1))) {
X			unlink (DTMP);
X			exit (1);
X		}
X		strcpy (pass, cp);
X		if (! (cp = getpass (PASS2))) {
X			unlink (DTMP);
X			exit (1);
X		}
X		if (strcmp (pass, cp)) {
X			fprintf (stderr, NOMATCH, Prog);
X			unlink (DTMP);
X			exit (1);
X		}
X		dent.du_passwd = pw_encrypt (pass, (char *) 0);
X	}
X	if ((fd = open (DTMP, O_CREAT|O_EXCL|O_RDWR, 0600)) < 0) {
X		sprintf (pass, "%s: can't create %s", Prog, DTMP);
X		perror (pass);
X		exit (1);
X	}
X	if (! (fp = fdopen (fd, "r+"))) {
X		sprintf (pass, "%s: can't open %s", Prog, DTMP);
X		perror (pass);
X		exit (1);
X	}
X	while (dial = getduent ()) {
X		if (strcmp (dial->du_shell, shell) == 0) {
X			found = 1;
X			break;
X		}
X		if (putduent (dial, fp))
X			goto failure;
X	}
X	if (dflg && ! found) {
X		fprintf (stderr, NOMATCH, Prog, shell);
X		exit (1);
X	}
X	if (aflg)
X		if (putduent (&dent, fp))
X			goto failure;
X
X	while (dial = getduent ())
X		if (putduent (dial, fp))
X			goto failure;
X
X	if (fflush (fp))
X		goto failure;
X
X	if (! stat (DIALPWD, &sb)) {
X		chown (DTMP, sb.st_uid, sb.st_gid);
X		chmod (DTMP, sb.st_mode);
X		unlink (DIALPWD);
X	} else {
X		chown (DTMP, 0, 0);
X		chmod (DTMP, 0400);
X	}
X	link (DTMP, DIALPWD);
X	unlink (DTMP);
X
X	sync ();
X	exit (0);
X
Xfailure:
X	unlink (DTMP);
X	exit (1);
X}
SHAR_EOF
if test 2895 -ne "`wc -c < 'dpmain.c'`"
then
	echo shar: "error transmitting 'dpmain.c'" '(should have been 2895 characters)'
fi
fi
echo shar: "extracting 'mail.c'" '(998 characters)'
if test -f 'mail.c'
then
	echo shar: "will not over-write existing file 'mail.c'"
else
sed 's/^X//' << \SHAR_EOF > 'mail.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X
X#ifndef	BSD
X#include <string.h>
X#include <memory.h>
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X
X#include "config.h"
X#ifdef	MAILCHECK
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)mail.c	3.2	13:28:27	12/19/90";
X#endif
X
Xextern	char	*getenv();
X
Xvoid	mailcheck ()
X{
X	struct	stat	statbuf;
X	char	*mailbox;
X
X	if (! (mailbox = getenv ("MAIL")))
X		return;
X
X	if (stat (mailbox, &statbuf) == -1 || statbuf.st_size == 0)
X		puts ("No mail.");
X	else if (statbuf.st_atime > statbuf.st_mtime)
X		puts ("You have mail.");
X	else
X		puts ("You have new mail.");
X}
X#endif
SHAR_EOF
if test 998 -ne "`wc -c < 'mail.c'`"
then
	echo shar: "error transmitting 'mail.c'" '(should have been 998 characters)'
fi
fi
echo shar: "extracting 'env.c'" '(1788 characters)'
if test -f 'env.c'
then
	echo shar: "will not over-write existing file 'env.c'"
else
sed 's/^X//' << \SHAR_EOF > 'env.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X#include <stdio.h>
X#ifndef	BSD
X#include <string.h>
X#else
X#define	strchr	index
X#define	strrchr	rindex
X#include <strings.h>
X#endif
X
X#ifndef	lint
Xstatic	char	_sccsid[] = "@(#)env.c	2.2	19:23:43	7/29/90";
X#endif
X
Xextern	char	**environ;
Xextern	char	*newenvp[];
Xextern	int	newenvc;
Xextern	int	maxenv;
X
Xchar	*strdup ();
Xvoid	free ();
X
Xstatic	char	*forbid[] = {
X	"HOME",
X	"IFS",
X	"PATH",
X	"SHELL",
X	(char *) 0
X};
X
Xvoid	addenv (entry)
Xchar	*entry;
X{
X	char	*cp;
X	int	i;
X	int	len;
X
X	if (cp = strchr (entry, '='))
X		len = cp - entry;
X	else
X		return;
X
X	for (i = 0;i < newenvc;i++)
X		if (strncmp (entry, newenvp[i], len) == 0 &&
X			(newenvp[i][len] == '=' || newenvp[i][len] == '\0'))
X			break;
X
X	if (i == maxenv) {
X		puts ("Environment overflow");
X		return;
X	}
X	if (i == newenvc) {
X		newenvp[newenvc++] = strdup (entry);
X	} else {
X		free (newenvp[i]);
X		newenvp[i] = strdup (entry);
X	}
X}
X
Xvoid	setenv (argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	int	i;
X	int	n;
X	int	noname = 1;
X	char	variable[BUFSIZ];
X	char	*cp;
X
X	for (i = 0;i < argc;i++) {
X		if ((n = strlen (argv[i])) >= BUFSIZ)
X			continue;	/* ignore long entries */
X
X		if (! (cp = strchr (argv[i], '='))) {
X			(void) strcpy (variable, argv[i]);
X		} else {
X			(void) strncpy (variable, argv[i], cp - argv[i]);
X			variable[cp - argv[i]] = '\0';
X		}
X		for (n = 0;forbid[n] != (char *) 0;n++)
X			if (strcmp (variable, forbid[n]) == 0)
X				break;
X
X		if (forbid[n] != (char *) 0) {
X			printf ("You may not change $%s\n", forbid[n]);
X			continue;
X		}
X		if (cp) {
X			addenv (argv[i]);
X		} else {
X			sprintf (variable, "L%d=%s", noname++, argv[i]);
X			addenv (variable);
X		}
X	}
X}
SHAR_EOF
if test 1788 -ne "`wc -c < 'env.c'`"
then
	echo shar: "error transmitting 'env.c'" '(should have been 1788 characters)'
fi
fi
echo shar: "extracting 'pwd.h'" '(2383 characters)'
if test -f 'pwd.h'
then
	echo shar: "will not over-write existing file 'pwd.h'"
else
sed 's/^X//' << \SHAR_EOF > 'pwd.h'
X/*
X * Copyright 1990, John F. Haugh II and Steve Simmons
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X/*
X * Standard definitions for password files.  This is an independant
X * reimplementation of the definitions used by AT&T, BSD, and POSIX.
X * It is not derived from any of those sources.  Note that it can be
X * site-defined to have non-POSIX features as well.
X *
X * $RCSfile$	$Revision$
X *
X * $Author$	$Date$
X *
X * $State$	$Locker$
X *
X * $Log$
X */
X
X#ifndef	PWD_H
X#define	PWD_H
X
X#include	<sys/types.h>
X
X/*
X * For now these are defined here.  Later they should be moved to
X * the configuration file or this file should be generated by
X * a configurator.
X */
X
X#undef	BSD_QUOTAS
X#define	ATT_AGE
X#define	ATT_COMMENT
X
X/*
X * This is the data structure returned by the getpw* functions.  The
X * names of the elements and the structure are taken from traditional
X * usage.
X */
X
Xstruct passwd	{
X	char	*pw_name ;	/* User login name */
X	char	*pw_passwd ;	/* Encrypted passwd or dummy field */
X	uid_t	pw_uid ;	/* User uid number */
X	gid_t	pw_gid ;	/* User group id number */
X#ifdef	BSD_QUOTAS
X	/* Most BSD systems have quotas, most USG ones don't	*/
X	int	pw_quota ;	/* The BSD magic doodah */
X#endif
X#ifdef	ATT_AGE
X	/* Use ATT-style password aging	*/
X	char	*pw_age ;	/* ATT radix-64 encoded data */
X#endif
X#ifdef	ATT_COMMENT
X	/* Provide the unused comment field */
X	char	*pw_comment;	/* Unused comment field */
X#endif
X	char	*pw_gecos ;	/* ASCII user name, other data */
X	char	*pw_dir ;	/* User home directory */
X	char	*pw_shell ;	/* User startup shell */
X} ;
X
X#ifdef	ATT_COMMENT
X/* Provide the unused comment structure */
Xstruct comment {
X	char	*c_dept;
X	char	*c_name;
X	char	*c_acct;
X	char	*c_bin;
X};
X#endif
X
X#ifdef	__STDC__
X
Xextern	struct	passwd	*getpwent( void ) ;
Xextern	struct	passwd	*getpwuid( uid_t user_uid ) ;
Xextern	struct	passwd	*getpwnam( char *name ) ;
Xint	setpwent( void );
Xint	endpwent( void );
X
X#else
X
Xextern	struct	passwd	*getpwent();
Xextern	struct	passwd	*getpwuid();
Xextern	struct	passwd	*getpwnam();
Xint	setpwent();
Xint	endpwent();
X
X#endif	/* of ifdef __STDC__ */
X
X#endif	/* of ifdef PWD_H */
SHAR_EOF
if test 2383 -ne "`wc -c < 'pwd.h'`"
then
	echo shar: "error transmitting 'pwd.h'" '(should have been 2383 characters)'
fi
fi
echo shar: "extracting 'grpack.c'" '(1512 characters)'
if test -f 'grpack.c'
then
	echo shar: "will not over-write existing file 'grpack.c'"
else
sed 's/^X//' << \SHAR_EOF > 'grpack.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X *
X * Duplication is permitted for non-commercial [ profit making ]
X * purposes provided this and other copyright notices remain
X * intact.
X */
X
X#include <stdio.h>
X#include <grp.h>
X#ifdef	BSD
X#include <strings.h>
X#else
X#include <string.h>
X#endif
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)grpack.c	3.2	08:11:20	11/21/90";
X#endif
X
Xint	gr_pack (group, buf)
Xstruct	group	*group;
Xchar	*buf;
X{
X	char	*cp;
X	int	i;
X
X	cp = buf;
X	strcpy (cp, group->gr_name);
X	cp += strlen (cp) + 1;
X
X	strcpy (cp, group->gr_passwd);
X	cp += strlen (cp) + 1;
X
X	memcpy (cp, (void *) &group->gr_gid, sizeof group->gr_gid);
X	cp += sizeof group->gr_gid;
X
X	for (i = 0;group->gr_mem[i];i++) {
X		strcpy (cp, group->gr_mem[i]);
X		cp += strlen (cp) + 1;
X	}
X	*cp++ = '\0';
X
X	return cp - buf;
X}
X
Xint	gr_unpack (buf, len, group)
Xchar	*buf;
Xint	len;
Xstruct	group	*group;
X{
X	char	*org = buf;
X	int	i;
X
X	group->gr_name = buf;
X	buf += strlen (buf) + 1;
X	if (buf - org > len)
X		return -1;
X
X	group->gr_passwd = buf;
X	buf += strlen (buf) + 1;
X	if (buf - org > len)
X		return -1;
X
X	memcpy ((void *) &group->gr_gid, (void *) buf, sizeof group->gr_gid);
X	buf += sizeof group->gr_gid;
X	if (buf - org > len)
X		return -1;
X
X	for (i = 0;*buf && i < 1024;i++) {
X		group->gr_mem[i] = buf;
X		buf += strlen (buf) + 1;
X
X		if (buf - org > len)
X			return -1;
X	}
X	group->gr_mem[i] = (char *) 0;
X	return 0;
X}
SHAR_EOF
if test 1512 -ne "`wc -c < 'grpack.c'`"
then
	echo shar: "error transmitting 'grpack.c'" '(should have been 1512 characters)'
fi
fi
echo shar: "extracting 'shadow.h'" '(1823 characters)'
if test -f 'shadow.h'
then
	echo shar: "will not over-write existing file 'shadow.h'"
else
sed 's/^X//' << \SHAR_EOF > 'shadow.h'
X/*
X * Copyright 1988, 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X#ifndef	_H_SHADOW
X#define	_H_SHADOW
X
X/*
X * This information is not derived from AT&T licensed sources.  Posted
X * to the USENET 11/88, and updated 11/90 with information from SVR4.
X *
X *	@(#)shadow.h	3.3	09:06:50	12/7/90
X */
X
X#ifdef	ITI_AGING
Xtypedef	time_t	sptime;
X#else
Xtypedef	long	sptime;
X#endif
X
X/*
X * Shadow password security file structure.
X */
X
Xstruct	spwd {
X	char	*sp_namp;	/* login name */
X	char	*sp_pwdp;	/* encrypted password */
X	sptime	sp_lstchg;	/* date of last change */
X	sptime	sp_min;		/* minimum number of days between changes */
X	sptime	sp_max;		/* maximum number of days between changes */
X	sptime	sp_warn;	/* number of days of warning before password
X				   expires */
X	sptime	sp_inact;	/* number of days after password expires
X				   until the account becomes unusable. */
X	sptime	sp_expire;	/* days since 1/1/70 until account expires */
X	unsigned long	sp_flag; /* reserved for future use */
X};
X
X/*
X * Shadow password security file functions.
X */
X
Xstruct	spwd	*getspent ();
Xstruct	spwd	*getspnam ();
Xstruct	spwd	*sgetspent ();
Xstruct	spwd	*fgetspent ();
Xvoid	setspent ();
Xvoid	endspent ();
Xint	putspent ();
X
X#define  SHADOW "/etc/shadow"
X
X/*
X * Shadow group security file structure
X */
X
Xstruct	sgrp {
X	char	*sg_name;	/* group name */
X	char	*sg_passwd;	/* group password */
X	char	**sg_adm;	/* group administator list */
X	char	**sg_mem;	/* group membership list */
X};
X
X/*
X * Shadow group security file functions.
X */
X
Xstruct	sgrp	*getsgent ();
Xstruct	sgrp	*getsgnam ();
Xstruct	sgrp	*sgetsgent ();
Xstruct	sgrp	*fgetsgent ();
Xvoid	setsgent ();
Xvoid	endsgent ();
Xint	putsgent ();
X
X#define	GSHADOW	"/etc/gshadow"
X#endif
SHAR_EOF
if test 1823 -ne "`wc -c < 'shadow.h'`"
then
	echo shar: "error transmitting 'shadow.h'" '(should have been 1823 characters)'
fi
fi
echo shar: "extracting 'log.c'" '(1543 characters)'
if test -f 'log.c'
then
	echo shar: "will not over-write existing file 'log.c'"
else
sed 's/^X//' << \SHAR_EOF > 'log.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <sys/types.h>
X#include <utmp.h>
X#include "pwd.h"
X#include <fcntl.h>
X#include <time.h>
X#ifndef	BSD
X#include <string.h>
X#include <memory.h>
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X#include "config.h"
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)log.c	3.1	12:30:53	12/12/90";
X#endif
X
X#ifdef	LASTLOG
X
X#include "lastlog.h"
X
Xextern	struct	utmp	utent;
Xextern	struct	passwd	pwent;
Xextern	struct	lastlog	lastlog;
Xextern	char	**environ;
X
Xlong	lseek ();
Xtime_t	time ();
X
Xvoid	log ()
X{
X	int	fd;
X	off_t	offset;
X	struct	lastlog	newlog;
X
X	if ((fd = open ("/usr/adm/lastlog", O_RDWR)) == -1)
X		return;
X
X	offset = pwent.pw_uid * sizeof lastlog;
X
X	if (lseek (fd, offset, 0) != offset) {
X		(void) close (fd);
X		return;
X	}
X	if (read (fd, (char *) &lastlog, sizeof lastlog) != sizeof lastlog)
X#ifndef	BSD
X		memset ((char *) &lastlog, sizeof lastlog, 0);
X#else
X		bzero ((char *) &lastlog, sizeof lastlog);
X#endif
X	newlog = lastlog;
X
X	(void) time (&newlog.ll_time);
X	(void) strncpy (newlog.ll_line, utent.ut_line, sizeof newlog.ll_line);
X	(void) lseek (fd, offset, 0);
X	(void) write (fd, (char *) &newlog, sizeof newlog);
X	(void) close (fd);
X}
X#endif
SHAR_EOF
if test 1543 -ne "`wc -c < 'log.c'`"
then
	echo shar: "error transmitting 'log.c'" '(should have been 1543 characters)'
fi
fi
echo shar: "extracting 'grent.c'" '(9437 characters)'
if test -f 'grent.c'
then
	echo shar: "will not over-write existing file 'grent.c'"
else
sed 's/^X//' << \SHAR_EOF > 'grent.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <stdio.h>
X#include <grp.h>
X#ifdef	BSD
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#else	/* !BSD */
X#include <string.h>
X#endif	/* BSD */
X#include "config.h"
X
X#ifdef	AUTOSHADOW
X#include "shadow.h"
X#endif	/* AUTOSHADOW */
X
X#ifdef	NDBM
X#include <ndbm.h>
X#include <fcntl.h>
XDBM	*gr_dbm;
Xint	gr_dbm_mode = -1;
X#endif	/* NDBM */
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)grent.c	3.7	08:03:08	12/20/90";
X#endif	/* !lint */
X
X#define	NFIELDS	4
X#define	MAXMEM	1024
X
Xstatic	char	grpbuf[4*BUFSIZ];
Xstatic	char	*grpfields[NFIELDS];
Xstatic	char	*members[MAXMEM+1];
Xstatic	struct	group	grent;
X
Xstatic	FILE	*grpfp;
Xstatic	char	*grpfile = GRPFILE;
X#ifdef	NDBM
Xstatic	int	dbmopened;
Xstatic	int	dbmerror;
X#endif	/* NDBM */
X
Xchar *
Xfgetsx (buf, cnt, f)
Xchar	*buf;
Xint	cnt;
XFILE	*f;
X{
X	int	c;
X	char	*cp = buf;
X	char	*ep;
X
X	while (cnt > 0) {
X		if (fgets (cp, cnt, f) == 0)
X			if (cp == buf)
X				return 0;
X			else
X				break;
X
X		if ((ep = strrchr (cp, '\\')) && *(ep + 1) == '\n') {
X			if ((cnt -= ep - cp) > 0)
X				*(cp = ep) = '\0';
X		} else
X			break;
X	}
X	return buf;
X}
X
Xint
Xfputsx (s, stream)
Xchar	*s;
XFILE	*stream;
X{
X	int	i;
X
X	for (i = 0;*s;i++, s++) {
X		if (putc (*s, stream) == EOF)
X			return EOF;
X
X		if (i > (BUFSIZ/2)) {
X			if (putc ('\\', stream) == EOF ||
X			    putc ('\n', stream) == EOF)
X				return EOF;
X
X			i = 0;
X		}
X	}
X	return 0;
X}
X
Xstatic char **
Xlist (s)
Xchar	*s;
X{
X	int	nmembers = 0;
X
X	while (*s) {
X		members[nmembers++] = s;
X		if (s = strchr (s, ','))
X			*s++ = '\0';
X	}
X	members[nmembers] = (char *) 0;
X	return members;
X}
X
Xstruct	group	*sgetgrent (buf)
Xchar	*buf;
X{
X	int	i;
X	char	*cp;
X
X	strncpy (grpbuf, buf, sizeof grpbuf);
X	grpbuf[sizeof grpbuf - 1] = '\0';
X	if (cp = strrchr (grpbuf, '\n'))
X		*cp = '\0';
X
X	for (cp = grpbuf, i = 0;i < NFIELDS && cp;i++) {
X		grpfields[i] = cp;
X		if (cp = strchr (cp, ':'))
X			*cp++ = 0;
X	}
X	if (i < (NFIELDS-1) || *grpfields[2] == '\0')
X		return ((struct group *) 0);
X
X	grent.gr_name = grpfields[0];
X	grent.gr_passwd = grpfields[1];
X	grent.gr_gid = atoi (grpfields[2]);
X	grent.gr_mem = list (grpfields[3]);
X
X	return (&grent);
X}
X
Xint
Xputgrent (g, f)
Xstruct	group	*g;
XFILE	*f;
X{
X	int	i;
X	char	*cp;
X	char	buf[BUFSIZ*4];
X
X	if (! g || ! f)
X		return -1;
X
X	sprintf (buf, "%s:%s:%d:", g->gr_name, g->gr_passwd, g->gr_gid);
X	if (g->gr_mem) {
X		cp = strchr (buf, '\0');
X		for (i = 0;g->gr_mem[i];i++) {
X			if ((cp - buf) + strlen (g->gr_mem[i]) + 2
X					>= sizeof buf)
X				return -1;
X
X			if (i > 0) {
X				strcpy (cp, ",");
X				cp++;
X			}
X			strcpy (cp, g->gr_mem[i]);
X			cp = strchr (cp, '\0');
X		}
X		strcat (cp, "\n");
X	} else
X		strcat (buf, "\n");
X
X	if (fputsx (buf, f) == EOF || ferror (f))
X		return -1;
X
X	return 0;
X}
X
X/*
X * fgetgrent - get a group file entry from a stream
X *
X * fgetgrent() reads the next line from a group file formatted stream
X * and returns a pointer to the group structure for that line.
X */
X
Xstruct	group	*fgetgrent (fp)
XFILE	*fp;
X{
X	char	buf[BUFSIZ*4];
X	char	*cp;
X
X	if (fgetsx (buf, sizeof buf, fp) != (char *) 0) {
X		if (cp = strchr (buf, '\n'))
X			*cp = '\0';
X
X		return (sgetgrent (buf));
X	}
X	return 0;
X}
X
X/*
X * endgrent - close a group file
X *
X * endgrent() closes the group file if open.
X */
X
Xint	endgrent ()
X{
X	if (grpfp)
X		if (fclose (grpfp))
X			return -1;
X
X	grpfp = 0;
X#ifdef	NDBM
X	if (dbmopened && gr_dbm) {
X		dbm_close (gr_dbm);
X		gr_dbm = 0;
X	}
X	dbmopened = 0;
X	dbmerror = 0;
X#endif	/* NDBM */
X	return 0;
X}
X
X/*
X * getgrent - get a group entry from the group file
X *
X * getgrent() opens the group file, if not already opened, and reads
X * a single entry.  NULL is returned if any errors are encountered reading
X * the group file.
X */
X
Xstruct	group	*getgrent ()
X{
X	if (! grpfp && setgrent ())
X		return 0;
X
X	return fgetgrent (grpfp);
X}
X
X/*
X * getgrgid - locate the group entry for a given GID
X *
X * getgrgid() locates the first group file entry for the given GID.
X * If there is a valid DBM file, the DBM files are queried first for
X * the entry.  Otherwise, a linear search is begun of the group file
X * searching for an entry which matches the provided GID.
X */
X
Xstruct	group	*getgrgid (gid)
Xint	gid;
X{
X	struct	group	*grp;
X#ifdef NDBM
X	datum	key;
X	datum	content;
X	int	cnt;
X	int	i;
X	char	*cp;
X	char	grpkey[64];
X#endif	/* NDBM */
X#ifdef	AUTOSHADOW
X	struct	sgrp	*sgrp;
X#endif	/* AUTOSHADOW */
X
X	if (setgrent ())
X		return 0;
X
X#ifdef NDBM
X
X	/*
X	 * If the DBM file are now open, create a key for this GID and
X	 * try to fetch the entry from the database.  A matching record
X	 * will be unpacked into a static structure and returned to
X	 * the user.
X	 */
X
X	if (dbmopened) {
X		grent.gr_gid = gid;
X		key.dsize = sizeof grent.gr_gid;
X		key.dptr = (char *) &grent.gr_gid;
X		content = dbm_fetch (gr_dbm, key);
X		if (content.dptr == 0)
X			return 0;
X
X		if (content.dsize == sizeof (int)) {
X			memcpy (&cnt, content.dptr, content.dsize);
X			for (cp = grpbuf, i = 0;i < cnt;i++) {
X				memcpy (grpkey, &i, sizeof i);
X				memcpy (grpkey + sizeof i, &grent.gr_gid,
X					sizeof grent.gr_gid);
X
X				key.dsize = sizeof i + sizeof grent.gr_gid;
X				key.dptr = grpkey;
X
X				content = dbm_fetch (gr_dbm, key);
X				if (content.dptr == 0)
X					return 0;
X
X				memcpy (cp, content.dptr, content.dsize);
X				cp += content.dsize;
X			}
X			grent.gr_mem = members;
X			gr_unpack (grpbuf, cp - grpbuf, &grent);
X#ifdef	AUTOSHADOW
X			if (sgrp = getsgnam (grent.gr_name)) {
X				grent.gr_passwd = sgrp->sg_passwd;
X				grent.gr_mem = sgrp->sg_mem;
X			}
X#endif	/* AUTOSHADOW */
X			return &grent;
X		} else {
X			grent.gr_mem = members;
X			memcpy (grpbuf, content.dptr, content.dsize);
X			gr_unpack (grpbuf, content.dsize, &grent);
X#ifdef	AUTOSHADOW
X			if (sgrp = getsgnam (grent.gr_name)) {
X				grent.gr_passwd = sgrp->sg_passwd;
X				grent.gr_mem = sgrp->sg_mem;
X			}
X#endif	/* AUTOSHADOW */
X			return &grent;
X		}
X	}
X#endif	/* NDBM */
X	/*
X	 * Search for an entry which matches the GID.  Return the
X	 * entry when a match is found.
X	 */
X
X	while (grp = getgrent ())
X		if (grp->gr_gid == gid)
X			break;
X
X#ifdef	AUTOSHADOW
X	if (grp) {
X		if (sgrp = getsgnam (grent.gr_name)) {
X			grp->gr_passwd = sgrp->sg_passwd;
X			grp->gr_mem = sgrp->sg_mem;
X		}
X	}
X#endif	/* AUTOSHADOW */
X	return grp;
X}
X
Xstruct	group	*getgrnam (name)
Xchar	*name;
X{
X	struct	group	*grp;
X#ifdef NDBM
X	datum	key;
X	datum	content;
X	int	cnt;
X	int	i;
X	char	*cp;
X	char	grpkey[64];
X#endif	/* NDBM */
X#ifdef	AUTOSHADOW
X	struct	sgrp	*sgrp;
X#endif	/* AUTOSHADOW */
X
X	if (setgrent ())
X		return 0;
X
X#ifdef NDBM
X
X	/*
X	 * If the DBM file are now open, create a key for this GID and
X	 * try to fetch the entry from the database.  A matching record
X	 * will be unpacked into a static structure and returned to
X	 * the user.
X	 */
X
X	if (dbmopened) {
X		key.dsize = strlen (name);
X		key.dptr = name;
X		content = dbm_fetch (gr_dbm, key);
X		if (content.dptr == 0)
X			return 0;
X
X		if (content.dsize == sizeof (int)) {
X			memcpy (&cnt, content.dptr, content.dsize);
X			for (cp = grpbuf, i = 0;i < cnt;i++) {
X				memcpy (grpkey, &i, sizeof i);
X				strcpy (grpkey + sizeof i, name);
X
X				key.dsize = sizeof i + strlen (name);
X				key.dptr = grpkey;
X
X				content = dbm_fetch (gr_dbm, key);
X				if (content.dptr == 0)
X					return 0;
X
X				memcpy (cp, content.dptr, content.dsize);
X				cp += content.dsize;
X			}
X			grent.gr_mem = members;
X			gr_unpack (grpbuf, cp - grpbuf, &grent);
X#ifdef	AUTOSHADOW
X			if (sgrp = getsgnam (grent.gr_name)) {
X				grent.gr_passwd = sgrp->sg_passwd;
X				grent.gr_mem = sgrp->sg_mem;
X			}
X#endif	/* AUTOSHADOW */
X			return &grent;
X		} else {
X			grent.gr_mem = members;
X			memcpy (grpbuf, content.dptr, content.dsize);
X			gr_unpack (grpbuf, content.dsize, &grent);
X#ifdef	AUTOSHADOW
X			if (sgrp = getsgnam (grent.gr_name)) {
X				grent.gr_passwd = sgrp->sg_passwd;
X				grent.gr_mem = sgrp->sg_mem;
X			}
X#endif	/* AUTOSHADOW */
X			return &grent;
X		}
X	}
X#endif	/* NDBM */
X	/*
X	 * Search for an entry which matches the name.  Return the
X	 * entry when a match is found.
X	 */
X
X	while (grp = getgrent ())
X		if (strcmp (grp->gr_name, name) == 0)
X			break;
X
X#ifdef	AUTOSHADOW
X	if (grp) {
X		if (sgrp = getsgnam (grent.gr_name)) {
X			grp->gr_passwd = sgrp->sg_passwd;
X			grp->gr_mem = sgrp->sg_mem;
X		}
X	}
X#endif	/* AUTOSHADOW */
X	return 0;
X}
X
X/*
X * setgrent - open the group file
X *
X * setgrent() opens the system group file, and the DBM group files
X * if they are present.  The system group file is rewound if it was
X * open already.
X */
X
Xint
Xsetgrent ()
X{
X#ifdef	NDBM
X	int	mode;
X#endif	/* NDBM */
X
X	if (! grpfp) {
X		if (! (grpfp = fopen (grpfile, "r")))
X			return -1;
X	} else {
X		if (fseek (grpfp, 0L, 0) != 0)
X			return -1;
X	}
X
X	/*
X	 * Attempt to open the DBM files if they have never been opened
X	 * and an error has never been returned.
X	 */
X
X#ifdef NDBM
X	if (! dbmerror && ! dbmopened) {
X		char	dbmfiles[BUFSIZ];
X
X		strcpy (dbmfiles, grpfile);
X		strcat (dbmfiles, ".pag");
X		if (gr_dbm_mode == -1)
X			mode = O_RDONLY;
X		else
X			mode = (gr_dbm_mode == O_RDONLY ||
X				gr_dbm_mode == O_RDWR) ? gr_dbm_mode:O_RDONLY;
X
X		if (access (dbmfiles, 0) ||
X			(! (gr_dbm = dbm_open (grpfile, mode, 0))))
X			dbmerror = 1;
X		else
X			dbmopened = 1;
X	}
X#endif	/* NDBM */
X	return 0;
X}
SHAR_EOF
if test 9437 -ne "`wc -c < 'grent.c'`"
then
	echo shar: "error transmitting 'grent.c'" '(should have been 9437 characters)'
fi
fi
echo shar: "extracting 'motd.c'" '(750 characters)'
if test -f 'motd.c'
then
	echo shar: "will not over-write existing file 'motd.c'"
else
sed 's/^X//' << \SHAR_EOF > 'motd.c'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X#include <stdio.h>
X#ifndef	BSD
X#include <string.h>
X#include <memory.h>
X#else
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#endif
X#include "config.h"
X
X#ifndef	lint
Xstatic	char	_sccsid[] = "@(#)motd.c	2.2	19:23:58	7/29/90";
X#endif
X
Xextern	char	home[];
X#ifdef	HUSHLOGIN
Xextern	int	hushed;
X#endif
X
X#ifdef	MOTD
Xvoid	motd ()
X{
X	FILE	*fp;
X	register int	c;
X
X#ifdef	HUSHLOGIN
X	if (hushed)
X		return;
X#endif
X	if ((fp = fopen ("/etc/motd", "r")) == (FILE *) 0)
X		return;
X
X	while ((c = getc (fp)) != EOF)
X		putchar (c);
X
X	fclose (fp);
X	fflush (stdout);
X}
X#endif
SHAR_EOF
if test 750 -ne "`wc -c < 'motd.c'`"
then
	echo shar: "error transmitting 'motd.c'" '(should have been 750 characters)'
fi
fi
echo shar: "extracting 'dialup.h'" '(1598 characters)'
if test -f 'dialup.h'
then
	echo shar: "will not over-write existing file 'dialup.h'"
else
sed 's/^X//' << \SHAR_EOF > 'dialup.h'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X/*
X * Structure of the /etc/d_passwd file
X *
X *	The d_passwd file contains the names of login shells which require
X *	dialup passwords.  Each line contains the fully qualified path name
X *	for the shell, followed by an optional password.  Each field is
X *	separated by a ':'.
X *
X * Structure of the /etc/dialups file
X *
X *	The dialups file contains the names of ports which may be dialup
X *	lines.  Each line consists of the last component of the path
X *	name.  The leading "/dev/" string is removed.
X *
X *	@(#)dialup.h	3.1	19:44:22	12/10/90
X */
X
X#ifndef	_DIALUP_H
X#define	_DIALUP_H
X
X#include <stdio.h>
X
Xstruct	dialup {
X	char	*du_shell;
X	char	*du_passwd;
X};
X
X#ifdef	__STDC__
Xextern	void	setduent ();
Xextern	void	endduent ();
Xextern	struct	dialup	*fgetduent ();
Xextern	struct	dialup	*getduent ();
Xextern	struct	dialup	*getdushell ();
Xextern	int	putduent ();
Xextern	int	isadialup ();
X#else
Xextern	void	setduent (void);
Xextern	void	endduent (void);
Xextern	struct	dialup	*fgetduent (FILE *);
Xextern	struct	dialup	*getduent (void);
Xextern	struct	dialup	*getdushell (char *);
Xextern	int	putduent (struct dialup *, FILE *);
Xextern	int	isadialup (char *);
X#endif
X
X#define	DIALPWD	"/etc/d_passwd"
X#define	DIALUPS	"/etc/dialups"
X
X#endif
SHAR_EOF
if test 1598 -ne "`wc -c < 'dialup.h'`"
then
	echo shar: "error transmitting 'dialup.h'" '(should have been 1598 characters)'
fi
fi
echo shar: "extracting 'fields.c'" '(1230 characters)'
if test -f 'fields.c'
then
	echo shar: "will not over-write existing file 'fields.c'"
else
sed 's/^X//' << \SHAR_EOF > 'fields.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X#include <ctype.h>
X#include <string.h>
X#include <stdio.h>
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)fields.c	3.2	08:26:23	11/26/90";
X#endif
X
Xextern	char	*Progname;
X
X/*
X * valid_field - insure that a field contains all legal characters
X *
X * The supplied field is scanned for non-printing and other illegal
X * characters.  If any illegal characters are found, valid_field
X * returns -1.  Zero is returned for success.
X */
X
Xint
Xvalid_field (field, illegal)
Xchar	*field;
Xchar	*illegal;
X{
X	char	*cp;
X
X	for (cp = field;*cp && isprint (*cp) && ! strchr (illegal, *cp);cp++)
X		;
X
X	if (*cp)
X		return -1;
X	else
X		return 0;
X}
X
X/*
X * change_field - change a single field if a new value is given.
X *
X * prompt the user with the name of the field being changed and the
X * current value.
X */
X
Xvoid
Xchange_field (buf, prompt)
Xchar	*buf;
Xchar	*prompt;
X{
X	char	new[BUFSIZ];
X	char	*cp;
X
X	printf ("\t%s [%s]: ", prompt, buf);
X	if (fgets (new, BUFSIZ, stdin) != new)
X		return;
X
X	if (cp = strchr (new, '\n'))
X		*cp = '\0';
X	else
X		return;
X
X	if (new[0])
X		strcpy (buf, new);
X}
SHAR_EOF
if test 1230 -ne "`wc -c < 'fields.c'`"
then
	echo shar: "error transmitting 'fields.c'" '(should have been 1230 characters)'
fi
fi
echo shar: "extracting 'gsdbm.c'" '(1823 characters)'
if test -f 'gsdbm.c'
then
	echo shar: "will not over-write existing file 'gsdbm.c'"
else
sed 's/^X//' << \SHAR_EOF > 'gsdbm.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)gsdbm.c	3.3	11:25:29	12/19/90";
X#endif
X
X#include <string.h>
X#include <stdio.h>
X#include "shadow.h"
X#include "config.h"
X
X#ifdef	NDBM
X#include <ndbm.h>
XDBM	*sgr_dbm;
X
X#define	GRP_FRAG	256
X
X/*
X * sgr_dbm_update
X *
X * Updates the DBM password files, if they exist.
X */
X
Xint
Xsgr_dbm_update (sgr)
Xstruct	sgrp	*sgr;
X{
X	datum	key;
X	datum	content;
X	char	data[BUFSIZ*8];
X	char	sgrpkey[60];
X	char	*cp;
X	int	len;
X	int	i;
X	int	cnt;
X	static	int	once;
X
X	if (! once) {
X		if (! sgr_dbm)
X			setsgent ();
X
X		once++;
X	}
X	if (! sgr_dbm)
X		return 0;
X
X	len = sgr_pack (sgr, data);
X
X	if (len <= GRP_FRAG) {
X		content.dsize = len;
X		content.dptr = data;
X
X		key.dsize = strlen (sgr->sg_name);
X		key.dptr = sgr->sg_name;
X		if (dbm_store (sgr_dbm, key, content, DBM_REPLACE))
X			return 0;
X	} else {
X		content.dsize = sizeof cnt;
X		content.dptr = (char *) &cnt;
X		cnt = (len + (GRP_FRAG-1)) / GRP_FRAG;
X
X		key.dsize = strlen (sgr->sg_name);
X		key.dptr = sgr->sg_name;
X		if (dbm_store (sgr_dbm, key, content, DBM_REPLACE))
X			return 0;
X
X		for (cp = data, i = 0;i < cnt;i++) {
X			content.dsize = len > GRP_FRAG ? GRP_FRAG:len;
X			len -= content.dsize;
X			content.dptr = cp;
X			cp += content.dsize;
X
X			key.dsize = sizeof i + strlen (sgr->sg_name);
X			key.dptr = sgrpkey;
X			memcpy (sgrpkey, &i, sizeof i);
X			strcpy (sgrpkey + sizeof i, sgr->sg_name);
X			if (dbm_store (sgr_dbm, key, content, DBM_REPLACE))
X				return 0;
X		}
X	}
X	return 1;
X}
X#endif
SHAR_EOF
if test 1823 -ne "`wc -c < 'gsdbm.c'`"
then
	echo shar: "error transmitting 'gsdbm.c'" '(should have been 1823 characters)'
fi
fi
exit 0
#	End of shell archive
-- 
John F. Haugh II        | Distribution to  | UUCP: ...!cs.utexas.edu!rpp386!jfh
Ma Bell: (512) 255-8251 | GEnie PROHIBITED :-) |  Domain: jfh@rpp386.cactus.org
"If liberals interpreted the 2nd Amendment the same way they interpret the
 rest of the Constitution, gun ownership would be mandatory."

jfh@rpp386.cactus.org (John F Haugh II) (05/16/91)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	groupio.c
#	shadowio.c
#	sgroupio.c
# This archive created: Sun Mar  3 13:27:37 1991
# By:	John F Haugh II (River Parishes Programming, Austin TX)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'groupio.c'" '(10707 characters)'
if test -f 'groupio.c'
then
	echo shar: "will not over-write existing file 'groupio.c'"
else
sed 's/^X//' << \SHAR_EOF > 'groupio.c'
X/*
X * Copyright 1990, John F. Haugh II
X * An unpublished work.
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X *
X *	This file implements a transaction oriented group database
X *	library.  The group file is updated one entry at a time.
X *	After each transaction the file must be logically closed and
X *	transferred to the existing group file.  The sequence of
X *	events is
X *
X *	gr_lock				-- lock group file
X *	gr_open				-- logically open group file
X *	while transaction to process
X *		gr_(locate,update,remove) -- perform transaction
X *	done
X *	gr_close			-- commit transactions
X *	gr_unlock			-- remove group lock
X */
X
X#include <sys/stat.h>
X#include <fcntl.h>
X#include <errno.h>
X#include <grp.h>
X#include <stdio.h>
X#ifdef	BSD
X#include <strings.h>
X#else
X#include <string.h>
X#endif
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)groupio.c	3.6 15:59:12 12/9/90";
X#endif
X
Xstatic	int	islocked;
Xstatic	int	isopen;
Xstatic	int	open_modes;
Xstatic	FILE	*grfp;
X
Xstruct	gr_file_entry {
X	char	*grf_line;
X	int	grf_changed;
X	struct	group	*grf_entry;
X	struct	gr_file_entry *grf_next;
X};
X
Xstatic	struct	gr_file_entry	*grf_head;
Xstatic	struct	gr_file_entry	*grf_tail;
Xstatic	struct	gr_file_entry	*grf_cursor;
Xstatic	int	gr_changed;
Xstatic	int	lock_pid;
X
X#define	GR_LOCK	"/etc/group.lock"
X#define	GR_TEMP "/etc/grp.%d"
X#define	GROUP	"/etc/group"
X
Xstatic	char	gr_filename[BUFSIZ] = GROUP;
X
Xextern	char	*strdup();
Xextern	struct	group	*sgetgrent();
X
X/*
X * gr_dup - duplicate a group file entry
X *
X *	gr_dup() accepts a pointer to a group file entry and
X *	returns a pointer to a group file entry in allocated
X *	memory.
X */
X
Xstatic struct group *
Xgr_dup (grent)
Xstruct	group	*grent;
X{
X	struct	group	*gr;
X	int	i;
X
X	if (! (gr = (struct group *) malloc (sizeof *gr)))
X		return 0;
X
X	if ((gr->gr_name = strdup (grent->gr_name)) == 0 ||
X			(gr->gr_passwd = strdup (grent->gr_passwd)) == 0)
X		return 0;
X
X	for (i = 0;grent->gr_mem[i];i++)
X		;
X
X	gr->gr_mem = (char **) malloc (sizeof (char *) * (i + 1));
X	for (i = 0;grent->gr_mem[i];i++)
X		if (! (gr->gr_mem[i] = strdup (grent->gr_mem[i])))
X			return 0;
X
X	gr->gr_mem[i] = 0;
X	gr->gr_gid = grent->gr_gid;
X
X	return gr;
X}
X
X/*
X * gr_free - free a dynamically allocated group file entry
X *
X *	gr_free() frees up the memory which was allocated for the
X *	pointed to entry.
X */
X
Xstatic void
Xgr_free (grent)
Xstruct	group	*grent;
X{
X	int	i;
X
X	free (grent->gr_name);
X	free (grent->gr_passwd);
X
X	for (i = 0;grent->gr_mem[i];i++)
X		free (grent->gr_mem[i]);
X
X	free (grent->gr_mem);
X}
X
X/*
X * gr_name - change the name of the group file
X */
X
Xint
Xgr_name (name)
Xchar	*name;
X{
X	if (isopen || strlen (name) > (BUFSIZ-10))
X		return -1;
X
X	strcpy (gr_filename, name);
X	return 0;
X}
X
X/*
X * gr_lock - lock a group file
X *
X *	gr_lock() encapsulates the lock operation.  it returns
X *	TRUE or FALSE depending on the group file being
X *	properly locked.  the lock is set by creating a semaphore
X *	file, GR_LOCK.
X */
X
Xint
Xgr_lock ()
X{
X	int	fd;
X	int	pid;
X	int	len;
X	char	file[BUFSIZ];
X	char	buf[32];
X	struct	stat	sb;
X
X	if (islocked)
X		return 1;
X
X	if (strcmp (gr_filename, GROUP) != 0)
X		return 0;
X
X	/*
X	 * Create a lock file which can be switched into place
X	 */
X
X	sprintf (file, GR_TEMP, lock_pid = getpid ());
X	if ((fd = open (file, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1)
X		return 0;
X
X	sprintf (buf, "%d", lock_pid);
X	if (write (fd, buf, strlen (buf) + 1) != strlen (buf) + 1) {
X		(void) close (fd);
X		(void) unlink (file);
X		return 0;
X	}
X	close (fd);
X
X	/*
X	 * Simple case first -
X	 *	Link fails (in a sane environment ...) if the target
X	 *	exists already.  So we try to switch in a new lock
X	 *	file.  If that succeeds, we assume we have the only
X	 *	valid lock.  Needs work for NFS where this assumption
X	 *	may not hold.  The simple hack is to check the link
X	 *	count on the source file, which should be 2 iff the
X	 *	link =really= worked.
X	 */
X
X	if (link (file, GR_LOCK) == 0) {
X		if (stat (file, &sb) != 0)
X			return 0;
X
X		if (sb.st_nlink != 2)
X			return 0;
X
X		(void) unlink (file);
X		islocked = 1;
X		return 1;
X	}
X
X	/*
X	 * Invalid lock test -
X	 *	Open the lock file and see if the lock is valid.
X	 *	The PID of the lock file is checked, and if the PID
X	 *	is not valid, the lock file is removed.  If the unlink
X	 *	of the lock file fails, it should mean that someone
X	 *	else is executing this code.  They will get success,
X	 *	and we will fail.
X	 */
X
X	if ((fd = open (GR_LOCK, O_RDWR)) == -1 ||
X			(len = read (fd, buf, BUFSIZ)) <= 0) {
X		errno = EINVAL;
X		return 0;
X	}
X	buf[len] = '\0';
X	if ((pid = strtol (buf, (char **) 0, 10)) == 0) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (kill (pid, 0) == 0)  {
X		errno = EEXIST;
X		return 0;
X	}
X	if (unlink (GR_LOCK)) {
X		(void) close (fd);
X		(void) unlink (file);
X
X		return 0;
X	}
X
X	/*
X	 * Re-try lock -
X	 *	The invalid lock has now been removed and I should
X	 *	be able to acquire a lock for myself just fine.  If
X	 *	this fails there will be no retry.  The link count
X	 *	test here makes certain someone executing the previous
X	 *	block of code didn't just remove the lock we just
X	 *	linked to.
X	 */
X
X	if (link (file, GR_LOCK) == 0) {
X		if (stat (file, &sb) != 0)
X			return 0;
X
X		if (sb.st_nlink != 2)
X			return 0;
X
X		(void) unlink (file);
X		islocked = 1;
X		return 1;
X	}
X	(void) unlink (file);
X	return 0;
X}
X
X/*
X * gr_unlock - logically unlock a group file
X *
X *	gr_unlock() removes the lock which was set by an earlier
X *	invocation of gr_lock().
X */
X
Xint
Xgr_unlock ()
X{
X	if (isopen) {
X		open_modes = O_RDONLY;
X		if (! gr_close ())
X			return 0;
X	}
X	if (islocked) {
X		islocked = 0;
X		if (lock_pid != getpid ())
X			return 0;
X
X		(void) unlink (GR_LOCK);
X		return 1;
X	}
X	return 0;
X}
X
X/*
X * gr_open - open a group file
X *
X *	gr_open() encapsulates the open operation.  it returns
X *	TRUE or FALSE depending on the group file being
X *	properly opened.
X */
X
Xint
Xgr_open (mode)
Xint	mode;
X{
X	char	buf[8192];
X	char	*cp;
X	struct	gr_file_entry	*grf;
X	struct	group	*grent;
X
X	if (isopen || (mode != O_RDONLY && mode != O_RDWR))
X		return 0;
X
X	if (mode != O_RDONLY && ! islocked &&
X			strcmp (gr_filename, GROUP) == 0)
X		return 0;
X
X	if ((grfp = fopen (gr_filename, mode == O_RDONLY ? "r":"r+")) == 0)
X		return 0;
X
X	grf_head = grf_tail = grf_cursor = 0;
X	gr_changed = 0;
X
X	while (fgetsx (buf, sizeof buf, grfp) != (char *) 0) {
X		if (cp = strrchr (buf, '\n'))
X			*cp = '\0';
X
X		if (! (grf = (struct gr_file_entry *) malloc (sizeof *grf)))
X			return 0;
X
X		grf->grf_changed = 0;
X		grf->grf_line = strdup (buf);
X		if ((grent = sgetgrent (buf)) && ! (grent = gr_dup (grent)))
X			return 0;
X
X		grf->grf_entry = grent;
X
X		if (grf_head == 0) {
X			grf_head = grf_tail = grf;
X			grf->grf_next = 0;
X		} else {
X			grf_tail->grf_next = grf;
X			grf->grf_next = 0;
X			grf_tail = grf;
X		}
X	}
X	isopen++;
X	open_modes = mode;
X
X	return 1;
X}
X
X/*
X * gr_close - close the group file
X *
X *	gr_close() outputs any modified group file entries and
X *	frees any allocated memory.
X */
X
Xint
Xgr_close ()
X{
X	char	backup[BUFSIZ];
X	int	fd;
X	int	mask;
X	int	c;
X	int	i;
X	int	errors = 0;
X	FILE	*bkfp;
X	struct	gr_file_entry *grf;
X	struct	gr_file_entry *ogrf;
X
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (islocked && lock_pid != getpid ()) {
X		isopen = 0;
X		islocked = 0;
X		errno = EACCES;
X		return 0;
X	}
X	strcpy (backup, gr_filename);
X	strcat (backup, "-");
X
X	if (open_modes == O_RDWR && gr_changed) {
X		mask = umask (0222);
X		if ((bkfp = fopen (backup, "w")) == 0) {
X			umask (mask);
X			return 0;
X		}
X		umask (mask);
X
X		rewind (grfp);
X		while ((c = getc (grfp)) != EOF) {
X			if (putc (c, bkfp) == EOF) {
X				fclose (bkfp);
X				return 0;
X			}
X		}
X		if (fclose (bkfp))
X			return 0;
X
X		isopen = 0;
X		(void) fclose (grfp);
X
X		mask = umask (0222);
X		if (! (grfp = fopen (gr_filename, "w"))) {
X			umask (mask);
X			return 0;
X		}
X		umask (mask);
X
X		for (grf = grf_head;! errors && grf;grf = grf->grf_next) {
X			if (grf->grf_changed) {
X				if (putgrent (grf->grf_entry, grfp))
X					errors++;
X			} else {
X				if (fputsx (grf->grf_line, grfp))
X					errors++;
X
X				if (putc ('\n', grfp) == EOF)
X					errors++;
X			}
X		}
X		if (fflush (grfp))
X			errors++;
X
X		if (errors) {
X			unlink (gr_filename);
X			link (backup, gr_filename);
X			unlink (backup);
X			return 0;
X		}
X	}
X	if (fclose (grfp))
X		return 0;
X
X	grfp = 0;
X
X	while (grf_head != 0) {
X		grf = grf_head;
X		grf_head = grf->grf_next;
X
X		if (grf->grf_entry) {
X			gr_free (grf->grf_entry);
X			free (grf->grf_entry);
X		}
X		if (grf->grf_line)
X			free (grf->grf_line);
X
X		free (grf);
X	}
X	grf_tail = 0;
X	return 1;
X}
X
Xint
Xgr_update (grent)
Xstruct	group	*grent;
X{
X	struct	gr_file_entry	*grf;
X	struct	group	*ngr;
X
X	if (! isopen || open_modes == O_RDONLY) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (grf = grf_head;grf != 0;grf = grf->grf_next) {
X		if (grf->grf_entry == 0)
X			continue;
X
X		if (strcmp (grent->gr_name, grf->grf_entry->gr_name) != 0)
X			continue;
X
X		if (! (ngr = gr_dup (grent)))
X			return 0;
X		else {
X			gr_free (grf->grf_entry);
X			*(grf->grf_entry) = *ngr;
X		}
X		grf->grf_changed = 1;
X		grf_cursor = grf;
X		return gr_changed = 1;
X	}
X	grf = (struct gr_file_entry *) malloc (sizeof *grf);
X	if (! (grf->grf_entry = gr_dup (grent)))
X		return 0;
X
X	grf->grf_changed = 1;
X	grf->grf_next = 0;
X	grf->grf_line = 0;
X
X	if (grf_tail)
X		grf_tail->grf_next = grf;
X
X	if (! grf_head)
X		grf_head = grf;
X
X	grf_tail = grf;
X
X	return gr_changed = 1;
X}
X
Xint
Xgr_remove (name)
Xchar	*name;
X{
X	struct	gr_file_entry	*grf;
X	struct	gr_file_entry	*ogrf;
X
X	if (! isopen || open_modes == O_RDONLY) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (ogrf = 0, grf = grf_head;grf != 0;
X			ogrf = grf, grf = grf->grf_next) {
X		if (! grf->grf_entry)
X			continue;
X
X		if (strcmp (name, grf->grf_entry->gr_name) != 0)
X			continue;
X
X		if (grf == grf_cursor)
X			grf_cursor = ogrf;
X
X		if (ogrf != 0)
X			ogrf->grf_next = grf->grf_next;
X		else
X			grf_head = grf->grf_next;
X
X		if (grf == grf_tail)
X			grf_tail = ogrf;
X
X		return gr_changed = 1;
X	}
X	errno = ENOENT;
X	return 0;
X}
X
Xstruct group *
Xgr_locate (name)
Xchar	*name;
X{
X	struct	gr_file_entry	*grf;
X
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (grf = grf_head;grf != 0;grf = grf->grf_next) {
X		if (grf->grf_entry == 0)
X			continue;
X
X		if (strcmp (name, grf->grf_entry->gr_name) == 0) {
X			grf_cursor = grf;
X			return grf->grf_entry;
X		}
X	}
X	errno = ENOENT;
X	return 0;
X}
X
Xint
Xgr_rewind ()
X{
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	grf_cursor = 0;
X	return 1;
X}
X
Xstruct group *
Xgr_next ()
X{
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (grf_cursor == 0)
X		grf_cursor = grf_head;
X	else
X		grf_cursor = grf_cursor->grf_next;
X
X	while (grf_cursor) {
X		if (grf_cursor->grf_entry)
X			return grf_cursor->grf_entry;
X
X		grf_cursor = grf_cursor->grf_next;
X	}
X	return 0;
X}
SHAR_EOF
if test 10707 -ne "`wc -c < 'groupio.c'`"
then
	echo shar: "error transmitting 'groupio.c'" '(should have been 10707 characters)'
fi
fi
echo shar: "extracting 'shadowio.c'" '(10637 characters)'
if test -f 'shadowio.c'
then
	echo shar: "will not over-write existing file 'shadowio.c'"
else
sed 's/^X//' << \SHAR_EOF > 'shadowio.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X *
X *	This file implements a transaction oriented password database
X *	library.  The password file is updated one entry at a time.
X *	After each transaction the file must be logically closed and
X *	transferred to the existing password file.  The sequence of
X *	events is
X *
X *	spw_lock			-- lock shadow file
X *	spw_open			-- logically open shadow file
X *	while transaction to process
X *		spw_(locate,update,remove) -- perform transaction
X *	done
X *	spw_close			-- commit transactions
X *	spw_unlock			-- remove shadow lock
X */
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)shadowio.c	3.4	07:54:20	12/1/90";
X#endif
X
X#include <sys/stat.h>
X#include <fcntl.h>
X#include <errno.h>
X#include <stdio.h>
X#ifdef	BSD
X#include <strings.h>
X#else
X#include <string.h>
X#endif
X#include "shadow.h"
X
Xstatic	int	islocked;
Xstatic	int	isopen;
Xstatic	int	open_modes;
Xstatic	FILE	*spwfp;
X
Xstruct	spw_file_entry {
X	char	*spwf_line;
X	int	spwf_changed;
X	struct	spwd	*spwf_entry;
X	struct	spw_file_entry *spwf_next;
X};
X
Xstatic	struct	spw_file_entry	*spwf_head;
Xstatic	struct	spw_file_entry	*spwf_tail;
Xstatic	struct	spw_file_entry	*spwf_cursor;
Xstatic	int	sp_changed;
Xstatic	int	lock_pid;
X
X#define	SPW_LOCK	"/etc/shadow.lock"
X#define	SPW_TEMP	"/etc/spwd.%d"
X#define	SHADOW		"/etc/shadow"
X
Xstatic	char	spw_filename[BUFSIZ] = SHADOW;
X
Xextern	char	*strdup();
Xextern	struct	spwd	*sgetspent();
X
X/*
X * spw_dup - duplicate a shadow file entry
X *
X *	spw_dup() accepts a pointer to a shadow file entry and
X *	returns a pointer to a shadow file entry in allocated
X *	memory.
X */
X
Xstatic struct spwd *
Xspw_dup (spwd)
Xstruct	spwd	*spwd;
X{
X	struct	spwd	*spw;
X
X	if (! (spw = (struct spwd *) malloc (sizeof *spw)))
X		return 0;
X
X	*spw = *spwd;
X	if ((spw->sp_namp = strdup (spwd->sp_namp)) == 0 ||
X			(spw->sp_pwdp = strdup (spwd->sp_pwdp)) == 0)
X		return 0;
X
X	return spw;
X}
X
X/*
X * spw_free - free a dynamically allocated shadow file entry
X *
X *	spw_free() frees up the memory which was allocated for the
X *	pointed to entry.
X */
X
Xstatic void
Xspw_free (spwd)
Xstruct	spwd	*spwd;
X{
X	free (spwd->sp_namp);
X	free (spwd->sp_pwdp);
X}
X
X/*
X * spw_name - change the name of the shadow password file
X */
X
Xint
Xspw_name (name)
Xchar	*name;
X{
X	if (isopen || strlen (name) > (BUFSIZ-10))
X		return -1;
X
X	strcpy (spw_filename, name);
X	return 0;
X}
X
X/*
X * spw_lock - lock a password file
X *
X *	spw_lock() encapsulates the lock operation.  it returns
X *	TRUE or FALSE depending on the password file being
X *	properly locked.  the lock is set by creating a semaphore
X *	file, SPW_LOCK.
X */
X
Xint
Xspw_lock ()
X{
X	int	fd;
X	int	pid;
X	int	len;
X	char	file[BUFSIZ];
X	char	buf[32];
X	struct	stat	sb;
X
X	if (islocked)
X		return 1;
X
X	if (strcmp (spw_filename, SHADOW) != 0)
X		return 0;
X
X	/*
X	 * Create a lock file which can be switched into place
X	 */
X
X	sprintf (file, SPW_TEMP, lock_pid = getpid ());
X	if ((fd = open (file, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1)
X		return 0;
X
X	sprintf (buf, "%d", lock_pid);
X	if (write (fd, buf, strlen (buf) + 1) != strlen (buf) + 1) {
X		(void) close (fd);
X		(void) unlink (file);
X		return 0;
X	}
X	close (fd);
X
X	/*
X	 * Simple case first -
X	 *	Link fails (in a sane environment ...) if the target
X	 *	exists already.  So we try to switch in a new lock
X	 *	file.  If that succeeds, we assume we have the only
X	 *	valid lock.  Needs work for NFS where this assumption
X	 *	may not hold.  The simple hack is to check the link
X	 *	count on the source file, which should be 2 iff the
X	 *	link =really= worked.
X	 */
X
X	if (link (file, SPW_LOCK) == 0) {
X		if (stat (file, &sb) != 0)
X			return 0;
X
X		if (sb.st_nlink != 2)
X			return 0;
X
X		(void) unlink (file);
X		islocked = 1;
X		return 1;
X	}
X
X	/*
X	 * Invalid lock test -
X	 *	Open the lock file and see if the lock is valid.
X	 *	The PID of the lock file is checked, and if the PID
X	 *	is not valid, the lock file is removed.  If the unlink
X	 *	of the lock file fails, it should mean that someone
X	 *	else is executing this code.  They will get success,
X	 *	and we will fail.
X	 */
X
X	if ((fd = open (SPW_LOCK, O_RDWR)) == -1 ||
X			(len = read (fd, buf, BUFSIZ)) <= 0) {
X		errno = EINVAL;
X		return 0;
X	}
X	buf[len] = '\0';
X	if ((pid = strtol (buf, (char **) 0, 10)) == 0) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (kill (pid, 0) == 0)  {
X		errno = EEXIST;
X		return 0;
X	}
X	if (unlink (SPW_LOCK)) {
X		(void) close (fd);
X		(void) unlink (file);
X
X		return 0;
X	}
X
X	/*
X	 * Re-try lock -
X	 *	The invalid lock has now been removed and I should
X	 *	be able to acquire a lock for myself just fine.  If
X	 *	this fails there will be no retry.  The link count
X	 *	test here makes certain someone executing the previous
X	 *	block of code didn't just remove the lock we just
X	 *	linked to.
X	 */
X
X	if (link (file, SPW_LOCK) == 0) {
X		if (stat (file, &sb) != 0)
X			return 0;
X
X		if (sb.st_nlink != 2)
X			return 0;
X
X		(void) unlink (file);
X		islocked = 1;
X		return 1;
X	}
X	(void) unlink (file);
X	return 0;
X}
X
X/*
X * spw_unlock - logically unlock a shadow file
X *
X *	spw_unlock() removes the lock which was set by an earlier
X *	invocation of spw_lock().
X */
X
Xint
Xspw_unlock ()
X{
X	if (isopen) {
X		open_modes = O_RDONLY;
X		if (! spw_close ())
X			return 0;
X	}
X  	if (islocked) {
X  		islocked = 0;
X		if (lock_pid != getpid ())
X			return 0;
X
X		(void) unlink (SPW_LOCK);
X		return 1;
X	}
X	return 0;
X}
X
X/*
X * spw_open - open a password file
X *
X *	spw_open() encapsulates the open operation.  it returns
X *	TRUE or FALSE depending on the shadow file being
X *	properly opened.
X */
X
Xint
Xspw_open (mode)
Xint	mode;
X{
X	char	buf[BUFSIZ];
X	char	*cp;
X	struct	spw_file_entry	*spwf;
X	struct	spwd	*spwd;
X
X	if (isopen || (mode != O_RDONLY && mode != O_RDWR))
X		return 0;
X
X	if (mode != O_RDONLY && ! islocked &&
X			strcmp (spw_filename, SHADOW) == 0)
X		return 0;
X
X	if ((spwfp = fopen (spw_filename, mode == O_RDONLY ? "r":"r+")) == 0)
X		return 0;
X
X	spwf_head = spwf_tail = spwf_cursor = 0;
X	sp_changed = 0;
X
X	while (fgets (buf, sizeof buf, spwfp) != (char *) 0) {
X		if (cp = strrchr (buf, '\n'))
X			*cp = '\0';
X
X		if (! (spwf = (struct spw_file_entry *) malloc (sizeof *spwf)))
X			return 0;
X
X		spwf->spwf_changed = 0;
X		spwf->spwf_line = strdup (buf);
X		if ((spwd = sgetspent (buf)) && ! (spwd = spw_dup (spwd)))
X			return 0;
X
X		spwf->spwf_entry = spwd;
X
X		if (spwf_head == 0) {
X			spwf_head = spwf_tail = spwf;
X			spwf->spwf_next = 0;
X		} else {
X			spwf_tail->spwf_next = spwf;
X			spwf->spwf_next = 0;
X			spwf_tail = spwf;
X		}
X	}
X	isopen++;
X	open_modes = mode;
X
X	return 1;
X}
X
X/*
X * spw_close - close the password file
X *
X *	spw_close() outputs any modified password file entries and
X *	frees any allocated memory.
X */
X
Xint
Xspw_close ()
X{
X	char	backup[BUFSIZ];
X	int	fd;
X	int	mask;
X	int	c;
X	int	i;
X	int	errors = 0;
X	FILE	*bkfp;
X	struct	spw_file_entry *spwf;
X	struct	spw_file_entry *ospwf;
X
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (islocked && lock_pid != getpid ()) {
X		isopen = 0;
X		islocked = 0;
X		errno = EACCES;
X		return 0;
X	}
X	strcpy (backup, spw_filename);
X	strcat (backup, "-");
X
X	if (open_modes == O_RDWR && sp_changed) {
X		mask = umask (077);
X		if ((bkfp = fopen (backup, "w")) == 0) {
X			umask (mask);
X			return 0;
X		}
X		umask (mask);
X
X		rewind (spwfp);
X		while ((c = getc (spwfp)) != EOF) {
X			if (putc (c, bkfp) == EOF) {
X				fclose (bkfp);
X				return 0;
X			}
X		}
X		if (fclose (bkfp))
X			return 0;
X
X		isopen = 0;
X		(void) fclose (spwfp);
X
X		mask = umask (077);
X		if (! (spwfp = fopen (spw_filename, "w"))) {
X			umask (mask);
X			return 0;
X		}
X		umask (mask);
X
X		for (spwf = spwf_head;errors == 0 && spwf;
X						spwf = spwf->spwf_next) {
X			if (spwf->spwf_changed) {
X				if (putspent (spwf->spwf_entry, spwfp))
X					errors++;
X			} else {
X				if (fputs (spwf->spwf_line, spwfp) == EOF)
X					errors++;
X				if (putc ('\n', spwfp) == EOF)
X					errors++;
X			}
X		}
X		if (fflush (spwfp))
X			errors++;
X
X		if (errors) {
X			unlink (spw_filename);
X			link (backup, spw_filename);
X			unlink (backup);
X			return 0;
X		}
X	}
X	if (fclose (spwfp))
X		return 0;
X
X	spwfp = 0;
X
X	while (spwf_head != 0) {
X		spwf = spwf_head;
X		spwf_head = spwf->spwf_next;
X
X		if (spwf->spwf_entry) {
X			spw_free (spwf->spwf_entry);
X			free (spwf->spwf_entry);
X		}
X		if (spwf->spwf_line)
X			free (spwf->spwf_line);
X
X		free (spwf);
X	}
X	spwf_tail = 0;
X	return 1;
X}
X
Xint
Xspw_update (spwd)
Xstruct	spwd	*spwd;
X{
X	struct	spw_file_entry	*spwf;
X	struct	spwd	*nspwd;
X
X	if (! isopen || open_modes == O_RDONLY) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (spwf = spwf_head;spwf != 0;spwf = spwf->spwf_next) {
X		if (spwf->spwf_entry == 0)
X			continue;
X
X		if (strcmp (spwd->sp_namp, spwf->spwf_entry->sp_namp) != 0)
X			continue;
X
X		if (! (nspwd = spw_dup (spwd)))
X			return 0;
X		else {
X			spw_free (spwf->spwf_entry);
X			*(spwf->spwf_entry) = *nspwd;
X		}
X		spwf->spwf_changed = 1;
X		spwf_cursor = spwf;
X		return sp_changed = 1;
X	}
X	spwf = (struct spw_file_entry *) malloc (sizeof *spwf);
X	if (! (spwf->spwf_entry = spw_dup (spwd)))
X		return 0;
X
X	spwf->spwf_changed = 1;
X	spwf->spwf_next = 0;
X	spwf->spwf_line = 0;
X
X	if (spwf_tail)
X		spwf_tail->spwf_next = spwf;
X
X	if (! spwf_head)
X		spwf_head = spwf;
X
X	spwf_tail = spwf;
X
X	return sp_changed = 1;
X}
X
Xint
Xspw_remove (name)
Xchar	*name;
X{
X	struct	spw_file_entry	*spwf;
X	struct	spw_file_entry	*ospwf;
X
X	if (! isopen || open_modes == O_RDONLY) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (ospwf = 0, spwf = spwf_head;spwf != 0;
X			ospwf = spwf, spwf = spwf->spwf_next) {
X		if (! spwf->spwf_entry)
X			continue;
X
X		if (strcmp (name, spwf->spwf_entry->sp_namp) != 0)
X			continue;
X
X		if (spwf == spwf_cursor)
X			spwf_cursor = ospwf;
X
X		if (ospwf != 0)
X			ospwf->spwf_next = spwf->spwf_next;
X		else
X			spwf_head = spwf->spwf_next;
X
X		if (spwf == spwf_tail)
X			spwf_tail = ospwf;
X
X		return sp_changed = 1;
X	}
X	errno = ENOENT;
X	return 0;
X}
X
Xstruct spwd *
Xspw_locate (name)
Xchar	*name;
X{
X	struct	spw_file_entry	*spwf;
X
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (spwf = spwf_head;spwf != 0;spwf = spwf->spwf_next) {
X		if (spwf->spwf_entry == 0)
X			continue;
X
X		if (strcmp (name, spwf->spwf_entry->sp_namp) == 0) {
X			spwf_cursor = spwf;
X			return spwf->spwf_entry;
X		}
X	}
X	errno = ENOENT;
X	return 0;
X}
X
Xint
Xspw_rewind ()
X{
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	spwf_cursor = 0;
X	return 1;
X}
X
Xstruct spwd *
Xspw_next ()
X{
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (spwf_cursor == 0)
X		spwf_cursor = spwf_head;
X	else
X		spwf_cursor = spwf_cursor->spwf_next;
X
X	while (spwf_cursor) {
X		if (spwf_cursor->spwf_entry)
X			return spwf_cursor->spwf_entry;
X
X		spwf_cursor = spwf_cursor->spwf_next;
X	}
X	return 0;
X}
SHAR_EOF
if test 10637 -ne "`wc -c < 'shadowio.c'`"
then
	echo shar: "error transmitting 'shadowio.c'" '(should have been 10637 characters)'
fi
fi
echo shar: "extracting 'sgroupio.c'" '(11482 characters)'
if test -f 'sgroupio.c'
then
	echo shar: "will not over-write existing file 'sgroupio.c'"
else
sed 's/^X//' << \SHAR_EOF > 'sgroupio.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X *
X *	This file implements a transaction oriented shadow group
X *	database library.  The shadow group file is updated one
X *	entry at a time.  After each transaction the file must be
X *	logically closed and transferred to the existing shadow
X *	group file.  The sequence of events is
X *
X *	sgr_lock			-- lock shadow group file
X *	sgr_open			-- logically open shadow group file
X *	while transaction to process
X *		sgr_(locate,update,remove) -- perform transaction
X *	done
X *	sgr_close			-- commit transactions
X *	sgr_unlock			-- remove shadow group lock
X */
X
X#include <sys/stat.h>
X#include <fcntl.h>
X#include <errno.h>
X#include <stdio.h>
X#ifdef	BSD
X#include <strings.h>
X#define	strchr	index
X#define	strrchr	rindex
X#else
X#include <string.h>
X#endif
X#include "shadow.h"
X
X#ifndef	lint
Xstatic	char	sccsid[] = "@(#)sgroupio.c	3.1	08:13:51	12/14/90";
X#endif
X
Xstatic	int	islocked;
Xstatic	int	isopen;
Xstatic	int	open_modes;
Xstatic	FILE	*sgrfp;
X
Xstruct	sg_file_entry {
X	char	*sgr_line;
X	int	sgr_changed;
X	struct	sgrp	*sgr_entry;
X	struct	sg_file_entry *sgr_next;
X};
X
Xstatic	struct	sg_file_entry	*sgr_head;
Xstatic	struct	sg_file_entry	*sgr_tail;
Xstatic	struct	sg_file_entry	*sgr_cursor;
Xstatic	int	sgr_changed;
Xstatic	int	lock_pid;
X
X#define	SG_LOCK	"/etc/gshadow.lock"
X#define	GR_TEMP "/etc/gshadow.%d"
X#define	SGROUP	"/etc/gshadow"
X
Xstatic	char	sg_filename[BUFSIZ] = SGROUP;
X
Xextern	char	*strdup();
Xextern	struct	sgrp	*sgetgsent();
Xextern	char	*fgetsx();
X
X/*
X * sgr_dup - duplicate a shadow group file entry
X *
X *	sgr_dup() accepts a pointer to a shadow group file entry and
X *	returns a pointer to a shadow group file entry in allocated memory.
X */
X
Xstatic struct sgrp *
Xsgr_dup (sgrent)
Xstruct	sgrp	*sgrent;
X{
X	struct	sgrp	*sgr;
X	int	i;
X
X	if (! (sgr = (struct sgrp *) malloc (sizeof *sgr)))
X		return 0;
X
X	if ((sgr->sg_name = strdup (sgrent->sg_name)) == 0 ||
X			(sgr->sg_passwd = strdup (sgrent->sg_passwd)) == 0)
X		return 0;
X
X	for (i = 0;sgrent->sg_mem[i];i++)
X		;
X
X	sgr->sg_mem = (char **) malloc (sizeof (char *) * (i + 1));
X	for (i = 0;sgrent->sg_mem[i];i++)
X		if (! (sgr->sg_mem[i] = strdup (sgrent->sg_mem[i])))
X			return 0;
X
X	sgr->sg_mem[i] = 0;
X
X	sgr->sg_adm = (char **) malloc (sizeof (char *) * (i + 1));
X	for (i = 0;sgrent->sg_adm[i];i++)
X		if (! (sgr->sg_adm[i] = strdup (sgrent->sg_adm[i])))
X			return 0;
X
X	sgr->sg_adm[i] = 0;
X
X	return sgr;
X}
X
X/*
X * sgr_free - free a dynamically allocated shadow group file entry
X *
X *	sgr_free() frees up the memory which was allocated for the
X *	pointed to entry.
X */
X
Xstatic void
Xsgr_free (sgrent)
Xstruct	sgrp	*sgrent;
X{
X	int	i;
X
X	free (sgrent->sg_name);
X	free (sgrent->sg_passwd);
X
X	for (i = 0;sgrent->sg_mem[i];i++)
X		free (sgrent->sg_mem[i]);
X
X	free (sgrent->sg_mem);
X
X	for (i = 0;sgrent->sg_adm[i];i++)
X		free (sgrent->sg_adm[i]);
X
X	free (sgrent->sg_adm);
X}
X
X/*
X * sgr_name - change the name of the shadow group file
X */
X
Xint
Xsgr_name (name)
Xchar	*name;
X{
X	if (isopen || strlen (name) > (BUFSIZ-10))
X		return -1;
X
X	strcpy (sg_filename, name);
X	return 0;
X}
X
X/*
X * sgr_lock - lock a shadow group file
X *
X *	sgr_lock() encapsulates the lock operation.  it returns
X *	TRUE or FALSE depending on the shadow group file being
X *	properly locked.  the lock is set by creating a semaphore
X *	file, SG_LOCK.
X */
X
Xint
Xsgr_lock ()
X{
X	int	fd;
X	int	pid;
X	int	len;
X	char	file[BUFSIZ];
X	char	buf[32];
X	struct	stat	sb;
X
X	if (islocked)
X		return 1;
X
X	if (strcmp (sg_filename, SGROUP) != 0)
X		return 0;
X
X	/*
X	 * Create a lock file which can be switched into place
X	 */
X
X	sprintf (file, GR_TEMP, lock_pid = getpid ());
X	if ((fd = open (file, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1)
X		return 0;
X
X	sprintf (buf, "%d", lock_pid);
X	if (write (fd, buf, strlen (buf) + 1) != strlen (buf) + 1) {
X		(void) close (fd);
X		(void) unlink (file);
X		return 0;
X	}
X	close (fd);
X
X	/*
X	 * Simple case first -
X	 *	Link fails (in a sane environment ...) if the target
X	 *	exists already.  So we try to switch in a new lock
X	 *	file.  If that succeeds, we assume we have the only
X	 *	valid lock.  Needs work for NFS where this assumption
X	 *	may not hold.  The simple hack is to check the link
X	 *	count on the source file, which should be 2 iff the
X	 *	link =really= worked.
X	 */
X
X	if (link (file, SG_LOCK) == 0) {
X		if (stat (file, &sb) != 0)
X			return 0;
X
X		if (sb.st_nlink != 2)
X			return 0;
X
X		(void) unlink (file);
X		islocked = 1;
X		return 1;
X	}
X
X	/*
X	 * Invalid lock test -
X	 *	Open the lock file and see if the lock is valid.
X	 *	The PID of the lock file is checked, and if the PID
X	 *	is not valid, the lock file is removed.  If the unlink
X	 *	of the lock file fails, it should mean that someone
X	 *	else is executing this code.  They will get success,
X	 *	and we will fail.
X	 */
X
X	if ((fd = open (SG_LOCK, O_RDWR)) == -1 ||
X			(len = read (fd, buf, BUFSIZ)) <= 0) {
X		errno = EINVAL;
X		return 0;
X	}
X	buf[len] = '\0';
X	if ((pid = strtol (buf, (char **) 0, 10)) == 0) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (kill (pid, 0) == 0)  {
X		errno = EEXIST;
X		return 0;
X	}
X	if (unlink (SG_LOCK)) {
X		(void) close (fd);
X		(void) unlink (file);
X
X		return 0;
X	}
X
X	/*
X	 * Re-try lock -
X	 *	The invalid lock has now been removed and I should
X	 *	be able to acquire a lock for myself just fine.  If
X	 *	this fails there will be no retry.  The link count
X	 *	test here makes certain someone executing the previous
X	 *	block of code didn't just remove the lock we just
X	 *	linked to.
X	 */
X
X	if (link (file, SG_LOCK) == 0) {
X		if (stat (file, &sb) != 0)
X			return 0;
X
X		if (sb.st_nlink != 2)
X			return 0;
X
X		(void) unlink (file);
X		islocked = 1;
X		return 1;
X	}
X	(void) unlink (file);
X	return 0;
X}
X
X/*
X * sgr_unlock - logically unlock a shadow group file
X *
X *	sgr_unlock() removes the lock which was set by an earlier
X *	invocation of sgr_lock().
X */
X
Xint
Xsgr_unlock ()
X{
X	if (isopen) {
X		open_modes = O_RDONLY;
X		if (! sgr_close ())
X			return 0;
X	}
X	if (islocked) {
X		islocked = 0;
X		if (lock_pid != getpid ())
X			return 0;
X
X		(void) unlink (SG_LOCK);
X		return 1;
X	}
X	return 0;
X}
X
X/*
X * sgr_open - open a shadow group file
X *
X *	sgr_open() encapsulates the open operation.  it returns
X *	TRUE or FALSE depending on the shadow group file being
X *	properly opened.
X */
X
Xint
Xsgr_open (mode)
Xint	mode;
X{
X	char	buf[8192];
X	char	*cp;
X	struct	sg_file_entry	*sgrf;
X	struct	sgrp	*sgrent;
X
X	if (isopen || (mode != O_RDONLY && mode != O_RDWR))
X		return 0;
X
X	if (mode != O_RDONLY && ! islocked &&
X			strcmp (sg_filename, SGROUP) == 0)
X		return 0;
X
X	if ((sgrfp = fopen (sg_filename, mode == O_RDONLY ? "r":"r+")) == 0)
X		return 0;
X
X	sgr_head = sgr_tail = sgr_cursor = 0;
X	sgr_changed = 0;
X
X	while (fgetsx (buf, sizeof buf, sgrfp) != (char *) 0) {
X		if (cp = strrchr (buf, '\n'))
X			*cp = '\0';
X
X		if (! (sgrf = (struct sg_file_entry *) malloc (sizeof *sgrf)))
X			return 0;
X
X		sgrf->sgr_changed = 0;
X		sgrf->sgr_line = strdup (buf);
X		if ((sgrent = sgetgsent (buf)) && ! (sgrent = sgr_dup (sgrent)))
X			return 0;
X
X		sgrf->sgr_entry = sgrent;
X
X		if (sgr_head == 0) {
X			sgr_head = sgr_tail = sgrf;
X			sgrf->sgr_next = 0;
X		} else {
X			sgr_tail->sgr_next = sgrf;
X			sgrf->sgr_next = 0;
X			sgr_tail = sgrf;
X		}
X	}
X	isopen++;
X	open_modes = mode;
X
X	return 1;
X}
X
X/*
X * sgr_close - close the shadow group file
X *
X *	sgr_close() outputs any modified shadow group file entries and
X *	frees any allocated memory.
X */
X
Xint
Xsgr_close ()
X{
X	char	backup[BUFSIZ];
X	int	fd;
X	int	mask;
X	int	c;
X	int	i;
X	int	errors = 0;
X	FILE	*bkfp;
X	struct	sg_file_entry *sgrf;
X	struct	sg_file_entry *osgrf;
X
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (islocked && lock_pid != getpid ()) {
X		isopen = 0;
X		islocked = 0;
X		errno = EACCES;
X		return 0;
X	}
X	strcpy (backup, sg_filename);
X	strcat (backup, "-");
X
X	if (open_modes == O_RDWR && sgr_changed) {
X		mask = umask (0222);
X		if ((bkfp = fopen (backup, "w")) == 0) {
X			umask (mask);
X			return 0;
X		}
X		umask (mask);
X
X		rewind (sgrfp);
X		while ((c = getc (sgrfp)) != EOF) {
X			if (putc (c, bkfp) == EOF) {
X				fclose (bkfp);
X				return 0;
X			}
X		}
X		if (fclose (bkfp))
X			return 0;
X
X		isopen = 0;
X		(void) fclose (sgrfp);
X
X		mask = umask (0222);
X		if (! (sgrfp = fopen (sg_filename, "w"))) {
X			umask (mask);
X			return 0;
X		}
X		umask (mask);
X
X		for (sgrf = sgr_head;! errors && sgrf;sgrf = sgrf->sgr_next) {
X			if (sgrf->sgr_changed) {
X				if (putgsent (sgrf->sgr_entry, sgrfp))
X					errors++;
X			} else {
X				if (fputsx (sgrf->sgr_line, sgrfp))
X					errors++;
X
X				if (putc ('\n', sgrfp) == EOF)
X					errors++;
X			}
X		}
X		if (fflush (sgrfp))
X			errors++;
X
X		if (errors) {
X			unlink (sg_filename);
X			link (backup, sg_filename);
X			unlink (backup);
X			return 0;
X		}
X	}
X	if (fclose (sgrfp))
X		return 0;
X
X	sgrfp = 0;
X
X	while (sgr_head != 0) {
X		sgrf = sgr_head;
X		sgr_head = sgrf->sgr_next;
X
X		if (sgrf->sgr_entry) {
X			sgr_free (sgrf->sgr_entry);
X			free (sgrf->sgr_entry);
X		}
X		if (sgrf->sgr_line)
X			free (sgrf->sgr_line);
X
X		free (sgrf);
X	}
X	sgr_tail = 0;
X	return 1;
X}
X
Xint
Xsgr_update (sgrent)
Xstruct	sgrp	*sgrent;
X{
X	struct	sg_file_entry	*sgrf;
X	struct	sgrp	*nsgr;
X
X	if (! isopen || open_modes == O_RDONLY) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (sgrf = sgr_head;sgrf != 0;sgrf = sgrf->sgr_next) {
X		if (sgrf->sgr_entry == 0)
X			continue;
X
X		if (strcmp (sgrent->sg_name, sgrf->sgr_entry->sg_name) != 0)
X			continue;
X
X		if (! (nsgr = sgr_dup (sgrent)))
X			return 0;
X		else {
X			sgr_free (sgrf->sgr_entry);
X			*(sgrf->sgr_entry) = *nsgr;
X		}
X		sgrf->sgr_changed = 1;
X		sgr_cursor = sgrf;
X		return sgr_changed = 1;
X	}
X	sgrf = (struct sg_file_entry *) malloc (sizeof *sgrf);
X	if (! (sgrf->sgr_entry = sgr_dup (sgrent)))
X		return 0;
X
X	sgrf->sgr_changed = 1;
X	sgrf->sgr_next = 0;
X	sgrf->sgr_line = 0;
X
X	if (sgr_tail)
X		sgr_tail->sgr_next = sgrf;
X
X	if (! sgr_head)
X		sgr_head = sgrf;
X
X	sgr_tail = sgrf;
X
X	return sgr_changed = 1;
X}
X
Xint
Xsgr_remove (name)
Xchar	*name;
X{
X	struct	sg_file_entry	*sgrf;
X	struct	sg_file_entry	*osgrf;
X
X	if (! isopen || open_modes == O_RDONLY) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (osgrf = 0, sgrf = sgr_head;sgrf != 0;
X			osgrf = sgrf, sgrf = sgrf->sgr_next) {
X		if (! sgrf->sgr_entry)
X			continue;
X
X		if (strcmp (name, sgrf->sgr_entry->sg_name) != 0)
X			continue;
X
X		if (sgrf == sgr_cursor)
X			sgr_cursor = osgrf;
X
X		if (osgrf != 0)
X			osgrf->sgr_next = sgrf->sgr_next;
X		else
X			sgr_head = sgrf->sgr_next;
X
X		if (sgrf == sgr_tail)
X			sgr_tail = osgrf;
X
X		return sgr_changed = 1;
X	}
X	errno = ENOENT;
X	return 0;
X}
X
Xstruct sgrp *
Xsgr_locate (name)
Xchar	*name;
X{
X	struct	sg_file_entry	*sgrf;
X
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	for (sgrf = sgr_head;sgrf != 0;sgrf = sgrf->sgr_next) {
X		if (sgrf->sgr_entry == 0)
X			continue;
X
X		if (strcmp (name, sgrf->sgr_entry->sg_name) == 0) {
X			sgr_cursor = sgrf;
X			return sgrf->sgr_entry;
X		}
X	}
X	errno = ENOENT;
X	return 0;
X}
X
Xint
Xsgr_rewind ()
X{
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	sgr_cursor = 0;
X	return 1;
X}
X
Xstruct sgrp *
Xsgr_next ()
X{
X	if (! isopen) {
X		errno = EINVAL;
X		return 0;
X	}
X	if (sgr_cursor == 0)
X		sgr_cursor = sgr_head;
X	else
X		sgr_cursor = sgr_cursor->sgr_next;
X
X	while (sgr_cursor) {
X		if (sgr_cursor->sgr_entry)
X			return sgr_cursor->sgr_entry;
X
X		sgr_cursor = sgr_cursor->sgr_next;
X	}
X	return 0;
X}
SHAR_EOF
if test 11482 -ne "`wc -c < 'sgroupio.c'`"
then
	echo shar: "error transmitting 'sgroupio.c'" '(should have been 11482 characters)'
fi
fi
exit 0
#	End of shell archive
-- 
John F. Haugh II        | Distribution to  | UUCP: ...!cs.utexas.edu!rpp386!jfh
Ma Bell: (512) 255-8251 | GEnie PROHIBITED :-) |  Domain: jfh@rpp386.cactus.org
"If liberals interpreted the 2nd Amendment the same way they interpret the
 rest of the Constitution, gun ownership would be mandatory."

jfh@rpp386.cactus.org (John F Haugh II) (05/16/91)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	chage.1
#	chfn.1
#	chsh.1
#	login.1
#	passwd.1
#	su.1
#	shadow.3
#	faillog.4
#	passwd.4
#	porttime.4
#	shadow.4
#	faillog.8
#	pwconv.8
#	pwunconv.8
#	sulogin.8
# This archive created: Sun Mar  3 13:27:41 1991
# By:	John F Haugh II (River Parishes Programming, Austin TX)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'chage.1'" '(1875 characters)'
if test -f 'chage.1'
then
	echo shar: "will not over-write existing file 'chage.1'"
else
sed 's/^X//' << \SHAR_EOF > 'chage.1'
X.\" Copyright 1990, John F. Haugh II
X.\" All rights reserved.
X.\"
X.\" Use, duplication, and disclosure prohibited without
X.\" the express written permission of the author.
X.\"
X.\"	@(#)chage.1	3.1	09:34:14	11/21/90
X.\"
X.TH CHAGE 1
X.SH NAME
Xchage \- change user password expirate information
X.SH SYNOPSIS
X\fBchage\fR [ \fB-m \fImindays\fR ] [ \fB-M \fImaxdays\fR ] [ \fB-d \fIlastday\fR ] \fIuser\fR
X.SH DESCRIPTION
X\fIchage\f changes the number of days between password changes and the
Xdate of the last password change.
XThis information is used by the system to determine when a user must
Xchange their password.
XThe \fIchage\f command is restricted to the root user.
X.PP
XThe value of \fImindays\f is the minimum number of days between
Xpassword changes.
XA value of zero for this field indicates that the user may change
Xher password at any time.
X.PP
XThe value of \fImaxdays\f is the maximum number of days during
Xwhich a password is valid.
XWhen \fImaxdays\f plus \fIlastday\f is less than the current day,
Xthe user will be required to change her password before being
Xable to use her account.
X.PP
XThe value of \fIlastday\f is the number of days since January 1st,
X1970 when the password was last changed.
X.PP
XAll of the above values are stored exactly as days when the shadow
Xpassword file is used, but are converted to and from weeks when the
Xstandard password file is used.
XBecause of this conversion, rounding errors may result.
X.PP
XIf none of the options are selected, \fIchage\f operates in an interactive
Xfashion, prompting the user with the current values for all of the fields.
XEnter the new value to change the field, or leave the line blank to use
Xthe current value.
XThe current value is displayed between a pair of \fB[ ]\f marks.
X.SH Files
X/etc/passwd \- user account information
X.br
X/etc/shadow \- shadow user account information
X.SH See Also
Xpasswd(4),
Xshadow(4)
SHAR_EOF
if test 1875 -ne "`wc -c < 'chage.1'`"
then
	echo shar: "error transmitting 'chage.1'" '(should have been 1875 characters)'
fi
fi
echo shar: "extracting 'chfn.1'" '(1598 characters)'
if test -f 'chfn.1'
then
	echo shar: "will not over-write existing file 'chfn.1'"
else
sed 's/^X//' << \SHAR_EOF > 'chfn.1'
X.\" Copyright 1990, John F. Haugh II
X.\" All rights reserved.
X.\"
X.\" Use, duplication, and disclosure prohibited without
X.\" the express written permission of the author.
X.\"
X.\"	@(#)chfn.1	3.1	09:34:16	11/21/90
X.\"
X.TH CHFN 1
X.SH NAME
Xchfn \- change user name and information
X.SH SYNOPSIS
X\fBchfn\fR [ \fB-f \fIfull_name\fR ] [ \fB-r \fIroom_no\fR ]
X.br
X[ \fB-w \fIwork_ph\fR ] [ \fB-h \fIhome_ph\fR ] [ \fB-o \fIother\fR ]
X[ \fIuser\fR ]
X.SH DESCRIPTION
X\fIchfn\f changes user fullname, office number, office extension, and home
Xphone number information for a user's account. 
XThis information is typically printed by \fIfinger(1)\f and similiar
Xprograms.
XA normal user may only change the fields for their own account,
Xthe super user may change the fields for any account.
XAlso, only the super user may use the \fB-o\f option to change the
Xundefined portions of the GCOS field.
X.PP
XThe only restrictions placed on the contents of the fields is that no
Xcontrol characters may be present, nor any of comma, colon, or equal sign.
XThe \fIother\f field does not have this restriction, and is used to
Xstore accounting information used by other applications.
X.PP
XIf none of the options are selected, \fIchfn\f operates in an interactive
Xfashion, prompting the user with the current values for all of the fields.
XEnter the new value to change the field, or leave the line blank to use
Xthe current value.
XThe current value is displayed between a pair of \fB[ ]\f marks.
XWithout options, chfn prompts for the current user account.
X.SH Files
X/etc/passwd \- user account information
X.SH See Also
Xpasswd(4)
SHAR_EOF
if test 1598 -ne "`wc -c < 'chfn.1'`"
then
	echo shar: "error transmitting 'chfn.1'" '(should have been 1598 characters)'
fi
fi
echo shar: "extracting 'chsh.1'" '(1475 characters)'
if test -f 'chsh.1'
then
	echo shar: "will not over-write existing file 'chsh.1'"
else
sed 's/^X//' << \SHAR_EOF > 'chsh.1'
X.\" Copyright 1990, John F. Haugh II
X.\" All rights reserved.
X.\"
X.\" Use, duplication, and disclosure prohibited without
X.\" the express written permission of the author.
X.\"
X.\"	@(#)chsh.1	3.1	09:34:17	11/21/90
X.\"
X.TH CHSH 1
X.SH NAME
Xchsh \- change login shell
X.SH SYNOPSIS
X\fBchsh\fR [ \fB-s \fIlogin_shell\fR ] [ \fIuser\fR ]
X.SH DESCRIPTION
X\fIchsh\f changes the user login shell.
XThis determines the name of the user's initial login command.
XA normal user may only change the login shell for their own account,
Xthe super user may change the login shell for any account.
X.PP
XThe only restrictions placed on the login shell is that the
Xcommand name must be listed in \fB/etc/shells\fR, unless the
Xinvoker is the super-user, and then any value may be added.
XAn account with a restricted login shell may not change
Xtheir login shell.
XFor this reason, placing \fB/bin/rsh\fR in \fB/etc/shells\fR
Xis discouraged since accidentally changing to a restricted
Xshell would prevent the user from every changing their login
Xshell back to its original value.
X.PP
XIf the \fB-s\fR option is not selected, \fIchsh\f operates in an interactive
Xfashion, prompting the user with the current login shell.
XEnter the new value to change the field, or leave the line blank to use
Xthe current value.
XThe current value is displayed between a pair of \fB[ ]\f marks.
X.SH Files
X/etc/passwd \- user account information
X.br
X/etc/shells \- list of valid login shells
X.SH See Also
Xchfn(1),
Xpasswd(4)
SHAR_EOF
if test 1475 -ne "`wc -c < 'chsh.1'`"
then
	echo shar: "error transmitting 'chsh.1'" '(should have been 1475 characters)'
fi
fi
echo shar: "extracting 'login.1'" '(3344 characters)'
if test -f 'login.1'
then
	echo shar: "will not over-write existing file 'login.1'"
else
sed 's/^X//' << \SHAR_EOF > 'login.1'
X.\" Copyright 1989, 1990, John F. Haugh II
X.\" All rights reserved.
X.\"
X.\" Use, duplication, and disclosure prohibited without
X.\" the express written permission of the author.
X.\"
X.\"	@(#)login.1	3.1	09:34:21	11/21/90
X.\"
X.TH LOGIN 1
X.SH NAME
Xlogin \- Begin session on the system
X.SH SYNOPSIS
X.B login
X[ username [ environmental-variables ] ]
X.SH DESCRIPTION
X.I login
Xis used to establish a new session with the system.
XIt is normally invoked automatically by responding to the
X.B login:
Xprompt on the user\'s terminal.
X.I login
Xmay be special to the shell and may not be invoked as a sub-process.
XTypically,
X.I login
Xis treated by the shell as \fBexec login\fR which causes the user
Xto exit from the current shell.
XAttempting to execute \fIlogin\fR from any shell but the login shell
Xwill produce an error message.
X.PP
XWhen invoked from the \fBlogin:\fR prompt, the user may enter
Xenvironmental variables after the username.
XThese variables are entered in the form \fBNAME=VALUE\fR.
XNot all variables may be set in the fashion, notably \fBPATH\fR,
X\fBHOME\fR and \fBSHELL\fR.
XAdditionally, \fBIFS\fR may be inhibited if the user\'s login
Xshell is \fB/bin/sh\fR.
X.PP
XThe user is then prompted for a password, where appropriate.
XEchoing is disabled to prevent revealing the password.
XOnly a small number of password failures are permitted before
X\fIlogin\fR exits and the communications link is severed.
X.PP
XIf password aging has been enabled for your account, you may be
Xprompted for a new password before proceeding.
XYou will be forced to provide your old password and the new
Xpassword before continuing.
XPlease refer to \fIpasswd(1)\fR for more information.
X.PP
XAfter a successful login,
Xyou will be informed of any system messages and the presence
Xof mail.
XYou may turn off the printing of the system message file,
X\fI/etc/motd\fR, by creating a zero-length file \fI.hushlogin\fR
Xin your login directory.
XThe mail message will be one of "\fIYou have new mail.\fR",
X"\fIYou have mail.\fR", or "\fINo Mail.\fR" according to
Xthe condition of your mailbox.
X.PP
XYour user and group ID will be set according to their values in
Xthe \fI/etc/passwd\fR file.
XThe value for \fB$HOME\fR, \fB$SHELL\fR, \fB$PATH\fR, \fB$LOGNAME\fR,
Xand \fB$MAIL\fR are set according to the appropriate fields in the
Xpassword entry.
XUlimit, umask and nice values may also be set according to
Xentries in the GECOS field.
X.PP
XOn some installations, the environmental variable \fB$TERM\fR will be
Xinitialize to the terminal type on your tty line, as specified in
X\fI/etc/ttytype\fR.
X.PP
XAn initialization script for your command interpreter may also be
Xexecuted.
XPlease see the appropriate manual section for more information on
Xthis function.
X.SH CAVEATS
X.PP
XThis version of \fIlogin\fR has many compilation options, only some of which
Xmay be in use at any particular site.
X.SH Files
X/etc/utmp \- list of current login sessions
X.br
X/etc/wtmp \- list of previous login sessions
X.br
X/etc/passwd \- user account information
X.br
X/etc/shadow \- encrypted passwords and age information
X.br
X/etc/motd \- system message file
X.br
X/etc/ttytype \- list of terminal types
X.br
X$HOME/.profile \- initialization script for default shell
X.br
X$HOME/.hushlogin \- suppress printing of system messages
X.br
X.SH See Also
X.PP
Xgetty(1M),
Xmail(1),
Xpasswd(1),
Xsh(1),
Xsu(1),
Xd_passwd(4),
Xpasswd(4)
SHAR_EOF
if test 3344 -ne "`wc -c < 'login.1'`"
then
	echo shar: "error transmitting 'login.1'" '(should have been 3344 characters)'
fi
fi
echo shar: "extracting 'passwd.1'" '(4116 characters)'
if test -f 'passwd.1'
then
	echo shar: "will not over-write existing file 'passwd.1'"
else
sed 's/^X//' << \SHAR_EOF > 'passwd.1'
X.\" Copyright 1989, 1990, John F. Haugh II
X.\" All rights reserved.
X.\"
X.\" Use, duplication, and disclosure prohibited without
X.\" the express written permission of the author.
X.\"
X.\"	@(#)passwd.1	3.1	09:34:23	11/21/90
X.\"
X.TH PASSWD 1
X.SH NAME
Xpasswd \- change user password
X.SH SYNOPSIS
X\fBpasswd\fR [ \fB-f\fR ] [ \fIname\fR ]
X.br
X\fBpasswd\fR [ \fB-g\fR ] [ \fB-r\fR ] \fIgroup\fR
X.SH DESCRIPTION
X\fIpasswd\f changes passwords for user accounts.
XA normal user may only change the password for their own account,
Xthe super user may change the password for any account.
X.PP
XThe user is first prompted for their old password,
Xif one is present.
XThis password is then encrypted and compared against the
Xstored password.
XThe user has only one chance to enter the correct password.
XThe super user is permitted to bypass this step so that forgotten
Xpasswords may be changed.
X.PP
XAfter the password has been entered password aging information
Xis checked to see if the user is permitted to change their password
Xat this time.
XIf not, \fIpasswd\fR refuses to change the password and exits.
X.PP
XThe user is then prompted for a replacement password.
XThis password is tested for complexity.
XAs a general guideline,
Xpasswords should consist of 6 to 8 characters including
Xone or more from each of following sets:
X.IP "" .5i
XLower case alphabetics
X.IP "" .5i
XUpper case alphabetics
X.IP "" .5i
XDigits 0 thru 9
X.IP "" .5i
XPunctuation marks
X.PP
XCare must be taken not to include the system default erase
Xor kill characters.
X\fIpasswd\fR will reject any password which is not suitably
Xcomplex.
X.PP
XIf the password is accepted,
X\fIpasswd\fR will prompt again and compare the second entry
Xagainst the first.
XBoth entries are require to match in order for the password
Xto be changed.
X.PP
XWhen the \fB-g\f option is used, the password for the named
Xgroup is changed.
XThe user must either be the super user, or the first group
Xmember listed for the named group.
XThe current group password is not prompted for.
XThe \fB-r\f option is used with the \fB-g\f option to remove
Xthe current password from the named group.
X.SH Hints for user passwords
XThe security of a password depends upon the strength of the
Xencryption algorithm and the size of the key space.
XThe \fB\s-2UNIX\s+2\fR System encryption method is based on
Xthe NBS DES algorithm and is very secure.
XThe size of the key space depends upon the randomness of the
Xpassword which is selected.
X.PP
XCompromises in password security normally result from careless
Xpassword selection or handling.
XFor this reason, you should select a password which does not
Xappear in a dictionary or which must be written down.
XThe password should also not be a proper name, your license
Xnumber, birth date, or street address.
XAny of these may be used as guesses to violate system security.
X.PP
XYour password must easily remembered so that you will not
Xbe forced to write it on a piece of paper.
XThis can be accomplished by appending two small words together
Xand separating each with a special character or digit.
XFor example, Pass%word.
X.PP
XOther methods of construction involve selecting an easily
Xremembered phrase from literature and selecting the first
Xor last letter from each.
XAn example of this is
X.IP "" .5i
XAsk not for whom the bell tolls.
X.PP
Xwhich produces
X.IP "" .5i
XAn4wtbt.
X.PP
XYou may be reasonably sure few crackers will have
Xincluded this in their dictionary.
X.SH Notes about group passwords
XGroup passwords are an inherent security problem since more
Xthan one person is permitted to know the password.
XHowever, groups are a useful tool for permitting co-operation
Xbetween different users.
X.SH CAVEATS
XNot all options may be supported.
XPassword complexity checking may vary from site to site.
XThe user is urged to select as complex a password as they
Xfeel comfortable with.
XA \fB-f\fR option exists to permit the superuser to override
Xany password complexity testing;
Xnormal users must create passwords which pass the complexity
Xtest.
X.SH Files
X/etc/passwd \- user account information
X.br
X/etc/shadow \- encrypted user passwords
X.SH See Also
Xpasswd(3),
Xshadow(3),
Xgroup(4),
Xpasswd(4)
SHAR_EOF
if test 4116 -ne "`wc -c < 'passwd.1'`"
then
	echo shar: "error transmitting 'passwd.1'" '(should have been 4116 characters)'
fi
fi
echo shar: "extracting 'su.1'" '(1697 characters)'
if test -f 'su.1'
then
	echo shar: "will not over-write existing file 'su.1'"
else
sed 's/^X//' << \SHAR_EOF > 'su.1'
X.\" Copyright 1989, 1990, John F. Haugh II
X.\" All rights reserved.
X.\"
X.\" Use, duplication, and disclosure prohibited without
X.\" the express written permission of the author.
X.\"
X.\"	@(#)su.1	3.1	09:34:28	11/21/90
X.\"
X.TH SU 1
X.SH NAME
Xsu \- Change user ID or become super-user
X.SH SYNOPSIS
X.B su
X[ - ] [ username [ args ] ]
X.SH DESCRIPTION
X.I su
Xis used to become another user during a login session.
XInvoked without a username, \fIsu\fR defaults to becoming
Xthe super user.
XThe optional argument \fB\-\fR may be used to provide an
Xenvironment similiar to what the user would expect had
Xthe user logged in directly.
X.PP
XAdditional arguments may be provided after the username,
Xin which case they are supplied to the user\'s login shell.
XIn particular, an argument of \fB-c\fR will cause the
Xnext argument to be treated as a command by most command
Xinterpreters.
XThe command will be executed under the shell specified by
X\fB$SHELL\fR, or if undefined, by the one specified in
X\fI/etc/passwd\fR.
X.PP
XThe user will be prompted for a password, if appropriate.
XInvalid passwords will produce an error message.
XAll attempts, both valid and invalid, are logged to detect
Xabuses of the system.
X.PP
XThe current environment is passed to the new shell.
XThe value of \fB$PATH\fR is reset to \fB/bin:/usr/bin\fR
Xfor normal users, or \fB/bin:/usr/bin:/etc\fR for the super user.
X.SH CAVEATS
X.PP
XThis version of \fIsu\fR has many compilation options, only some of which
Xmay be in use at any particular site.
X.SH Files
X/etc/passwd \- user account information
X.br
X/etc/shadow \- encrypted passwords and age information
X.br
X$HOME/.profile \- initialization script for default shell
X.SH See Also
Xlogin(1),
Xsh(1)
SHAR_EOF
if test 1697 -ne "`wc -c < 'su.1'`"
then
	echo shar: "error transmitting 'su.1'" '(should have been 1697 characters)'
fi
fi
echo shar: "extracting 'shadow.3'" '(2801 characters)'
if test -f 'shadow.3'
then
	echo shar: "will not over-write existing file 'shadow.3'"
else
sed 's/^X//' << \SHAR_EOF > 'shadow.3'
X.\" Copyright 1989, 1990, John F. Haugh II
X.\" All rights reserved.
X.\"
X.\" Use, duplication, and disclosure prohibited without
X.\" the express written permission of the author.
X.\"
X.\"	@(#)shadow.3	3.1	23:49:42	11/11/90
X.\"
X.TH SHADOW 3
X.SH NAME
Xshadow \- encrypted password file routines
X.SH Syntax
X.IP "" .5i
X#include <shadow.h>
X.IP "" .5i
Xstruct spwd *getspent();
X.br
Xstruct spwd *getspnam(char * name);
X.br
Xvoid setspent();
X.br
Xvoid endspent();
X.br
Xstruct spwd *fgetspent(FILE *fp);
X.br
Xstruct spwd *sgetspent(char *cp);
X.br
Xint putspent(struct spwd *p,FILE *fp);
X.SH DESCRIPTION
X.I shadow
Xmanipulates the contents of the shadow password file,
X\fB/etc/shadow\fR.
XThe structure in the \fI#include\fR file is
X.IP "" .5i
Xstruct spwd {
X.br
X	char	*sp_namp; /* user login name */
X.br
X	char	*sp_pwdp; /* encrypted password */
X.br
X	long	sp_lstchg; /* last password change */
X.br
X	int	sp_min; /* days until change allowed. */
X.br
X	int	sp_max; /* days before change required */
X.br
X	int	sp_warn; /* days warning for expiration */
X.br
X	int	sp_inact; /* days before account inactive */
X.br
X	int	sp_expire; /* date when account expires */
X.br
X	int	sp_flag; /* reserved for future use */
X.br
X}
X.PP
XThe meanings of each field are
X.IP "" .5i
Xsp_namp \- pointer to null-terminated user name.
X.IP "" .5i
Xsp_pwdp \- pointer to null-terminated password.
X.IP "" .5i
Xsp_lstchg \- days since Jan 1, 1970 password was last changed.
X.IP "" .5i
Xsp_min \- days before which password may not be changed.
X.IP "" .5i
Xsp_max \- days after which password must be changed.
X.IP "" .5i
Xsp_warn \- days before password is to expire that user is warned
Xof pending password expiration.
X.IP "" .5i
Xsp_inact \- days after password expires that account is considered
Xinactive and disabled.
X.IP "" .5i
Xsp_expire \- days since Jan 1, 1970 that account will be disabled.
X.IP "" .5i
Xsp_flag \- reserved for future use.
X.SH Description
X\fIgetspent\fR, \fIgetspname\fR, \fIfgetspent\fR, and \fIsgetspent\fR
Xeach return a pointer to a \fBstruct spwd\fR.
X\fIgetspent\fR returns the
Xnext entry from the file, and \fIfgetspent\fR returns the next
Xentry from the given stream, which is assumed to be a file of
Xthe proper format.
X\fIsgetspent\fR returns a pointer to a \fBstruct spwd\fR using the
Xprovided string as input.
X\fIgetspnam\fR searches from the current position in the file for
Xan entry matching \fIname\fR.
X.PP
X\fIsetspent\fR and \fIendspent\fR may be used to begin and end,
Xrespectively, access to the shadow password file.
X.SH Diagnostics
XRoutines return NULL if no more entries are available or if an
Xerror occurs during processing.
X.SH Caveats
XThese routines may only be used by the super user as access to
Xthe shadow password file is restricted.
X.SH Files
X/etc/shadow \- encrypted user passwords
X.SH See Also
Xgetpwent(3),
Xshadow(4)
SHAR_EOF
if test 2801 -ne "`wc -c < 'shadow.3'`"
then
	echo shar: "error transmitting 'shadow.3'" '(should have been 2801 characters)'
fi
fi
echo shar: "extracting 'faillog.4'" '(969 characters)'
if test -f 'faillog.4'
then
	echo shar: "will not over-write existing file 'faillog.4'"
else
sed 's/^X//' << \SHAR_EOF > 'faillog.4'
X.\" Copyright 1989, 1990, John F. Haugh II
X.\" All rights reserved.
X.\"
X.\" Use, duplication, and disclosure prohibited without
X.\" the express written permission of the author.
X.\"
X.\"	@(#)faillog.4	3.1	09:34:18	11/21/90
X.\"
X.TH FAILLOG 4
X.SH NAME
Xfaillog \- Login failure logging file
X.SH DESCRIPTION
X.I faillog
Xmaintains a count of login failures and the limits for each account.
XThe file is fixed length record, indexed by numerical UID.
XEach record contains the count of login failures since the last
Xsuccessful login;
Xthe maximum number of failures before the account is disabled;
Xthe line the last login failure occured on;
Xand the date the last login failure occured.
X.PP
XThe structure of the file is
X.DS
X
X        struct	faillog {
X                short   fail_cnt;
X                short   fail_max;
X                char    fail_line[12];
X                time_t  fail_time;
X        };
X
X.DE
X.SH Files
X/usr/adm/faillog \- login failure log
X.SH See Also
Xfaillog(8)
SHAR_EOF
if test 969 -ne "`wc -c < 'faillog.4'`"
then
	echo shar: "error transmitting 'faillog.4'" '(should have been 969 characters)'
fi
fi
echo shar: "extracting 'passwd.4'" '(2520 characters)'
if test -f 'passwd.4'
then
	echo shar: "will not over-write existing file 'passwd.4'"
else
sed 's/^X//' << \SHAR_EOF > 'passwd.4'
X.\" Copyright 1989, 1990, John F. Haugh II
X.\" All rights reserved.
X.\"
X.\" Use, duplication, and disclosure prohibited without
X.\" the express written permission of the author.
X.\"
X.\"	@(#)passwd.4	3.1	09:34:24	11/21/90
X.\"
X.TH PASSWD 4
X.SH NAME
Xpasswd \- The password file
X.SH DESCRIPTION
X.I passwd
Xcontains various pieces of information for each user account.
XIncluded is
X.IP "" .5i
XLogin name
X.IP "" .5i
XOptional encrypted password
X.IP "" .5i
XNumerical user ID
X.IP "" .5i
XNumerical group ID
X.IP "" .5i
XUser name or comment field
X.IP "" .5i
XUser home directory
X.IP "" .5i
XUser command interpreter
X.PP
XThe password field may not be filled if shadow passwords
Xhave been enabled.
XIf shadow passwords are being used, the encrypted password will
Xbe found in \fB/etc/shadow\fR.
XThe encryped password consists of 13 characters from the
X64 character alphabet
Xa thru z, A thru Z, 0 thru 9, \. and /.
XRefer to \fIcrypt(3)\fR for details on how this string is
Xinterpreted.
X.PP
XAn optional password age string may follow the encrypted
Xpassword, separated by a comma, from the same alphabet
Xas the password itself.
XThe first character gives the number of weeks during which the
Xpassword is valid.
XThe second character gives the number of weeks which must pass
Xbefore the user is permitted to change the password.
XThe last two characters give the week since Jan 1970 when the
Xpassword was last changed.
XWhen the number of weeks during which the password is valid
Xhave passed, the user will be required to provide a new
Xpassword.
X.PP
XThe comment field is used by various system utilities, such as
X\fIfinger(1)\fR.
XThree additional values may be present in the comment field.
XThey are
X.IP "" .5i
Xpri= \- set initial value of nice
X.IP "" .5i
Xumask= \- set initial value of umask
X.IP "" .5i
Xulimit= \- set initial value of ulimit
X.PP
XThese fields are separated from each other and from any other
Xcomment field by a comma.
X.PP
XThe home directory field provides the name of the initial
Xworking directory.
X\fILogin\fR uses this information to set the value of
Xthe \fBHOME\fR environmental variable.
X.PP
XThe command interpreter field provides the name of the user's
Xcommand language interpreter, or the name of the initial program
Xto execute.
X\fILogin\fR uses this information to set the value of the
X\fBSHELL\fR environmental variable.
XIf this field is empty, it defaults to the value \fB/bin/sh\fR.
X.SH Files
X/etc/passwd \- user account information
X.SH See Also
Xlogin(1),
Xpasswd(1),
Xsu(1),
Xsulogin(1M),
Xshadow(4),
Xpwconv(8),
Xpwunconv(8)
SHAR_EOF
if test 2520 -ne "`wc -c < 'passwd.4'`"
then
	echo shar: "error transmitting 'passwd.4'" '(should have been 2520 characters)'
fi
fi
echo shar: "extracting 'porttime.4'" '(1872 characters)'
if test -f 'porttime.4'
then
	echo shar: "will not over-write existing file 'porttime.4'"
else
sed 's/^X//' << \SHAR_EOF > 'porttime.4'
X.\" Copyright 1989, 1990, John F. Haugh II
X.\" All rights reserved.
X.\"
X.\" Use, duplication, and disclosure prohibited without
X.\" the express written permission of the author.
X.\"
X.\"	@(#)porttime.4	3.2	08:36:07	2/8/91
X.\"
X.TH PORTTIME 4
X.SH NAME
Xporttime \- port access time file
X.SH DESCRIPTION
X.I porttime
Xcontains a list of tty devices, user names, and permitted login times.
X.PP
XEach entry consists of three colon separated fields.
XThe first field is a comma separated list of tty devices,
Xor an asterisk to indicate that all tty devices are matched by this entry.
XThe second field is a comma separated list of user names, or an
Xasterisk to indicated that all user names are matched by this entry.
XThe third field is a comma separated list of permitted access times.
X.PP
XEach access time entry consists of zero or more days of the week,
Xabbreviated \fBSu\fR, \fBMo\fR, \fBTu\fR, \fBWe\fR, \fBTh\fR,
X\fBFr\fR, and \fBSa\fR, followed by a pair of times separated by
Xa hyphen.
XThe abbreviation \fBWk\fR may be used to represent Monday thru Friday,
Xand \fBAl\fR may be used to indicate every day.
XIf no days are given, \fBAl\fR is assumed.
X.SH Examples
XThe following entry allows access to user \fBjfh\fR on every port
Xduring weekdays from 9am to 5pm.
X.br
X.sp 1
X*:jfh:Wk0900-1700
X.br
X.sp 1
XThe following entries allow access only to the users \fBroot\fR and
X\fBoper\fR on /dev/console at any time.
XThis illustrates how the
X\fB/etc/porttime\fR file is an ordered list of access times.
XAny other user would match the second entry which does not permit
Xaccess at any time.
X.br
X.sp 1
Xconsole:root,oper:Al0000-2400
X.br
Xconsole:*:
X.br
X.sp 1
XThe following entry allows access for the user \fBgames\fR on any
Xport during non-working hours.
X.br
X.sp 1
X*:games:Wk1700-0900,SaSu0000-2400
X.br
X.sp 1
X.SH Files
X/etc/porttime \- file containing port access times
X.SH See Also
Xlogin(1)
SHAR_EOF
if test 1872 -ne "`wc -c < 'porttime.4'`"
then
	echo shar: "error transmitting 'porttime.4'" '(should have been 1872 characters)'
fi
fi
echo shar: "extracting 'shadow.4'" '(2122 characters)'
if test -f 'shadow.4'
then
	echo shar: "will not over-write existing file 'shadow.4'"
else
sed 's/^X//' << \SHAR_EOF > 'shadow.4'
X.\" Copyright 1989, 1990, John F. Haugh II
X.\" All rights reserved.
X.\"
X.\" Use, duplication, and disclosure prohibited without
X.\" the express written permission of the author.
X.\"
X.\"	@(#)shadow.4	3.1	23:49:44	11/11/90
X.\"
X.TH SHADOW 4
X.SH NAME
Xshadow \- encrypted password file
X.SH DESCRIPTION
X.I shadow
Xcontains the encrypted password information for user's accounts
Xand optional the password aging information.
XIncluded is
X.IP "" .5i
XLogin name
X.IP "" .5i
XEncrypted password
X.IP "" .5i
XDays since Jan 1, 1970 that password was last changed
X.IP "" .5i
XDays before password may be changed
X.IP "" .5i
XDays after which password must be changed
X.IP "" .5i
XDays before password is to expire that user is warned
X.IP "" .5i
XDays after password expires that account is disabled
X.IP "" .5i
XDays since Jan 1, 1970 that account is disabled
X.IP "" .5i
XA reserved field
X.PP
XThe password field must be filled.
XThe encryped password consists of 13 to 24 characters from the
X64 character alphabet
Xa thru z, A thru Z, 0 thru 9, \. and /.
XRefer to \fIcrypt(3)\fR for details on how this string is
Xinterpreted.
X.PP
XThe date of the last password change is given as the number
Xof days since Jan 1, 1970.
XThe password may not be changed again until the proper number
Xof days have passed, and must be changed after the maximum
Xnumber of days.
XIf the minimum number of days required is greater than the
Xmaximum number of day allowed, this password may not be
Xchanged by the user.
X.PP
XAn account is considered to be inactive and is disabled if
Xthe password is not changed within the specified number of
Xdays after the password expires.
XAn account will also be disabled on the specified day
Xregardless of other password expiration information.
X.PP
XThis information supercedes any password or password age
Xinformation present in \fB/etc/passwd\fR.
X.PP
XThis file must not be readable by regular users if password
Xsecurity is to be maintained.
X.SH Files
X/etc/passwd \- user account information
X.br
X/etc/shadow \- encrypted user passwords
X.SH See Also
Xchage(1),
Xlogin(1),
Xpasswd(1),
Xsu(1),
Xsulogin(1M),
Xshadow(3),
Xpasswd(4),
Xpwconv(8),
Xpwunconv(8)
SHAR_EOF
if test 2122 -ne "`wc -c < 'shadow.4'`"
then
	echo shar: "error transmitting 'shadow.4'" '(should have been 2122 characters)'
fi
fi
echo shar: "extracting 'faillog.8'" '(2030 characters)'
if test -f 'faillog.8'
then
	echo shar: "will not over-write existing file 'faillog.8'"
else
sed 's/^X//' << \SHAR_EOF > 'faillog.8'
X.\" Copyright 1989, 1990, John F. Haugh II
X.\" All rights reserved.
X.\"
X.\" Use, duplication, and disclosure prohibited without
X.\" the express written permission of the author.
X.\"
X.\"	@(#)faillog.8	3.1	09:34:20	11/21/90
X.\"
X.TH FAILLOG 8
X.SH NAME
Xfaillog \- examine faillog and set login failure limits
X.SH SYNOPSIS
X/etc/faillog [ -u uid ] [ -t days ] [ -m max ] [ -pr ] 
X.SH DESCRIPTION
X\fIPlastlog\fR formats the contents of the failure log,
X\fI/usr/adm/faillog\fR, and maintains failure counts and
Xlimits.
XThe order of the arguments to \fIfaillog\fR is significant.
XEach argument is processed immediately in the order given.
X.PP
XThe \fB-p\fR flag causes failure entries to be printed in UID
Xorder.
XEntering \fB-u login-name\fR flag will
Xcause the failure record for \fBlogin-name\fR only to be printed.
XEntering \fB-t days\fR will cause only the
Xfailures more recent than \fBdays\fR to be printed.
XThe \fB-t\fR flag overrides the use of \fB-u\fR.
X.PP
XThe \fB-r\fR flag is used to reset the count of login failures.
XWrite access to \fI/usr/adm/faillog\fR is required for
Xthis option.
XEntering \fB-u login-name\fR will cause only the failure count
Xfor \fBlogin-name\fR to be reset.
X.PP
XThe \fB-m\fR flag is used to set the maximum number of login
Xfailures before the account is disabled.
XWrite access to \fB/usr/adm/faillog\fR is required for this
Xoption.
XEntering \fB-m max\fR will cause all accounts to be disabled
Xafter \fBmax\fR failed logins occur.
XThis may be modified with \fB-u login-name\fR to limit this
Xfunction to \fBlogin-name\fR only.
XSelecting a \fBmax\fR value of 0 has the effect of not placing
Xa limit on the number of failed logins.
XThe maximum failure count
Xshould always be 0 for \fBroot\fR to prevent
Xa denial of services attack against the system.
X.PP
XOptions may be combined in virtually any fashion.
XEach \fB-p\fR, \fB-r\fR, and \fB-m\fR option will cause
Ximmediate execution using any \fB-u\fR or \fB-t\fR modifier.
X.SH Files
X/usr/adm/faillog \- failure logging file
X.SH See Also
Xlogin(1),
Xfaillog(4)
SHAR_EOF
if test 2030 -ne "`wc -c < 'faillog.8'`"
then
	echo shar: "error transmitting 'faillog.8'" '(should have been 2030 characters)'
fi
fi
echo shar: "extracting 'pwconv.8'" '(1396 characters)'
if test -f 'pwconv.8'
then
	echo shar: "will not over-write existing file 'pwconv.8'"
else
sed 's/^X//' << \SHAR_EOF > 'pwconv.8'
X.\" Copyright 1989, 1990, John F. Haugh II
X.\" All rights reserved.
X.\"
X.\" Use, duplication, and disclosure prohibited without
X.\" the express written permission of the author.
X.\"
X.\"	@(#)pwconv.8	3.1	23:51:34	11/11/90
X.\"
X.TH PWCONV 8
X.SH NAME
Xpwconv \- convert and update shadow password files
X.SH SYNOPSIS
X/etc/pwconv
X.SH DESCRIPTION
X\fIPwconv\fR copies the old password file information to a new shadow
Xpassword file,
Xmerging entries from an optional existing shadow file.
XThe new password file is left in \fBnpasswd\fR,
Xthe new shadow file is left in \fBnshadow\fR.
XBoth of these are files are created with modes which only permit
Xread access to the owner.
XExisting shadow entries are copied as is.
XShadow entries in the old format will be silently converted to the
Xnew format on output.
XAny entries which are missing fields will have those fields
Xfilled in with default values where appropriate.
XNew entries are created with passwords which expire in 10000 days,
Xwith a last changed date of today,
Xunless password aging information was already present.
XEntries with blank passwords are not copied to the shadow file at all.
X.SH Files
X/etc/passwd \- old encrypted passwords and password aging
X.br
X/etc/shadow \- previously converted shadow password file
X.br
X./npasswd \- new password file
X.br
X./nshadow \- new shadow password file
X.SH See Also
Xpasswd(1),
Xpasswd(4),
Xshadow(4),
Xpwunconv(8)
SHAR_EOF
if test 1396 -ne "`wc -c < 'pwconv.8'`"
then
	echo shar: "error transmitting 'pwconv.8'" '(should have been 1396 characters)'
fi
fi
echo shar: "extracting 'pwunconv.8'" '(926 characters)'
if test -f 'pwunconv.8'
then
	echo shar: "will not over-write existing file 'pwunconv.8'"
else
sed 's/^X//' << \SHAR_EOF > 'pwunconv.8'
X.\" Copyright 1989, 1990, John F. Haugh II
X.\" All rights reserved.
X.\"
X.\" Use, duplication, and disclosure prohibited without
X.\" the express written permission of the author.
X.\"
X.\"	@(#)pwunconv.8	3.1	09:34:27	11/21/90
X.\"
X.TH PWUNCONV 8
X.SH NAME
Xpwunconv \- restore old password file from shadow password file
X.SH SYNOPSIS
X/etc/pwunconv
X.SH DESCRIPTION
X\fIPwunconv\fR copies the password file information from the shadow
Xpassword file,
Xmerging entries from an optional existing shadow file.
XThe new password file is left in \fBnpasswd\fR.
XThis file is created with modes which allow read access for
Xthe owner only.
XThere is no new shadow file.
XPassword aging information is translated where possible.
X.SH Files
X/etc/passwd \- old encrypted passwords and password aging
X.br
X/etc/shadow \- previously converted shadow password file
X.br
X./npasswd \- new password file
X.SH See Also
Xpasswd(1),
Xpasswd(4),
Xshadow(4),
Xpwconv(8)
SHAR_EOF
if test 926 -ne "`wc -c < 'pwunconv.8'`"
then
	echo shar: "error transmitting 'pwunconv.8'" '(should have been 926 characters)'
fi
fi
echo shar: "extracting 'sulogin.8'" '(1611 characters)'
if test -f 'sulogin.8'
then
	echo shar: "will not over-write existing file 'sulogin.8'"
else
sed 's/^X//' << \SHAR_EOF > 'sulogin.8'
X.\" Copyright 1989, 1990, John F. Haugh II
X.\" All rights reserved.
X.\"
X.\" Use, duplication, and disclosure prohibited without
X.\" the express written permission of the author.
X.\"
X.\"	@(#)sulogin.8	3.1	09:34:30	11/21/90
X.\"
X.TH SULOGIN 8
X.SH NAME
Xsulogin \- Single-user login
X.SH DESCRIPTION
X.I sulogin
Xis invoked by \fB/etc/init\fR prior to allowing the user
Xaccess to the system when in single user mode.
XThis feature may only be available on certain systems where
X\fIinit\fR has been modified accordingly, or where the
X\fB/etc/inittab\fR has an entry for a single user login.
X.PP
XThe user is prompted
X.IP "" .5i
XType control-d for normal startup,
X.br
X(or give root password for system maintenance):
X.PP
XIf the user enters the correct root password, a login session
Xis initiated.
XWhen \fBEOF\fR is pressed instead, the system enters multi-user
Xmode.
X.PP
XAfter the user exits the single-user shell, or presses \fBEOF\fR,
Xthe system begins the initialization process required to enter
Xmulti-user mode.
X.SH CAVEATS
X.PP
XThis command can only be used if \fIinit\fR has been modified to call
X\fB/etc/sulogin\fR instead of \fB/bin/sh\fR,
Xor if the user has set the \fIinittab\fR to support a single user
Xlogin.
X.PP
XAs complete an environment as possible is created.
XHowever, various devices may be unmounted or uninitialized and many 
Xof the user commands may be unavailable or nonfunctional as a result.
X.SH Files
X/etc/passwd \- user account information
X.br
X/etc/shadow \- encrypted passwords and age information
X.br
X/.profile \- initialization script for single user shell
X.SH See Also
Xlogin(1),
Xinit(1M),
Xsh(1)
SHAR_EOF
if test 1611 -ne "`wc -c < 'sulogin.8'`"
then
	echo shar: "error transmitting 'sulogin.8'" '(should have been 1611 characters)'
fi
fi
exit 0
#	End of shell archive
-- 
John F. Haugh II        | Distribution to  | UUCP: ...!cs.utexas.edu!rpp386!jfh
Ma Bell: (512) 255-8251 | GEnie PROHIBITED :-) |  Domain: jfh@rpp386.cactus.org
"If liberals interpreted the 2nd Amendment the same way they interpret the
 rest of the Constitution, gun ownership would be mandatory."