home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-07-06 | 71.7 KB | 2,702 lines |
- Newsgroups: comp.sources.unix
- From: will@surya.caltech.edu (Will Deich)
- Subject: v26i292: super-2.0 - allow certain users to use certain commands as root, Part01/01
- Sender: unix-sources-moderator@gw.home.vix.com
- Approved: vixie@gw.home.vix.com
-
- Submitted-By: will@surya.caltech.edu (Will Deich)
- Posting-Number: Volume 26, Issue 292
- Archive-Name: super-2.0/part01
-
- Super(1) is a small program that allows users to execute other programs
- (particularly scripts) as root, without unduly compromising security.
-
- Sample uses:
- - to call a script that allows users to use mount(8) on
- cdrom's or floppy disks, but not other devices.
-
- - to call a script that allows users to send STOP/CONT
- signals to certain jobs, but not others.
-
- - to restrict which users, on which hosts, may execute a
- setuid-root program.
-
- -------------------
-
- What's new for version 2.0:
- A. A couple of bugfixes. (These fixes were first introduced
- in version 1.2.)
-
- B. You can restrict commands to particular users on particular
- hosts. This allows one "super.tab" file to serve many hosts.
-
- C. Entries in "super.tab" can now span multiple lines. Helpful
- when one file serves many users + hosts.
-
- D. csh-style brace-expansion: super's pattern-matching previously
- was done with the BSD 4.x regex routines. This is now extended
- allow csh-style braces. For instance, to allow users pam and
- to execute a command from hosts alpha and beta, you can
- use an entry like {pam,sammy}@{alpha,beta}
-
- -------------------
-
- A "super.tab" file names each command that super is willing to execute, and
- says who can use it. It contains lines like:
-
- command fullpathname valid-user/group/host ed-type patterns
-
- To execute a super command, type
-
- % super command [args...]
-
- If <command> is "-h" or "-?", or missing, super prints its current
- list of allowed commands, but nothing is executed.
-
- If a user is allowed to execute a given <command>, the <fullpathname>
- is exec'd, with <command> as argv[0]. The superuser is always
- allowed to execute any super command.
-
- For security, the environment variables are discarded, save for TERM,
- LINES, and COLUMNS. If TERM contains any characters other than
- [a-z][A-Z][0-9]_, it is discarded. If LINES or COLUMNS contains any
- characters other than [0-9], they are discarded. To these are added
- reasonable values for IFS, PATH, USER and HOME (USER and HOME are set
- to the username and login directory, respectively, of the person who
- who runs super). LOGNAME is set to the same as USER. SUPERCMD is set
- to the <command>. All descriptors excepting 0,1,2 are closed. Signals
- are all reset to have default handling.
-
- --------------------
-
- /* Will Deich
- * Caltech 105-24, Pasadena, CA 91125
- * Internet: will@surya.caltech.edu
- */
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 1 (of 1)."
- # Contents: MANIFEST Makefile README approve.c braces.c getlogdir.c
- # ingroup.c re_fail.c regex.c sample.cdmount sample.tab super.1
- # super.c version.h word.c
- # Wrapped by vixie@gw.home.vix.com on Wed Jul 7 01:39:17 1993
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'MANIFEST' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'MANIFEST'\"
- else
- echo shar: Extracting \"'MANIFEST'\" \(579 characters\)
- sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
- X File Name Archive # Description
- X-----------------------------------------------------------
- X MANIFEST 1 This shipping list
- X Makefile 1
- X README 1
- X approve.c 1
- X braces.c 1
- X getlogdir.c 1
- X ingroup.c 1
- X re_fail.c 1
- X regex.c 1
- X sample.cdmount 1
- X sample.tab 1
- X super.1 1
- X super.c 1
- X version.h 1
- X word.c 1
- END_OF_FILE
- if test 579 -ne `wc -c <'MANIFEST'`; then
- echo shar: \"'MANIFEST'\" unpacked with wrong size!
- fi
- # end of 'MANIFEST'
- fi
- if test -f 'Makefile' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'Makefile'\"
- else
- echo shar: Extracting \"'Makefile'\" \(1484 characters\)
- sed "s/^X//" >'Makefile' <<'END_OF_FILE'
- XBINDIR=/usr/local/bin
- XMANDIR=/usr/man/manl
- XMANEXT=l
- X
- X# Things to set or unset in CFLAGS:
- X
- X# 1. USE_NETGROUP: if you want hostnames like `+xxx' to mean NIS netgroup xxx,
- X# define USE_NETGROUP in CFLAGS; otherwise comment it out.
- X
- X# 2. MAXFD: if you don't have the system call getdtablesize() to return the
- X# number of file descriptors, define
- X# MAXFD=<expression to give max value of a file descr>.
- X# If MAXFD not defined, getdtablesize() is used.
- X# Examples: -DMAXFD=63 -- hardwired for 64 max descriptors;
- X# -DMAXFD="sysconf(_SC_OPEN_MAX)-1" -- HP-UX 8.x, 9.x
- X
- X# 3. SUPERFILE: if you want the superfile to be other than
- X# /usr/local/lib/super.tab, add -DSUPERFILE=FullPathName
- XCFLAGS= -O -DUSE_NETGROUP # -DMAXFD=63 -DSUPERFILE=/etc/super.tab
- X
- X# SunOS: Link statically to avoid possible security holes in dynamic linking.
- X# Most other OS's: comment out next line, or use equivalent.
- XLDFLAGS=-Bstatic
- X
- X####################################################################
- X
- XSRC= super.c approve.c ingroup.c word.c getlogdir.c regex.c re_fail.c braces.c version.h
- XOBJ= super.o approve.o ingroup.o word.o getlogdir.o regex.o re_fail.o braces.o
- XALL= README Makefile super.1 sample.tab sample.cdmount $(SRC)
- X
- Xall: super
- X
- Xsuper: $(OBJ)
- X $(CC) $(LDFLAGS) -o super $(OBJ)
- X
- Xapprove.o: version.h
- X $(CC) $(CFLAGS) -c approve.c
- X
- Xinstall:
- X cp super $(BINDIR)
- X chmod 04755 $(BINDIR)/super
- X cp super.1 $(MANDIR)/super.$(MANEXT)
- X
- Xclean:
- X rm -f super *.o
- Xshar: $(ALL)
- X shar $(ALL) > super.shar
- END_OF_FILE
- if test 1484 -ne `wc -c <'Makefile'`; then
- echo shar: \"'Makefile'\" unpacked with wrong size!
- fi
- # end of 'Makefile'
- fi
- if test -f 'README' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'README'\"
- else
- echo shar: Extracting \"'README'\" \(5308 characters\)
- sed "s/^X//" >'README' <<'END_OF_FILE'
- XSuper(1) is a small program that allows users to execute other programs
- X(particularly scripts) as root, without unduly compromising security.
- X
- XSample uses:
- X - to call a script that allows users to use mount(8) on
- X cdrom's or floppy disks, but not other devices.
- X
- X - to call a script that allows users to send STOP/CONT
- X signals to certain jobs, but not others.
- X
- X - to restrict which users, on which hosts, may execute a
- X setuid-root program.
- X
- X-------------------
- XWhat's new for version 2.0:
- X A. A couple of bugfixes. (These fixes were first introduced
- X in version 1.2.)
- X
- X B. You can restrict commands to particular users on particular
- X hosts. This allows one "super.tab" file to serve many hosts.
- X
- X C. Entries in "super.tab" can now span multiple lines. Helpful
- X when one file serves many users + hosts.
- X
- X D. csh-style brace-expansion: super's pattern-matching previously
- X was done with the BSD 4.x regex routines. This is now extended
- X allow csh-style braces. For instance, to allow users
- X pam and sammy, executing from hosts alpha and beta, you can
- X use an entry like {pam,sammy}@{alpha,beta}
- X
- X-------------------
- X
- XA "super.tab" file names each command that super is willing to execute, and
- Xsays who can use it. It contains lines like:
- X
- X command fullpathname valid-user/group/host ed-type patterns
- X
- XTo execute a super command, type
- X
- X % super command [args...]
- X
- XIf <command> is "-h" or "-?", or missing, super prints its current
- Xlist of allowed commands, but nothing is executed.
- X
- XIf a user is allowed to execute a given <command>, the <fullpathname>
- Xis exec'd, with <command> as argv[0]. The superuser is always
- Xallowed to execute any super command.
- X
- XFor security, the environment variables are discarded, save for TERM,
- XLINES, and COLUMNS. If TERM contains any characters other than
- X[a-z][A-Z][0-9]_, it is discarded. If LINES or COLUMNS contains any
- Xcharacters other than [0-9], they are discarded. To these are added
- Xreasonable values for IFS, PATH, USER and HOME (USER and HOME are set
- Xto the username and login directory, respectively, of the person who
- Xwho runs super). LOGNAME is set to the same as USER. SUPERCMD is set
- Xto the <command>. All descriptors excepting 0,1,2 are closed. Signals
- Xare all reset to have default handling.
- X
- X--------------------
- X
- XAcknowledgements:
- X For the regular-expression code: Ozan Yigit's regex routines,
- X plus the BSD brace-expansion code.
- X
- X--------------------
- X
- XMaking and installation:
- X
- XThe Makefile is only about 40 lines long. Modify it to suit yourself.
- XYou have to be root to install super, as it must run setuid root.
- X
- XMany SysV-derived Unix's don't have the getdtablesize() routine which
- Xreturns the maximum value allowed for a file descriptor. In that case,
- Xuse -DMAXFD=nnn when compiling, and it will be used instead of getdtablesize().
- X
- XBy default, super expects to find its table of valid commands+users/groups/hosts
- Xin /usr/local/lib/super.tab. If you change this, you must also change the
- Xdocumentation.
- X
- XA sample super.tab file is found in sample.tab.
- X
- XOne sample script is enclosed: sample.cdmount, to mount cd-rom's under SunOS.
- X
- X--------------------
- XTesting the entries in super.tab:
- X
- XYou can test if super is restricting users properly by putting a
- Xharmless entry like
- X
- X ECHO /bin/echo <a user/group/host pattern to test>
- X
- Xin your super.tab file, then typing
- X
- X % super ECHO arg1 arg2 arg3
- X
- X--------------------
- XNotes on super scripts:
- X
- X1. Scripts run via super(1) must start "#!/bin/sh" (or whatever interpreter
- X is being used).
- X
- X2. It's nice to be able to type something like
- X % cdmount
- X instead of
- X % super cdmount
- X
- X You can make your script automatically execute super on
- X itself by starting a script in the following manner:
- X
- X #!/bin/sh
- X prog=`basename $0`
- X test "$SUPERCMD" = "$prog" || exec /usr/local/bin/super $prog "$@"
- X
- X
- X3. Some variants of csh will not run setuid scripts unless the -b flag
- X (force a "break" from option processing) is set:
- X #!/bin/csh -fb
- X
- X4. Some programs need certain directories in the path. Your
- X super scripts may have to add directories like /etc or /usr/etc
- X to make commands work. One common problem for SunOS 4.x users is
- X that /usr/etc has to be in the path in order to mount HSFS-format
- X cd-rom's.
- X
- X
- X5. Super only changes the effective uid. Some programs (e.g. exportfs
- X under SunOS 4.1.x) require the real uid to be root. In that case,
- X your super script will have to change the real uid to root before
- X executing the command.
- X
- X6. A brief security comment:
- X You must be exceedingly careful when writing scripts for super.
- X A surprising variety of seemingly-ordinary commands can, when
- X run setuid-root, be exploited to nasty purpose. Always make your
- X scripts do as little as possible, and give the user as few options
- X as possible.
- X
- X Think twice about side-effects and alternative uses of these
- X scripts. For instance, you might write a script to allow users to
- X mount cd-rom's by executing mount(8). But if you don't write it
- X carefully, a user could mount a floppy disk containing, say, a
- X setuid-root shell.
- X
- X--------------------
- X
- X/* Will Deich
- X * Caltech 105-24, Pasadena, CA 91125
- X * Internet: will@surya.caltech.edu
- X */
- END_OF_FILE
- if test 5308 -ne `wc -c <'README'`; then
- echo shar: \"'README'\" unpacked with wrong size!
- fi
- # end of 'README'
- fi
- if test -f 'approve.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'approve.c'\"
- else
- echo shar: Extracting \"'approve.c'\" \(12888 characters\)
- sed "s/^X//" >'approve.c' <<'END_OF_FILE'
- X#include <stdio.h>
- X#include <string.h>
- X#include <ctype.h>
- X#include <pwd.h>
- X
- X#include "version.h"
- X
- Xstatic char SccsId[] = "@(#)approve.c 1.9\t7/6/93" ;
- X
- X/*
- X * Copyright (c) 1993 by California Institute of Technology.
- X * Written by William Deich. Not derived from licensed software.
- X
- X * Permission is granted to anyone to use this software for any
- X * purpose on any computer system, and to redistribute it freely,
- X * subject to the following restrictions:
- X *
- X * 1. The author is not responsible for the consequences of use of this
- X * software, no matter how awful, even if they arise from defects in it.
- X *
- X * 2. The origin of this software must not be misrepresented, either
- X * by explicit claim or by omission.
- X *
- X * 3. Altered versions must be plainly marked as such, and must not
- X * be misrepresented as being the original software.
- X */
- X
- X
- Xstatic FILE *fp;
- X
- Xextern errno;
- X
- X#define MIN(a,b) ((a) < (b) ? (a) : (b))
- X#define UNTIL(e) while(!(e))
- X
- X
- Xextern char *user;
- Xextern char *prog;
- X
- X/* approve() returns:
- X * - NULL ptr if error:
- X * a) username not found;
- X * b) superfile can't be opened for reading;
- X * c) no such command as usrcmd in superfile;
- X * d) user not allowed to execute this command.
- X * - ptr to empty string if all ok, but no program should be executed.
- X * - ptr to path of file to exec, if user allowed to do so.
- X * Any error also generates a message to stderr.
- X
- X * New calls to approve() overwrite the buffer containing the returned path.
- X */
- X
- Xstruct Ebuf { /* an expandable buffer */
- X char *buf;
- X int l;
- X int nalloc;
- X};
- Xstatic struct Ebuf ebuf = { NULL, 0, 0 };
- X
- X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- X/* For printing the errors uncovered in matching user/group/host patterns */
- X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- Xchar *
- Xpaterror(errmsg, origtext, line, nl, file)
- Xchar *errmsg;
- Xchar *origtext; /* the original text with the problem pattern */
- Xint line, nl; /* the first line + number of lines in the input block */
- Xchar *file; /* the input file */
- X{
- X if (nl <= 1)
- X (void) fprintf(stderr, "%s: %s. Line %d in file `%s'\n",
- X prog, errmsg, line, file);
- X else
- X (void) fprintf(stderr, "%s: %s. Lines %d..%d in file `%s'\n",
- X prog, errmsg, line, line+nl-1, file);
- X (void) fprintf(stderr, "\tOffending text: %s\n", origtext);
- X return errmsg;
- X}
- X
- X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- X/* Copies in to out, prefixing with "^" and suffixing with "$"
- X * if these are missing.
- X */
- X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- Xvoid
- Xanchor(in, out)
- Xchar *in;
- Xchar *out;
- X{
- X int i;
- X i = (*in != '^');
- X if (i)
- X out[0] = '^';
- X (void) strcpy(out+i, in);
- X i = strlen(out);
- X if (out[i-1] != '$')
- X out[i++] = '$';
- X out[i] = '\0';
- X}
- X
- X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- X/* Grow an expandable buffer by 2K */
- X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- Xchar *
- Xgrow(cb)
- Xstruct Ebuf *cb;
- X{
- X char *realloc(), *malloc();
- X if (cb->buf)
- X cb->buf = realloc(cb->buf, cb->nalloc += 2048);
- X else
- X cb->buf = malloc(cb->nalloc += 2048);
- X
- X return cb->buf;
- X}
- X
- X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- X/* Grow buffer if less than 1K free */
- X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- Xchar *
- Xchecksize(cb)
- Xstruct Ebuf *cb;
- X{
- X if (cb->nalloc - cb->l < 1024)
- X return grow(cb);
- X else
- X return cb->buf;
- X}
- X
- X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- X/* Check if string s1 ends with string s2 */
- X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- Xchar *ends(s1, s2)
- Xchar *s1, *s2;
- X/* If s1 ends with string s2, a pointer to the ending of s1 is returned;
- X * else null
- X */
- X{
- X int l1, l2;
- X l1 = strlen(s1);
- X l2 = strlen(s2);
- X if (l1 < l2)
- X return NULL;
- X else if (strcmp(s1+l1-l2, s2) == 0)
- X return s1+l1-l2;
- X else
- X return NULL;
- X}
- X
- X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- X/* Do fgets to get one logical line: join lines that are terminated
- X * with backslash-newline. Don't discard backslash or newline (so that
- X * we can print the exact text, if desired).
- X * The result is stored in "ebuf" and a pointer to the string
- X * is returned.
- X */
- X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- Xchar *
- Xfillbuffer(fp, nl)
- XFILE *fp;
- Xint *nl; /* If non-null, number of lines read in is returned */
- X{
- X char *s;
- X ebuf.l = 0; /* clear the extensible buffer */
- X
- X /* Collect lines until we have a non-zero buffer (which happens with
- X * the first line, of course) and it isn't terminated "\\\n".
- X */
- X if (nl) *nl = 0;
- X UNTIL(ebuf.l && !(s=ends(ebuf.buf, "\\\n"))) {
- X if (!checksize(&ebuf)) {
- X /* Needed to, but couldn't increase the allocated space */
- X return NULL;
- X }
- X if (!fgets(ebuf.buf+ebuf.l, ebuf.nalloc - ebuf.l, fp))
- X return NULL;
- X if (ebuf.l != 0 && !isspace(*(ebuf.buf+ebuf.l))) {
- X (void) fprintf(stderr,
- X "%s: SUPER.TAB FORMAT ERROR -- Continued line not indented\n",
- X prog);
- X (void) fprintf(stderr,
- X "\tProblem text:\n%s\n",
- X ebuf.buf);
- X (void) exit(1);
- X }
- X ebuf.l += strlen(ebuf.buf+ebuf.l);
- X if (nl) (*nl)++;
- X }
- X return ebuf.buf;
- X}
- X
- X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- X/* Look up command "usrcmd" in file "superfile". Return path to execute
- X * if approved, empty string if no action should be taken, NULL ptr
- X * if error.
- X */
- X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- Xchar *
- Xapprove(superfile, usrcmd)
- Xchar *superfile; /* file of approved commands+users */
- Xchar *usrcmd; /* command we're to check on.
- X * If command is one of:
- X * "" | "-h" | "-?"
- X * then a list of super commands is printed
- X * instead of attempting to execute anything.
- X */
- X{
- X int uid;
- X struct passwd *usrpw;
- X /* has to be static: holds the returned path used by the caller. */
- X char *userlist, *command, *path, *tok;
- X char *wd, **wdlist;
- X int globbraces();
- X char *word(), *re_comp(), *check_ugh();
- X char *buf, *s;
- X char *sep;
- X int i, iwd, nl, line, givehelp, commandfound;
- X int gid;
- X int gethostname();
- X char hostname[1024];
- X void anchor();
- X
- X uid = getuid();
- X usrpw = getpwuid(uid);
- X if (!usrpw) {
- X (void) fprintf(stderr,
- X "%s - approve(): Couldn't get your password entry: ", prog);
- X perror("");
- X return NULL;
- X }
- X user = usrpw->pw_name;
- X gid = usrpw->pw_gid;
- X
- X if (gethostname(hostname, sizeof(hostname)) == -1) {
- X (void) fprintf(stderr, "%s: Couldn't get your hostname: ",
- X prog);
- X perror("");
- X return NULL;
- X }
- X
- X if ((fp = fopen(superfile, "r")) == NULL) {
- X (void) fprintf(stderr, "%s: Couldn't open valid-users text file `%s': ",
- X prog, superfile);
- X perror("");
- X return NULL;
- X }
- X
- X sep = " \t\v\n"; /* How to split fields on input lines */
- X
- X /* Do we just give help or really match a command with this user? */
- X givehelp = (usrcmd == NULL) ||
- X strcmp(usrcmd, "-h")==0 || strcmp(usrcmd, "-?")==0;
- X
- X if (givehelp) {
- X (void) fprintf(stderr, "%s version %s patchlevel %s\n",
- X prog, Version, Patchlevel);
- X (void) fprintf(stderr, "Use:\n\t%s command [args]\n\n", prog);
- X (void) fprintf(stderr, "%-16s %-24s %s\n",
- X "Command", "Full Path ",
- X "Valid User:Group@Host Patterns");
- X (void) fprintf(stderr, "%-16s %-24s %s\n",
- X "-------", "--------------------",
- X "------------------------------");
- X }
- X
- X commandfound = 0;
- X for (line=1; buf=fillbuffer(fp, &nl); line += nl) {
- X if (!*buf || *buf == '#' ) /* Strip comments */
- X continue;
- X buf = strtok(buf, "#"); /* Strip more comments */
- X if (re_comp("^[ \t\v\n]*$") != NULL) { /* Skip whitespace */
- X paterror("bad whitespace pattern!", buf, line, nl, superfile);
- X return NULL;
- X }
- X if (re_exec(buf) == 1)
- X continue;
- X
- X if (isspace(*(ebuf.buf))) {
- X (void) fprintf(stderr,
- X "%s: SUPER.TAB FORMAT ERROR -- Commandnames must begin in column 1\n",
- X prog);
- X (void) fprintf(stderr, "\tline %d, file %s\n", line, superfile);
- X (void) exit(1);
- X }
- X
- X userlist = word(buf, " \t\v\n", 3); /* Find the okusernames before
- X * carving up the line.
- X */
- X command = strtok(buf, sep); /* get the command... */
- X path = strtok(NULL, sep); /* ...and its full path */
- X if (!command || !path)
- X continue; /* Skip improper lines */
- X
- X if (givehelp) {
- X (void) fprintf(stderr,
- X "%-16s %-24s %s",
- X command, path, userlist);
- X continue;
- X }
- X
- X if (strcmp(command, usrcmd) != 0)
- X continue; /* Skip non-matching commands */
- X
- X commandfound++;
- X
- X /* Check our user against all valid user patterns */
- X if (uid == 0)
- X return path; /* root is always legit */
- X
- X for (wd = strtok(NULL, sep); wd; wd = strtok(NULL, sep)) {
- X if ((i=globbraces(wd, &wdlist)) != 0) {
- X char errmsg[20];
- X sprintf(errmsg, "Missing `%c'", i);
- X paterror(errmsg, buf, line, nl, superfile);
- X } else {
- X for (iwd=0; tok=wdlist[iwd]; iwd++) {
- X s = check_ugh(superfile, wd, tok, line, nl,
- X user, gid, hostname);
- X#ifdef DEBUG
- X if (s == NULL) {
- X (void) fprintf(stderr,
- X "MATCH:\n\tpath = %s\n\tpat=%s\n", path, wd);
- X return "";
- X } else {
- X (void) fprintf(stderr,
- X "user=%s gid=%d hostname=%s; pat=%s; reason: %s\n",
- X user, gid,hostname,tok, s);
- X }
- X#else
- X if (s == NULL)
- X return path;
- X#endif
- X }
- X }
- X }
- X }
- X if (givehelp)
- X return "";
- X else if (!commandfound)
- X (void) fprintf(stderr, "%s: No such super command as `%s'.\n",
- X prog, usrcmd);
- X else
- X (void) fprintf(stderr, "%s %s: Permission denied to user %s\n",
- X prog, usrcmd, user);
- X return NULL;
- X}
- X
- X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- X/* Check a single user/group/host string */
- X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- Xchar *
- Xcheck_ugh(superfile, origtext, token, line, nl, user, gid, hostname)
- Xchar *superfile; /* used just for reporting errors about this file */
- Xchar *origtext; /* original text containing token -- for err messages */
- Xchar *token; /* user/group/host to check */
- Xint line; /* line number in superfile, for reporting errors */
- Xint nl; /* number of lines (ie lines n..n+nl-1 are the present
- X * block)
- X */
- Xchar *user; /* username to check */
- Xint gid; /* gid for user */
- Xchar *hostname; /* host to check */
- X{
- X char tok[1024];
- X char chkbuf[1024];
- X int re_exec(), ingroup();
- X char *userpat, *grouppat, *hostpat;
- X int i;
- X
- X /* Strip trailing backslash that may have preceded a newline -- it was
- X * until now so that we can print, if necessary, the identical text
- X * to that in the super.tab file.
- X */
- X (void) strcpy(tok, token);
- X if ((i = strlen(tok)) && tok[i-1] == '\\') tok[i-1] = '\0';
- X
- X /* Split into user:group@host */
- X hostpat = strchr(tok, '@');
- X if (hostpat && !*(hostpat+1))
- X return paterror("Missing hostname", origtext, line, nl, superfile);
- X else if (hostpat)
- X *hostpat++ = '\0';
- X
- X#ifdef USE_NETGROUP
- X if (hostpat && *hostpat == '+') { /* Interpret as +netgroupname */
- X if (*(hostpat+1) == '\0')
- X return paterror("Missing netgroupname",
- X origtext, line, nl, superfile);
- X if (!innetgr(hostpat+1, hostname, (char *) NULL, (char *) NULL))
- X return "Not in netgroup"; /* Not in this netgroup */
- X }
- X#else
- X (void) fprintf(stderr,
- X "%s: hostnames may not begin with `+' since this super()\n", prog);
- X (void) fputs("was compiled without ``#define USE_NETGROUP''\n", stderr);
- X return 0;
- X#endif
- X
- X if (hostpat && *hostpat != '+') { /* Interpret as a hostname pattern */
- X anchor(hostpat, chkbuf); /* Force all matches to be anchored */
- X if (re_comp(chkbuf) != NULL)
- X return paterror("bad host pattern", origtext, line, nl, superfile);
- X if (re_exec(hostname) != 1)
- X return "Doesn't match hostname"; /* Didn't match hostname */
- X }
- X
- X grouppat = strchr(tok, ':');
- X userpat = tok;
- X if (grouppat && *(grouppat+1)) { /* pat is "uuu:ggg or ":ggg" */
- X if (tok == grouppat)
- X userpat = "^.*$"; /* pat is ":ggg" */
- X grouppat++;
- X } else { /* pat is "uuu" or "uuu:" */
- X if (grouppat)
- X *grouppat = '\0'; /* pat was "uuu:" */
- X grouppat = "^.*$";
- X }
- X anchor(userpat, chkbuf); /* Force all matches to be anchored */
- X if (re_comp(chkbuf) != NULL)
- X return paterror("bad user pattern", origtext, line, nl, superfile);
- X if (re_exec(user) != 1)
- X return "Doesn't match user"; /* Didn't match user */
- X
- X anchor(grouppat, chkbuf);
- X i = ingroup(user, gid, chkbuf);
- X if (i == -1)
- X return paterror("bad group pattern", origtext, line, nl, superfile);
- X else if (i != 1)
- X return "Doesn't match group";
- X
- X return NULL; /* Success! */
- X}
- END_OF_FILE
- if test 12888 -ne `wc -c <'approve.c'`; then
- echo shar: \"'approve.c'\" unpacked with wrong size!
- fi
- # end of 'approve.c'
- fi
- if test -f 'braces.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'braces.c'\"
- else
- echo shar: Extracting \"'braces.c'\" \(6387 characters\)
- sed "s/^X//" >'braces.c' <<'END_OF_FILE'
- X/*
- X * brace.c: csh brace expansion -- includes pieces of
- X * [t]csh's sh.misc.c and sh.glob.c.
- X *
- X * Modified for use as a standalone brace-globbing subroutine.
- X * - Will Deich, Mar 93.
- X */
- X
- X#include "version.h"
- Xstatic char SccsId[] = "@(#)braces.c 1.3\t4/13/93" ;
- X
- X/*-
- X * Copyright (c) 1980, 1991 The Regents of the University of California.
- X * All rights reserved.
- X *
- X * Redistribution and use in source and binary forms, with or without
- X * modification, are permitted provided that the following conditions
- X * are met:
- X * 1. Redistributions of source code must retain the above copyright
- X * notice, this list of conditions and the following disclaimer.
- X * 2. Redistributions in binary form must reproduce the above copyright
- X * notice, this list of conditions and the following disclaimer in the
- X * documentation and/or other materials provided with the distribution.
- X * 3. All advertising materials mentioning features or use of this software
- X * must display the following acknowledgement:
- X * This product includes software developed by the University of
- X * California, Berkeley and its contributors.
- X * 4. Neither the name of the University nor the names of its contributors
- X * may be used to endorse or promote products derived from this software
- X * without specific prior written permission.
- X *
- X * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- X * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- X * SUCH DAMAGE.
- X */
- X
- X#define _POSIX_SOURCE
- X
- X#include <sys/param.h>
- X#include <string.h>
- X
- Xtypedef void * ptr_t;
- X
- X#ifndef NULL
- X#define NULL 0
- X#endif
- X
- X#ifndef MAXPATHLEN
- X#define MAXPATHLEN 1024
- X#endif
- X
- Xextern void *malloc();
- Xextern void *realloc();
- Xextern void free();
- X
- X#define xmalloc malloc
- X#define xrealloc realloc
- X#define xfree free
- X
- X#define GLOBSPACE 100 /* Alloc increment */
- X
- X#define LBRC '{'
- X#define RBRC '}'
- X#define LBRK '['
- X#define RBRK ']'
- X#define EOS '\0'
- X
- Xstatic char *strsave();
- Xstatic void blkfree();
- Xstatic int blklen();
- X
- Xint
- Xglobbraces(v, blp)
- Xchar *v;
- Xchar ***blp;
- X{
- X char *s;
- X char **nv, **vl, **el;
- X int size = GLOBSPACE;
- X int glob1brace();
- X
- X
- X nv = vl = (char **) xmalloc((size_t) sizeof(char *) * size);
- X *vl++ = strsave(v);
- X *vl = NULL;
- X
- X el = vl;
- X vl = nv;
- X for (s = *vl; s; s = *++vl) {
- X char *b;
- X char **vp, **bp;
- X
- X if ((b = strchr(s, LBRC)) && b[1] != '\0' && b[1] != RBRC) {
- X char **bl;
- X int len;
- X
- X if ((len = glob1brace(s, &bl)) < 0) {
- X blkfree(nv);
- X return -len;
- X }
- X xfree((ptr_t) s);
- X if (len == 1) {
- X *vl-- = *bl;
- X xfree((ptr_t) bl);
- X continue;
- X }
- X len = blklen(bl);
- X if (&el[len] >= &nv[size]) {
- X int l, e;
- X
- X l = &el[len] - &nv[size];
- X size += GLOBSPACE > l ? GLOBSPACE : l;
- X l = vl - nv;
- X e = el - nv;
- X nv = (char **) xrealloc((ptr_t) nv, (size_t)
- X size * sizeof(char *));
- X vl = nv + l;
- X el = nv + e;
- X }
- X vp = vl--;
- X *vp = *bl;
- X len--;
- X for (bp = el; bp != vp; bp--)
- X bp[len] = *bp;
- X el += len;
- X vp++;
- X for (bp = bl + 1; *bp; *vp++ = *bp++)
- X continue;
- X xfree((ptr_t) bl);
- X }
- X }
- X *blp = nv;
- X return 0;
- X}
- X
- Xint
- Xglob1brace(s, bl)
- X char *s, ***bl;
- X{
- X char *p;
- X int i, len;
- X char *pm, *pe, *lm, *pl;
- X char **nv, **vl;
- X char gbuf[MAXPATHLEN];
- X int size = GLOBSPACE;
- X
- X nv = vl = (char **) xmalloc((size_t) sizeof(char *) * size);
- X *vl = NULL;
- X
- X len = 0;
- X /* copy part up to the brace */
- X for (lm = gbuf, p = s; *p != LBRC; *lm++ = *p++)
- X continue;
- X
- X /* check for balanced braces */
- X for (i = 0, pe = ++p; *pe; pe++)
- X if (*pe == LBRK) {
- X /* Ignore everything between [] */
- X for (++pe; *pe != RBRK && *pe != EOS; pe++)
- X continue;
- X if (*pe == EOS) {
- X blkfree(nv);
- X return (-RBRK);
- X }
- X }
- X else if (*pe == LBRC)
- X i++;
- X else if (*pe == RBRC) {
- X if (i == 0)
- X break;
- X i--;
- X }
- X
- X if (i != 0 || *pe == '\0') {
- X blkfree(nv);
- X return (-RBRC);
- X }
- X
- X for (i = 0, pl = pm = p; pm <= pe; pm++)
- X switch (*pm) {
- X case LBRK:
- X for (++pm; *pm != RBRK && *pm != EOS; pm++)
- X continue;
- X if (*pm == EOS) {
- X *vl = NULL;
- X blkfree(nv);
- X return (-RBRK);
- X }
- X break;
- X case LBRC:
- X i++;
- X break;
- X case RBRC:
- X if (i) {
- X i--;
- X break;
- X }
- X /* FALLTHROUGH */
- X case ',':
- X if (i && *pm == ',')
- X break;
- X else {
- X char savec = *pm;
- X
- X *pm = EOS;
- X (void) strcpy(lm, pl);
- X (void) strcat(gbuf, pe + 1);
- X *pm = savec;
- X *vl++ = strsave(gbuf);
- X len++;
- X pl = pm + 1;
- X if (vl == &nv[size]) {
- X size += GLOBSPACE;
- X nv = (char **) xrealloc((ptr_t) nv, (size_t)
- X size * sizeof(char *));
- X vl = &nv[size - GLOBSPACE];
- X }
- X }
- X break;
- X default:
- X break;
- X }
- X *vl = NULL;
- X *bl = nv;
- X return (len);
- X}
- X
- Xstatic char *
- Xstrsave(s)
- X register char *s;
- X{
- X char *n;
- X register char *p;
- X
- X if (s == NULL)
- X s = (char *) "";
- X for (p = (char *) s; *p++;);
- X n = p = (char *) xmalloc((size_t) ((p - s) * sizeof(char)));
- X while (*p++ = *s++);
- X return (n);
- X}
- X
- Xstatic void
- Xblkfree(av0)
- X char **av0;
- X{
- X register char **av = av0;
- X
- X if (!av0)
- X return;
- X for (; *av; av++)
- X xfree((ptr_t) * av);
- X xfree((ptr_t) av0);
- X}
- X
- Xstatic int
- Xblklen(av)
- X char **av;
- X{
- X register int i = 0;
- X
- X while (*av++)
- X i++;
- X return i;
- X}
- X
- X#ifdef TEST
- X#include <stdio.h>
- X
- Xmain(argc, argv)
- X int argc;
- X char **argv;
- X{
- X int i, status;
- X char **p;
- X
- X while (*(++argv)) {
- X status = globbraces(*argv, &p);
- X if (status == 0 - '}')
- X printf("} error\n");
- X else if (status == 0 - ']')
- X printf("] error\n");
- X else if (status < 0)
- X printf("globbraces returns %d\n", status);
- X else {
- X printf("%s expands to:\n", *argv);
- X while (*p)
- X printf("\t`%s'\t\n", *p++);
- X }
- X }
- X}
- X#endif
- END_OF_FILE
- if test 6387 -ne `wc -c <'braces.c'`; then
- echo shar: \"'braces.c'\" unpacked with wrong size!
- fi
- # end of 'braces.c'
- fi
- if test -f 'getlogdir.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'getlogdir.c'\"
- else
- echo shar: Extracting \"'getlogdir.c'\" \(934 characters\)
- sed "s/^X//" >'getlogdir.c' <<'END_OF_FILE'
- X#include <string.h>
- X#include <pwd.h>
- X
- Xstatic char SccsId[] = "@(#)getlogdir.c 1.3\t4/13/93";
- X
- X#ifndef NULL
- X#define NULL (char *) 0
- X#endif
- X
- Xint getlogdir(user, buf)
- Xchar *user;
- Xchar *buf;
- X{
- X /* Gets the login directory of the named user, and puts it into buf.
- X * If user==NULL || *user == '\0', the current user is obtained.
- X * Best if buf is MAXPATHLEN long.
- X * 0 is returned on success; -1 on error.
- X */
- X
- X struct passwd *pass;
- X char *p;
- X char *getlogin();
- X
- X buf[0] = '\0';
- X if (user != NULL && *user != '\0') {
- X /* Name given; use getpwnam */
- X pass = getpwnam(user);
- X } else if ((p = getlogin()) != NULL) {
- X /* No name given; use current login name */
- X pass = getpwnam(p);
- X } else {
- X /* No user given && getlogin() returned NULL; use current uid */
- X pass = getpwuid(getuid());
- X }
- X
- X
- X if (pass == (struct passwd *) NULL)
- X return -1;
- X
- X (void) strcpy(buf, pass->pw_dir);
- X
- X return 0;
- X}
- END_OF_FILE
- if test 934 -ne `wc -c <'getlogdir.c'`; then
- echo shar: \"'getlogdir.c'\" unpacked with wrong size!
- fi
- # end of 'getlogdir.c'
- fi
- if test -f 'ingroup.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'ingroup.c'\"
- else
- echo shar: Extracting \"'ingroup.c'\" \(1448 characters\)
- sed "s/^X//" >'ingroup.c' <<'END_OF_FILE'
- X#include <grp.h>
- X
- Xstatic char SccsId[] = "@(#)ingroup.c 1.5\t7/6/93";
- X
- X/* Use:
- X * ingroup(user, gid, gp_pat)
- X * Returns:
- X * 1 if the user is in a group matching the regex pattern gp_pat.
- X * 0 if the user isn't in a group matching the pattern.
- X * -1 if pattern failed to compile.
- X
- X * SIDE-EFFECT: uses re_comp! -- messes up caller's use of same!
- X
- X * Uses re_{comp,exec} routines to compare.
- X
- X * Examples:
- X * ingroup("joe", joes_gid, "xyz")
- X * returns !0 if user joe is in group "xyz".
- X * ingroup("joe", joes_gid, "xy.*")
- X * returns !0 if user joe is in any group matching "xy.*".
- X
- X */
- X
- Xingroup(user, gid, gp_pat)
- Xchar *user;
- Xint gid;
- Xchar *gp_pat; /* pattern to match */
- X{
- X char *re_comp();
- X int re_exec();
- X struct group *gp;
- X char **mem;
- X void setgrent();
- X
- X if (re_comp(gp_pat) != (char *)0 )
- X return -1;
- X
- X /* Search group file for groups user's in.
- X * Test for group matches wherever user belongs.
- X */
- X setgrent();
- X for (gp = getgrent(); gp; gp = getgrent()) {
- X /* gr_mem only shows names added in the /etc/groups file, not
- X * the group assigned in passwd. Check that group (gp->gr_gid)
- X * explicitly, then check gp->gr_mem list.
- X */
- X if (gid != gp->gr_gid) {
- X for (mem = gp->gr_mem; *mem ; mem++)
- X if (strcmp(*mem, user) == 0)
- X break;
- X if (!*mem)
- X continue; /* not in group */
- X }
- X if (re_exec(gp->gr_name) == 1) /* in group; compare gp name with pat */
- X return 1;
- X }
- X return 0;
- X}
- END_OF_FILE
- if test 1448 -ne `wc -c <'ingroup.c'`; then
- echo shar: \"'ingroup.c'\" unpacked with wrong size!
- fi
- # end of 'ingroup.c'
- fi
- if test -f 're_fail.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'re_fail.c'\"
- else
- echo shar: Extracting \"'re_fail.c'\" \(354 characters\)
- sed "s/^X//" >'re_fail.c' <<'END_OF_FILE'
- X
- X#ifdef vms
- X#include stdio
- X#else
- X#include <stdio.h>
- X#endif
- X
- Xstatic char SccsId[] = "@(#)re_fail.c 1.3\t4/13/93" ;
- X
- X/*
- X * re_fail:
- X * internal error handler for re_exec.
- X *
- X * should probably do something like a
- X * longjump to recover gracefully.
- X */
- Xvoid
- Xre_fail(s, c)
- Xchar *s;
- Xchar c;
- X{
- X (void) fprintf(stderr, "%s [opcode %o]\n", s, c);
- X exit(1);
- X}
- END_OF_FILE
- if test 354 -ne `wc -c <'re_fail.c'`; then
- echo shar: \"'re_fail.c'\" unpacked with wrong size!
- fi
- # end of 're_fail.c'
- fi
- if test -f 'regex.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'regex.c'\"
- else
- echo shar: Extracting \"'regex.c'\" \(18831 characters\)
- sed "s/^X//" >'regex.c' <<'END_OF_FILE'
- X/*
- X * regex - Regular expression pattern matching
- X * and replacement
- X *
- X *
- X * By: Ozan S. Yigit (oz)
- X * Dept. of Computer Science
- X * York University
- X *
- X *
- X * These routines are the PUBLIC DOMAIN equivalents
- X * of regex routines as found in 4.nBSD UN*X, with minor
- X * extensions.
- X *
- X * These routines are derived from various implementations
- X * found in software tools books, and Conroy's grep. They
- X * are NOT derived from licensed/restricted software.
- X * For more interesting/academic/complicated implementations,
- X * see Henry Spencer's regexp routines, or GNU Emacs pattern
- X * matching module.
- X *
- X * Routines:
- X * re_comp: compile a regular expression into
- X * a DFA.
- X *
- X * char *re_comp(s)
- X * char *s;
- X *
- X * re_exec: execute the DFA to match a pattern.
- X *
- X * int re_exec(s)
- X * char *s;
- X *
- X * re_modw change re_exec's understanding of what
- X * a "word" looks like (for \< and \>)
- X * by adding into the hidden word-character
- X * table.
- X *
- X * void re_modw(s)
- X * char *s;
- X *
- X * re_subs: substitute the matched portions in
- X * a new string.
- X *
- X * int re_subs(src, dst)
- X * char *src;
- X * char *dst;
- X *
- X * re_fail: failure routine for re_exec.
- X *
- X * void re_fail(msg, op)
- X * char *msg;
- X * char op;
- X *
- X * Regular Expressions:
- X *
- X * [1] char matches itself, unless it is a special
- X * character (metachar): . \ [ ] * + ^ $
- X *
- X * [2] . matches any character.
- X *
- X * [3] \ matches the character following it, except
- X * when followed by a left or right round bracket,
- X * a digit 1 to 9 or a left or right angle bracket.
- X * (see [7], [8] and [9])
- X * It is used as an escape character for all
- X * other meta-characters, and itself. When used
- X * in a set ([4]), it is treated as an ordinary
- X * character.
- X *
- X * [4] [set] matches one of the characters in the set.
- X * If the first character in the set is "^",
- X * it matches a character NOT in the set. A
- X * shorthand S-E is used to specify a set of
- X * characters S upto E, inclusive. The special
- X * characters "]" and "-" have no special
- X * meaning if they appear as the first chars
- X * in the set.
- X * examples: match:
- X *
- X * [a-z] any lowercase alpha
- X *
- X * [^]-] any char except ] and -
- X *
- X * [^A-Z] any char except uppercase
- X * alpha
- X *
- X * [a-zA-Z] any alpha
- X *
- X * [5] * any regular expression form [1] to [4], followed by
- X * closure char (*) matches zero or more matches of
- X * that form.
- X *
- X * [6] + same as [5], except it matches one or more.
- X *
- X * [7] a regular expression in the form [1] to [10], enclosed
- X * as \(form\) matches what form matches. The enclosure
- X * creates a set of tags, used for [8] and for
- X * pattern substution. The tagged forms are numbered
- X * starting from 1.
- X *
- X * [8] a \ followed by a digit 1 to 9 matches whatever a
- X * previously tagged regular expression ([7]) matched.
- X *
- X * [9] \< a regular expression starting with a \< construct
- X * \> and/or ending with a \> construct, restricts the
- X * pattern matching to the beginning of a word, and/or
- X * the end of a word. A word is defined to be a character
- X * string beginning and/or ending with the characters
- X * A-Z a-z 0-9 and _. It must also be preceded and/or
- X * followed by any character outside those mentioned.
- X *
- X * [10] a composite regular expression xy where x and y
- X * are in the form [1] to [10] matches the longest
- X * match of x followed by a match for y.
- X *
- X * [11] ^ a regular expression starting with a ^ character
- X * $ and/or ending with a $ character, restricts the
- X * pattern matching to the beginning of the line,
- X * or the end of line. [anchors] Elsewhere in the
- X * pattern, ^ and $ are treated as ordinary characters.
- X *
- X *
- X * Acknowledgements:
- X *
- X * HCR's Hugh Redelmeier has been most helpful in various
- X * stages of development. He convinced me to include BOW
- X * and EOW constructs, originally invented by Rob Pike at
- X * the University of Toronto.
- X *
- X * References:
- X * Software tools Kernighan & Plauger
- X * Software tools in Pascal Kernighan & Plauger
- X * Grep [rsx-11 C dist] David Conroy
- X * ed - text editor Un*x Programmer's Manual
- X * Advanced editing on Un*x B. W. Kernighan
- X * RegExp routines Henry Spencer
- X *
- X * Notes:
- X *
- X * This implementation uses a bit-set representation for character
- X * classes for speed and compactness. Each character is represented
- X * by one bit in a 128-bit block. Thus, CCL or NCL always takes a
- X * constant 16 bytes in the internal dfa, and re_exec does a single
- X * bit comparison to locate the character in the set.
- X *
- X * Examples:
- X *
- X * pattern: foo*.*
- X * compile: CHR f CHR o CLO CHR o END CLO ANY END END
- X * matches: fo foo fooo foobar fobar foxx ...
- X *
- X * pattern: fo[ob]a[rz]
- X * compile: CHR f CHR o CCL 2 o b CHR a CCL bitset END
- X * matches: fobar fooar fobaz fooaz
- X *
- X * pattern: foo\\+
- X * compile: CHR f CHR o CHR o CHR \ CLO CHR \ END END
- X * matches: foo\ foo\\ foo\\\ ...
- X *
- X * pattern: \(foo\)[1-3]\1 (same as foo[1-3]foo)
- X * compile: BOT 1 CHR f CHR o CHR o EOT 1 CCL bitset REF 1 END
- X * matches: foo1foo foo2foo foo3foo
- X *
- X * pattern: \(fo.*\)-\1
- X * compile: BOT 1 CHR f CHR o CLO ANY END EOT 1 CHR - REF 1 END
- X * matches: foo-foo fo-fo fob-fob foobar-foobar ...
- X *
- X */
- Xstatic char SccsId[] = "@(#)regex.c 1.3\t4/13/93" ;
- X
- X#define MAXDFA 1024
- X#define MAXTAG 10
- X
- X#define OKP 1
- X#define NOP 0
- X
- X#define CHR 1
- X#define ANY 2
- X#define CCL 3
- X#define NCL 4
- X#define BOL 5
- X#define EOL 6
- X#define BOT 7
- X#define EOT 8
- X#define BOW 9
- X#define EOW 10
- X#define REF 11
- X#define CLO 12
- X
- X#define END 0
- X
- X/*
- X * The following defines are not meant
- X * to be changeable. They are for readibility
- X * only.
- X *
- X */
- X#define MAXCHR 128
- X#define CHRBIT 8
- X#define BITBLK MAXCHR/CHRBIT
- X#define BLKIND 0170
- X#define BITIND 07
- X
- X#define ASCIIB 0177
- X
- Xtypedef /*unsigned*/ char CHAR;
- X
- Xstatic int tagstk[MAXTAG]; /* subpat tag stack..*/
- Xstatic CHAR dfa[MAXDFA]; /* automaton.. */
- Xstatic int sta = NOP; /* status of lastpat */
- X
- Xstatic CHAR bittab[BITBLK]; /* bit table for CCL */
- X
- Xstatic void
- Xchset(c) register CHAR c; { bittab[((c)&BLKIND)>>3] |= 1<<((c)&BITIND); }
- X
- X#define badpat(x) return(*dfa = END, x)
- X#define store(x) *mp++ = x
- X
- Xchar *
- Xre_comp(pat)
- Xchar *pat;
- X{
- X register char *p; /* pattern pointer */
- X register CHAR *mp=dfa; /* dfa pointer */
- X register CHAR *lp; /* saved pointer.. */
- X register CHAR *sp=dfa; /* another one.. */
- X
- X register int tagi = 0; /* tag stack index */
- X register int tagc = 1; /* actual tag count */
- X
- X register int n;
- X int c1, c2;
- X
- X if (!pat || !*pat)
- X if (sta)
- X return(0);
- X else
- X badpat("No previous regular expression");
- X sta = NOP;
- X
- X for (p = pat; *p; p++) {
- X lp = mp;
- X switch(*p) {
- X
- X case '.': /* match any char.. */
- X store(ANY);
- X break;
- X
- X case '^': /* match beginning.. */
- X if (p == pat)
- X store(BOL);
- X else {
- X store(CHR);
- X store(*p);
- X }
- X break;
- X
- X case '$': /* match endofline.. */
- X if (!*(p+1))
- X store(EOL);
- X else {
- X store(CHR);
- X store(*p);
- X }
- X break;
- X
- X case '[': /* match char class..*/
- X
- X if (*++p == '^') {
- X store(NCL);
- X p++;
- X }
- X else
- X store(CCL);
- X
- X if (*p == '-') /* real dash */
- X chset(*p++);
- X if (*p == ']') /* real brac */
- X chset(*p++);
- X while (*p && *p != ']') {
- X if (*p == '-' && *(p+1) && *(p+1) != ']') {
- X p++;
- X c1 = *(p-2) + 1;
- X c2 = *p++;
- X while (c1 <= c2)
- X chset(c1++);
- X }
- X#ifdef EXTEND
- X else if (*p == '\\' && *(p+1)) {
- X p++;
- X chset(*p++);
- X }
- X#endif
- X else
- X chset(*p++);
- X }
- X if (!*p)
- X badpat("Missing ]");
- X
- X for (n = 0; n < BITBLK; bittab[n++] = (char) 0)
- X store(bittab[n]);
- X
- X break;
- X
- X case '*': /* match 0 or more.. */
- X case '+': /* match 1 or more.. */
- X if (p == pat)
- X badpat("Empty closure");
- X lp = sp; /* previous opcode */
- X if (*lp == CLO) /* equivalence.. */
- X break;
- X switch(*lp) {
- X
- X case BOL:
- X case BOT:
- X case EOT:
- X case BOW:
- X case EOW:
- X case REF:
- X badpat("Illegal closure");
- X default:
- X break;
- X }
- X
- X if (*p == '+')
- X for (sp = mp; lp < sp; lp++)
- X store(*lp);
- X
- X store(END);
- X store(END);
- X sp = mp;
- X while (--mp > lp)
- X *mp = mp[-1];
- X store(CLO);
- X mp = sp;
- X break;
- X
- X case '\\': /* tags, backrefs .. */
- X switch(*++p) {
- X
- X case '(':
- X if (tagc < MAXTAG) {
- X tagstk[++tagi] = tagc;
- X store(BOT);
- X store(tagc++);
- X }
- X else
- X badpat("Too many \\(\\) pairs");
- X break;
- X case ')':
- X if (*sp == BOT)
- X badpat("Null pattern inside \\(\\)");
- X if (tagi > 0) {
- X store(EOT);
- X store(tagstk[tagi--]);
- X }
- X else
- X badpat("Unmatched \\)");
- X break;
- X case '<':
- X store(BOW);
- X break;
- X case '>':
- X if (*sp == BOW)
- X badpat("Null pattern inside \\<\\>");
- X store(EOW);
- X break;
- 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 n = *p-'0';
- X if (tagi > 0 && tagstk[tagi] == n)
- X badpat("Cyclical reference");
- X if (tagc > n) {
- X store(REF);
- X store(n);
- X }
- X else
- X badpat("Undetermined reference");
- X break;
- X#ifdef EXTEND
- X case 'b':
- X store(CHR);
- X store('\b');
- X break;
- X case 'n':
- X store(CHR);
- X store('\n');
- X break;
- X case 'f':
- X store(CHR);
- X store('\f');
- X break;
- X case 'r':
- X store(CHR);
- X store('\r');
- X break;
- X case 't':
- X store(CHR);
- X store('\t');
- X break;
- X#endif
- X default:
- X store(CHR);
- X store(*p);
- X }
- X break;
- X
- X default : /* an ordinary char */
- X store(CHR);
- X store(*p);
- X break;
- X }
- X sp = lp;
- X }
- X if (tagi > 0)
- X badpat("Unmatched \\(");
- X store(END);
- X sta = OKP;
- X return(0);
- X}
- X
- X
- Xstatic char *bol;
- Xstatic char *bopat[MAXTAG];
- Xstatic char *eopat[MAXTAG];
- Xchar *pmatch();
- X
- X/*
- X * re_exec:
- X * execute dfa to find a match.
- X *
- X * special cases: (dfa[0])
- X * BOL
- X * Match only once, starting from the
- X * beginning.
- X * CHR
- X * First locate the character without
- X * calling pmatch, and if found, call
- X * pmatch for the remaining string.
- X * END
- X * re_comp failed, poor luser did not
- X * check for it. Fail fast.
- X *
- X * If a match is found, bopat[0] and eopat[0] are set
- X * to the beginning and the end of the matched fragment,
- X * respectively.
- X *
- X */
- X
- Xint
- Xre_exec(lp)
- Xregister char *lp;
- X{
- X register char c;
- X register char *ep = 0;
- X register CHAR *ap = dfa;
- X
- X bol = lp;
- X
- X bopat[0] = 0;
- X bopat[1] = 0;
- X bopat[2] = 0;
- X bopat[3] = 0;
- X bopat[4] = 0;
- X bopat[5] = 0;
- X bopat[6] = 0;
- X bopat[7] = 0;
- X bopat[8] = 0;
- X bopat[9] = 0;
- X
- X switch(*ap) {
- X
- X case BOL: /* anchored: match from BOL only */
- X ep = pmatch(lp,ap);
- X break;
- X case CHR: /* ordinary char: locate it fast */
- X c = *(ap+1);
- X while (*lp && *lp != c)
- X lp++;
- X if (!*lp) /* if EOS, fail, else fall thru. */
- X return(0);
- X default: /* regular matching all the way. */
- X while (*lp) {
- X if ((ep = pmatch(lp,ap)))
- X break;
- X lp++;
- X }
- X break;
- X case END: /* munged automaton. fail always */
- X return(0);
- X }
- X if (!ep)
- X return(0);
- X
- X bopat[0] = lp;
- X eopat[0] = ep;
- X return(1);
- X}
- X
- X/*
- X * pmatch:
- X * internal routine for the hard part
- X *
- X * This code is mostly snarfed from an early
- X * grep written by David Conroy. The backref and
- X * tag stuff, and various other mods are by oZ.
- X *
- X * special cases: (dfa[n], dfa[n+1])
- X * CLO ANY
- X * We KNOW ".*" will match ANYTHING
- X * upto the end of line. Thus, go to
- X * the end of line straight, without
- X * calling pmatch recursively. As in
- X * the other closure cases, the remaining
- X * pattern must be matched by moving
- X * backwards on the string recursively,
- X * to find a match for xy (x is ".*" and
- X * y is the remaining pattern) where
- X * the match satisfies the LONGEST match
- X * for x followed by a match for y.
- X * CLO CHR
- X * We can again scan the string forward
- X * for the single char without recursion,
- X * and at the point of failure, we execute
- X * the remaining dfa recursively, as
- X * described above.
- X *
- X * At the end of a successful match, bopat[n] and eopat[n]
- X * are set to the beginning and end of subpatterns matched
- X * by tagged expressions (n = 1 to 9).
- X *
- X */
- X
- Xextern void re_fail();
- X
- X/*
- X * character classification table for word boundary
- X * operators BOW and EOW. the reason for not using
- X * ctype macros is that we can let the user add into
- X * our own table. see re_modw. This table is not in
- X * the bitset form, since we may wish to extend it
- X * in the future for other character classifications.
- X *
- X * TRUE for 0-9 A-Z a-z _
- X */
- Xstatic char chrtyp[MAXCHR] = {
- X 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- X 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- X 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- X 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- X 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
- X 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
- X 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
- X 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- X 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- X 1, 0, 0, 0, 0, 1, 0, 1, 1, 1,
- X 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- X 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- X 1, 1, 1, 0, 0, 0, 0, 0
- X };
- X
- X#define inascii(x) (0177&(x))
- X#define iswordc(x) chrtyp[inascii(x)]
- X#define isinset(x,y) ((x)[((y)&BLKIND)>>3] & (1<<((y)&BITIND)))
- X
- X/*
- X * skip values for CLO XXX to skip past the closure
- X *
- X */
- X
- X#define ANYSKIP 2 /* CLO ANY END ... */
- X#define CHRSKIP 3 /* CLO CHR chr END ... */
- X#define CCLSKIP 18 /* CLO CCL 16bytes END ... */
- X
- Xstatic char *
- Xpmatch(lp, ap)
- Xregister char *lp;
- Xregister CHAR *ap;
- X{
- X register char *e; /* extra pointer for CLO */
- X register char *bp; /* beginning of subpat.. */
- X register char *ep; /* ending of subpat.. */
- X register int op, c, n;
- X char *are; /* to save the line ptr. */
- X
- X while ((op = *ap++) != END)
- X switch(op) {
- X
- X case CHR:
- X if (*lp++ != *ap++)
- X return(0);
- X break;
- X case ANY:
- X if (!*lp++)
- X return(0);
- X break;
- X case CCL:
- X c = *lp++;
- X if (!isinset(ap,c))
- X return(0);
- X ap += BITBLK;
- X break;
- X case NCL:
- X c = *lp++;
- X if (isinset(ap,c))
- X return(0);
- X ap += BITBLK;
- X break;
- X case BOL:
- X if (lp != bol)
- X return(0);
- X break;
- X case EOL:
- X if (*lp)
- X return(0);
- X break;
- X case BOT:
- X bopat[*ap++] = lp;
- X break;
- X case EOT:
- X eopat[*ap++] = lp;
- X break;
- X case BOW:
- X if (!(lp!=bol && iswordc(lp[-1])) && iswordc(*lp))
- X break;
- X return(0);
- X case EOW:
- X if ((lp!=bol && iswordc(lp[-1])) && !iswordc(*lp))
- X break;
- X return(0);
- X case REF:
- X n = *ap++;
- X bp = bopat[n];
- X ep = eopat[n];
- X while (bp < ep)
- X if (*bp++ != *lp++)
- X return(0);
- X break;
- X case CLO:
- X are = lp;
- X switch(*ap) {
- X
- X case ANY:
- X while (*lp)
- X lp++;
- X n = ANYSKIP;
- X break;
- X case CHR:
- X c = *(ap+1);
- X while (*lp && c == *lp)
- X lp++;
- X n = CHRSKIP;
- X break;
- X case CCL:
- X case NCL:
- X while (*lp && (e = pmatch(lp, ap)))
- X lp = e;
- X n = CCLSKIP;
- X break;
- X default:
- X re_fail("closure: bad dfa.", *ap);
- X return(0);
- X }
- X
- X ap += n;
- X
- X while (lp >= are) {
- X if (e = pmatch(lp, ap))
- X return(e);
- X --lp;
- X }
- X return(0);
- X default:
- X re_fail("re_exec: bad dfa.", op);
- X return(0);
- X }
- X return(lp);
- X}
- X
- X/*
- X * re_modw:
- X * add new characters into the word table to
- X * change the re_exec's understanding of what
- X * a word should look like. Note that we only
- X * accept additions into the word definition.
- X *
- X * If the string parameter is 0 or null string,
- X * the table is reset back to the default, which
- X * contains A-Z a-z 0-9 _. [We use the compact
- X * bitset representation for the default table]
- X *
- X */
- X
- Xstatic char deftab[16] = {
- X 0, 0, 0, 0, 0, 0, 377, 003, 376, 377, 377, 207,
- X 376, 377, 377, 007
- X};
- X
- Xvoid
- Xre_modw(s)
- Xregister char *s;
- X{
- X register int i;
- X
- X if (!s || !*s) {
- X for (i = 0; i < MAXCHR; i++)
- X if (!isinset(deftab,i))
- X iswordc(i) = 0;
- X }
- X else
- X while(*s)
- X iswordc(*s++) = 1;
- X}
- X
- X/*
- X * re_subs:
- X * substitute the matched portions of the src in
- X * dst.
- X *
- X * & substitute the entire matched pattern.
- X *
- X * \digit substitute a subpattern, with the given
- X * tag number. Tags are numbered from 1 to
- X * 9. If the particular tagged subpattern
- X * does not exist, null is substituted.
- X *
- X */
- Xint
- Xre_subs(src, dst)
- Xregister char *src;
- Xregister char *dst;
- X{
- X register char c;
- X register int pin;
- X register char *bp;
- X register char *ep;
- X
- X if (!*src || !bopat[0])
- X return(0);
- X
- X while (c = *src++) {
- X switch(c) {
- X
- X case '&':
- X pin = 0;
- X break;
- X
- X case '\\':
- X c = *src++;
- X if (c >= '0' && c <= '9') {
- X pin = c - '0';
- X break;
- X }
- X
- X default:
- X *dst++ = c;
- X continue;
- X }
- X
- X if ((bp = bopat[pin]) && (ep = eopat[pin])) {
- X while (*bp && bp < ep)
- X *dst++ = *bp++;
- X if (bp < ep)
- X return(0);
- X }
- X }
- X *dst = (char) 0;
- X return(1);
- X}
- X
- X#ifdef DEBUG
- X/*
- X * symbolic - produce a symbolic dump of the
- X * dfa
- X */
- Xsymbolic(s)
- Xchar *s;
- X{
- X printf("pattern: %s\n", s);
- X printf("dfacode:\n");
- X dfadump(dfa);
- X}
- X
- Xstatic
- Xdfadump(ap)
- XCHAR *ap;
- X{
- X register int n;
- X
- X while (*ap != END)
- X switch(*ap++) {
- X case CLO:
- X printf("CLOSURE");
- X dfadump(ap);
- X switch(*ap) {
- X case CHR:
- X n = CHRSKIP;
- X break;
- X case ANY:
- X n = ANYSKIP;
- X break;
- X case CCL:
- X case NCL:
- X n = CCLSKIP;
- X break;
- X }
- X ap += n;
- X break;
- X case CHR:
- X printf("\tCHR %c\n",*ap++);
- X break;
- X case ANY:
- X printf("\tANY .\n");
- X break;
- X case BOL:
- X printf("\tBOL -\n");
- X break;
- X case EOL:
- X printf("\tEOL -\n");
- X break;
- X case BOT:
- X printf("BOT: %d\n",*ap++);
- X break;
- X case EOT:
- X printf("EOT: %d\n",*ap++);
- X break;
- X case BOW:
- X printf("BOW\n");
- X break;
- X case EOW:
- X printf("EOW\n");
- X break;
- X case REF:
- X printf("REF: %d\n",*ap++);
- X break;
- X case CCL:
- X printf("\tCCL [");
- X for (n = 0; n < MAXCHR; n++)
- X if (isinset(ap,(CHAR)n))
- X printf("%c",n);
- X printf("]\n");
- X ap += BITBLK;
- X break;
- X case NCL:
- X printf("\tNCL [");
- X for (n = 0; n < MAXCHR; n++)
- X if (isinset(ap,(CHAR)n))
- X printf("%c",n);
- X printf("]\n");
- X ap += BITBLK;
- X break;
- X default:
- X printf("bad dfa. opcode %o\n", ap[-1]);
- X exit(1);
- X break;
- X }
- X}
- X#endif
- END_OF_FILE
- if test 18831 -ne `wc -c <'regex.c'`; then
- echo shar: \"'regex.c'\" unpacked with wrong size!
- fi
- # end of 'regex.c'
- fi
- if test -f 'sample.cdmount' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'sample.cdmount'\"
- else
- echo shar: Extracting \"'sample.cdmount'\" \(780 characters\)
- sed "s/^X//" >'sample.cdmount' <<'END_OF_FILE'
- X#!/bin/sh
- X
- Xprog=`basename $0`
- X# If script invoked w/o super, then exec super to run this script.
- Xif [ X$SUPERCMD = X ] ; then exec /usr/local/bin/super $prog "$@" ; fi
- X
- Xusage() {
- Xcat <<-END
- X Use:
- X $prog hsfs | 4.2
- X
- X Purpose:
- X Mounts a cdrom on /cdrom.
- X
- X Argument: the cdrom type; specify one of
- X hsfs - cdrom is High Sierra File System
- X 4.2 - usual Unix disk format
- X
- XEND
- X}
- X
- Xcase $# in
- X 1 ) ;;
- X * ) usage ; exit 1 ;;
- Xesac
- X
- Xtype="$1"
- Xcase "$type" in
- X 4.2 | hsfs ) ;;
- X -h ) usage ; exit 0 ;;
- X * ) echo "$prog: unknown cd type $1" ; usage ; exit 1 ;;
- Xesac
- X
- XPATH=$PATH:/usr/etc # SunOS 4.x needs this to understand type hsfs
- Xexport PATH
- X
- Xecho /etc/mount -v -r -t $type -o nosuid /dev/sr0 /cdrom
- X /etc/mount -v -r -t $type -o nosuid /dev/sr0 /cdrom
- END_OF_FILE
- if test 780 -ne `wc -c <'sample.cdmount'`; then
- echo shar: \"'sample.cdmount'\" unpacked with wrong size!
- fi
- # end of 'sample.cdmount'
- fi
- if test -f 'sample.tab' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'sample.tab'\"
- else
- echo shar: Extracting \"'sample.tab'\" \(2104 characters\)
- sed "s/^X//" >'sample.tab' <<'END_OF_FILE'
- X# This file lists commands that super(1) will execute for you as root.
- X
- X# The format for data lines in this file is
- X
- X# commandname fullpathname ed patterns of valid users/groups/hosts
- X
- X# -- The commandname must begin in column 1. Lines may be continued with a
- X# backslash immediately preceding a newline; the continuation line must
- X# begin with whitespace. (This requirement helps super() catch typos.)
- X
- X# The format for a users/groups/hosts entry is
- X# user[:][@host] or :group[@host] or user:group[@host]
- X
- X# -- Since you can specify a host in the entry, a single super.tab file can
- X# be used by many different machines.
- X
- X# -- All patterns are "anchored"; i.e. they are forced to match the entire
- X# username, groupname, or hostname.
- X
- X# -- Patterns can use csh-style "brace expansion". For instance,
- X# a{x,y,z}b is replaced with axb ayb azb.
- X
- X# -- If a hostname is of the form "+xyz", then "xyz" is interpreted as
- X# a netgroup name and any host in netgroup xyz is matched. In that
- X# case, "xyz" CANNOT CONTAIN WILDCARDS. Note: netgroup lookup is only
- X# implemented if the function innetgr() is available and if super()
- X# was compiled with -DUSE_NETGROUP.
- X
- X# Example entry:
- X
- X# doit /usr/local/bin/doit me \
- X# you@{h1,h2} \
- X# ja.*:ok_j \
- X# :goodguys
- X
- X# ...allows /usr/local/bin/doit to be run setuid root by:
- X
- X# user "me" on any host,
- X# user "you" on hosts h1 and h2;
- X# any users named "ja.*" in group "ok_j";
- X# and anybody in group "goodguys".
- X# ...by typing
- X# % super doit [ doit args ]
- X
- X# ---------------------------------------------------------------------------
- X# Cmd Full Path Valid-User/Group/Host Patterns
- X
- Xtimeout /usr/local/bin/timeout :operator :wheel gv phillips srk
- Xrestart /usr/local/bin/restart :operator :wheel gv phillips srk
- X
- X# Local restrictions on CD-ROM mounting: tas is the only user who may mount
- X# cd's on elgar; anybody in group xyz may mount cd's on
- X# alpha or delta; and anybody on a host in the netgroup "india"
- X# may mount a CD on the "india" machines.
- X
- Xcdmount /usr/local/bin/cdmount tas@elgar \
- X :xyz@{alpha,delta} \
- X .*@+india
- END_OF_FILE
- if test 2104 -ne `wc -c <'sample.tab'`; then
- echo shar: \"'sample.tab'\" unpacked with wrong size!
- fi
- # end of 'sample.tab'
- fi
- if test -f 'super.1' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'super.1'\"
- else
- echo shar: Extracting \"'super.1'\" \(3793 characters\)
- sed "s/^X//" >'super.1' <<'END_OF_FILE'
- X.TH SUPER 1 local
- X.SH NAME
- Xsuper \- execute commands setuid root.
- X.SH SYNOPSIS
- X.B super
- X.I command
- X[
- X.I args
- X]
- X.SH DESCRIPTION
- X.I Super
- Xallows users to execute scripts (or other commands) as if they were root.
- XIt
- Xis intended to be a secure alternative to making scripts setuid root.
- XIt also permits restricting certain commands to particular combinations
- Xof user, group, and host.
- X.PP
- X.I Super
- Xconsults a file to see if the user is allowed to execute the requested
- X.IR command .
- XIf so,
- X.I super
- Xwill exec
- X.IR command\ [\ args\ ].
- XRoot is always permitted to execute any command in the
- Xsuper file.
- X.PP
- X.I Super
- Xwithout any arguments will display its list of commands and their allowed users.
- X.PP
- XFor security, the following precautions are taken before exec'ing:
- X.HP
- X\fI(a)\fP all descriptors save 0,1,2 are closed;
- X.HP
- X\fI(b)\fP all of the user's environment variables are
- Xdiscarded, save for TERM, LINES, and COLUMNS.
- XIf TERM contains any characters other than
- X[a-z][A-Z][0-9]_, it is discarded.
- XIf LINES or COLUMNS contains any
- Xcharacters other than [0-9], it are discarded.
- XTo these
- Xare added reasonable values for:
- X.RS
- X.HP
- XUSER and LOGNAME: both are set to the username
- Xof the real user running
- X.IR super ;
- X.HP
- XHOME: set to the login directory
- Xof the real user running
- X.IR super ;
- X.HP
- XIFS: set to blank, tab, newline;
- X.HP
- XPATH: set to \fI/bin:/usr/bin:/usr/ucb\fP.
- X.HP
- XSUPERCMD: set to \fIcommand\fP.
- X.RE
- X.in -.5i
- X.HP
- X\fI(c)\fP all signal handling is reset to the default.
- X.SH OPTIONS
- X.HP
- X.BR \-h \ |\ \-?
- XIf no arguments are given, or if the first argument is ``\-h'' or ``\-?'',
- X.I super
- Xprints a usage line and lists all the available commands.
- X.SH FILES
- X.HP
- X.I /usr/local/lib/super.tab
- X\(em contains the list of commands that
- X.I super
- Xmay execute, along with the names of the user/group combinations
- Xwho may execute each command. The valid-user line can restrict use to
- Xparticular users or groups on different hosts, so a single super.tab
- Xfile can be used across a network.
- X.SH CREATING SUPER SCRIPTS
- XYou must be exceedingly careful when writing scripts for
- X.IR super .
- XA surprising variety of ordinary commands can, when
- Xrun setuid-root, be exploited for nasty purposes. Always make your
- Xscripts do as little as possible, and give the user as few options
- Xas possible.
- X.PP
- XThink twice about side-effects and alternative uses
- Xof these scripts. For instance, you might write a script to allow
- Xusers to mount cd-rom's by executing
- X.IR mount(8) .
- XBut if you don't write it carefully, a user could mount a floppy
- Xdisk containing, say, a setuid-root shell.
- X.PP
- XSecurity issues aside, here are some hints on creating super scripts:
- X.HP
- X1. Scripts must begin with
- X.BI #! interpreter-path .
- X.HP
- X2. Some variants of csh will not run setuid scripts unless the \-b flag
- X(force a "break" from option processing) is set:
- X.ti +.5i
- X#!/bin/csh -fb
- X.br
- X.HP
- X3. It's nice to make the
- X.I super
- Xcall transparent to users, so that they can type
- X.ti +.5i
- X% cdmount \fIargs\fP
- X.br
- Xinstead of
- X.ti +.5i
- X% super cdmount \fIargs\fP
- X.br
- XYou can make a script
- X.I super
- Xitself by beginning the script in the following way:
- X.in +.5i
- X.nf
- X#!/bin/sh
- Xprog=`basename $0`
- Xif [ X$SUPERCMD != X$prog ] ; then
- X exec /usr/local/bin/super $prog "$@"
- Xfi
- X.fi
- X.in -1i
- X.HP
- X4. Some programs need certain directories in the path. Your
- Xsuper scripts may have to add directories like
- X.I /etc
- Xor
- X.I /usr/etc
- Xto make commands work.
- XFor instance, SunOS\ 4.1 needs
- X.I /usr/etc
- Xin the path before it can mount filesystems of type ``hsfs''.
- X.HP
- X5. \fISuper\fP only changes the effective uid.
- XSome programs (e.g. \fIexportfs\fP under SunOS\ 4.1.x)
- Xrequire the real uid to be root. In that case, your super
- Xscript will have to change the real uid to root before executing the command.
- X.SH AUTHOR
- XWill Deich
- X.br
- Xwill@surya.caltech.edu
- END_OF_FILE
- if test 3793 -ne `wc -c <'super.1'`; then
- echo shar: \"'super.1'\" unpacked with wrong size!
- fi
- # end of 'super.1'
- fi
- if test -f 'super.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'super.c'\"
- else
- echo shar: Extracting \"'super.c'\" \(5731 characters\)
- sed "s/^X//" >'super.c' <<'END_OF_FILE'
- X#include <stdio.h>
- X#include <string.h>
- X#include <signal.h>
- X#include <sys/param.h>
- X#include <unistd.h>
- X
- Xstatic char SccsId[] = "@(#)super.c 1.12\t4/13/93";
- X
- X#ifndef SUPERFILE
- X#define SUPERFILE "/usr/local/lib/super.tab"
- X#endif
- X
- X/*
- X * Copyright (c) 1993 by California Institute of Technology.
- X * Written by William Deich. Not derived from licensed software.
- X
- X * Permission is granted to anyone to use this software for any
- X * purpose on any computer system, and to redistribute it freely,
- X * subject to the following restrictions:
- X *
- X * 1. The author is not responsible for the consequences of use of this
- X * software, no matter how awful, even if they arise from defects in it.
- X *
- X * 2. The origin of this software must not be misrepresented, either
- X * by explicit claim or by omission.
- X *
- X * 3. Altered versions must be plainly marked as such, and must not
- X * be misrepresented as being the original software.
- X */
- X
- X/*
- X * Super allows users to execute other programs (particularly
- X * scripts) as root, without unduly compromising security.
- X *
- X * Use:
- X *
- X * $0 commandname args...
- X *
- X * If the commandname is "-h" or "-?", or missing, super prints its current
- X * list of allowed commands, but nothing executed.
- X *
- X * The super.tab file names each command that super will execute, and
- X * says who can use it. It contains lines like:
- X *
- X * commandname fullpathname valid-user/group ed-type patterns
- X *
- X * See sample.tab for how to specify user & group patterns.
- X * If a user is allowed to execute a given <commandname>, the <fullpathname>
- X * is exec'd, with <commandname> as argv[0].
- X *
- X * For security, the environment variables are discarded, save for TERM,
- X * LINES, and COLUMNS. If TERM contains any characters other than
- X * [a-z][A-Z][0-9]_, it is discarded. If LINES or COLUMNS contains any
- X * characters other than [0-9], they are discarded. To these are added
- X * reasonable values for IFS, PATH, USER and HOME (USER and HOME are set
- X * to the username and login directory, respectively, of the person who
- X * who runs super). LOGNAME is set to the same as USER. SUPERCMD is set
- X * to the <command>. All descriptors excepting 0,1,2 are closed. Signals
- X * are all reset to have default handling.
- X */
- X
- X
- Xstatic char *SAFE_IFS = "IFS= \t\n";
- Xstatic char *SAFE_PATH = "PATH=/bin:/usr/bin:/usr/ucb";
- X
- Xchar *prog; /* This program */
- Xchar *user; /* who's invoking prog */
- X
- Xmain(argc, argv)
- Xint argc;
- Xchar **argv;
- X{
- X char *s, *path, *approve();
- X char **buttonup(), **envp;
- X void exit();
- X
- X s = strchr(argv[0], '/');
- X prog = (s && *(s+1)) ? s+1 : argv[0];
- X
- X if ((path = approve(SUPERFILE, argv[1])) == NULL)
- X (void) exit(1);
- X else if (*path == '\0')
- X (void) exit(0);
- X
- X /* Button up for security, and get a modified environment */
- X envp = buttonup(argv[1]);
- X
- X if (execve(path, &argv[1], envp) == -1) {
- X (void) fprintf(stderr, "%s: Couldn't exec %s (%s): ",
- X prog, argv[1], path);
- X perror("");
- X (void) exit(1);
- X }
- X return 0;
- X}
- X
- Xchar **
- Xbuttonup(cmd)
- Xchar *cmd; /* name of command being started */
- X{
- X /* Closes all descriptors save 0,1,2.
- X * Resets all signal-handling to SIG_DFL.
- X * Discards all env. variables save for TERM, LINES, and COLUMNS.
- X * Don't allow TERM to have any but alpha characters and underscore.
- X * Don't allow LINES, COLUMNS to have anything but digits.
- X * To these are added reasonable values for IFS, PATH, USER, and HOME.
- X * LOGNAME is set to the same as USER, and SUPERCMD is set to cmd.
- X * Returned:
- X * a pointer to the modified environment list.
- X */
- X int i, fd, n;
- X char *Getenv();
- X int getlogdir();
- X int checkenv();
- X static char *env[10];
- X static char User[100]; /* USER */
- X static char Logname[100]; /* LOGNAME (alias for USER) */
- X static char Home[MAXPATHLEN+5]; /* HOME */
- X static char Cmd[1200]; /* SUPERCMD */
- X void (*signal())();
- X
- X#ifdef MAXFD
- X n = MAXFD ;
- X#else
- X n = getdtablesize();
- X#endif
- X for (fd=3; fd <= n; fd++)
- X (void) close(fd);
- X
- X for (i=0; i<NSIG; i++)
- X (void) signal(i, SIG_DFL);
- X
- X (void) sprintf(User, "USER=%s", user);
- X (void) sprintf(Logname, "LOGNAME=%s", user);
- X (void) sprintf(Cmd, "SUPERCMD=%s", cmd);
- X (void) strcpy(Home, "HOME=");
- X (void) getlogdir(user, Home+5);
- X i = 0;
- X env[i] = Getenv("TERM");
- X if (env[i] && checkenv("TERM", env[i]+5, "^[a-zA-Z0-9]*$")) i++;
- X env[i] = Getenv("LINES");
- X if (env[i] && checkenv("LINES", env[i]+6, "^[0-9]*$")) i++;
- X env[i] = Getenv("COLUMNS");
- X if (env[i] && checkenv("COLUMNS", env[i]+8, "^[0-9]*$")) i++;
- X env[i++] = SAFE_IFS;
- X env[i++] = SAFE_PATH;
- X env[i++] = User;
- X env[i++] = Logname;
- X env[i++] = Cmd;
- X env[i] = (char *) NULL;
- X
- X return &env[0];
- X}
- X
- Xchar *
- XGetenv(s)
- Xchar *s;
- X{
- X /* Like getenv(), but returns ptr to the <name> in "name=xxxx",
- X * not just the xxxx.
- X */
- X char **envp;
- X int l;
- X extern char **environ;
- X
- X if (!s)
- X return (char *) NULL;
- X l = strlen(s);
- X for (envp=environ; *envp ; envp++)
- X if (strncmp(*envp, s, l) == 0 && *(*envp+l) == '=')
- X return *envp;
- X return (char *) NULL;
- X}
- X
- X/* Returns 1 if pat matched; 0 otherwise. */
- Xcheckenv(name, value, pat)
- Xchar *name; /* variable name to check (e.g. "TERM") */
- Xchar *value; /* contents of variable (e.g. "vt100") */
- Xchar *pat; /* pattern that value must match */
- X{
- X char *re_comp();
- X int re_exec();
- X
- X if (!value)
- X return 0;
- X
- X if (re_comp(pat)) {
- X (void) fprintf(stderr,
- X "%s: Warning: couldn't compile pattern `%s'\n", prog, pat);
- X return 0;
- X }
- X
- X if (re_exec(value) != 1) {
- X (void) fprintf(stderr,
- X "%s: %s (%s) doesn't match %s.\n", prog, name, value, pat);
- X return 0;
- X }
- X return 1;
- X}
- END_OF_FILE
- if test 5731 -ne `wc -c <'super.c'`; then
- echo shar: \"'super.c'\" unpacked with wrong size!
- fi
- # end of 'super.c'
- fi
- if test -f 'version.h' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'version.h'\"
- else
- echo shar: Extracting \"'version.h'\" \(46 characters\)
- sed "s/^X//" >'version.h' <<'END_OF_FILE'
- X#define Version "2.0"
- X#define Patchlevel "1"
- END_OF_FILE
- if test 46 -ne `wc -c <'version.h'`; then
- echo shar: \"'version.h'\" unpacked with wrong size!
- fi
- # end of 'version.h'
- fi
- if test -f 'word.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'word.c'\"
- else
- echo shar: Extracting \"'word.c'\" \(1222 characters\)
- sed "s/^X//" >'word.c' <<'END_OF_FILE'
- X#include <ctype.h>
- X
- Xstatic char SccsId[] = "@(#)word.c 1.3\t4/13/93";
- X
- X/* Returns a pointer to the start of the k'th word in s.
- X * Numbering is that k=1 for the first word.
- X * A pointer to a null string is returned if there is no k'th word.
- X */
- X
- X/*
- X * Compile with -DPROGRAM to get a standalone program that's used as
- X * $0 k string
- X * and prints the k'th whitespace-separated word of the string on stdout.
- X */
- X
- Xchar *
- Xword(s, sep, k)
- Xregister char *s;
- Xregister char *sep; /* word separators */
- Xint k;
- X{
- X int i;
- X
- X /* Skip leading separators */
- X s += strspn(s, sep);
- X
- X /* Skip words before the k'th word */
- X for (i=1; i<k; i++) {
- X if (*s) s += strcspn(s, sep); /* Skip a word... */
- X if (*s) s += strspn(s, sep); /* ...and the trailing separators */
- X }
- X return s;
- X}
- X
- X#ifdef TEST
- X#include <stdio.h>
- X#include <string.h>
- Xmain(argc, argv)
- Xint argc;
- Xchar **argv;
- X{
- X char *prog, *word();
- X int atoi();
- X
- X prog = strrchr(argv[0], '/');
- X prog = (prog && *(prog+1)) ? prog+1 : (prog) ? prog : argv[0];
- X if (argc != 3) {
- X (void) fprintf(stderr, "Usage: %s k \"string\"\n", prog);
- X (void) exit(1);
- X }
- X printf("%s\n", word(argv[2], " \t\v", atoi(argv[1])));
- X return 0;
- X}
- X#endif
- END_OF_FILE
- if test 1222 -ne `wc -c <'word.c'`; then
- echo shar: \"'word.c'\" unpacked with wrong size!
- fi
- # end of 'word.c'
- fi
- echo shar: End of archive 1 \(of 1\).
- cp /dev/null ark1isdone
- MISSING=""
- for I in 1 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have the archive.
- rm -f ark[1-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
-