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