[comp.sources.misc] v04i122: extended access control for Unix

george@rebel.UUCP (George M. Sipe) (10/11/88)

Posting-number: Volume 4, Issue 122
Submitted-by: "George M. Sipe" <george@rebel.UUCP>
Archive-name: access

[I stand corrected...  (Sorry, Rich, you slowpoke!  ;-)  ++bsa]

#! /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
#	Makefile
#	access.c
#	access.man
# This archive created: Tue Jul 19 13:02:15 1988
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'README'
then
	echo shar: "will not over-write existing file 'README'"
else
sed 's/^X//' << \SHAR_EOF > 'README'
XAccess provides a simple yet powerful method of controlling user access
Xto a system.  Simply specify access in place of a login shell in the
Xpassword file for each user who is to have restricted accessibility to
Xa system.  Access will in turn, execute the login shell specified in
Xits argument list if the current tty and time falls within one or more
Xof the access constraints given.
X
XAccessibility constraints limit users by tty port and/or up to 6
Xaccessibility classes of time.
X
XOne of the most valued uses of access is to reserve modem use for UUCP
Xor high priority users.  See the manual page for examples.
X
XBe sure to disable users ability to change their login shell and
Xthereby defeat this control.  For example, "chmod o-x /usr/ucb/chsh".
X
X
X     Copyright (c) 1988 by George M. Sipe.  All rights reserved.
X
XThis software may only be redistributed without fee and without any
Xother form of monetary gain (including sold, rented, leased, or
Xtraded), unless the express written permission of the copyright holder
Xis obtained in advance.
X
XThis copyright notice must be reproduced in its entirety on all copies
Xof this software.  Further, acknowledgment of the authorship of this
Xsoftware must not be removed from its current or derived
Xdocumentation.
X
XNo expressed or implied warranty is made for this software.  No party
Xconnected with this software assumes any liability or responsibility
Xfor its use, the correctness of its operation, or its fitness for any
Xpurpose.
X
XAny distributor of copies of this software shall grant the recipient
Xpermission for further redistribution as permitted by this notice.
X
XPermission is hereby granted to copy, reproduce, redistribute and
Xotherwise use this software as long as the conditions above are
Xstrictly adhered to.
SHAR_EOF
fi
if test -f 'Makefile'
then
	echo shar: "will not over-write existing file 'Makefile'"
else
sed 's/^X//' << \SHAR_EOF > 'Makefile'
X# Makefile for:  access - limit system usage to specified ttys and times
X
XDEFINES		= -DBSD
XCFLAGS		= -O $(DEFINES)
XROFF		= nroff
XRFLAGS		= -man
XLDFLAGS		= -s
XDESTBIN		= ${HOME}
XDESTMAN		= ${HOME}
XDESTCAT		= ${HOME}
XDESTOWN		= root
XDESTGRP		= staff
XSHELL		= /bin/sh
XMAKEFILE	= Makefile
XPROGRAM		= access
XMANPAGES	= access.man
XCATPAGES	= access.cat
XSRCS		= access.c
XOBJS		= access.o
X
X.man.cat:
X		$(ROFF) $(RFLAGS) $< > $@
X
X.SUFFIXES:	.man .cat
X
X# Compile and load the program and format its manual pages.
Xall:		$(PROGRAM) $(CATPAGES)
X
X$(PROGRAM):	$(OBJS)
X		$(CC) $(LDFLAGS) $(OBJS) -o $(PROGRAM)
X
X# Run lint on source files, put results on standard output.
Xlint:
X		lint -u $(DEFINES) $(SRCS)
X
X# Create a tags file for use by a source code editor.
Xtags:		$(SRCS)
X		ctags $(SRCS)
X
X# Edit the makefile and regenerate the dependency information.
Xdepend:
X		mkmf -f $(MAKEFILE) PROGRAM=$(PROGRAM) DESTBIN=$(DESTBIN)
X
X# Print an index of functions on standard output.
Xindex:
X		ctags -wx $(SRCS)
X
X# Print source code files on standard output.
Xprint:
X		lpr -p $(SRCS) $(MANPAGES)
X
X# Compile and load the program, format manual pages, and move them
X# to their destination directories.
Xinstall:	$(PROGRAM) $(CATPAGES)
X		cp $(PROGRAM) $(DESTBIN)
X		chmod 755 $(DESTBIN)/$(PROGRAM)
X		chown $(DESTOWN) $(DESTBIN)/$(PROGRAM)
X		chgrp $(DESTGRP) $(DESTBIN)/$(PROGRAM)
X		-for manpage in $(MANPAGES); do \
X			basepage=`basename $$manpage .man`; \
X			cp $$basepage.man $(DESTMAN)/$$basepage.l; \
X			chmod 644 $(DESTMAN)/$$basepage.l; \
X			chown $(DESTOWN) $(DESTMAN)/$$basepage.l; \
X			chgrp $(DESTGRP) $(DESTMAN)/$$basepage.l; \
X			cp $$basepage.cat $(DESTCAT)/$$basepage.l; \
X			chmod 644 $(DESTCAT)/$$basepage.l; \
X			chown $(DESTOWN) $(DESTCAT)/$$basepage.l; \
X			chgrp $(DESTGRP) $(DESTCAT)/$$basepage.l; \
X		done
X
X# Remove the program and its formatted manual pages from their
X# destination directories.
Xuninstall:
X		rm -f $(DESTBIN)/$(PROGRAM)
X		-for manpage in $(MANPAGES); do \
X			basepage=`basename $$manpage .man`; \
X			rm -f $(DESTMAN)/$$basepage.l; \
X			rm -f $(DESTCAT)/$$basepage.l; \
X		done
X
X# Remove all target and intermediate files.
Xclean:
X		-rm -f $(PROGRAM) $(CATPAGES) $(OBJS)
X		-rm -f core a.out made *.o
SHAR_EOF
fi
if test -f 'access.c'
then
	echo shar: "will not over-write existing file 'access.c'"
else
sed 's/^X//' << \SHAR_EOF > 'access.c'
X/*   Copyright (c) 1988 by George M. Sipe.  All rights reserved.
X
XThis software may only be redistributed without fee and without any
Xother form of monetary gain (including sold, rented, leased, or
Xtraded), unless the express written permission of the copyright holder
Xis obtained in advance.
X
XThis copyright notice must be reproduced in its entirety on all copies
Xof this software.  Further, acknowledgment of the authorship of this
Xsoftware must not be removed from its current or derived
Xdocumentation.
X
XNo expressed or implied warranty is made for this software.  No party
Xconnected with this software assumes any liability or responsibility
Xfor its use, the correctness of its operation, or its fitness for any
Xpurpose.
X
XAny distributor of copies of this software shall grant the recipient
Xpermission for further redistribution as permitted by this notice.
X
XPermission is hereby granted to copy, reproduce, redistribute and
Xotherwise use this software as long as the conditions above are
Xstrictly adhered to.							*/
X
X#include <stdio.h>
X#include <ctype.h>
X#include <pwd.h>
X#include <sys/types.h>
X#ifndef	BSD
X#include <time.h>
X#define	MAXPATHLEN	1024
Xvoid exit();
Xchar *strchr();
Xchar *strrchr();
Xlong time();
X#else
X#include <sys/time.h>
X#include <sys/param.h>
X#undef	MIN
X#define	strchr(s,c)	index(s,c)
X#define	strrchr(s,c)	rindex(s,c)
Xexit();
Xchar *index();
Xchar *rindex();
Xtime_t time();
X#endif
X
Xint execve();
Xint fclose();
Xint fgetc();
Xchar *fgets();
XFILE *fopen();
Xint fputc();
Xint fputs();
Xint geteuid();
Xstruct passwd *getpwuid();
Xstruct tm *localtime();
Xchar *strcat();
Xchar *strcpy();
Xint strlen();
Xint strncmp();
Xchar *ttyname();
Xint ungetc();
X
X/* #define	DEBUG		/* define for tracing information */
X
X#ifndef	TRUE
X#define	TRUE	1		/* true and false */
X#define	FALSE	0
X#endif
X#define	UNSET	3		/* unset flag */
X
X#ifndef	DEBUG
X#define	ACFILE	"/usr/local/lib/access"
X#else
X#define	ACFILE	"test"
X#endif	DEBUG
X#define	MAXENT	1024		/* maximum entry length */
X#define	MAXARGS	99		/* maximum number of arguments in file */
X#define	MAXSH	MAXPATHLEN+7	/* maximum size of SHELL environment variable */
X
X#define	TTY	't'		/* ttyname restrictor */
X#define	MIN	'm'		/* minute restrictor */
X#define	HOUR	'h'		/* hour restrictor */
X#define	WDAY	'w'		/* day of week restrictor */
X#define	MDAY	'D'		/* day of month restrictor */
X#define	MON	'M'		/* month restrictor */
X#define	YEAR	'Y'		/* year restrictor */
X#define	OR	'o'		/* logical or separator */
X#define	PATH	'/'		/* pathname prefix */
X
X#define	ERRNTTY	101		/* not connected to a tty */
X#define	ERRSEP	102		/* missing separator between values */
X#define	ERRRNG1	103		/* '-' appears twice in range */
X#define	ERRRNG2	104		/* '-' does not follow value */
X#define	ERRNUL	105		/* null value */
X#define	ERRMIN	106		/* value below minimum */
X#define	ERRMAX	107		/* value exceeds maximum */
X#define	ERRBAD	108		/* invalid character */
X
Xtypedef	char BOOL;
X
Xstatic struct tm *timenow;	/* the current local time */
Xstatic char *name = NULL;	/* pointer to username */
Xstatic int namesz;		/* size of username */
Xstatic char *ttynm = NULL;	/* pointer to ttyname */
Xstatic int ttysz;		/* size of ttyname */
X
Xstatic BOOL user;		/* TRUE or FALSE */
Xstatic BOOL tty;		/* UNSET, TRUE, or FALSE */
Xstatic BOOL min[60];		/* 0 to 59 */
Xstatic BOOL hour[24];		/* 0 to 23 */
Xstatic BOOL wday[8];		/* 0 (Sunday) to 6 (Saturday);  7 = 0 too */
Xstatic BOOL mday[32];		/* 1 to 31 */
Xstatic BOOL mon[13];		/* 1 to 12 */
Xstatic BOOL year[99];		/* 87 to 99 */
X
X/* Display userchk(), ttychk(), and expand() error message. */
X
Xstatic void error(type, string, code)
Xchar *type;
Xchar *string;
Xint code;
X{
X	fputs("\n*** system access processing failure - ", stderr);
X	fputs("report to your system manager ***\n\n", stderr);
X
X	if (code == 0) {
X		fputs(type, stderr);
X		fputs(string, stderr);
X		fputc('\n', stderr);
X		exit(0);
X	}
X
X	fputs("invalid ", stderr);
X	fputs(type, stderr);
X	fputs(" specification:  '", stderr);
X	fputs(string, stderr);
X	fputs("'  ", stderr);
X	switch (code) {
X		case ERRNTTY:
X			fputs("(not connected to a tty)\n", stderr);
X			break;
X		case ERRSEP:
X			fputs("(missing separator between values)\n", stderr);
X			break;
X		case ERRRNG1:
X			fputs("('-' appears twice in range)\n", stderr);
X			break;
X		case ERRRNG2:
X			fputs("('-' does not follow value)\n", stderr);
X			break;
X		case ERRNUL:
X			fputs("(null value)\n", stderr);
X			break;
X		case ERRMIN:
X			fputs("(value below minimum)\n", stderr);
X			break;
X		case ERRMAX:
X			fputs("(value exceeds maximum)\n", stderr);
X			break;
X		case ERRBAD:
X			fputs("(invalid character)\n", stderr);
X			break;
X		default:
X			fputs("(indeterminant error)\n", stderr);
X			break;
X	}
X	exit(0);
X}
X
X/* Given a string containing usernames, check them against the current
X   user's name.  If there is a match, set the global 'user' to TRUE
X   otherwise set 'user' to FALSE.  In addition to a direct match, 'user'
X   may be set to TRUE by '*'.  Further, '!' may be used to negate its
X   meaning.  Return FALSE if no errors, error number otherwise.
X*/
X
Xstatic int userchk(string)
Xchar *string;
X{
X	register char *cp, *s1;
X	register BOOL seen = FALSE;
X	register BOOL negate = FALSE;
X	struct passwd *passwd;
X
X	if (!name) {
X		if (!(passwd = getpwuid(geteuid())))
X			error("could not access passwd entry", "", 0);
X		name = passwd->pw_name;
X		namesz = strlen(name);
X#ifdef	DEBUG
X		fputs("My username is:  ", stderr);
X		fputs(name, stderr);
X		fputs("\n", stderr);
X#endif	DEBUG
X	}
X	user = FALSE;
X	for (cp = string; ; ) switch (*cp) {
X		case ' ':	/* whitespace terminates immediately */
X		case '\t':
X		case '#':
X		case '\n':
X			return (FALSE);
X		case '!':	/* negation requested */
X		case '^':
X			/* be sure nothing yet specified */
X			if (seen) return (ERRSEP);
X			negate = !negate;
X			++cp;
X			break;
X		case '*':	/* every username */
X		case '@':
X			/* be sure nothing yet specified */
X			if (seen) return (ERRSEP);
X			seen = TRUE;
X			user = !negate;
X			++cp;
X			break;
X		case '\000':	/* string/specification end */
X		case ',':	/* specification end */
X			/* be sure value was found */
X			if (!seen) return (ERRNUL);
X			if (!*cp) return (FALSE);
X			seen = FALSE;
X			negate = FALSE;
X			++cp;
X			break;
X		default:	/* user specification found */
X			/* be sure nothing yet specified */
X			if (seen) return (ERRSEP);
X			seen = TRUE;
X			s1 = cp;
X			/* find the end of the specification */
X			while (*cp && !strchr(" \t#\n!^*@,", *cp)) ++cp;
X			/* see if it doesn't match by definition */
X			if ((cp - s1) != namesz) break;
X			/* see if it doesn't match */
X			if (strncmp(name, s1, namesz)) break;
X			user = !negate;
X			break;
X	}
X}
X
X/* Given a string containing the trailing portion of a ttyname, check
X   it against the current tty's name.  If it matches, set the global
X   'tty' to TRUE.  The first time ttychk() is called within an overall
X   specification, 'tty' is set to FALSE.  It may be set to TRUE by '*'
X   at any time.  Further, '!' may be used to negate its meaning.  Return
X   FALSE if no errors, error number otherwise.
X*/
X
Xstatic int ttychk(string)
Xchar *string;
X{
X	register char *cp, *s1, *s2;
X	register BOOL seen = FALSE;
X	register BOOL negate = FALSE;
X
X	if (!ttynm) {
X		if (!(ttynm = ttyname(0))) return (ERRNTTY);
X		ttysz = strlen(ttynm);
X#ifdef	DEBUG
X		fputs("My ttyname is:  ", stderr);
X		fputs(ttynm, stderr);
X		fputs("\n", stderr);
X#endif	DEBUG
X	}
X	if (tty == UNSET) tty = FALSE;
X	cp = string + 1;			/* skip first byte */
X	for (;;) switch (*cp) {
X		case ' ':	/* skip whitespace */
X		case '\t':
X			++cp;
X			break;
X		case '!':	/* negation requested */
X		case '^':
X			/* be sure nothing yet specified */
X			if (seen) return (ERRSEP);
X			negate = !negate;
X			++cp;
X			break;
X		case '*':	/* every ttyname */
X		case '@':
X			/* be sure nothing yet specified */
X			if (seen) return (ERRSEP);
X			seen = TRUE;
X			tty = !negate;
X			++cp;
X			break;
X		case '\000':	/* string/specification end */
X		case ',':	/* specification end */
X			/* be sure value was found */
X			if (!seen) return (ERRNUL);
X			if (!*cp) return (FALSE);
X			seen = FALSE;
X			negate = FALSE;
X			++cp;
X			break;
X		case '-':	/* '-' does not follow value */
X			return (ERRRNG2);
X		default:	/* tty specification found */
X			/* be sure nothing yet specified */
X			if (seen) return (ERRSEP);
X			seen = TRUE;
X			s1 = cp;
X			/* find the end of the specification */
X			while (*cp && !strchr(" \t!^*@,-", *cp)) ++cp;
X			s2 = ttynm + ttysz - (cp - s1);
X			/* see if it doesn't match by definition */
X			if (s2 < ttynm) break;
X			/* check for simple non-range specification */
X			if (*cp != '-') {
X				/* see if it doesn't match */
X				if (strncmp(s2, s1, cp - s1)) break;
X				tty = !negate;
X				break;
X			}
X			/* see if < start of range */
X			if (strncmp(s2, s1, cp - s1) < 0) {
X				/* it is, skip past range limit */
X				++cp;
X				while (*cp && !strchr(" \t!^*@,-", *cp)) ++cp;
X				if (*cp == '-') return (ERRRNG1);
X				break;
X			}
X			/* find the start and end of range limit */
X			s1 = ++cp;
X			while (*cp && !strchr(" \t!^*@,-", *cp)) ++cp;
X			if (*cp == '-') return (ERRRNG1);
X			s2 = ttynm + ttysz - (cp - s1);
X			/* see if it doesn't match by definition */
X			if (s2 < ttynm) break;
X			/* see if > end of range */
X			if (strncmp(s2, s1, cp - s1) > 0) break;
X			tty = !negate;
X			break;
X	}
X}
X
X/* Extract a positive, integer value from the string pointed to by
X   'cp' into the int pointed to by 'pnum'.  Return the first non-digit
X   position in 'cp'.
X*/
X
Xstatic char *getnum(cp, pnum)
Xchar *cp;
Xint *pnum;
X{
X	register int num = 0;
X
X	while (isdigit(*cp)) num = num * 10 + (*cp++ - '0');
X	*pnum = num;
X	return (cp);
X}
X
X/* Given a string containing numeric values and ranges separated by
X   commas - generate a truth vector between 0 and 'max'-1 for those
X   values which are specified.  Example:  "2,4,6-10,14".  The first
X   time expand() is called for a given vector, all values are set to
X   FALSE.  Thereafter, new values are added to the existing vector.
X   Note:  any single number or range may be preceeded by '!' to negate
X   its meaning (set the specified values to FALSE).  An example might
X   be "*,!9-17".  Further note:  the first byte of the specified string
X   is ignored.  Return FALSE if no errors, error number otherwise.
X*/
X
Xstatic int expand(string, vector, minimum, maximum)
Xchar *string;
XBOOL vector[];
Xint minimum, maximum;
X{
X	register char *cp;
X	register int i;
X	register int range = -1;
X	register BOOL negate = FALSE;
X	int num = -1;
X
X	if (vector[0] == UNSET)
X		for (i = 0; i <= maximum; ++i) vector[i] = FALSE;
X	cp = string + 1;	/* skip first byte */
X	for (;;) switch (*cp) {
X		case ' ':	/* skip whitespace */
X		case '\t':
X			++cp;
X			break;
X		case '!':	/* negation requested */
X		case '^':
X			/* be sure value is new */
X			if (num >= 0) return (ERRSEP);
X			negate = !negate;
X			++cp;
X			break;
X		case '*':	/* full range */
X		case '@':
X			/* be sure value is new */
X			if (num >= 0) return (ERRSEP);
X			/* check for range started */
X			if (range >= 0) {
X				num = maximum;
X				++cp;
X				break;
X			}
X			/* no range yet */
X			++cp;
X			while (*cp == ' ' || *cp == '\t') ++cp;
X			/* check for range continued */
X			if (*cp == '-') {
X				num = minimum;
X			} else {
X				range = minimum;
X				num = maximum;
X			}
X			break;
X		case '0':	/* value found */
X		case '1':
X		case '2':
X		case '3':
X		case '4':
X		case '5':
X		case '6':
X		case '7':
X		case '8':
X		case '9':
X			/* be sure value is new */
X			if (num >= 0) return (ERRSEP);
X			cp = getnum(cp, &num);
X			break;
X		case '-':	/* range specified */
X			/* be sure range is new */
X			if (range >= 0) return (ERRRNG1);
X			/* be sure value was found */
X			if (num < 0) return (ERRRNG2);
X			range = num;	/* save first value */
X			num = -1;
X			++cp;
X			break;
X		case '\000':	/* string/specification end */
X		case ',':	/* specification end */
X			/* be sure value was found */
X			if (num < 0) return (ERRNUL);
X			/* range of one if not specified */
X			if (range < 0) range = num;
X			/* set range as increasing if needed */
X			if (range > num) {
X				i = num;
X				num = range;
X				range = i;
X			}
X			/* check against minimum */
X			if (num < minimum) return (ERRMIN);
X			/* check against maximum */
X			if (range > maximum) return (ERRMAX);
X			/* update truth vector */
X			for (i = range; i <= num; ++i)
X				vector[i] = !negate;
X			negate = FALSE;
X			/* specification done */
X			num = range = -1;
X			if (!*cp) return (FALSE);
X			++cp;
X			break;
X		default:
X			return (ERRBAD);
X	}
X}
X
X#ifdef	DEBUG
X
X/* Display the specified string followed by a dump of the specified
X   truth vector.
X*/
X
Xstatic void dump(string, vector, minimum, maximum)
Xchar *string;
XBOOL vector[];
Xint minimum, maximum;
X{
X	register int i;
X
X	if (vector[0] == UNSET) return;
X	fputs(string, stderr);
X	for (i = minimum; i <= maximum; ++i)
X		if (vector[i])
X			fputc('T', stderr);
X		else
X			fputc('-', stderr);
X	fputc('\n', stderr);
X}
X
X#endif	DEBUG
X
X/* Evaluate a specific series of time specifications relative to the
X   current time.  Return TRUE if current time is within them.  Update
X   count to reflect the number of specs processed.
X*/
X
Xstatic BOOL within(specs, count)
Xchar **specs;
Xint *count;
X{
X	register int i, code;
X
X	tty = UNSET;
X	for (i = 0; i < 60; ++i) min[i]  = UNSET;
X	for (i = 0; i < 24; ++i) hour[i] = UNSET;
X	for (i = 0; i < 32; ++i) mday[i] = UNSET;
X	for (i = 0; i < 13; ++i) mon[i]  = UNSET;
X	for (i = 0; i < 99; ++i) year[i] = UNSET;
X	for (i = 0; i <  8; ++i) wday[i] = UNSET;
X	*count = 0;
X	if (**specs == '-') ++*specs;
X	do {
X#ifdef	DEBUG
X		fputs("processing specification:  ", stderr);
X		fputs(*specs, stderr);
X		fputc('\n', stderr);
X#endif	DEBUG
X		switch (**specs) {
X			case TTY:
X				if (code = ttychk(*specs))
X					error("tty", *specs, code);
X				break;
X			case MIN:
X				if (code = expand(*specs, min, 0, 59))
X					error("minute", *specs, code);
X				break;
X			case HOUR:
X				if (code = expand(*specs, hour, 0, 23))
X					error("hour", *specs, code);
X				break;
X			case MDAY:
X				if (code = expand(*specs, mday, 1, 31))
X					error("day of month", *specs, code);
X				break;
X			case MON:
X				if (code = expand(*specs, mon, 1, 12))
X					error("month", *specs, code);
X				break;
X			case YEAR:
X				if (code = expand(*specs, year, 87, 99))
X					error("year", *specs, code);
X				break;
X			case WDAY:
X				if (code = expand(*specs, wday, 0, 7))
X					error("day of week", *specs, code);
X				if (wday[0] || wday[7])
X					wday[0] = wday[7] = TRUE;
X				break;
X			default:
X				error("invalid specification:  ", *specs, 0);
X		}
X		++specs;
X		++*count;
X		if (**specs == '-') ++*specs;
X	} while (**specs != OR && **specs != PATH);
X	if (**specs == OR) {
X		if (*(*specs+1) && (*(*specs+1) != 'r' || *(*specs+2)))
X			error("invalid specification:  ", *specs, 0);
X		++*count;
X	}
X
X#ifdef	DEBUG
X	fputs("acceptible restrictions are...\n", stderr);
X	dump("  minutes:        ", min, 0, 59);
X	dump("  hours:          ", hour, 0, 23);
X	dump("  days of week:   ", wday, 0, 6);
X	dump("  days of month:  ", mday, 1, 31);
X	dump("  months:         ", mon, 1, 12);
X	dump("  years:          ", year, 87, 99);
X	if (tty != UNSET) {
X		fputs("(also no restriction on tty '", stderr);
X		fputs(ttynm, stderr);
X		if (tty) fputs("' - PASSED)\n", stderr);
X		else fputs("' - FAILED)\n", stderr);
X	}
X	fputc('\n', stderr);
X#endif	DEBUG
X
X	return (tty && min[timenow->tm_min] && hour[timenow->tm_hour] &&
X		wday[timenow->tm_wday] && mday[timenow->tm_mday] &&
X		mon[timenow->tm_mon] && year[timenow->tm_year]);
X}
X
Xvoid main(argc, argv, environ)
Xint argc;
Xchar **argv, *environ[];
X{
X	register int i;
X	register BOOL runable = FALSE;
X	register char *cp;
X	BOOL testing = **argv != '-';
X	int count;
X	time_t clock;
X	FILE *acfp;
X	char buf[MAXENT];
X	char *fargv[MAXARGS];
X	char shell[MAXSH];
X
X	(void) time(&clock);
X	timenow = localtime(&clock);
X	++(timenow->tm_mon);
X
X#ifdef	DEBUG
X	min[timenow->tm_min] = TRUE;
X	hour[timenow->tm_hour] = TRUE;
X	wday[timenow->tm_wday] = TRUE;
X	mday[timenow->tm_mday] = TRUE;
X	mon[timenow->tm_mon] = TRUE;
X	year[timenow->tm_year] = TRUE;
X
X	fputs("time now is...\n", stderr);
X	dump("  minute:         ", min, 0, 59);
X	dump("  hour:           ", hour, 0, 23);
X	dump("  day of week:    ", wday, 0, 6);
X	dump("  day of month:   ", mday, 1, 31);
X	dump("  month:          ", mon, 1, 12);
X	dump("  year:           ", year, 87, 99);
X	fputc('\n', stderr);
X#endif	DEBUG
X
X	if (argc < 2) {
X		if (!(acfp = fopen(ACFILE, "r")))
X			error("could not open control file:  ", ACFILE, 0);
X		/* find this user's entry in the control file */
X		while (!user && fgets(buf, MAXENT, acfp))
X			if (i = userchk(buf)) {
X				for (cp = buf; *cp && !strchr(" \t#\n", *cp);
X					++cp) ;
X				*cp = '\000';
X				error("user", buf, i);
X			}
X		if (!user) error(name, " not in control file", 0);
X		/* find the end of the first line */
X		for (cp = buf; *cp && !strchr("#\n", *cp); ++cp) ;
X		*cp = '\000';
X		/* append continuation lines */
X		while (count = MAXENT - (cp - buf)) {
X			i = fgetc(acfp);
X			if (!strchr(" \t#\n", i)) break;
X			(void) ungetc(i, acfp);
X			if (!fgets(cp, count, acfp)) break;
X			while (*cp && !strchr("#\n", *cp)) ++cp;
X			*cp = '\000';
X		}
X		(void) fclose(acfp);
X#ifdef	DEBUG
X		fputs("control entry:  ", stderr);
X		fputs(buf, stderr);
X		fputc('\n', stderr);
X#endif	DEBUG
X		argc = 0;
X		argv = fargv;
X		argv[argc++] = cp = buf;
X		/* process arguments */
X		while (*cp) {
X			/* scan past last argument */
X			while (*cp && *cp != ' ' && *cp != '\t') ++cp;
X			/* terminate it with null */
X			if (*cp) *cp++ = '\000';
X			/* find the next argument */
X			while (*cp && (*cp == ' ' || *cp == '\t')) ++cp;
X			if (*cp) {
X				/* found one, enter it into argv */
X				if (argc == MAXARGS)
X					error("too many arguments ",
X						"in control file", 0);
X				argv[argc++] = cp;
X			}
X		}
X		argv[argc] = NULL;
X#ifdef	DEBUG
X		for (i = 1; i < argc; ++i) {
X			fputs("found specification:  ", stderr);
X			fputs(argv[i], stderr);
X			fputs("\n", stderr);
X		}
X		fputs("\n", stderr);
X#endif	DEBUG
X	}
X
X	for (i = 1; i < argc; ++i) if (*argv[i] == PATH) break;
X	if (i == argc)
X		error("full pathname to exec() required", "", 0);
X
X	if (**(++argv) == PATH) runable = TRUE;
X	else do {
X		runable |= within(argv, &count);
X		argv += count;
X	} while (**argv != PATH);
X
X	if (runable) {
X#ifdef	DEBUG
X		fputs("\ntime and tty IS within specifications\n", stderr);
X#endif	DEBUG
X		/* set the shell buffer to be "SHELL=pathname" */
X		(void) strcpy(shell, "SHELL=");
X		(void) strcat(shell, *argv);
X		/* fix argv[0] to point to '-progname' */
X		if (cp = strrchr(*argv, '/')) {
X			*cp = '-';
X			(void) strcpy(*argv, cp);
X		}
X		/* set cp to point to the pathname to execute */
X		cp = shell + 6;
X		/* find and fix the SHELL environment variable */
X		for (i = 0; environ[i]; ++i) {
X			if (!strncmp(environ[i], "SHELL=", 6)) {
X				environ[i] = shell;
X				break;
X			}
X		}
X		if (testing) {
X			fputs(cp, stderr);
X			fputs(": ", stderr);
X			for (i = 0; argv[i]; ++i) {
X				fputs(" \"", stderr);
X				fputs(argv[i], stderr);
X				fputc('"', stderr);
X			}
X			fputc('\n', stderr);
X			exit(0);
X		}
X		(void) execve(cp, argv, environ);
X		error("could not exec() ", cp, 0);
X	} else {
X#ifdef	DEBUG
X		fputs("\ntime or tty is NOT within specifications\n", stderr);
X#endif	DEBUG
X		fputs("\n*** your access ", stderr);
X		if (ttynm) fputs("(on this tty port) ", stderr);
X		fputs("is not permitted at this time ***\n", stderr);
X	}
X}
SHAR_EOF
fi
if test -f 'access.man'
then
	echo shar: "will not over-write existing file 'access.man'"
else
sed 's/^X//' << \SHAR_EOF > 'access.man'
X.TH ACCESS l "19 June 1988"
X.SH NAME
Xaccess \- limit system usage to specified ttys and times
X.SH SYNTAX
X.B "/usr/local/etc/access"
X.SH DESCRIPTION
X.I Access
Xprovides a simple yet powerful method of limiting user access to a
Xsystem.  Simply specify
X.B /usr/local/etc/access
Xin place of a login shell in the password file for each user who is to
Xhave restricted accessibility to a system.
X.I Access
Xwill lookup that user's entry in the control file
X.B /usr/local/lib/access
Xand execute the specified real login shell (with optional arguments) if
Xthe current tty and time falls within one or more of the access
Xconstraints given.
X.PP
XEach entry in the control file has the form:
X.PP
Xuserspec [constraint1] [...[-]o[r] constraintN]
X.br
X	full_pathname [args]
X.PP
XBlank lines are ignored, while `#' marks the start of a comment which
Xcontinues to the end of the line.  Entries may be continued by indenting
Xcontinuation lines with whitespace (blanks or tabs).  Entries are
Xlimited to a maximum of 1k characters.
X.PP
XAccessibility constraints limit the tty port and/or up to 6
Xaccessibility classes of time.  Accessibility constraints are composed
Xof multiple specifications separated by spaces and/or tabs.  Additional
Xconstraints may be specified by connecting them with the
X.B OR
Xoperator (see below).
X.PP
XEach specification within an accessibility constraint is composed of a
Xletter (optionally preceded by `-') followed by the specification.  They
Xare as follows:
X.PP
X.nf
X.ta 0.8i 1.6i 2.4i
X	class	range	description
X	-----	-----	-----------
X	`t'		tty name restrictor
X	`m'	0-59	minute restrictor
X	`h'	0-23	hour restrictor
X	`w'	0-6	day of week restrictor (0 = Sunday)
X	`D'	1-31	day of month restrictor
X	`M'	1-12	month restrictor
X	`Y'	87-99	year restrictor
X	`o[r]'		logical OR separator
X.fi
X.PP
XWhere an accessibility class is not specified within an accessibility
Xconstraint, no restriction will be applied to that class.  For instance,
Xif `m' does not appear within an accessibility constraint then
Xaccessibility will not be constrained by the current minute.
X.PP
XNumeric accessibility classes are specified with single values or a
Xrange of values separated by `-'.  Multiple specifications may be given
Xat one time separated by `,'.  Any given class may appear more than
Xonce.  The sense of any specific specification may be negated by `!'
X(or `^').  Further, `*' (or `@') may be used to indicate infinity.  For
Xexample, each of the following are equivalent:  "h0-23", "h*-23",
X"h0-*", "h*-*", "h*", "h0,1,2-10 h11-*".
X.PP
XThe tty accessibility class is composed of one or more string segments,
Xone of which must match the tail of the current tty's name.  As with the
Xnumeric accessibility classes, "-,!^*@" are all supported.
X.PP
XThe first entry in the control file, in which the current user's name is
Xmatched within the "userspec", will be the entry processed for that
Xuser.  The "userspec" is composed of one or more full usernames, one of
Xwhich must exactly match the current user's name.  As with the numeric
Xand tty accessibility classes, ",!^*@" are all supported.  Note that
Xranges specified by `-' are NOT supported in the "userspec".
X.SH EXAMPLES
Xjoe h9-17 /bin/csh
X.IP "" 5
XAllow `joe' access only between 9AM and 5PM.
X.PP
Xuucp,net ttya7 w1-5 h*,!9-17 or ttya0-ab w0,6
X.br
X	/usr/lib/uucp/uucico
X.IP "" 5
XAllow `uucp' and `net' access on (/dev/t)tya7 weekdays except between
X9AM and 5PM
X.B or
Xon (/dev/t)tya0 thru (/dev/tty)ab (anytime) on weekends.  Note the "*"
Xis required in the hour specification.  Otherwise, since "h" appeared -
Xhours would be constrained, no unrestricted hours specified, then
Xfurther constrained to not be between 9 and 17.  In other words no hour
Xwould be acceptable and only the second accessibility constraint could
Xpossibly pass.
X.PP
X*,!root Y*-87 t*,!console or M*-9 Y88 t*,!console /bin/sh
X.IP "" 5
XAllow everyone access except `root' thru September 1988 on any tty
Xexcept consoles.  Note that if
X.I access
Xis specified as `root's login shell as this example implies, then `root'
Xmust have an entry somewhere following this example entry.  Alternately,
X`root' may have an entry before this one in which event the ",!root"
Xwould be extraneous since
X.I access
Xwould never get this far for user `root'.
X.PP
Xuucp /usr/lib/uucp/uucico
X.br
X*    /bin/csh
X.IP "" 5
XGive `uucp' unrestricted access to `uucico' and everyone else
Xunrestricted access to the `csh'.  Effectively,
X.I access
Xwould not be doing anything.  This would be one method of removing
Xaccess constraints for everyone without changing the password file.
X.SH TESTING
XIf
X.I access
Xis invoked from a shell then the access constraints will be derived from
Xits arguments (do not specify the `userspec').  In place of actually
Xexecuting the real login shell specified, that program's name and its
Xargument vector will be displayed.
X.SH INSTALLATION
XBe sure to disable users ability to change their login shell and thereby
Xdefeat this control.  For example, "chmod o-x /usr/ucb/chsh".
X.SH "SEE ALSO"
XFiles:  /etc/passwd and /usr/local/lib/access
X.SH DIAGNOSTICS
XIf at least one accessibility constraint passes, the presence of
X.I access
Xis invisible.  If no accessibility constraint passes, then a message is
Xissued and the user immediately logged off.
X.SH AUTHOR
XGeorge M. Sipe currently (7/88) at rebel!george
SHAR_EOF
fi
exit 0
#	End of shell archive
-- 
George M. Sipe,		Phone: (404) 662-1533
Tolerant Systems, 6961 Peachtree Industrial, Norcross, GA  30071
UUCP: ...!{decvax,hplabs,linus,rutgers,seismo}!gatech!rebel!george