Shadow Login Suite, version 3 (part 3 of 8)

John F Haugh II jfh at rpp386.cactus.org
Fri May 17 02:31:42 AEST 1991


#! /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 at rpp386.cactus.org
"If liberals interpreted the 2nd Amendment the same way they interpret the
 rest of the Constitution, gun ownership would be mandatory."



More information about the Alt.sources mailing list