home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: comp.sources.unix
- From: will@surya.caltech.edu (Will Deich)
- Subject: v26i062: super - execute limited command set as root
- Sender: unix-sources-moderator@pa.dec.com
- Approved: vixie@pa.dec.com
-
- Submitted-By: will@surya.caltech.edu (Will Deich)
- Posting-Number: Volume 26, Issue 62
- Archive-Name: super
-
- 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 may execute a setuid-root program.
-
- /* 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 shell archive."
- # Contents: README Makefile approve.c getlogdir.c ingroup.c sample.tab
- # super.1 super.c word.c
- # Wrapped by vixie@cognition.pa.dec.com on Fri Jul 3 11:05:52 1992
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'README' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'README'\"
- else
- echo shar: Extracting \"'README'\" \(3178 characters\)
- sed "s/^X//" >'README' <<'END_OF_FILE'
- Super(1) is a small program that allows users to execute other programs
- X(particularly scripts) as root, without unduly compromising security.
- X
- X
- Sample 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 may execute a setuid-root program.
- X
- A "super.tab" file names each command that super is willing to execute, and
- says who can use it. It contains lines like:
- X
- X command fullpathname valid-user/group ed-type patterns
- X
- To execute a super command, type
- X
- X % super command [args...]
- X
- If <command> is "-h" or "-?", or missing, super prints its current
- list of allowed commands, but nothing is executed.
- X
- 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.
- X
- XFor security, the environment variables are discarded, save for
- TERM, LINES, and COLUMNS. 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.
- X
- X--------------------
- X
- Making and installation:
- X
- The makefile is only 21 lines long. Modify it to suit yourself.
- You have to be root to install super, as it must run setuid root.
- X
- By default, super expects to find its table of valid commands+users/groups
- in /usr/local/lib/super.tab. If you change this (it's #define'd in
- super.c), you must also change the documentation.
- X
- A sample super.tab file is found in sample.tab.
- X
- X--------------------
- Notes 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 if [ X$SUPERCMD != X$prog ] ; then
- X exec /usr/local/bin/super $prog "$@"
- X fi
- 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. 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 3178 -ne `wc -c <'README'`; then
- echo shar: \"'README'\" unpacked with wrong size!
- fi
- # end of 'README'
- fi
- if test -f 'Makefile' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'Makefile'\"
- else
- echo shar: Extracting \"'Makefile'\" \(442 characters\)
- sed "s/^X//" >'Makefile' <<'END_OF_FILE'
- X
- BINDIR=/usr/local/bin
- MANDIR=/usr/man/manl
- MANEXT=l
- CFLAGS= -g
- X
- SRC= super.c approve.c ingroup.c word.c getlogdir.c
- OBJ= super.o approve.o ingroup.o word.o getlogdir.o
- X
- super: $(OBJ)
- X $(CC) $(CFLAGS) -o super $(OBJ)
- X
- install:
- X install -m 04755 -o root super $(BINDIR)
- X install super.1 $(MANDIR)/super.$(MANEXT)
- X
- shar: README Makefile $(SRC) super.1 sample.tab
- X shar README Makefile $(SRC) super.1 sample.tab > super.shar
- X
- lint:
- X lint $(SRC)
- END_OF_FILE
- if test 442 -ne `wc -c <'Makefile'`; then
- echo shar: \"'Makefile'\" unpacked with wrong size!
- fi
- # end of 'Makefile'
- 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'\" \(5045 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
- static char SccsId[] = "%W%\t%G%";
- X
- static FILE *fp;
- X
- extern errno;
- X
- X#define MIN(a,b) ((a) < (b) ? (a) : (b))
- X
- extern char *user;
- extern char *prog;
- X
- X/* 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
- X#define GROUPSEP ':' /* ok users are named as "user<GROUPSEP>group" */
- X
- char *
- approve(superfile, usrcmd)
- char *superfile; /* file of approved commands+users */
- char *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 static char buf[1024];
- X char chkbuf[1024];
- X char *allusers, *line, *command, *path, *okuser, *okgroup;
- X char *msg, *word(), *re_comp();
- X int re_exec(), ingroup();
- X char *sep;
- X int i, n, givehelp, commandfound;
- X char *nil = (char *) NULL;
- X static char *empty = "";
- 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
- X if ((fp = fopen(superfile, "r")) == NULL) {
- X (void) fprintf(stderr, "%s: Couldn't open valid-users text file `%s': ",
- X superfile);
- X perror("");
- X return NULL;
- X }
- X
- X sep = " \t\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 == nil) ||
- 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 ", "Valid User:Group Patterns");
- X (void) fprintf(stderr, "%-16s %-24s %s\n",
- X "-------", "--------------------", "-------------------------");
- X }
- X
- X commandfound = 0;
- X for (n=1; fgets(buf, sizeof(buf), fp); n++) {
- X if (!*buf || *buf == '#') /* Skip empty lines and comments */
- X continue;
- X line = strtok(buf, "#"); /* Strip comments */
- X
- X allusers = word(buf, " \t\v", 3); /* Find the okusernames before
- X * carving up the line.
- X */
- X
- X command = strtok(line, sep); /* get the command... */
- X path = strtok(nil, sep); /* ...and its full path */
- X if (!command || !path)
- X continue; /* Skip improper lines */
- X
- X if (givehelp) {
- X (void) fprintf(stderr, "%-16s %-24s %s", command, path, allusers);
- X continue;
- X }
- X if (strcmp(command, usrcmd) != 0)
- X continue; /* Skip non-matching commands */
- X commandfound++;
- X
- X /* Check our user against all valid user patterns */
- X if (uid == 0)
- X return path; /* root is always legit */
- X for (okuser = strtok(nil, sep); okuser; okuser = strtok(nil, sep)) {
- X /* Split into user,group */
- X okgroup = strchr(okuser, GROUPSEP);
- X if (okgroup && *(okgroup+1)) {
- X /* pat was "uuu:ggg or ":ggg" */
- X if (okuser == okgroup)
- X okuser = "^.*$"; /* pat was ":ggg" */
- X *okgroup++ = '\0';
- X } else {
- X /* pat was "uuu" or "uuu:" */
- X if (okgroup)
- X *okgroup = '\0'; /* pat was "uuu:" */
- X okgroup = "^.*$";
- X }
- X /* Force all matches to be anchored: prepend/append
- X * ^, $ if they haven't been put in by user
- X */
- X anchor(okuser, chkbuf);
- X if ((msg = re_comp(chkbuf)) != NULL) {
- X (void) fprintf(stderr,
- X "%s: bad pattern `%s' on line %d in %s: %s\n",
- X prog, okuser, n, superfile, msg);
- X continue;
- X }
- X if (re_exec(user) != 1)
- X continue; /* Skip non-matched user */
- X
- X anchor(okgroup, chkbuf);
- X i = ingroup(user, chkbuf);
- X if (i == -1) {
- X (void) fprintf(stderr,
- X "%s: bad pattern `%s%c%s' on line %d in %s\n",
- X prog, okuser, GROUPSEP, okgroup, n, superfile);
- X continue;
- X }
- X if (i == 1)
- X return path; /* Matched cmd+user+group */
- X }
- X }
- X if (givehelp)
- X return empty;
- 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
- void
- anchor(in, out)
- char *in;
- char *out;
- X{
- X /* Copies in to out, prefixing with "^" and suffixing with "$"
- X * if these are missing.
- 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}
- END_OF_FILE
- if test 5045 -ne `wc -c <'approve.c'`; then
- echo shar: \"'approve.c'\" unpacked with wrong size!
- fi
- # end of 'approve.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'\" \(935 characters\)
- sed "s/^X//" >'getlogdir.c' <<'END_OF_FILE'
- X#include <string.h>
- X#include <pwd.h>
- X
- static char SccsId[] = "@(#)getlogdir.c 1.1\t12/13/91";
- X
- X#ifndef NULL
- X#define NULL (char *) 0
- X#endif
- X
- int getlogdir(user, buf)
- char *user;
- char *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 935 -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'\" \(1802 characters\)
- sed "s/^X//" >'ingroup.c' <<'END_OF_FILE'
- X#include <grp.h>
- X
- static char SccsId[] = "@(#)ingroup.c 1.3\t12/21/91";
- X
- X/* Use:
- X * ingroup(user, 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", "xyz")
- X * returns !0 if user joe is in group "xyz".
- X * ingroup("joe", "xy.*")
- X * returns !0 if user joe is in any group matching "xy.*".
- X
- X
- X * If compiled with -DPROGRAM, then a small program is compiled; use is:
- X * % ingroup username pat...
- X * Prints name of first matching pattern; prints nothing on no matches.
- X */
- X
- ingroup(user, gp_pat)
- char *user;
- char *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 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}
- X
- X#ifdef PROGRAM
- X#include <stdio.h>
- main(argc, argv)
- int argc;
- char **argv;
- X{
- X char **pat;
- X int i;
- X
- X if (argc < 3) {
- X (void) fprintf(stderr, "Use: %s user pat...\n", argv[0]);
- X (void) exit(1);
- X }
- X for (pat = &argv[2]; *pat; pat++) {
- X if ((i=ingroup(argv[1], *pat)) == -1)
- X (void) fprintf(stderr, "Invalid re_comp pattern ``%s''\n", *pat);
- X else if (i) {
- X puts(*pat);
- X (void) exit(0);
- X }
- X }
- X}
- X#endif
- END_OF_FILE
- if test 1802 -ne `wc -c <'ingroup.c'`; then
- echo shar: \"'ingroup.c'\" unpacked with wrong size!
- fi
- # end of 'ingroup.c'
- 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'\" \(917 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 for valid users/groups.
- X
- X# The format for a users/groups entry is
- X# user[:] or :group or user:group
- X#
- X# All patterns are "anchored"; i.e. they are forced to match the entire
- X# username or groupname.
- X
- X
- X# Example entry:
- X
- X# doit /usr/local/bin/doit me you ja.*:ok_j :goodguys
- X
- X# ...allows users "me", "you", any users named "ja.*" in group "ok_j",
- X# and anybody in group "goodguys" to run /usr/local/bin/doit setuid root,
- X# by typing
- X# % super doit [ doit args ]
- X
- X# ---------------------------------------------------------------------------
- X# Cmd Full Path Valid-User/Group Patterns
- X
- timeout /usr/local/bin/timeout :operator :wheel gv phillips srk
- restart /usr/local/bin/restart :operator :wheel gv phillips srk
- cdmount /usr/local/bin/cdmount .*
- END_OF_FILE
- if test 917 -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'\" \(2869 characters\)
- sed "s/^X//" >'super.1' <<'END_OF_FILE'
- X.TH SUPER 1 local
- X.SH NAME
- super \- execute commands setuid root.
- X.SH SYNOPSIS
- X.B super
- X.I command
- X[
- X.I args
- X]
- X.SH DESCRIPTION
- X.I Super
- allows users to execute scripts (or other commands) as if they were root.
- It
- is intended to be a secure alternative to making scripts setuid root.
- X.PP
- X.I Super
- consults a file to see if the user is allowed to execute the requested
- X.IR command .
- If so,
- X.I super
- will exec
- X.IR command\ [\ args\ ].
- Root is always permitted to execute any command in the
- super file.
- X.PP
- X.I Super
- without 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
- discarded, save for TERM, LINES, and COLUMNS. To these
- are added reasonable values for:
- X.RS
- X.HP
- USER and LOGNAME: both are set to the username
- of the real user running
- X.IR super ;
- X.HP
- HOME: set to the login directory
- of the real user running
- X.IR super ;
- X.HP
- IFS: set to blank, tab, newline;
- X.HP
- PATH: set to \fI/bin:/usr/bin:/usr/ucb\fP.
- X.HP
- SUPERCMD: 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 \ |\ \-?
- If no arguments are given, or if the first argument is ``\-h'' or ``\-?'',
- X.I super
- prints 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
- may execute, along with the names of the users who may execute each
- command.
- X.SH CREATING SUPER SCRIPTS
- You must be exceedingly careful when writing scripts for
- X.IR super .
- A surprising variety of ordinary commands can, when
- run setuid-root, be exploited for nasty purposes. Always make your
- scripts do as little as possible, and give the user as few options
- as possible.
- X.PP
- Think twice about side-effects and alternative uses
- of these scripts. For instance, you might write a script to allow
- users to mount cd-rom's by executing
- X.IR mount(8) .
- But if you don't write it carefully, a user could mount a floppy
- disk containing, say, a setuid-root shell.
- X.PP
- Security 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
- call transparent to users, so that they can type
- X.ti +.5i
- X% cdmount \fIargs\fP
- X.br
- instead of
- X.ti +.5i
- X% super cdmount \fIargs\fP
- X.br
- You can make a script
- X.I super
- itself by beginning the script in the following way:
- X.in +.5i
- X.nf
- X#!/bin/sh
- prog=`basename $0`
- if [ X$SUPERCMD != X$prog ] ; then
- X exec /usr/local/bin/super $prog "$@"
- fi
- X.fi
- X.in -1i
- X.SH AUTHOR
- Will Deich
- X.br
- will@surya.caltech.edu
- END_OF_FILE
- if test 2869 -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'\" \(3952 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
- X#include "version.h"
- X
- static char SccsId[] = "%W%\t%G%";
- X
- X#ifndef SUPERTAB
- X#define SUPERTAB "/usr/local/lib/super.tab"
- X#endif
- 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
- X * TERM, LINES, and COLUMNS. To these are added reasonable values for
- X * IFS, PATH, USER and HOME (USER and HOME are set to the username and
- X * login directory, respectively, of the person who who runs super).
- X * LOGNAME is set to the same as USER.
- X * Finally, SUPERCMD is set to the name of the super command
- X * being executed. All descriptors excepting 0,1,2 are closed.
- X * Signals are all reset to have default handling.
- X */
- X
- static char *SAFE_IFS = "IFS= \t\n";
- static char *SAFE_PATH = "PATH=/bin:/usr/bin:/usr/ucb";
- X
- char *prog; /* This program */
- char *user; /* who's invoking prog */
- X
- main(argc, argv)
- int argc;
- char **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(SUPERTAB, 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
- char **
- buttonup(cmd)
- char *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 * 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 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 n = getdtablesize();
- 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"); if (env[i]) i++;
- X env[i] = Getenv("LINES"); if (env[i]) i++;
- X env[i] = Getenv("COLUMNS"); if (env[i]) 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
- char *
- Getenv(s)
- char *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}
- END_OF_FILE
- if test 3952 -ne `wc -c <'super.c'`; then
- echo shar: \"'super.c'\" unpacked with wrong size!
- fi
- # end of 'super.c'
- 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'\" \(1225 characters\)
- sed "s/^X//" >'word.c' <<'END_OF_FILE'
- X#include <ctype.h>
- X
- static char SccsId[] = "@(#)word.c 1.1\t12/4/91";
- 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
- char *
- word(s, sep, k)
- register char *s;
- register char *sep; /* word separators */
- int 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 PROGRAM
- X#include <stdio.h>
- X#include <string.h>
- main(argc, argv)
- int argc;
- char **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 1225 -ne `wc -c <'word.c'`; then
- echo shar: \"'word.c'\" unpacked with wrong size!
- fi
- # end of 'word.c'
- fi
- echo shar: End of shell archive.
- exit 0
-