Super(1) is a small program that allows users to execute other programs
(particularly scripts) as root, without unduly compromising security.
From the README:
Super(1) is a setuid-root program that offers
o restricted setuid-root access to executables, adjustable
on a per-program and per-user basis;
o a relatively secure environment for scripts, so that well-written
scripts can be run as root (or some other uid/gid), 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 restrict which users, on which hosts, may execute a
setuid-root program.
- to call a script that allows users to send STOP/CONT
signals to certain jobs, but not others.
-Will
Will Deich
Caltech 105-24, Pasadena, CA 91125
wi...@astro.caltech.edu
-----------------------------
#! /bin/sh
# This is a shell archive. Remove anything before this line, then feed it
# into a shell via "sh file" or similar. To overwrite existing files,
# type "sh file -c".
# Contents: super-3.4.5 super-3.4.5/re_fail.c super-3.4.5/regex.c
# super-3.4.5/sample.cdmount super-3.4.5/sample.tab
# super-3.4.5/strqtok.c super-3.4.5/super.5 super-3.4.5/version.h
# Wrapped by kent@sparky on Sun May 8 15:43:27 1994
PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin:$PATH ; export PATH
echo If this archive is complete, you will see the following message:
echo ' "shar: End of archive 1 (of 3)."'
if test ! -d 'super-3.4.5' ; then
echo shar: Creating directory \"'super-3.4.5'\"
mkdir 'super-3.4.5'
fi
if test -f 'super-3.4.5/re_fail.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'super-3.4.5/re_fail.c'\"
else
echo shar: Extracting \"'super-3.4.5/re_fail.c'\" \(299 characters\)
sed "s/^X//" >'super-3.4.5/re_fail.c' <<'END_OF_FILE'
X
X#ifdef vms
X#include stdio
X#else
X#include <stdio.h>
X#endif
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 299 -ne `wc -c <'super-3.4.5/re_fail.c'`; then
echo shar: \"'super-3.4.5/re_fail.c'\" unpacked with wrong size!
fi
# end of 'super-3.4.5/re_fail.c'
fi
if test -f 'super-3.4.5/regex.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'super-3.4.5/regex.c'\"
else
echo shar: Extracting \"'super-3.4.5/regex.c'\" \(18803 characters\)
sed "s/^X//" >'super-3.4.5/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 */
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];
Xstatic char *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 unsigned char deftab[16] = {
X 0, 0, 0, 0, 0, 0, 0377, 003, 0376, 0377, 0377, 0207,
X 0376, 0377, 0377, 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 18803 -ne `wc -c <'super-3.4.5/regex.c'`; then
echo shar: \"'super-3.4.5/regex.c'\" unpacked with wrong size!
fi
# end of 'super-3.4.5/regex.c'
fi
if test -f 'super-3.4.5/sample.cdmount' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'super-3.4.5/sample.cdmount'\"
else
echo shar: Extracting \"'super-3.4.5/sample.cdmount'\" \(780 characters\)
sed "s/^X//" >'super-3.4.5/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 <'super-3.4.5/sample.cdmount'`; then
echo shar: \"'super-3.4.5/sample.cdmount'\" unpacked with wrong size!
fi
# end of 'super-3.4.5/sample.cdmount'
fi
if test -f 'super-3.4.5/sample.tab' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'super-3.4.5/sample.tab'\"
else
echo shar: Extracting \"'super-3.4.5/sample.tab'\" \(9242 characters\)
sed "s/^X//" >'super-3.4.5/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# Cmd FullPathName [ ed-patterns-of-valid-users/groups/hosts | options ]
X
X# -- The Cmd 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 FullPathName is the path of a program to execute in response to
X# `super Cmd' being typed. If entered in this file as
X# "FullPathName -o1 -o2 ..."
X# then the whitespace-separated options will be used as argv[1],
X# argv[2],..., preceding any arguments the user put on the super
X# command line. Backslash escapes may be used inside the string:
X# backslash-newline is discarded; \q -> q, where q is the quotechar
X# enclosing the string; \x -> plain x.
X
X# -- The format for a users/groups/hosts entry is
X# user[:][@host] or :group[@host] or user:group[@host]
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# -- Since you can specify a host in the entry, a single super.tab file can
X# be used by many different machines. If a host is specified, then
X# the named user and/or group can execute the command only on the
X# specified host(s). If no host is specified, there is no restriction
X# on the host from which a user can execute a command.
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# -- Entries are scanned top-to-bottom until both the command and the
X# user/group/host are matched; the command will be executed
X# using the associated options.
X
X# -- Options may be interspersed with the valid-user entries.
X# Valid options are:
X# info=xxx The info string is printed out when listing commands
X# available to this user.
X# uid=xxx The real+effective uid is set to xxx (xxx is first
X# tried as a name, then a number).
X# gid=xxx The real+effective gid is set to xxx (xxx is first
X# tried as a name, then a number).
X# u+g=xxx The real+effective uid is set to xxx, and the
X# real+effective gid is set to xxx's login gid.
X# env=var[,...] The comma-separated list of names var,... is
X# added to the set of environment variables that are
X# NOT discarded for this command.
X# fd=n[,...] The comma-separated list of descriptors is added
X# to the default set of file descriptors that are
X# NOT closed (0,1,2) before executing this command.
X# password=y|n The user's password is/isn't required before
X# running the command. Default depends on global
X# password setting.
X# **** PLEASE READ THE WARNING UNDER timestampbyhost. ****
X# ** This can also be entered as a global option.
X# ** The local password option, if present, overrides
X# ** the global setting.
X# timeout=mmm For commands requiring a password, the password
X# will remain valid for mmm minutes after entering it.
X# Default depends on global timeout setting.
X# timeout <= 0 requires password every time.
X# ** This can also be entered as a global option.
X# ** The local timeout option, if present, overrides
X# ** the global setting.
X
X# -- There are a few global options, which must be entered before any
X# other non-comment lines. (The results are undefined if global
X# options don't come first.) Use Cmd="/", FullPathName="/", then
X# set the global options in the same way as regular options:
X# / / logfile=xxx patterns=yyy ...
X# Valid global options are:
X# timestampbyhost=y|n If y (the default), timestamp files for
X# user joe@host are TIMESTAMP_DIR/host/joe. Otherwise,
X# the file is TIMESTAMP_DIR/joe.
X# WARNING: the hostname used is that from gethostname().
X# Note that this is not necessarily unique across
X# internet domains, since it is frequently not a
X# fully-qualified domain name. Therefore you should
X# not share timestamp directories with hosts outside
X# the local domain.
X# timestampuid=xxx Timestamp files are created using uid=xxx.
X# Useful when a network of hosts are sharing a
X# cross-mounted timestamp directory, but the network is
X# configured so that root is "nobody" for NFS access.
X# This option allows you to have the file created/opened
X# under another uid that does have cross-host access,
X# such as the uid of a system manager.
X# Default: timestampuid=0.
X# logfile=xxx Each time super is used (except for super -h), a
X# message is written to logfile `xxx' showing the
X# timestamp, attempted command, and success/failure.
X# loguid=xxx The logfile is opened for writing using uid=xxx.
X# Useful when a network of hosts are sharing a
X# cross-mounted logfile, but the network is configured so
X# that root is "nobody" for NFS access. This option
X# allows you to have the file created/opened under
X# another uid that does have cross-host access, such
X# as the uid of a system manager.
X# Default: loguid=0.
X# syslog=y|n The same info as used in logfile=xxx is passed
X# to syslog(). Only valid if compiled with USE_SYSLOG.
X# mail="...." Notices of super failures are piped to the shell
X# command "....", e.g.
X# mail="mailx -s 'Super...' joeblow" (SysV)
X# or
X# mail="mail -s 'Super...' joeblow" (BSD)
X# password=y|n The user's password is/isn't required before
X# running the command. Default n.
X# **** PLEASE READ THE WARNING UNDER timestampbyhost. ****
X# ** This can also be entered as a local option.
X# ** The local password option, if present, overrides
X# ** the global setting.
X# timeout=mmm For commands requiring a password, the password
X# will remain valid for mmm minutes after entering it.
X# Default=5. timeout <= 0 requires password every time.
X# ** This can also be entered as a local option.
X# ** The local timeout option, if present, overrides
X# ** the global setting.
X# renewtime=y|n If renew == y, then each valid use of a password-
X# requiring command updates the timestamp to the
X# current time. Default=n.
X# patterns=yyy By default, valid user/group/hosts are interpreted
X# as ed-style regular expressions. You can choose
X# the pattern-matching style by setting
X# patterns=regex # usual ed-style
X# patterns=shell # Bourne-shell style
X# # pattern matching.
X# See the super.5 man page for details of these
X# pattern-matching styles.
X
X# Example entry 1:
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# Example entry 2:
X# doit /usr/local/bin/doit joe@PublicWorkstation \
X# password=y u+g=smith env=TZ,TAPE \
X# info="This command does it all \
X# Usage: doit args..." \
X# password=y timeout=0
X
X# doit /usr/local/bin/doit joe \
X# u+g=smith env=TZ,TAPE \
X# info="This command does it all" \
X# password=n
X
X# ...allows user joe to run /usr/local/bin/doit with (1) uid=smith and
X# gid = smith's login gid; (2) keeps the environment variables
X# TZ and TAPE (in addition to the standard set); and (3) requires
X# the user's password before proceeding, iff joe is at PublicWorkstation.
X# Note that the order of entries is significant, because super stops scanning
X# the file as soon as it finds matches the command plus user/group/host;
X# had the above pair been the other way around, the non-password version
X# would always match and joe would never need to enter a password.
X# Note that the info line is split across multiple lines; it will be printed
X# that way, too when help info is given.
X
X# When super is giving help, this command is listed as:
X#
X# # This command does it all:
X# super doit -> /usr/local/bin/doit
X
X
X
X# ---------------------------------------------------------------------------
X# ---------------------------------------------------------------------------
X
X# Global options. Use "/" as placeholders for the required cmd and
X# fullpath. Enable loguid and timestampuid options to have the logfile
X# and timestamp files created/opened under uid=sysmgr.
X/ / logfile=/usr/adm/super.log \
X mail="mailx -s joeblow" \
X renewtime=y # timestampuid=sysmgr loguid=sysmgr
X
Xtimeout /usr/local/bin/timeout :operator :wheel gv phillips srk \
X info="Temporarily stop any processes of any user:" \
X password=y
X
Xrestart /usr/local/bin/restart :operator :wheel gv phillips srk \
X info="Restart a timeout'd process before the scheduled time:"
X
X
X# 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 \
X info="Mounts a CD-ROM on /cdrom" \
X tas@elgar \
X :xyz@{alpha,delta} \
X .*@+india
END_OF_FILE
if test 9242 -ne `wc -c <'super-3.4.5/sample.tab'`; then
echo shar: \"'super-3.4.5/sample.tab'\" unpacked with wrong size!
fi
# end of 'super-3.4.5/sample.tab'
fi
if test -f 'super-3.4.5/strqtok.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'super-3.4.5/strqtok.c'\"
else
echo shar: Extracting \"'super-3.4.5/strqtok.c'\" \(15162 characters\)
sed "s/^X//" >'super-3.4.5/strqtok.c' <<'END_OF_FILE'
Xstatic char RcsId[] = "$Header: /data1/src/super/RCS/strqtok.c,v 1.10 1994/04/10 21:57:18 will Exp $";
X#include <ctype.h>
X#include <string.h>
X
X/* strqtok.c, v2.1
X * Last Modified: 13 July 1993
X * Will Deich
X */
X
X/* If STRQTOK_DEBUG is defined, then "int strqtok_debug" is defined
X * and initialized to 0. If another routine sets strqtok_debug !0,
X * then the input arguments are printed at the beginning of each call
X * to strqtok() and the to-be returned token value is printed at the end of
X * the routine.
X * If STRQTOK_DEBUG is undefined, then strqtok() is compiled without
X * strqtok_debug and without any calls to stdio routines.
X */
X#define STRQTOK_DEBUG
X
X#ifdef STRQTOK_DEBUG
X#include <stdio.h>
Xint strqtok_debug=0;
X#endif
X
X#define translate_octal(cc, s) \
X{ \
X cc = 0; \
X if (isdigit(*s) && *s != '8' && *s != '9') { \
X cc = *s++ - '0'; \
X if (isdigit(*s) && *s != '8' && *s != '9') { \
X cc <<= 3; \
X cc |= *s++ - '0'; \
X if (isdigit(*s) && *s != '8' && *s != '9') { \
X cc <<= 3; \
X cc |= *s++ - '0'; \
X } \
X } \
X } \
X}
X
X#define translate_hex(cc, s) \
X for (cc = 0; isxdigit(*s); s++) { \
X cc <<= 4; \
X cc |= isdigit(*s) ? *s - '0' : \
X islower(*s) ? *s + 10 - 'a' : *s + 10 - 'A'; \
X }
X
X/* The pointer into the string, indicating where to start next token search.
X * The purpose of making it extern is that the caller can stash away
X * a copy of strq_start, process another string, then restore the pointer
X * and finish processing the first string.
X */
Xchar *strq_start=NULL;
X
X/* strqtok(s, delim, quotemarks, commentchars, flags)
X * ...is same as strtok(s, delim), except that
X * o quotemarks: tokens are quoted by pairs of these characters,
X * allowing characters that are normally
X * token delimiters to be part of a token.
X * o commentchars: if one of these characters occurs outside a
X * quoted token, it ends the line as if it were '\0'.
X * o flags:
X * flags & 01 -> quotemarks are stripped from tokens.
X * Default: quotemarks are left in returned
X * tokens.
X * flags & 02 -> use ANSI-C rules for interpreting backslash
X * sequences. If flag 02 is enabled, the
X * backslash must NOT be part of the delimiter,
X * quotemark, or comment char sets, or else the
X * results are undefined. This flag conflicts
X * with and overrides flag 0200.
X * Default: no ANSI C rules used.
X * flags & 04 -> don't stop at first delimiter; write the null,
X * then skip any additional delimiters before the
X * next word; upon return, the internal pointer
X * is left pointing to the next token or the
X * final NULL. See also flags 020 and 0100.
X * Default: processing stops at first delimiter;
X * the internal pointer is left pointing to
X * the character following the delimiter.
X * flags & 010 -> ANSI-C trigraphs are interpreted and replaced
X * with their corresponding characters. Implies
X * bit 02 (backslash sequences). This conflicts
X * with and overrides flag 0200 (shell-style
X * backslash interpretation).
X * flags & 020 -> allow empty tokens: force delimiter strings to
X * always be 1 character long, so that foo::bar
X * is interpreted as three tokens (`foo', `',
X * and `bar'), instead of the default two tokens.
X * This flag conflicts with bit 04 (don't stop at
X * first delim in string) and overrides it.
X * flags & 040 -> Don't modify the string, including don't write
X * write a null to delimit each token and don't
X * rewrite backslash sequences. This flag
X * conflicts with flags 01 (strip quotes)
X * 02 and 0200 (interpreting backslash sequences),
X * and 010 (interp. trigraphs), and overrides them.
X * Note: although backslash sequences aren't
X * re-written, strqtok() will still properly
X * interpret them when finding token boundaries.
X * flags & 0100 -> Comments are newline-terminated. By default,
X * comments run to the end of the string. This
X * flag makes comments end at a newline, and the
X * comment is then treated as ordinary whitespace:
X * more tokens can follow it. Note: if this flag
X * is set, comments are always skipped over before
X * returning a pointer to the token, regardless of
X * the setting of flags & 04. (This flag is a bad
X * hack; the better method would be to take a new
X * argument which has comment-terminators, but
X * that would require changing the strqtok
X * interface).
X * flags & 0200 -> use the following approximation of Bourne
X * shell rules for interpreting backslash-X:
X * A) If X is null: the backslash is discarded.
X * B) \newline is discarded entirely.
X * C) Otherwise, if outside a quote: \X is
X * replaced by a plain X, and not treated
X * as a delimiter, quotemark, or
X * comment character.
X * D) Otherwise we are inside a quote: discard
X * the backslash if preceding left quote
X * or another backslash; and in any case
X * the following character is plain:
X * \\ -> \
X * \Q -> Q (where Q is the quote character
X * used to start the the quote)
X * \X -> \X (ie the backslash is a plain
X * character)
X * If flag 0200 is enabled, the backslash must
X * NOT be part of the delimiter, quotemark, or
X * comment char sets, or else the results are
X * undefined.
X * See also flag 02.
X *
X * Note: strqtok() and strtok() are completely independent,
X * so one may safely use them in parallel.
X
X * See man page for details.
X */
X
Xchar *
Xstrqtok(
X s, /* String to tokenize. NULL to continue same str */
X delim, /* Token delimiters. Can be changed w/ each call. */
X quotemarks, /* Quotation marks. Can be changed w/ each call. */
X commentchars, /* Comment characters. Can be changed w/ each call. */
X flags) /* flags & 01 -> strip quotes;
X * flags & 02 -> enable backslash escapes;
X * flags & 04 -> skip all delims before return;
X * flags & 010 -> trigraphs (also implies 02);
X * flags & 020 -> allow empty tokens.
X * flags & 040 -> don't modify the string.
X * flags & 0100 -> Comments are newline-terminated.
X * flags & 0200 -> Shell-type backslash use.
X */
Xchar *s, *delim, *quotemarks, *commentchars;
Xunsigned int flags;
X{
X register char *p, *q;
X char c;
X char leftquote;
X char *token;
X#ifdef __STDC__
X char *Cchar(char *, char *, int, int *);
X#else
X char *Cchar();
X#endif
X int bn, any_backslash, backslashed, inquote, intok;
X
X /* Modify the string? */
X int modify = !(flags & 040);
X /* Strip quotemarks from tokens? */
X int stripquote = (flags & 01) && modify;
X /* Interpret C-style backslash sequences? */
X int c_backslash = ((flags & 02) | (flags & 010));
X /* Interpret shell-style backslash sequences? */
X int sh_backslash = ((flags & 0200) && !c_backslash);
X /* Skip sequence of delimiters at end of token? */
X int skipdelim = (flags & 04) && (flags & 020);
X /* Interpret trigraphs? */
X int trigraph = (flags & 010) && modify;
X /* Allow empty tokens? */
X int allow_empty = flags & 020;
X /* comments are newline-terminated? */
X int cmt_nl = flags & 0100;
X
X /* have any backslash rules been enabled? */
X any_backslash = c_backslash || sh_backslash;
X
X /* New string? */
X if (s)
X strq_start = s;
X
X if (!strq_start)
X return (char *) NULL;
X
X#ifdef STRQTOK_DEBUG
X if (strqtok_debug) {
X (void) fprintf(stderr, "@@@ strqtok: start with `%s'\n", strq_start);
X (void) fprintf(stderr, "@@@ strqtok: delim = `%s'\n", delim);
X (void) fprintf(stderr, "@@@ strqtok: quotemarks = `%s'\n", quotemarks);
X (void) fprintf(stderr, "@@@ strqtok: commentchars = `%s'\n",
X commentchars);
X (void) fprintf(stderr, "@@@ strqtok: flags = %#o\n", flags);
X (void) fprintf(stderr,
X "@@@ strqtok: modify=%d stripquote=%d\n",
X modify, stripquote);
X (void) fprintf(stderr,
X "@@@ strqtok: c_backslash=%d sh_backslash=%d\n",
X c_backslash, sh_backslash);
X (void) fprintf(stderr,
X "@@@ strqtok: skipdelim=%d trigraph=%d allow_empty=%d cmt_nl=%d\n",
X skipdelim, trigraph, allow_empty, cmt_nl);
X }
X#endif
X
X /* Initialize p; skip leading delimiters if empty tokens not allowed */
X if (allow_empty)
X p = strq_start;
X else
X for (p=strq_start, bn=0; *p && (strchr(delim, *p)
X || (bn = (*p=='\\' && sh_backslash && *(p+1) == '\n'))); p++) {
X if (bn) {
X /* bn is backslash-newline */
X p++;
X bn = 0;
X }
X }
X
X if (!(*p) || strchr(commentchars, *p))
X return (char *) NULL;
X
X /* Set `token' to point to returned string.
X * Use p and q to walk through the user's string:
X * p will follow input characters;
X * q will overwrite w/ outputted characters, minus possibly-stripped
X * quotes and including nulls after each token.
X */
X token = q = p;
X inquote = 0;
X intok = 1;
X if (c_backslash && modify) {
X while (intok && (p=Cchar(p, &c, trigraph, &backslashed))) {
X if (backslashed) {
X *q++ = c; /* treat as plain character */
X } else if (!inquote && *delim && strchr(delim, c)) {
X *q = '\0'; /* Reached end of token */
X intok = 0;
X } else if (!inquote && *commentchars && strchr(commentchars, c)) {
X *q = '\0'; /* Reached end of token */
X if (cmt_nl && (p = strchr(p, '\n')))
X p++;
X else
X p = NULL;
X intok = 0;
X } else if (!inquote && *quotemarks && strchr(quotemarks, c)) {
X inquote = 1; /* Beginning a quoted segment */
X leftquote = c; /* Save quote char for matching with */
X if (!stripquote) *q++ = c;
X } else if (inquote && leftquote == c) {
X inquote = 0; /* Ending a quoted segment */
X if (!stripquote)
X *q++ = c;
X } else {
X *q++ = c; /* Ordinary character */
X }
X }
X strq_start = p;
X *q = '\0';
X } else {
X /* Assert: ( (c_backslash && !modify) || !c_backslash )
X * Given this assertion, we note that if either form of backslash
X * processing is enabled, we can _act_ as if the sh_backslash rule
X * is correct, since it won't affect which characters go into tokens
X * if !modify is true, and it will be the correct backslash rule
X * otherwise.
X */
X while (intok && *p) {
X if (any_backslash && *p == '\\') {
X /* at ``\X''; Advance to X character and process it */
X if (! *(++p))
X /* reached end of string */
X break;
X
X /* Discard the backslash we just advanced past only if we
X * are not in quote or it was preceding left quote or
X * backslash; in any case the following character is plain.
X */
X if (inquote && (*p != '\\' && *p != leftquote))
X if (modify)
X *q++ = '\\';
X
X if (modify)
X *q++ = *p++;
X else
X p++;
X
X } else if (!inquote && *delim && strchr(delim, *p)) {
X if (modify) *q = '\0'; /* Reached end of token */
X p++; /* advance p for next token */
X intok = 0;
X } else if (!inquote && *commentchars && strchr(commentchars, *p)) {
X if (modify) *q = '\0'; /* Reached end of token */
X if (cmt_nl && (p = strchr(p, '\n')))
X p++;
X else
X p = NULL;
X intok = 0;
X } else if (!inquote && *quotemarks && strchr(quotemarks, *p)) {
X inquote = 1; /* Beginning a quoted segment */
X leftquote = *p++; /* Save quote char for matching with */
X if (modify && !stripquote)
X *q++ = leftquote;
X } else if (inquote && leftquote == *p) {
X inquote = 0; /* Ending a quoted segment */
X p++;
X if (modify && !stripquote)
X *q++ = leftquote;
X } else {
X if (modify)
X *q++ = *p++;
X else
X p++;
X }
X }
X strq_start = p;
X if (modify) *q = '\0';
X }
X#ifdef STRQTOK_DEBUG
X if (strqtok_debug)
X (void) fprintf(stderr, "@@@ strqtok: token is `%s'\n", token);
X#endif
X
X if (skipdelim && strq_start) {
X /* Skip trailing delimiters */
X bn = 0;
X while (*strq_start && (strchr(delim, *strq_start)
X || (bn = (*strq_start=='\\' &&
X sh_backslash && *(strq_start+1) == '\n')))) {
X if (bn) {
X strq_start++;
X bn = 0;
X }
X strq_start++;
X }
X }
X return token;
X}
X
Xchar *
XCchar(
X s, /* string from which to collect character */
X c, /* Return character here */
X do_trigraph, /* If !0, interpret trigraphs according to ANSI rules */
X backslashed) /* Return !0 if character was started w/ backslash */
X
X /* Gets the leading ANSI C character from a string. Here, "ANSI C
X * character" means that the string's leading characters are
X * interpreted according to ANSI C rules; i.e. backslash escapes
X * are properly interpreted. However, ANSI trigraphs are treated
X * as plain characters unless do_trigraph !=0.
X * Return value is ptr to next character in s; when at end of string,
X * returned ptr is NULL.
X */
Xregister char *s, *c;
Xint do_trigraph, *backslashed;
X{
X register char ch;
X
X if ((*backslashed = (*s == '\\'))) {
X switch (*++s) {
X#ifdef __STDC__
X case 'a':
X *c = '\a';
X break;
X#endif
X case 'b':
X *c = '\b';
X break;
X case 'f':
X *c = '\f';
X break;
X case 'n':
X *c = '\n';
X break;
X case 'r':
X *c = '\r';
X break;
X case 't':
X *c = '\t';
X break;
X case 'v':
X *c = '\v';
X break;
X case '\\':
X *c = '\\';
X break;
X case '^':
X *c = '^';
X break;
X case '\'':
X *c = '\'';
X break;
X case '"':
X *c = '"';
X break;
X case '?':
X *c = '?';
X break;
X case '0':
X case '1':
X case '2':
X case '3':
X case '4':
X case '5':
X case '6':
X case '7':
X translate_octal(ch, s);
X s--;
X *c = ch;
X break;
X case 'x':
X s++;
X translate_hex(ch, s);
X s--;
X *c = ch;
X break;
X default:
X *c = *s;
X break;
X }
X } else if (do_trigraph && (*s == '?') && (*(s+1) == '?')) {
X switch (*(s+2)) {
X case '=':
X *c = '#';
X s += 2;
X break;
X case '(':
X *c = '[';
X s += 2;
X break;
X case '/':
X *c = '\\';
X s += 2;
X break;
X case ')':
X *c = ']';
X s += 2;
X break;
X case '\'':
X *c = '^';
X s += 2;
X break;
X case '<':
X *c = '{';
X s += 2;
X break;
X case '!':
X *c = '|';
X s += 2;
X break;
X case '>':
X *c = '}';
X s += 2;
X break;
X case '-':
X *c = '~';
X s += 2;
X break;
X default:
X /* Not a trigraph sequence */
X *c = *s;
X }
X *c = *s;
X } else {
X *c = *s;
X }
X return (*s) ? s+1 : NULL;
X}
X
X#ifdef TEST
X
X#ifndef STRQTOK_DEBUG
X#include <stdio.h>
X#endif
X
X#include <stdlib.h>
Xmain()
X{
X char delim[1000];
X char quotes[1000];
X char comments[1000];
X char buf[1000];
X unsigned int flags;
X char *s;
X
X printf("Enter token delimiters: ");
X gets(delim);
X printf("Enter quote marks: ");
X gets(quotes);
X printf("Enter comment marks: ");
X gets(comments);
X printf("Enter flags:\n");
X printf(" 01 -> strip quotemarks from tokens\n");
X printf(" 02 -> interpret backslash sequences\n");
X printf(" 04 -> skip delimiters before returning\n");
X printf(" 010 -> interpret trigraphs (implies 02)\n");
X printf(" 020 -> allow empty tokens (implies !04)\n");
X gets(buf);
X flags = strtoul(buf, NULL, 0);
X printf("flags = %#o\n", flags);
X
X while (1) {
X printf("Enter string to parse: ");
X gets(buf);
X for (s = strqtok(buf, delim, quotes, comments, flags); s;
X s = strqtok(NULL, delim, quotes, comments, flags))
X printf("\t`%s'\n", s);
X }
X}
X#endif
END_OF_FILE
if test 15162 -ne `wc -c <'super-3.4.5/strqtok.c'`; then
echo shar: \"'super-3.4.5/strqtok.c'\" unpacked with wrong size!
fi
# end of 'super-3.4.5/strqtok.c'
fi
if test -f 'super-3.4.5/super.5' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'super-3.4.5/super.5'\"
else
echo shar: Extracting \"'super-3.4.5/super.5'\" \(14758 characters\)
sed "s/^X//" >'super-3.4.5/super.5' <<'END_OF_FILE'
X.TH SUPER 5 local
X.SH NAME
Xsuper.tab \- database of restricted commands for super(1)
X.SH DESCRIPTION
XThe
X.I super.tab
Xfile contains the restrictions on who can execute commands with
X.I super(1).
XIt may also contain options that modify the uid and/or gid under which
Xa command is run; the list of environment variables that are discarded before
Xexecuting a command, and so on.
X.PP
X.I Super.tab
Xis formatted as a series of control lines of the form
X.ti +.5i
X.sp
XCmd\ \ FullPathName\ \ options+Valid-User-List [ # comment-text ]
X.sp
X.br
XFields are whitespace-separated.
XWhitespace may be included in a field by enclosing text in single- or
Xdouble-quotes (the quoting is shell-like, in the sense that quotemarks
Xdon't have to surround the entire field, and you can switch between
Xquotemark types in a single entry. For instance, \fBX"a\ b"Y'd\ e'\fR
Xis equivalent to \fB"Xa\ bYd\ e"\fR).
XComments begin with a `#' and continue to the end of a line.
X.PP
XThere can also be blank or comment lines without any control data.
X.PP
XA control line may be continued by preceding the newline
Xwith a backslash, and indenting the continuation line with whitespace.
XThe indentation requirement helps super(1) catch typos.
XComments may be placed before each backslash, not just at the end
Xof a continued control line.
X.PP
XIn response to the user typing
X.I
X\fIsuper cmd \fR[\fIargs\fR],
Xthe
X.I cmd
Xis searched for in the
X.I super.tab
Xfile.
X.IR Cmd 's
Xmust begin in column 1.
X.PP
XIf the user is permitted to execute the command,
X.I FullPathName
Xgives the name of the actual command that will be executed with
X.IR execve() ,
Xand it can optionally contain a list of initial arguments that precede any
Xarguments passed by the user.
XFor example,
X.ti +.5i
X\fBxyz "/usr/local/bin/blah \-o1 \-o2 \-xrm 'a b c'" ...\fR
X.br
Xspecifies that when a valid user types
X.BR "super xyz" ,
Xthe command to execute is
X.B /usr/local/bin/blah
Xand its arguments will be
X.RS
Xargv[1]: \fB\-o1\fR
X.br
Xargv[2]: \fB\-o2\fR
X.br
Xargv[3]: \fB\-xrm\ 'a b c'\fR
X.RE
Xfollowed by any arguments that the user put on the super command line.
XThe
X.I FullPathName
Xstring is parsed using rules similar to the Bourne shell rules:
Xfor backslashes in quoted strings, namely:
X.RS
X.br
X(a) backslash-newline is discarded;
X.br
X(b) Otherwise, if outside a quoted substring,
X\fB\\\fIx\fR \(-> plain \fIx\fR, which will not be treated as a delimiter,
Xquotemark, or comment character;
X.br
X(c) Otherwise, inside a quoted substring of \fIFullPathName\fR:
X.RS
X\fB\\\\\fR \(-> \fB\\\fR;
X.br
X\fB\\\fR\fIq\fR \(-> \fIq\fR,
Xwhere
X.I q
Xis the quote character that encloses the
X.I FullPathName
Xstring;
X.br
Xother backslashes are preserved: \fB\\x\fR \(-> \fB\\x\fR.
X.RE
X.PP
XAfter writing a command with such backslash escapes,
Xyou should certainly use ``super -d cmd'' to check
Xthat your args are being parsed
Xas expected before you offer this command to your users.
X.PP
XThe same
X.I cmd
Xcan be listed several times in the
X.I super.tab
Xfile, in which case the first entry that allows the user to
Xexecute the command is chosen.
XThe EXAMPLES section shows how this can be useful.
X.PP
X``Local options'' and lists of valid users may be mixed together.
XThe options have the form
X.IR key = value ;
Xin the unlikely event that a valid-user pattern requires an equal sign,
Xit must be escaped by preceding it with a backslash.
XThe other whitespace-separated words on the control line form the list of
Xusers who may execute
X.I Cmd.
XEach word is a pattern in one of the formats
X.in +.5i
X.sp
X\(bu \fIuser\fR[\fB:\fR][\fB@\fIhost\fR]
X.sp
X\(bu \fR[\fB:\fIgroup\fR][\fB@\fIhost\fR]
X.sp
X\(bu \fIuser\fR[\fB:\fIgroup\fR][\fB@\fIhost\fR]
X.sp
X.in -.5i
XThe user's login name must match the
X.I user
Xpattern; the user must belong to a group whose name matches the
X.I group
Xpattern; and the hostname must match the
X.I host
Xpattern.
XIf the
X.I user,
X.I group,
Xor
X.I host
Xpart is not given, there are no corresponding restrictions.
XSince you can restrict users to particular hosts, a single super.tab file
Xcan be shared among many different machines.
XIf the
X.I host
Xpart is of the form
X.BI + xyz ,
Xthen
X.I xyz
Xis interpreted as a netgroup name and any host in netgroup
X.I xyz
Xis matched. In that case,
X.I xyz
Xis taken literally, and
X.I not
Xinterpreted as a pattern to be matched.
XNote: netgroup lookup is only
Ximplemented if the function
X.I innetgr()
Xis available and if
X.I super(1)
Xwas compiled with \-DUSE_NETGROUP.
X.PP
XThe patterns for valid users, groups, and hosts can be either
Xshell-style patterns (``regex'' \(em the BSD\ 4.x syntax used in the
X.I re_comp()/re_exec()
Xroutines) or
XBourne-shell-style
Xregular expressions. The default is ``regex''.
XIn both cases the patterns are extended to support csh-style
Xbrace expansion.
XFor instance,
X.B a{x,y,z}b
Xstands for the set of patterns
X.BR "axb ayb azb" .
X.PP
XAll pattern matching is "anchored"; i.e. patterns
Xare forced to match the entire username, groupname, or hostname.
X.PP
X.B Local Options
X.br
XLocal options apply only to the control line on which they are entered.
XThe following local options are recognized:
X.HP
X.BI info= xxx
X.br
XThe string
X.I xxx
Xis printed when giving help to users. It should be set to a helpful
Xone-line description of the command.
X.HP
X.BI uid= xxx
X.br
XSet the real and effective uid to
X.I xxx
Xjust before executing the command.
XThe uid
X.I xxx
Xis first tried as a login name and then as a number.
X.HP
X.BI gid= yyy
X.br
XSet the real and effective gid to
X.I yyy
Xjust before executing the command.
XThe gid
X.I yyy
Xis first tried as a group name and then as a number.
X.HP
X.BI u+g= zzz
X.br
XSet the real and effective uid to
X.I zzz
Xand the real and effective gid to
X.IR zzz 's
Xlogin gid, just before executing the command.
XSince this option conflicts with
X.BI uid=xxx
Xand
X.BI gid=yyy,
Xit may not be mixed with them.
X.HP
X\fBenv=\fIname\fR[,...]
X.br
XEach
X.I name
Xin the comma-separated list is an environment variable which should
X.I not
Xbe deleted before executing the
X.IR Cmd ;
Xthese variables are in addition to the normal variables created or passed
Xby super (TERM, IFS, PATH, USER, LOGNAME, HOME, ORIG_USER, ORIG_LOGNAME,
XORIG_HOME, LINES, COLUMNS, SUPERCMD).
XBe careful here; environment variables can sometimes be abused to create
Xsecurity holes.
X.HP
X\fBfd=\fInn\fR[,...]
X.br
XEach file descriptor
X.I nn
Xin the comma-separated list should
X.I not
Xbe closed before executing the
X.I Cmd.
XThese descriptors are added to the usual set of descriptors kept open,
Xnamely 0, 1, 2.
X.HP
X\fBnargs=[\fImmm\fB\-]\fInnn\fR
X.br
XThe user is required to enter
X.I mmm - nnn
Xarguments to the command. If just
X.I nnn
Xis given, the user must enter exactly
X.I nnn
Xarguments. These arguments are in addition to any arguments entered
Xin the command part of the super.tab file. The default is to allow
Xthe user to enter any number of arguments.
X.HP
X\fBpassword=\fIy\fR|\fIn\fR
X.br
XIf \fIy\fR, the user's password is required before the command is executed
XThe default is taken from the global \fIpassword\fR setting.
XSee also the options
X.I timeout
Xand
X.IR renewtime ,
Xand be sure to read the warning under
X.I timestampbyhost.
X.sp
XThe \fIpassword\fR option can be used as
X.I both
Xlocal and global options. If used as a local option, it
Xoverrides the setting from the global option.
X.HP
X\fBtimeout=\fIm\fR
X.br
XPasswords are good for \fIm\fR minutes; that is, the command may be executed
Xwithout re-entering the user's password
Xfor \fIm\fR minutes after the last time the user's password was entered
X(for any command). After \fIm\fR minutes, the password will be required
Xagain before the command can be executed. If \fItimeout\fR is zero or
Xnegative, a password is required every time the command is used.
XThe default is taken from the global \fItimeout\fR setting.
XThe timestamp for user \fIusr\fR is recorded in the file
X\fBTIMESTAMP_DIR/fR directory (see \fBtimestampbyhost\fR and
Xthe FILES section, below).
X.sp
XThe \fItimeout\fR option can be used as
X.I both
Xlocal and global options. If used as a local option, it
Xoverrides the setting from the global option.
X.PP
X.B "Global Options"
X.br
XGlobal options affect the overall
X.I super
Xprocessing. To set them, the FIRST non-comment line of the super.tab file
Xshould read
X.ti +.5i
X.sp
X/ / \fIglobaloptions\fP
X.sp
X.br
XThat is, the command name and full pathname are both ``/''.
XIf these options appear mixed in with regular control lines, the
Xresponse of super(1) is undefined.
X.HP
X\fBtimestampuid=\fIxxx\fR
X.br
XIf a password is required, the time at which it was entered is
Xrecorded as the mtime of a timestamp file. The timestamp file
Xis normally created with owner=root; however, this option
Xcauses it to be created/modified using uid=\fIxxx\fR
X(\fIxxx\fR can be either a username or numeric uid).
XThis option is useful when a network of hosts are
Xsharing a cross-mounted timestamp directory. Note that networks
Xare typically configured to not allow root on one
Xhost to have root access to files on another host,
Xwhich will forbid root on other hosts from creating
Xthe timestamp file unless it's world-writable.
XThis option allows you to
Xhave the file created/opened under another uid that
Xdoes have cross-host access, such as the uid of a
Xsystem manager.
XDefault: timestampuid=0.
X.HP
X\fBtimestampbyhost=\fIy\fR|\fIn\fR
X.br
XIf \fIy\fR (default), the timestamp files are given names that are
Xunique on each host. For instance, \fIjoeuser@somehost\fR will be
Xgiven a timestamp file named \fITIMESTAMP_DIR/somehost/joeuser\fR,
Xwhere \fITIMESTAMP_DIR\fR is defined in the FILES section.
XIf \fItimestmapbyhost=n\fR, the timestamp files are given names
Xthat are unique to each user, but not unique per host. For instance,
X\fIjoeuser\fR on any host will be given a timestamp file named
X\fITIMESTAMP_DIR/joeuser\fR.
X.br
X.I WARNING:
XThe hostname used is that from
X.I gethostname().
XNote that this is not necessarily unique across
Xinternet domains, since it is frequently not a
Xfully-qualified domain name. Therefore you should
Xnot share timestamp directories with hosts outside
Xthe local domain.
X.HP
X\fBlogfile=\fIxxx\fR
X.br
XEnables logging information.
XEach invocation of super() (aside from ones for help) generates an entry in
Xfile
X.IR xxx .
X.HP
X\fBloguid=\fIxxx\fR
X.br
XIf logging is enabled with logfile=\fIxxx\fR, the
Xlogfile will be opened for writing using uid=\fIxxx\fR
X(can be either a username or numeric uid).
XThis option allows you to
Xhave the file created/opened under another uid that
Xdoes have cross-host access, such as the uid of a
Xsystem manager.
X(See
X.I timestampuid=xxx
Xfor additional comments).
XDefault: loguid=0.
X.HP
X\fBsyslog=\fIy\fR|\fIn\fR
X.br
XLogging information is passed to
X.IR syslog(3) .
XError messages are logged at priority LOG_ERR, and successful
Xattempts to run programs are logged at priority LOG_INFO.
XThis is independent of the setting of the
X.BR logfile
Xoption (above),
Xand is only available if the function
X.I syslog(3)
Xis available and
X.I super(1)
Xwas compiled with \-DUSE_SYSLOG.
X.HP
X\fBmail="\fImail-command\fB"\fR
X.br
XNotices of super failures are piped to the shell command
X.IR mail-command .
XFor instance,
X\fImail="mail -s Super joeblow"\fR
Xwill cause error messages to be mailed to user
X.I joeblow
X(on some systems you may need to substitute
X.I mailx
Xfor
X.IR mail ).
XNote: the
X.I mail-command
Xis executed by passing it as an argument to
X.I popen(3).
X.HP
X\fBpassword=\fIm\fR
X.br
XThe \fIpassword\fR option can be used as
X.I both
Xglobal and local options. If used as a local option, it
Xoverrides the setting from the global option.
XSee the local option entry for \fIpassword\fR for usage information,
Xand be sure to read the warning under the
X.I timestampbyhost
Xoption.
X.HP
X\fBtimeout=\fIm\fR
X.br
XThe \fItimeout\fR option can be used as
X.I both
Xglobal and local options. If used as a local (per-command) option, it
Xoverrides the setting from the global option.
XThe default is \fItimeout=\fR5 minutes.
XSee the local option entry for \fItimeout\fR for usage information.
X.HP
X\fBrenewtime=\fIy\fR|\fIn\fR
X.br
XIf \fIy\fR, the user's timestamp file is updated to the current time
Xwhenever a password-requiring command is executed.
X.HP
X\fBpatterns=\fIxxx\fR
X.br
XSelect the pattern-matching type for the user/group/host patterns.
XThe string
X.I xxx
Xmust be one of:
X.RS
X.HP
X.B regex
X\(em patterns are ed-style
Xregular expressions, using the rules embodied in the BSD\ 4.x routines
X.IR re_comp()/re_exec() ,
Xwith the addition of csh-style brace expansion.
XThis is the default.
X.HP
X.B shell
X\(em patterns are approximately Bourne-shell style, with the addition
Xof csh-style brace expansion. The patterns are formed from:
X.RS
X.HP
X\\x force x to be a plain character;
X.HP
X? matches any single character;
X.HP
X* matches any sequence of 0 or more chars;
X.HP
X[\fIchars\fP] matches any single character in the set;
X.HP
X[^\fIchars\fP] matches any single char NOT in the set;
X.HP
X^\fIpat\fP inverts the sense of the match --
Xthe string must NOT match the pattern.
X.RE
X.RE
X.SH FILES
X.HP
X\fB/usr/local/lib/super.tab\fR The usual location of the \fIsuper.tab\fR file.
X(\fISuper\fR can be compiled with a different path, however.)
X.HP
X\fB/usr/local/lib/superstamps/\fIusername\fR
XThe name of the file whose \fImtime\fR is used as the timestamp for the
Xlast time the user entered his or her password for password-requiring
Xcommands.
X(Again, this path can be changed at compile time.)
X.SH EXAMPLES
X.HP
XExample 1.
XThe control line
X.sp
X.in +.5i
X.nf
Xdoit /usr/local/bin/doit me \\
X you@{h1,h32} \\
X ja.*:ok_j \\
X :goodguys
X.fi
X.in -.5i
X.sp
Xallows /usr/local/bin/doit to be run setuid-root by
X.in +.5i
X\(bu user "me" on any host,
X.br
X\(bu user "you" on hosts h1 and h32;
X.br
X\(bu any users named "ja.*" in group "ok_j";
X.br
X\(bu and anybody in group "goodguys".
X.br
X.in -.5i
X.HP
XExample 2.
XThe pair of control lines
X.sp
X.in +.5i
X.nf
Xdoit /usr/local/bin/doit u+g=smith env=TZ,TAPE \\
X password=y timeout=0 \\
X joe@PublicWorkstation
X
Xdoit /usr/local/bin/doit u+g=smith env=TZ,TAPE joe
X.fi
X.in -.5i
X.sp
Xallows user joe to run /usr/local/bin/doit with uid\ =\ smith,
Xgid\ =\ smith's login gid, and keeping the environment variables
XTZ and TAPE in addition to the standard set.
XIf user joe is at PublicWorkstation, the first entry will match, requiring
Xhis password every time the command is used; otherwise,
X.I super
Xwill match at the second entry, and no password is needed to run the command.
X.HP
XExample 3.
XHere is an entry restricting CD-ROM mounting on different hosts:
X.B tas
Xis the only user who may mount
XCd's on
X.BR elgar ;
Xanybody in group
X.B xyz
Xmay mount CD's on
X.B alpha
Xor
X.BR delta ;
Xand anybody on a host in the netgroup
X.B india
Xmay mount a CD on the
X.B india
Xhosts.
X.sp
X.in +.5i
X.nf
Xcdmount /usr/local/bin/cdmount tas@elgar \\
X :xyz@{alpha,delta} \\
X .*@+india
X.fi
X.in -.5i
X.sp
X.SH "SEE ALSO"
Xsuper(1)
END_OF_FILE
if test 14758 -ne `wc -c <'super-3.4.5/super.5'`; then
echo shar: \"'super-3.4.5/super.5'\" unpacked with wrong size!
fi
# end of 'super-3.4.5/super.5'
fi
if test -f 'super-3.4.5/version.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'super-3.4.5/version.h'\"
else
echo shar: Extracting \"'super-3.4.5/version.h'\" \(46 characters\)
sed "s/^X//" >'super-3.4.5/version.h' <<'END_OF_FILE'
X#define Version "3.4"
X#define Patchlevel "5"
END_OF_FILE
if test 46 -ne `wc -c <'super-3.4.5/version.h'`; then
echo shar: \"'super-3.4.5/version.h'\" unpacked with wrong size!
fi
# end of 'super-3.4.5/version.h'
fi
echo shar: End of archive 1 \(of 3\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 3 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 3 archives.
rm -f ark[1-9]isdone
else
echo You still must unpack the following archives:
echo " " ${MISSING}
fi
exit 0
exit 0 # Just in case...
#! /bin/sh
# This is a shell archive. Remove anything before this line, then feed it
# into a shell via "sh file" or similar. To overwrite existing files,
# type "sh file -c".
# Contents: super-3.4.5/super.c
# Wrapped by kent@sparky on Sun May 8 15:43:28 1994
PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin:$PATH ; export PATH
echo If this archive is complete, you will see the following message:
echo ' "shar: End of archive 2 (of 3)."'
if test -f 'super-3.4.5/super.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'super-3.4.5/super.c'\"
else
echo shar: Extracting \"'super-3.4.5/super.c'\" \(64226 characters\)
sed "s/^X//" >'super-3.4.5/super.c' <<'END_OF_FILE'
X/* The code should compile with either ANSI C or K&R compilers. */
X
X#define _XOPEN_SOURCE
X#define _ALL_SOURCE /* AIX needs this to define NSIG */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <errno.h>
X#include <fcntl.h>
X#include <string.h>
X#include <signal.h>
X#include <sys/param.h>
X#include <sys/stat.h>
X#include <pwd.h>
X#include <grp.h>
X#include <unistd.h>
X
X#ifdef USE_SYSLOG
X#include <syslog.h>
X#endif
X
X#ifdef __STDC__
X#include <stdlib.h>
X#else
X#include <malloc.h>
X#endif
X
X#ifdef SUNOS5
X#include <shadow.h>
X#include <sys/time.h>
X#include <sys/systeminfo.h>
Xextern int sysinfo();
X#define gethostname(buf, lbuf) (sysinfo(SI_HOSTNAME, (buf), (lbuf)))
X#undef NSIG
X#define NSIG _sys_nsig
X#else
Xextern int gethostname();
X#include <time.h>
X#endif
X
X#include "version.h"
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 (or another user/group), without unduly
X * compromising security.
X *
X * Use:
X *
X * $0 [-d] [-h | -? | -H] commandname [args...]
X *
X * Options:
X * -d -- debug mode: show what would be done, but don't actually
X * execute a command.
X * -h, -? -- help: print list of allowed commands, then exit.
X * -H -- a long-winded variant of -h.
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 ok-user-pats | options
X *
X * The `ok-user-pats' are (by default) ed-style patterns, with the addition of
X * csh-style brace expansion. The actual regular-expression code is
X * Ozan Yigit's implementation of regex; see regex.c for details.
X * Each ok-user-pattern is in the format
X * user[:group][@host] | :group[@host] | @host
X
X * (Alternatively, an option can be set so that shell-style patterns are
X * used; see global option "patterns=xxx", below.)
X
X * The options can be mixed in with user patterns. Each option is of the
X * form key=value (no whitespace around the equal sign is allowed).
X * The valid keys are:
X * o info=xxx This line is printed when help is given.
X
X * o uid=xxx By default, FullPathName is executed with effective
X * uid=root, but no changes to the gid or real uid.
X * This option sets the real and effective user id to xxx.
X * N.B. Uid's and gid's are first tried as names, then
X * as numbers.
X
X * o gid=xxx By default, the group id remains unchanged when
X * FullPathName is executed. This option sets the real
X * and effective group id to xxx.
X
X * o u+g=zzz The real and effective uid are set to zzz and
X * the real and effective gid is set to zzz's login gid.
X * (u+g conflicts with uid=xxx or gid=yyy, and can't be
X * used w/ them).
X
X * o env=var[,...] Super() discards most environment variables and keeps
X * only a restricted set (see below). This option adds
X * the variables var,... to the kept set. Use at your
X * own risk.
X
X * o fd=m[,...] Super() closes most file descriptors and normally keeps
X * open just fd=0,1,2 before running the FullPathName.
X * This option adds the descriptors m,... to the open set.
X
X * o password=y|n If y, the user's password is required to execute
X * the command. Default taken from global setting.
X * ** This can also be entered as a global option.
X * ** The local password option, if present, overrides
X * ** the global setting.
X
X * o timeout=mmm Passwords are good for mmm minutes; then they
X * have to be re-entered to execute another password
X * command. <=0 requires password every time.
X * Default taken from global setting.
X * ** This can also be entered as a global option.
X * ** The local timeout option, if present, overrides
X * ** the global setting.
X
X * o nargs=[mmm-]nnn In addition to any command-line options supplied
X * with the command string (e.g. "/Full/Path/Name arg1 "),
X * the user must supply from mmm to nnn args (if mmm and
X * nnn are given), else exactly nnn args (if nnn only
X * is given), else any number of args.
X
X * Global options.
X * The above options are set on a per-command basis. The following options
X * are global. To set them, the FIRST non-comment line of the super.tab file
X * should read
X
X * / / [globaloptions]
X
X * (i.e., commandname="/", FullPathName="/", then options).
X
X * If these options appear mixed in with regular control lines, the
X * response of super(1) is undefined.
X
X * o logfile=xxx Enable logging information. Each invocation of super()
X (aside from ones for help) generates an entry in
X file xxx.
X
X * o loguid=xxx The logfile is opened for writing using uid=xxx
X * (xxx can be either a username or numeric uid).
X * This option is useful
X * when a network of hosts are sharing a cross-mounted
X * logfile. Note that networks are typically configured
X * to not allow root on one host to have root access to
X * files on another host, so this option allows you to
X * have the file created/opened under another uid that
X * does have cross-host access, such as the uid of a
X * system manager.
X * Default: loguid=0.
X
X * o syslog=y|n If y, logfile messages are logged using syslog().
X * (This is done independently of the setting of
X * logfile=xxx).
X
X * o mail="mail-command" Notices of super failures are piped to
X * the shell command mail-command. For instance,
X * mail="mail -s Super joeblow" will cause error
X * messages to be mailed to user joeblow (on some
X * systems you may need to substitute "mailx" for "mail").
X * Note: the mail-command is passed as an argument
X * to popen(3), and the message piped in.
X
X * o password=y|n If y, the user's password is required to execute
X * the command. Default=n.
X * ** This can also be entered as a global option.
X * ** The local password option, if present, overrides
X * ** the global setting.
X
X * o timeout=mmm Passwords are good for mmm minutes; then they
X * have to be re-entered to execute another password
X * command. <=0 requires password every time.
X * Default = 5 min.
X * ** This can also be entered as a global option.
X * ** The local timeout option, if present, overrides
X * ** the global setting.
X
X * o renewtime=y|n If y, each successfully-executed password-requiring
X * command causes the timestamp to be changed to the
X * currrent time, allowing another timeout minutes of
X * use before the password need be re-entered. def=n.
X
X * o timestampbyhost=y|n If y, timestamp files are unique for each host
X * -- they won't be shared by the same username when
X * one timestamp directory is cross-mounted. If n, the
X * cross-mounted timestamp directories use the same
X * timestamp file for all accounts with the same username.
X
X * o timestampuid=xxx If given, timestamp files are created using uid=xxx
X * (xxx can be a username or uid). The default is to
X * use uid=root. The motivation is the same as for
X * the loguid option: to allow a cross-mounted directory
X * to be used for timestamp files, even though root may
X * be mapped to nobody for NFS accesses.
X
X * o patterns=xxx Select the pattern-matching type. xxx must be one of:
X
X * "shell" -- patterns are as follows (approximately
X * Bourne-shell style), with the addition
X * of csh-style brace expansion:
X * \x force x to be a plain character;
X * ? matches any single character;
X * * matches any sequence of 0 or more chars;
X * [x..y] matches any single character in the set;
X * [^x..y] matches any single char NOT in the set;
X * ^pat inverts the sense of the match --
X * the string must NOT match the pattern.
X
X * "regex" -- patterns are ed-style, with the addition
X * of csh-style brace expansion.
X
X * DEFAULT: regex.
X
X * If a user is allowed to execute a given <commandname>, the <fullpathname>
X * is exec'd, with <commandname> as argv[0].
X *
X * If a symlink is made from, say, mycmd to super, then executing
X * mycmd args...
X * or
X * /usr/local/bin/superlinks/mycmd args...
X * is equivalent to executing
X * super mycmd args...
X
X * Notes:
X * 1) when interpreting $argv[0] as the cmd, the leading directory parts
X * are stripped off.
X * 2) Links mustn't be named "super" -- the super program won't recognize
X * it's a link.
X *
X * For security, the environment variables are discarded, save for TERM, LINES
X * COLUMNS, and the env=... list. 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 real uid when
X * the command is run). LOGNAME is set to the same as USER. ORIG_USER,
X * ORIG_LOGNAME, and ORIG_HOME are set to the username and login directory
X * of the person invoking super(). SUPERCMD is set to the <commandname>.
X * All descriptors excepting 0,1,2 and the fd=... set are closed.
X * Signals are all reset to have default handling.
X */
X
X
Xstatic char *SEP = " \t\v\n"; /* How to split fields on input lines */
Xstatic char *QM = "\"'"; /* Quotemarks in fields */
Xstatic char *SAFE_IFS = "IFS= \t\n";
X
X#ifndef SAFE_PATH
X#define SAFE_PATH "PATH=/bin:/usr/bin:/usr/ucb"
X#endif
Xstatic char *SafePath = SAFE_PATH ;
X
X/* The name under this program assumes it is installed. If argv[0] isn't
X * [/.../]ONETRUENAME, we assume we're running via symlink.
X */
X#ifndef ONETRUENAME
X#define ONETRUENAME "super"
X#endif
X
X#ifndef SUPERFILE
X#define SUPERFILE "/usr/local/lib/super.tab"
X#endif
X
X#ifndef TIMESTAMP_DIR
X#define TIMESTAMP_DIR "/usr/local/lib/superstamps"
X#endif
X
X#ifndef MAXFD
Xint getdtablesize( /* void */ );
X#define MAXFD (getdtablesize()-1)
X#endif
X
X/* maximum number of tries at entering the password */
X#define MAXTRY 3
X
X/* Password information */
Xstruct PassInfo {
X int required; /* -1 means not in use */
X int timeout; /* Time before password must be re-entered */
X int renewtime; /* update the timestamp file with each use of cmd? */
X int perhost; /* create timestamp files separately for each host? */
X char user[1024]; /* value of timestampuid=xxx, if entered */
X};
X
X
X/* This struct is filled in a bit at a time until it completely
X * describes the caller, the program to invoke, any options
X * set in the control line, etc.
X */
Xstruct superinfo {
X char caller[128]; /* who's invoking prog */
X char hostname[1024]; /* whence came the user */
X#ifdef __STDC__
X uid_t orig_uid; /* caller's uid */
X gid_t orig_gid; /* caller's gid */
X#else
X int orig_uid;
X int orig_gid;
X#endif
X
X char *info; /* value of info=xxx option */
X
X char *user; /* value of uid=xxx option */
X char *group; /* value of gid=xxx option */
X char *u_g; /* value of u+g=xxx option */
X#ifdef __STDC__
X uid_t new_uid; /* new uid, computed from uid=xxx or u+g=xxx */
X gid_t new_gid; /* new gid, computed from gid=xxx or u+g=xxx */
X#else
X int new_uid;
X int new_gid;
X#endif
X char *env; /* value of env=var[,...] option */
X char *fdlist; /* value of fd=nnn[,...] option */
X int usr_args[2]; /* number of user-entered args allowed */
X
X int *fd; /* descriptors from fdlist string */
X struct PassInfo passinfo; /* password requirements on this command */
X char encr[20]; /* encrypted password */
X char salt[4]; /* salt from password */
X
X int line; /* For error messages: line in superfile */
X int nl; /* No. lines used for this cmd in superfile */
X char *superfile; /* name of superfile */
X int allread; /* !0 if others can read superfile */
X} si;
X
X/* an expandable input buffer */
Xstruct Ebuf {
X char *buf;
X int l;
X int nalloc;
X};
Xstatic struct Ebuf ebuf = { NULL, 0, 0 };
X
X#ifdef USE_NETGROUP
Xextern int innetgr();
X#endif
X
Xvoid logmsg( /* char * string */ );
Xint Error( /* int show_perror, int die, char * fmt, ... */);
Xint taglines( /* int retval */ );
Xint set_u_g( /* void */ );
Xint checkenv( /* (char *) name, (char *) value, (char *) pat */ );
Xint ingroup( /* (char *) user, (gid_t) gid, (char *) gp_pat */ );
Xint taglines( /* void */ );
Xvoid opensuperlog( /* void */ );
XFILE *open_writer( /* char *user, char *filename */ );
Xvoid close_writer( /* void */ );
Xint check_pass( /* char *cmd */ );
Xchar *makedirname( /* char *prefix, char *hostname, char *path */ );
Xint makedir( /* char *directories */ );
Xint get_password( /* char *cmd, char *user, char *salt, char *encr */ );
Xchar *str_val( /* char *left, char *str */ );
Xvoid printhelp( /* int verbose, char *cmd, char *path */);
X
Xextern int re_exec();
Xextern char *re_comp();
Xint shell_compare();
Xchar *shell_compile();
X
Xextern int error_syslog; /* syslog() bool, used by Error() */
Xextern int error_priority; /* syslog priority, used by Error() */
Xextern char *error_command; /* command used by Error() */
X
Xchar *prog; /* this program */
X
X/* Information for logging use */
Xstruct loginfo {
X FILE *fp; /* logfile pointer */
X char filename[1024]; /* logfile name */
X char user[1024]; /* value of loguid=xxx, if entered */
X int uid; /* UID under which we open logfile */
X} log = { NULL, "", "", 0 };
X
X/* Global password requirements */
Xstruct PassInfo passinfo = {
X 0, /* not required */
X 5, /* 5-minute expiration */
X 0, /* don't renew timestamp with ea. command use */
X 1, /* per-host timestamp files */
X ""}; /* value of timestampuid=xxx, if entered */
X
X/* Routines used to compile/match user/group/host patterns */
Xchar *(*pat_compile)() = re_comp;
Xint (*pat_compare)() = re_exec;
Xint need_re_anchor = 1;
X
X#define NELEM(x) (sizeof(x)/(sizeof(*x)))
X#define UNTIL(e) while(!(e))
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X int status, debug=0;
X char *s, **sp, *cmd, *path, *approve();
X char **buttonup(), **newargs(), **arglist, **envp;
X extern char *error_prog; /* same as prog, but used by error() if set */
X void debug_print( /* char **argv, char *path, char **arglist */ );
X
X s = strrchr(argv[0], '/');
X prog = (s && *(s+1)) ? s+1 : argv[0];
X error_prog = ONETRUENAME;
X
X if (gethostname(si.hostname, sizeof(si.hostname)) == -1)
X (void) Error(1, 1, "Couldn't get your hostname: ");
X
X /* Decide if we were invoked as "super cmd args...", or
X * as a link: cmd args...
X */
X if (strcmp(prog, ONETRUENAME) == 0) {
X /* Invoked as super cmd args... */
X if (argv[1] && strcmp(argv[1], "-d") == 0) {
X debug = 1;
X argv++;
X }
X argv++;
X cmd = argv[0];
X } else {
X /* Assume it's been invoked via link to super */
X s = strrchr(argv[0], '/');
X cmd = (s && *(s+1)) ? s+1 : argv[0];
X }
X
X /* Check for permission to execute, and change uid/gid as necessary */
X if ((path = approve(cmd, debug)) == NULL)
X return 1;
X else if (*path == '\0')
X return 0;
X
X /* Get the arglist for the command, and null-terminate cmd if not
X * already done so.
X */
X arglist = newargs(path, argv);
X
X /* Button up for security, and get a modified environment */
X envp = buttonup(cmd, debug);
X
X /* Set uid/gid if necessary */
X status = set_u_g();
X
X if (debug) {
X debug_print(path, arglist);
X return 0;
X }
X if (status == -1)
X return 1;
X
X /* Check password requirements */
X if (check_pass(cmd) != 0)
X return 1;
X
X logmsg(cmd);
X close_writer();
X
X if (execve(path, arglist, envp) == -1) {
X if (log.fp) opensuperlog();
X (void) Error(1, 1, "Couldn't exec `%s' (for cmd %s): ", path, argv[1]);
X }
X return 0;
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Print the debug info */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Xvoid
Xdebug_print(path, arglist)
Xchar *path;
Xchar **arglist;
X{
X char **sp;
X (void) fprintf(stderr, "%s -d:\n", prog);
X (void) fprintf(stderr, "\tCommand: `%s'\n", arglist[0]);
X (void) fprintf(stderr, "\tPath: `%s'\n", path);
X (void) fprintf(stderr, "\tArgv[]:\n");
X for (sp=arglist; *sp; sp++)
X (void) fprintf(stderr, "\t\t%s\n", *sp);
X if (si.usr_args[0] < 0)
X (void) fprintf(stderr,
X "\tAny number of user-entered args allowed.\n");
X else if (si.usr_args[0] == si.usr_args[1] && si.usr_args[0] == 0)
X (void) fprintf(stderr, "\tNo user-entered args are allowed.\n");
X else if (si.usr_args[0] == si.usr_args[1])
X (void) fprintf(stderr,
X "\t%d user-entered arg%s required.\n",
X si.usr_args[0],
X si.usr_args[0] == 1? " is" : "s are");
X else
X (void) fprintf(stderr,
X "\t%d - %d user-entered args are required.\n",
X si.usr_args[0], si.usr_args[1]);
X (void) fprintf(stderr, "\tExtra environment variables:\n");
X (void) fprintf(stderr, "\t\t%s\n",
X (si.env == NULL || *si.env == '\0') ? "(none)" : si.env);
X (void) fprintf(stderr,
X "\tFile descriptors not to be closed:\n\t\t0,1,2");
X if (si.fdlist)
X (void) fprintf(stderr, ",%s", si.fdlist);
X (void) fprintf(stderr, "\n\n\tID's:\treal effective\n");
X (void) fprintf(stderr, "\tuid\t%d\t%d\n", getuid(), geteuid());
X (void) fprintf(stderr, "\tgid\t%d\t%d\n", getgid(), getegid());
X (void) fprintf(stderr, "\n\tPassword required: %s\n",
X si.passinfo.required ? "yes" : "no");
X if (si.passinfo.required) {
X (void) fprintf(stderr, "\tPassword timeout: %d min\n",
X si.passinfo.timeout);
X (void) fprintf(stderr,
X "\tUpdate timestamp with each use of a password-requiring command? %s\n",
X si.passinfo.renewtime ? "Yes" : "No");
X }
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Log a super call */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X
Xvoid
Xlogmsg(string)
Xchar *string;
X{
X char * s;
X time_t tptr;
X
X if (!log.fp)
X return;
X (void) time(&tptr);
X s = ctime(&tptr);
X s[strlen(s) - 1] = '\0';
X (void) fprintf(log.fp, "%s@%s %s\t%s\n", si.caller, si.hostname, s, string);
X#ifdef USE_SYSLOG
X if (error_syslog) {
X syslog(LOG_INFO, string);
X }
X#endif
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Get the arglist for the command, and null-terminate cmd if nec */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Xchar **
Xnewargs(path_plus, argv)
Xchar *path_plus; /* string = "path [options]". Null-terminate and put
X * options into front of arglist. */
Xchar **argv; /* options to add to arglist */
X{
X int n, nargs, nxtra;
X char **arglist, **ap;
X char *s, *strqtok();
X
X /* Count user-entered args. */
X for (ap = argv; *ap; )
X ap++;
X
X /* Check number of user-entered args is ok. */
X if (si.usr_args[0] >= 0) {
X nargs = ap - argv - 1;
X if (nargs < si.usr_args[0] || nargs > si.usr_args[1]) {
X if (si.usr_args[0] == si.usr_args[1] && si.usr_args[0] == 0)
X (void) Error(0, 2,
X "you may not give any arguments to `%s'\n", argv[0]);
X else if (si.usr_args[0] == si.usr_args[1])
X (void) Error(0, 2,
X "You must give %d argument%s to `%s'\n",
X si.usr_args[0], si.usr_args[0] == 1 ? "" : "s", argv[0]);
X else
X (void) Error(0, 2,
X "You must give %d - %d arguments to `%s'\n",
X si.usr_args[0], si.usr_args[1], argv[0]);
X }
X }
X
X /* Count args from super.tab */
X for(nxtra=0, s=strqtok(path_plus, SEP, QM, "", 0240); s;
X s = strqtok(NULL, SEP, QM, "", 0240))
X nxtra++;
X
X n = (ap - argv) + nxtra + 1;
X arglist = (char **) malloc(sizeof(char *) * n);
X if (!arglist)
X (void) Error(1, 2,
X "failed to malloc space for %d args in arglist\n", n-1);
X
X /* Split command and option words */
X arglist[0] = *argv++;
X
X /* Null-terminate the path, but don't put it into argv */
X (void) strqtok(path_plus, SEP, QM, "", 0201);
X
X /* Put the rest of the path_plus args into argv */
X for (ap=&arglist[1], s = strqtok(NULL, SEP, QM, "", 0201);
X s ; s = strqtok(NULL, SEP, QM, "", 0201)) {
X *ap++ = s;
X }
X
X while (*argv)
X *ap++ = *argv++;
X *ap = NULL;
X
X return arglist;
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Get a safe environment for execution of the command */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Xchar **
Xbuttonup(cmd, debug)
Xchar *cmd; /* name of command being started */
Xint debug; /* print debug info? */
X{
X /* Closes all descriptors save 0,1,2 and the super.tab-specified fd list.
X * Resets all signal-handling to SIG_DFL.
X * Discards all env. variables save for TERM, LINES, COLUMNS, and
X * any variables listed in the super.tab file.
X * Don't allow TERM to have any but [-/:+._a-zA-Z0-9].
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 * USER and HOME refer to the uid under which the command is executed;
X * LOGNAME is set to the same as USER, and SUPERCMD is set to cmd.
X * ORIG_USER, ORIG_LOGNAME, and ORIG_HOME refer to the user who invoked
X * super.
X * Returned:
X * a pointer to the modified environment list.
X */
X int i, fd, maxfd;
X char *s, *Getenv();
X int getlogdir();
X int checkenv();
X int fd_log;
X static char *env[200];
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 OrigUser[100]; /* ORIG_USER */
X static char OrigLogname[100]; /* ORIG_LOGNAME */
X static char OrigHome[MAXPATHLEN+9]; /* ORIG_HOME */
X static char Cmd[1200]; /* SUPERCMD */
X void (*signal())();
X
X fd_log = log.fp ? fileno(log.fp) : -1; /* don't close logfile yet */
X maxfd = MAXFD;
X
X for (fd=3; fd <= maxfd; fd++)
X if (si.fd[fd] == 0 && fd != fd_log)
X (void) close(fd);
X
X for (i=0; i<NSIG; i++)
X (void) signal(i, SIG_DFL);
X
X s = si.user ? si.user : si.u_g ? si.u_g : si.caller;
X (void) sprintf(OrigUser, "ORIG_USER=%s", si.caller);
X (void) sprintf(User, "USER=%s", s);
X (void) sprintf(OrigLogname, "ORIG_LOGNAME=%s", si.caller);
X (void) sprintf(Logname, "LOGNAME=%s", s);
X (void) sprintf(Cmd, "SUPERCMD=%s", cmd);
X (void) strcpy(Home, "HOME=");
X (void) getlogdir(s, Home+5);
X (void) strcpy(OrigHome, "ORIG_HOME=");
X (void) getlogdir(si.caller, OrigHome+10);
X i = 0;
X env[i] = Getenv("TERM");
X if (env[i] && checkenv(debug,
X "TERM", env[i]+5, "^[-/:+._a-zA-Z0-9]*$") != -1) i++;
X env[i] = Getenv("LINES");
X if (env[i] && checkenv(debug, "LINES", env[i]+6, "^[0-9]*$") != -1) i++;
X env[i] = Getenv("COLUMNS");
X if (env[i] && checkenv(debug, "COLUMNS", env[i]+8, "^[0-9]*$") != -1) i++;
X env[i++] = SAFE_IFS;
X env[i++] = SafePath;
X env[i++] = User;
X env[i++] = Logname;
X env[i++] = Home;
X env[i++] = Cmd;
X env[i++] = OrigUser;
X env[i++] = OrigLogname;
X env[i++] = OrigHome;
X
X /* Now add the extra environment variables requested in the
X * super.tab file.
X */
X for (s=strtok(si.env, ","); i < NELEM(env)-1 && s; s=strtok(NULL, ",")) {
X env[i] = Getenv(s);
X if (env[i])
X i++;
X }
X if (i >= NELEM(env)-1)
X (void) Error(0, 1,
X "Asked to save too many environment variables (max allowed %d).\n",
X NELEM(env)-1);
X
X env[i] = (char *) NULL;
X
X return &env[0];
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Get environment variable */
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/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Check that an environment variable only includes allowed characters */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Returns 0 if pat matched; -1 otherwise. */
Xint
Xcheckenv(debug, name, value, pat)
Xint debug; /* print debug info? */
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
X if (!value)
X return -1;
X
X if (debug)
X (void) fprintf(stderr,
X "\tcheckenv args: name=\"%s\"; value=\"%s\"; pat=\"%s\"\n",
X name, value, pat);
X
X /* Environment variables are always checked with re_comp/re_exec:
X * the patterns are fixed internally, not supplied by the user.
X */
X if (re_comp(pat))
X return Error(0, 0, "checkenv(): couldn't compile pattern `%s'.\n", pat);
X
X if (re_exec(value) != 1)
X return Error(0, 0, "checkenv(): $%s (=%s) doesn't match pattern %s.\n",
X name, value, pat);
X return 0;
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Set user and group according to the specified args */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Xint
Xset_u_g()
X{
X /* Return 0 on success, -1 on failure */
X struct passwd *uid_pw;
X struct group *gp;
X void setgrent(), endgrent();
X int i, numeric=0;
X
X si.new_uid = si.orig_uid;
X si.new_gid = si.orig_gid;
X
X /* Check gid=xxx */
X if (si.group) {
X char c;
X numeric = (sscanf(si.group, "%d%c", &i, &c) == 1);
X si.new_gid = i;
X
X setgrent();
X for (gp = getgrent(); gp; gp = getgrent()) {
X if ((strcmp(si.group, gp->gr_name) == 0) ||
X (numeric && si.new_gid == gp->gr_gid)) {
X /* Found the gid in the group file */
X si.new_gid = gp->gr_gid;
X break;
X }
X }
X endgrent();
X if (!gp)
X return Error(1, 0, "No such group as `%s' in group file: ");
X }
X
X /* Check uid=xxx u+g=yyy */
X if (si.user || si.u_g) {
X if (!si.user)
X si.user = si.u_g;
X uid_pw = getpwnam(si.user);
X if (!uid_pw) {
X char c;
X numeric = (sscanf(si.user, "%d%c", &i, &c) == 1);
X si.new_uid = i;
X if (numeric)
X uid_pw = getpwuid(si.new_uid);
X }
X if (uid_pw) {
X si.new_uid = uid_pw->pw_uid;
X if (si.u_g)
X si.new_gid = uid_pw->pw_gid;
X } else {
X return Error(1, 0, "No password entry for uid %s: ", si.user);
X }
X }
X
X /* Now set uid & gid */
X if (si.group || si.u_g)
X if (setgid(si.new_gid) == -1)
X return Error(1, 0, "setgid(gid=%d) failed: ", si.new_gid);
X if (si.user || si.u_g)
X if (setuid(si.new_uid) == -1)
X return Error(1, 0, "setuid(uid=%d=%s) failed: ",
X si.new_uid, si.user);
X
X if (si.passinfo.required) {
X /* Get caller's encrypted password */
X#ifdef SUNOS5
X /* Shadow password lookup for Sunos 5.x */
X {
X struct spwd *caller_pw;
X if (!(caller_pw = getspnam(si.caller)))
X return Error(1, 0,
X "Failed to obtain shadow password entry for user %s: ",
X si.caller);
X strcpy(si.encr, caller_pw->sp_pwdp);
X strncpy(si.salt, caller_pw->sp_pwdp, 2);
X si.salt[2] = '\0';
X }
X#else
X {
X struct passwd *caller_pw;
X if (!(caller_pw = getpwnam(si.caller)))
X return Error(1, 0, "No password entry for user %s: ", si.caller);
X strcpy(si.encr, caller_pw->pw_passwd);
X strncpy(si.salt, caller_pw->pw_passwd, 2);
X si.salt[2] = '\0';
X }
X#endif
X }
X return 0;
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Checks if password is needed, and if so, prompts for same.
X * Returns 0 on success, -1 on error.
X
X * The timestamp directory faces the same problem as the logfile: if the
X * administrator wants to share an NFS-mounted directory across hosts
X * wherein root is translated to nobody for NFS access, we have to be
X * able to create the timestamp file under a special uid. This is done
X * just as in open_writer(): we fork, setuid(), and do the file
X * manipulation in the child. This allows us to implement a special uid
X * for the timestamp file, without needing the operating system to
X * offer saved uid's or interprocess file-descriptor passing, etc.
X */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Xint
Xcheck_pass(cmd)
Xchar *cmd;
X{
X char *s, str[MAXPATHLEN], file[MAXPATHLEN];
X char *crypt(), *getpass();
X struct stat st;
X int l, istat, file_exists, timed_out, got_pass;
X int status;
X pid_t child;
X
X if (!si.passinfo.required)
X return 0; /* don't need password */
X
X /* Create or update the timestamp file even if the lifetime is 0
X * (always ask for password). We do this because the user may
X * execute _another_ command which has a password expiry > 0,
X * and which will be happy to use the password that was already
X * entered with the 0-lifetime command.
X */
X child = fork();
X if (child == -1) {
X return Error(1, 0, "Failed to create child for timestamp processing: ");
X
X } else if (child > 0) {
X /* In parent -- wait to see if the child succeeded */
X if (wait(&status) < 0)
X return Error(1, 0, "wait() on timestamp creation process failed: ");
X else if (status == 0)
X return 0;
X else
X return Error(0, 0, "Timestamp creation failed\n");
X
X return 1;
X
X } else {
X /* In child. setuid, then make and/or test the directory */
X if (*si.passinfo.user != '\0') {
X si.user = si.passinfo.user;
X si.group = NULL;
X si.u_g = NULL;
X if (set_u_g() == -1) {
X (void) Error(1, 0,
X "failed to setuid %s before setting timestamp file: ",
X si.user);
X exit(1);
X }
X }
X /* Make the timestamp directory name */
X if (!makedirname(passinfo.perhost ? TIMESTAMP_DIR : "",
X si.hostname, file))
X exit(1);
X
X /* Make the timestamp directory */
X if (!makedir(file))
X exit(1);
X
X /* Make the file in the timestamp directory */
X l = strlen(file) + 1 + strlen(si.caller);
X if (l >= MAXPATHLEN)
X Error(1, 1,
X "Can't create timestamp file: it would exceed MAXPATHLEN = %d\n",
X MAXPATHLEN);
X strcat(file, "/");
X strcat(file, si.caller);
X
X istat = stat(file, &st);
X if (istat != 0 && errno != ENOENT)
X Error(1, 1, "Failed to stat timestamp file `%s': ", file);
X
X file_exists = (istat == 0);
X if (file_exists)
X timed_out = (si.passinfo.timeout < 1) ||
X ((time(NULL)-st.st_mtime) > si.passinfo.timeout*60);
X
X got_pass=0;
X if (!file_exists || timed_out) {
X got_pass = (get_password(cmd, si.caller, si.salt, si.encr) == 1);
X if (!got_pass)
X exit(1);
X }
X
X /* NOTE: A race condition is possible between two super's, with the
X * worst-case effect of an error message and failure to run the
X * requested command.
X */
X
X /* If file exists, and we haven't (a) gotten the password again, or
X * (b) supposed to automatically refresh the timestamp, do nothing to
X * the file except ensure that we own it.
X
X * Otherwise create the file (unlink it first if it exists).
X */
X if (file_exists && !(got_pass || si.passinfo.renewtime)) {
X if (st.st_uid != getuid())
X Error(1, 1, "Timestamp file `%s' is not owned by us (uid=%d)",
X file, getuid());
X
X } else {
X if (file_exists) {
X if (unlink(file) != 0)
X Error(1, 1,
X "Failed to unlink timestamp file `%s': ", file);
X }
X if (open(file, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0200) == -1)
X Error(1, 1,
X "Failed to create timestamp file `%s': ", file);
X }
X exit(0);
X }
X /* UNREACHABLE */
X Error(0, 1, "Unreachable code!\n");
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Gets a user's encrypted password. Returns -1 on failure, +1 on success */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Xint
Xget_password(cmd, user, salt, encr)
Xchar *cmd, *user, *salt, *encr;
X{
X /* No such file or password timed out -- get password */
X int ntry, match;
X char msg[500];
X char *encrypted = NULL;
X if (strcmp(encr, "") == 0) {
X return Error(0, 0,
X "Command `%s' requires a password, but user `%s' has no password\n",
X cmd, user);
X }
X for (ntry=0, match=0; ntry < MAXTRY && !match; ntry++) {
X if (ntry == 0) {
X (void) sprintf(msg,
X "Your password is required for super command `%s'...\nPassword: ",
X cmd);
X } else {
X strcpy(msg, "Password incorrect\nPassword: ");
X }
X
X encrypted = crypt(getpass(msg), salt);
X if (encr && encrypted)
X match = (strcmp(encr, encrypted) == 0);
X else
X match = 0;
X }
X if (!match)
X return Error(0, 0, "Password incorrect\n");
X return 1;
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 void re_anchor();
X if (need_re_anchor)
X re_anchor(in, out);
X else
X (void) strcpy(out, in);
X}
X
Xvoid
Xre_anchor(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 */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Xchar *
Xgrow(cb, nb)
Xstruct Ebuf *cb;
Xint nb; /* amount to grow, bytes */
X{
X if (cb->buf)
X cb->buf = realloc(cb->buf, cb->nalloc += nb);
X else
X cb->buf = malloc(cb->nalloc += nb);
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, 2048);
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 c, *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
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 c = *(ebuf.buf + ebuf.l) ;
X if (ebuf.l != 0 && !(isspace(c) || c == '#')) {
X /* Continued lines must be indented unless they are comments. */
X (void) Error(0, 1,
X "format error in super.tab file:\n\tcontinued line not indented: \
XProblem text:\n%s\n\t\n", ebuf.buf);
X }
X ebuf.l += strlen(ebuf.buf+ebuf.l);
X if (nl) (*nl)++;
X }
X return ebuf.buf;
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Check a single user/group/host string
X * Return -1 on failure to match; 0 on success.
X */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Xint
Xcheck_ugh(origtext, token)
Xchar *origtext; /* original text containing token -- for err messages */
Xchar *token; /* user/group/host pattern */
X{
X char chkbuf[1024];
X char *userpat, *grouppat, *hostpat;
X int match, i;
X
X if (si.orig_uid == 0)
X return 0; /* root is always legit */
X
X /* Split into user:group@host */
X hostpat = strchr(token, '@');
X if (hostpat && !*(hostpat+1))
X return taglines(Error(0, 0, "Missing hostname in `%s'.\n", origtext));
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 taglines(Error(0, 0,
X "Missing netgroupname in `%s'.\n", origtext));
X if (!innetgr(hostpat+1, si.hostname, (char *) NULL, (char *) NULL))
X return -1; /* hostname is not in this netgroup */
X }
X#else
X return taglines(Error(0, 0,
X "hostnames may not begin with `+' since this super()\n\
Xwas compiled without -DUSE_NETGROUP.\n"));
X#endif
X
X /* *** ***** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
X * *** NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE
X * ***
X * *** If compiled without -DUSE_NETGROUP, the following is unreachable.
X * *** In that case, don't worry if your compiler complains about that.
X * ***
X * *** ***** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
X */
X if (hostpat && *hostpat != '+') { /* Interpret as a hostname pattern */
X anchor(hostpat, chkbuf); /* Force all matches to be anchored */
X if ((*pat_compile)(chkbuf) != NULL)
X return taglines(Error(0, 0, "bad host pattern: `%s'.\n", origtext));
X if ((*pat_compare)(si.hostname) != 1)
X return -1; /* Doesn't match hostname */
X }
X
X grouppat = strchr(token, ':');
X userpat = token;
X if (*token == '\0' && !hostpat) {
X /* Nothing in pattern?! */
X return taglines(Error(0, 0, "Unacceptable pattern `%s'.\n", origtext));
X
X } else if (*token == '\0') {
X userpat = grouppat = "^.*$"; /* only hostname given */
X
X } else if (grouppat && *(grouppat+1)) { /* pat is "uuu:ggg or ":ggg" */
X if (token == grouppat)
X userpat = "^.*$"; /* pat is ":ggg" */
X grouppat++;
X
X } else { /* pat is "uuu" or "uuu:" */
X if (grouppat)
X *grouppat = '\0'; /* pat is "uuu:" */
X grouppat = "^.*$";
X }
X anchor(userpat, chkbuf); /* Anchor all matches */
X if ((*pat_compile)(chkbuf) != NULL)
X return taglines(Error(0, 0, "bad user pattern: `%s'.\n", origtext));
X match = (*pat_compare)(si.caller);
X#ifdef MATCH_DECIMAL_UID_GID
X if (match != 1) {
X /* Enabling MATCH_DECIMAL_UID_GID allows the userpat to be
X * numeric, as an alternative to being interpreted as a
X * user name: after checking the username, we check if the
X * user's uid, as a decimal text value, matches the user
X * pattern userpat.
X */
X char buf[20];
X (void) sprintf(buf, "%d", si.orig_uid);
X match = (*pat_compare)(buf);
X }
X#endif
X if (match != 1)
X return -1; /* Doesn't match user */
X
X anchor(grouppat, chkbuf);
X i = ingroup(si.caller, si.orig_gid, chkbuf);
X if (i == -1)
X return taglines(Error(0, 0, "bad group pattern\n", origtext));
X else if (i != 1)
X return -1; /* Doesn't match group */
X
X return 0; /* Success! */
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Collect global xxx=yyy options. */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Xint
Xglobal_option(word)
Xchar *word; /* opt=xxx */
X{
X /* Return 0 on success, -1 if formatting error. */
X int i, maxfd;
X char *strqtok();
X extern char *strq_start;
X extern int error_syslog;
X char *s, *value;
X
X
X if (strcmp("\\", word) == 0) {
X return 0;
X } else if ((s = str_val("patterns", word))) {
X word = s;
X if (strcmp(word, "shell") == 0) {
X pat_compile = shell_compile;
X pat_compare = shell_compare;
X need_re_anchor = 0;
X } else if (strcmp(word, "regex") == 0) {
X pat_compile = re_comp;
X pat_compare = re_exec;
X need_re_anchor = 1;
X } else {
X return taglines(Error(0, 1,
X "Invalid pattern type `%s'. Valid: \"shell\" and \"regex\"\n",
X word));
X }
X
X } else if ((s = str_val("syslog", word))) {
X if (strcmp(s, "n") == 0) {
X error_syslog = 0;
X } else {
X error_syslog = 1;
X if (strcmp(s, "y") != 0)
X (void) taglines(Error(0, 0,
X "Invalid option value in `%s' -- value must be `y' or `n'; using `y'.\n"
X ));
X#ifdef USE_SYSLOG
X error_priority = LOG_ERR;
X#else
X Error(0, 0,
X "Can't enable syslog() calls -- not compiled with USE_SYSLOG defined.\n");
X#endif
X }
X
X } else if ((s = str_val("mail", word))) {
X char *p = malloc(strlen(s)+1);
X if (!p)
X Error(1, 1, "Can't malloc space for option `%s'\n", word);
X strcpy(p, s);
X error_command = p;
X
X } else if ((s = str_val("logfile", word))) {
X strcpy(log.filename, s);
X
X } else if ((s = str_val("loguid", word))) {
X strcpy(log.user, s);
X
X } else if ((s = str_val("timestampuid", word))) {
X strcpy(passinfo.user, s);
X
X } else if ((s = str_val("timestampbyhost", word))) {
X if (strcmp(s, "n") == 0)
X passinfo.perhost = 0;
X else if (strcmp(s, "y") == 0)
X passinfo.perhost = 1;
X else
X (void) taglines(Error(0, 1,
X "Global option value `timestampbyhost' must have value `y' or `n'\n"));
X
X } else if ((s = str_val("renewtime", word))) {
X if (strcmp(s, "y") == 0) {
X passinfo.renewtime = 1;
X } else {
X passinfo.renewtime = 0;
X if (strcmp(s, "n") != 0)
X (void) taglines(Error(0, 0,
X "Global option value `renewtime' must have value `y' or `n'; using`n'\n"));
X }
X
X } else if ((s = str_val("timeout", word))) {
X passinfo.timeout = atoi(s);
X
X } else if ((s = str_val("password", word))) {
X if (strcmp(s, "n") == 0) {
X passinfo.required = 0;
X } else {
X passinfo.required = 1;
X if (strcmp(s, "y") != 0)
X (void) taglines(Error(0, 0,
X "Invalid option value in `%s' -- value must be `y' or `n'; using `y'.\n"
X ));
X }
X } else {
X return taglines(Error(0, 0,
X "Unrecognized global option `%s'.\n", word));
X }
X
X return 0;
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Collect local xxx=yyy options. */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Xint
Xlocal_option(word)
Xchar *word; /* null -> clear all per-command options; else opt=xxx */
X{
X /* Return 0 on success, -1 if formatting error. */
X int i, maxfd;
X char *s, *strqtok();
X extern char *strq_start;
X
X if (!word) {
X /* Clear options */
X maxfd = MAXFD;
X si.info = NULL;
X si.user = si.group = si.u_g = NULL;
X si.env = NULL;
X si.fdlist = NULL;
X if (!si.fd) {
X si.fd = (int *) malloc(sizeof(int) * maxfd);
X if (!si.fd)
X (void) Error(1, 1,
X "Failed to malloc space for file descriptor list: ");
X }
X for (i=0; i<=maxfd; i++) {
X si.fd[i] = 0;
X }
X si.usr_args[0] = si.usr_args[1] = -1;
X /* should use struct assignment, but that's not portable to
X * all k&r compilers.
X */
X si.passinfo.required = passinfo.required;
X si.passinfo.timeout = passinfo.timeout;
X si.passinfo.renewtime = passinfo.renewtime;
X si.passinfo.perhost = passinfo.perhost;
X strcpy(si.passinfo.user, passinfo.user);
X
X return 0;
X }
X
X if ((s = str_val("info", word))) {
X si.info = s;
X
X } else if ((s = str_val("timeout", word))) {
X si.passinfo.timeout = atoi(s);
X
X } else if ((s = str_val("password", word))) {
X if (strcmp(s, "n") == 0) {
X si.passinfo.required = 0;
X } else {
X si.passinfo.required = 1;
X if (strcmp(s, "y") != 0)
X (void) taglines(Error(0, 0,
X "Invalid option value in `%s' -- value must be `y' or `n'; using `y'.\n"
X ));
X }
X
X } else if ((s = str_val("uid", word))) {
X si.user = s;
X
X } else if ((s = str_val("gid", word))) {
X si.group = s;
X
X } else if ((s = str_val("u+g", word))) {
X si.u_g = s;
X
X } else if ((s = str_val("env", word))) {
X si.env = s;
X
X } else if ((s = str_val("fd", word))) {
X int n;
X char *w;
X char *save_strq_start = strq_start;
X maxfd = MAXFD;
X si.fdlist = s;
X for (w=strqtok(s, ",", "", "", 040); w;
X w=strqtok(NULL, ",", "", "", 040)) {
X if ((n=atoi(w)) >= 0 && n <= maxfd)
X si.fd[n] = 1;
X else
X (void) taglines(Error(0, 0,
X "Ridiculous value for file descriptor ignored: `%s'\n", word));
X }
X strq_start = save_strq_start;
X
X } else if ((s = str_val("nargs", word))) {
X int i, m, n;
X i = sscanf(s, "%u-%u", &m, &n);
X switch(i) {
X case 1: si.usr_args[0] = si.usr_args[1] = m;
X break;
X case 2: si.usr_args[0] = m;
X si.usr_args[1] = n;
X break;
X default:
X return taglines(Error(0, 0,
X "nargs must be nargs=nnn or nargs=mmm-nnn.\n",
X word));
X }
X
X } else {
X return taglines(Error(0, 0,
X "Unrecognized local option `%s'.\n", word));
X
X }
X if ((si.user || si.group) && si.u_g)
X return taglines(Error(0, 0,
X "Option u+g=zzz can't be used with uid=xxx or gid=yyy\n"));
X return 0;
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Parse one control line (may contain several text lines). */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Return -1 if caller should give up parsing the file;
X * return 0 if caller should loop to next chunk (either because we
X * are doing help processing or because of nomatch);
X * return 1 if success, in which case *path pts to the fullpathname.
X */
Xint
Xparseline(givehelp, buf, usrcmd, path, commandfound, debug)
Xint givehelp; /* request is to give help, not check valid command */
Xchar *buf; /* Input chunk. Gets modified by strqtok(). */
Xchar *usrcmd; /* Command user wants to execute */
Xchar **path; /* On success, returned with *path -> fullpathname */
Xchar **commandfound; /* Returns ptr to command name; NULL if no match */
Xint debug;
X{
X char *command, *tok;
X char *wd, **wdlist;
X int globbraces();
X int check_ugh();
X char *bbuf, *s, *p;
X int i, iwd, match;
X void anchor();
X int global_option();
X int local_option();
X extern char *strq_start, *strqtok();
X
X bbuf = buf;
X buf += strspn(buf, SEP); /* skip leading whitespace */
X if (!*buf || *buf == '#')
X return 0; /* Discard empty lines and pure-comment lines */
X
X if (bbuf != buf) {
X return taglines(Error(0, 0, "format error in super(1) file: \n\
X\tcommandnames must begin in column 1.\n"));
X }
X
X /* Get the command and full path. */
X command = strqtok(buf, SEP, QM, "#", 01);
X *path = strqtok(NULL, SEP, QM, "#", 01);
X if (!command || !*path)
X return taglines(Error(0, 0,
X "format error in super.tab file: Cmd or FullPathName missing.\n"));
X
X if (strcmp("/", command) == 0 && strcmp("/", *path) == 0) {
X /* Process global options and return */
X for (wd=strqtok(NULL, SEP, QM, "#", 0101); wd;
X wd = strqtok(NULL, SEP, QM, "#", 0101)) {
X global_option(wd);
X }
X if (*log.filename != '\0')
X opensuperlog();
X return 0;
X
X } else if (!givehelp && strcmp(command, usrcmd) != 0) {
X /* Skip non-matching commands */
X return 0;
X }
X
X *commandfound = command;
X
X /* Replace "\\\n" with " \n" -- the backslash has been left in until
X * here so that we have choice of printing help info with nearly identical
X * text to that in the super.tab file. Don't get rid of newline as
X * comments only go up to newlines.
X */
X s = strq_start;
X while ((s = strchr(s, '\\'))) {
X if (*(s+1) == '\n')
X *s++ = ' ';
X }
X
X /* Now process ok-user-patterns and options */
X for (match=0, wd=strqtok(NULL, SEP, QM, "", 0101); wd;
X wd = strqtok(NULL, SEP, QM, "", 0101)) {
X if ((p=strchr(wd, '=')) && p > wd && *(p-1) != '\\') {
X /* Is an option */
X local_option(wd);
X continue;
X
X } else if (match) {
X /* have match, but need to check the rest of the list for options */
X continue;
X
X } else {
X /* still looking for a matching ok-user pattern */
X
X /* Do brace globbing on each word */
X /* fprintf(stderr, "globbraces -> ``%s''\n", wd); */
X if ((i=globbraces(wd, &wdlist)) != 0) {
X (void) taglines(Error(0, 0, "Missing `%c'.\n", i));
X } else {
X char tokbuf[1000];
X for (iwd=0; (tok=wdlist[iwd]); iwd++) {
X strcpy(tokbuf, tok);
X if (check_ugh(wd, tokbuf) == 0) {
X match = 1;
X if (debug)
X (void) fprintf(stderr,
X "\tMatched pat=%s (user=%s gid=%d hostname=%s)\n",
X tok, si.caller, si.orig_gid, si.hostname);
X } else if (debug) {
X (void) fprintf(stderr,
X "\tNo match pat=%s (user=%s gid=%d hostname=%s)\n",
X tok, si.caller, si.orig_gid, si.hostname);
X }
X }
X }
X }
X }
X
X return match;
X}
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. As a ``side effect'': Sets the fields in the superinfo struct.
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 * e) invalid superfile contents.
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/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Xchar *
Xapprove(usrcmd, debug)
Xchar *usrcmd; /* command we're to check on.
X * If command is one of:
X * "" | "-h" | "-?" | "-H"
X * then a list of super commands is printed
X * instead of attempting to execute anything.
X * The "-h" and "-?" forms give terse help,
X * while the -H form gives long help.
X */
Xint debug; /* !0 If we are in debug mode */
X
X{
X char *path, *buf;
X int local_option();
X int givehelp, verbose;
X char *commandfound;
X void anchor();
X struct passwd *usrpw;
X FILE *fp;
X
X si.orig_uid = getuid();
X si.orig_gid = getgid();
X usrpw = getpwuid(si.orig_uid);
X if (!usrpw)
X (void) Error(1, 1, "approve(): Couldn't get your password entry: ");
X (void) strcpy(si.caller, usrpw->pw_name);
X
X if ((fp = fopen(SUPERFILE, "r")) == NULL)
X (void) Error(1, 1, "Couldn't open super.tab file `%s': ", SUPERFILE);
X
X /* Do we (1) give help, or (2) match a command with this user? */
X givehelp = (usrcmd == NULL) ||
X strcmp(usrcmd, "-H")==0 ||
X strcmp(usrcmd, "-h")==0 || strcmp(usrcmd, "-?")==0;
X verbose = (usrcmd != NULL && strcmp(usrcmd, "-H") == 0);
X
X if (givehelp) {
X if (verbose) {
X (void) printf("%s version %s patchlevel %s\n",
X prog, Version, Patchlevel);
X (void) printf("Use:\n\t%s command [args]\n\n", prog);
X (void) printf("Super.tab file: `%s'\n\n", SUPERFILE);
X }
X (void) printf("Commands available to user %s%s:\n\n", si.caller,
X verbose ? " (use option `-h' for a terse listing)" :
X " (use option `-H' for a long-winded listing)");
X if (!verbose) {
X (void) printf("Command Comments\n");
X (void) printf("------- --------\n");
X }
X }
X
X for (commandfound=NULL, si.line=1;
X (buf=fillbuffer(fp, &si.nl)); si.line += si.nl) {
X (void) local_option(NULL);
X switch(parseline(givehelp, buf, usrcmd, &path, &commandfound, debug)) {
X case -1:
X return NULL;
X break;
X
X case 0:
X break;
X
X default:
X /* Have an acceptable line */
X if (givehelp) {
X printhelp(verbose, commandfound, path);
X } else {
X return path;
X }
X }
X }
X if (givehelp)
X return "";
X else if (!commandfound)
X (void) Error(0, 0, "No such super command as `%s'.\n", usrcmd);
X else
X (void) Error(0, 0, "%s - Permission denied to user %s\n",
X usrcmd, si.caller);
X return NULL;
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Prints help information for a command */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Xvoid
Xprinthelp(verbose, commandfound, path)
Xchar *verbose, *commandfound, *path;
X{
X if (!verbose) {
X (void) printf("%-15s", commandfound);
X if (si.info)
X (void) printf(" %s\n", si.info);
X else
X (void) putchar('\n');
X return;
X }
X
X /* ASSERT verbose!=0 */
X
X if (si.info)
X (void) printf("# %s:\n", si.info);
X (void) printf("%s %s -> %s\n", prog, commandfound, path);
X if (si.user || si.group || si.u_g || si.fdlist || si.env) {
X (void) fputs("\t(Executes with:", stdout);
X if (si.user) (void) printf(" uid=%s", si.user);
X if (si.group) (void) printf(" gid=%s", si.group);
X if (si.u_g) (void) printf(" u+g=%s", si.u_g);
X if (si.env) (void) printf(" env=%s", si.env);
X if (si.fdlist) (void) printf(" fdlist=%s", si.fdlist);
X (void) fputs(")\n", stdout);
X }
X if (si.usr_args[0] < 0)
X ;
X else if (si.usr_args[0] == si.usr_args[1] &&
X si.usr_args[0] == 0)
X (void) printf("\t(No user-entered args are allowed)\n");
X else if (si.usr_args[0] == si.usr_args[1])
X (void) printf("\t(%d user-entered arg%s required)\n",
X si.usr_args[0], si.usr_args[0] == 1? " is" : "s are");
X else
X (void) printf("\t(%d - %d user-entered args are required)\n",
X si.usr_args[0], si.usr_args[1]);
X
X if (si.passinfo.required) {
X if (si.passinfo.timeout == 0)
X (void) printf("\t(Password is required on each use)\n");
X else
X (void) printf("\t(Password is required; good for %d minutes%s)\n",
X si.passinfo.timeout, si.passinfo.renewtime ?
X ", extended with each use" : "");
X }
X (void) putchar('\n');
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Determines if user's group matches a group pattern. */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Xint
Xingroup(user, gid, gp_pat)
Xchar *user;
Xint gid;
Xchar *gp_pat; /* pattern to match */
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 pat_compile/pat_compare!
X * -- messes up caller's use of same!
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
X struct group *gp;
X char **mem;
X void setgrent(), endgrent();
X
X if ((*pat_compile)(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 here, the user is in group gp; now check if group
X * name gp->gr_name matches group pattern gp_pat.
X */
X if ((*pat_compare)(gp->gr_name) == 1) {
X /* successful match -- user is in a group that matches gp_pat */
X endgrent();
X return 1;
X }
X#ifdef MATCH_DECIMAL_UID_GID
X else {
X /* Enabling MATCH_DECIMAL_UID_GID allows the gp_pat to be
X * numeric, as an alternative to being interpreted as a
X * group name: we check if the group id gp->gr_gid, as a
X * decimal text value, matches the group pattern gp_pat.
X */
X char buf[20];
X (void) sprintf(buf, "%d", gp->gr_gid);
X if ((*pat_compare)(buf) == 1){
X /* successful match -- user is in a group that matches gp_pat */
X endgrent();
X return 1;
X }
X }
X#endif
X }
X endgrent();
X return 0;
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Get login directory of a user */
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 if (pass == (struct passwd *) NULL)
X return -1;
X
X (void) strcpy(buf, pass->pw_dir);
X
X return 0;
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* For tagging error text in the superfile */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Xint
Xtaglines(retval)
Xint retval;
X{
X if (si.nl <= 1)
X (void) fprintf(stderr, "\tLine %d in file `%s'\n", si.line, SUPERFILE);
X else
X (void) fprintf(stderr, "\tLines %d..%d in file `%s'\n",
X si.line, si.line + si.nl - 1, SUPERFILE);
X return retval;
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Opens the logfile. */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Xvoid
Xopensuperlog()
X{
X extern FILE *error_logfile; /* to tell Error() where the log is */
X
X if (log.fp)
X (void) fclose(log.fp);
X
X if (*log.filename == '\0') {
X (void) Error(0, 0, "opensuperlog(): logfile name is (nil)\n");
X return;
X }
X
X log.fp = open_writer(log.user, log.filename);
X
X error_logfile = log.fp; /* so that error() writes here too */
X return;
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X /* In order to implement the loguser=xxx option, we (1) create a pipe,
X * (2) fork, in the child setuid to loguid; (3) child opens logfile;
X * (4) child copies from pipe to logfile. Meanwhile, we return a pointer
X * to a stream to the pipe as the log stream seen by the parent program.
X * This allows us to implement a special uid for the logfile writer,
X * without needing the operating system to offer saved uid's or
X * interprocess file-descriptor passing, etc.
X */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
XFILE *
Xopen_writer(user, filename)
Xchar *user;
Xchar *filename;
X{
X FILE *fp;
X int fildes[2];
X pid_t child;
X
X if (pipe(fildes) == -1) {
X (void) Error(1, 0, "Failed to created pipe for logfile; no logging: ");
X return NULL;
X }
X child = fork();
X if (child == -1) {
X (void) Error(1, 0, "Failed to create child for logfile; no logging: ");
X return NULL;
X
X } else if (child > 0) {
X /* In parent -- close read side, and aim logstream at write side */
X (void) close(fildes[0]);
X if (!(fp = fdopen(fildes[1], "w"))) {
X (void) Error(1, 0,
X "failed to fdopen logfile pipe writer; no logging: ");
X (void) close(fildes[1]);
X return NULL;
X }
X
X } else if (child == 0) {
X /* In child. Open log file and copy from pipe to log. */
X FILE *input;
X char text[2000];
X (void) close(fildes[1]);
X if (!(input = fdopen(fildes[0], "r"))) {
X (void) Error(1, 0,
X "failed to fdopen logfile pipe reader; no logging: ");
X (void) close(fildes[1]);
X exit(1);
X }
X if (user && *user != '\0') {
X si.user = user;
X si.group = NULL;
X si.u_g = NULL;
X if (set_u_g() == -1) {
X (void) Error(1, 0,
X "failed to setuid %s before opening logfile; no logging: ",
X log.user);
X exit(1);
X }
X }
X if (!(fp = fopen(log.filename, "a"))) {
X if (user && *user != '\0')
X (void) Error(1, 0,
X "failed to open logfile `%s' using uid `%s': ",
X log.filename, si.user);
X else
X (void) Error(1, 0,
X "failed to open logfile `%s': ", log.filename);
X exit(1);
X }
X while (fgets(text, sizeof(text), input)) {
X if (fputs(text, fp) == EOF)
X (void) Error(1, 0, "fputs to logfile failed: ");
X }
X (void) fclose(fp);
X exit(0);
X }
X return fp;
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Closes the logfile stream, then calls wait(). */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X
Xvoid
Xclose_writer()
X{
X if (!log.fp)
X return;
X
X if (fclose(log.fp) == EOF)
X (void) Error(1, 0, "failed to close log.fp: ");
X (void) wait((int *) NULL);
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X /* Construct a host-unique directory name:
X * xyz.home.caltech.edu -> prefix/xyz.home.caltech.edu/user
X * If hostname is empty string, file is prefix/user.
X
X * WARNING: the hostname used is that from gethostname().
X * Note that this is not necessarily unique across
X * internet domains, since it is frequently not a
X * fully-qualified domain name. Therefore you should NOT
X * share the timestamp directory outside the local domain.
X */
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Xchar *
Xmakedirname(prefix, hostname, file)
Xchar *prefix, *hostname, *file;
X{
X int l;
X char *s, str[MAXPATHLEN];
X
X l = strlen(prefix) + 1 + strlen(hostname) + 1;
X if (l >= MAXPATHLEN) {
X Error(1, 0,
X "Can't create timestamp directory: it would exceed MAXPATHLEN = %d\n",
X MAXPATHLEN);
X return NULL;
X }
X
X strcpy(file, prefix);
X
X if (!*hostname)
X return file;
X
X#ifdef L14
X strncpy(str, hostname, 14);
X str[14] = '\0'; /* in case exactly 14 chars were copied */
X#else
X strcpy(str, hostname);
X#endif
X for (s = strrchr(str, '.'); s; *s = '\0', s = strrchr(str, '.')) {
X strcat(file, "/");
X strcat(file, s+1);
X }
X strcat(file, "/");
X strcat(file, str);
X
X return file;
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Creates a directory, including any needed directories leading
X * to it. Returns 1 on success, dies otherwise.
X * WARNING: doesn't check if final component is a directory.
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Xint
Xmakedir(dir)
Xchar *dir; /* path with directories only. */
X{
X static struct stat st;
X char *q;
X char path[MAXPATHLEN];
X
X /* First create directories along way, if necessary */
X strcpy(path, dir);
X
X for (q=path; q && *q; ) {
X
X /* skip leading slashes */
X while (*q == '/')
X q++;
X
X /* check directory before next slash */
X q = strchr(q, '/');
X if (q)
X *q = '\0';
X
X /* Stat directory; if missing, create it */
X if (stat(path, &st) != 0) {
X if (errno != ENOENT) {
X Error(1, 1, "Failed to stat directory `%s'\n", path);
X } else {
X if (mkdir(path, 0700) != 0)
X Error(1, 1, "Failed to create directory `%s'\n", path);
X }
X }
X
X /* Restore slash */
X if (q)
X *q = '/';
X }
X
X return 1;
X}
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
X/* Input is string like "lhs=rhs"
X * If there is no `=', null ptr is returned.
X * If lhs == left, then ptr to rhs is returned; else null pointer.
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Xchar *
Xstr_val(left, str)
Xchar *left, *str;
X{
X char *s = strchr(str, '=');
X if (!s)
X return NULL;
X
X if (strncmp(left, str, s - str) != 0)
X return NULL;
X
X return s+1;
X}
X
X
X/*
X * -----------------------------------------
X * re_comp()/re_exec()-style interface to wildmat.
X * -----------------------------------------
X */
Xstatic char *shell_pattern;
Xchar *
Xshell_compile(s)
Xchar *s;
X{
X shell_pattern = s;
X return NULL;
X}
X
Xint
Xshell_compare(s)
Xchar *s;
X{
X extern int wildmat();
X return wildmat(s, shell_pattern);
X}
END_OF_FILE
if test 64226 -ne `wc -c <'super-3.4.5/super.c'`; then
echo shar: \"'super-3.4.5/super.c'\" unpacked with wrong size!
fi
# end of 'super-3.4.5/super.c'
fi
echo shar: End of archive 2 \(of 3\).
cp /dev/null ark2isdone
#! /bin/sh
# This is a shell archive. Remove anything before this line, then feed it
# into a shell via "sh file" or similar. To overwrite existing files,
# type "sh file -c".
# Contents: super-3.4.5/Makefile super-3.4.5/Makefile.orig
# super-3.4.5/README super-3.4.5/WhatsNew super-3.4.5/braces.c
# super-3.4.5/error.c super-3.4.5/super.1 super-3.4.5/wildmat.c
# Wrapped by kent@sparky on Sun May 8 15:43:28 1994
PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin:$PATH ; export PATH
echo If this archive is complete, you will see the following message:
echo ' "shar: End of archive 3 (of 3)."'
if test -f 'super-3.4.5/Makefile' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'super-3.4.5/Makefile'\"
else
echo shar: Extracting \"'super-3.4.5/Makefile'\" \(5077 characters\)
sed "s/^X//" >'super-3.4.5/Makefile' <<'END_OF_FILE'
X
X#######
X# Where the executable goes
XBINDIR=/usr/local/bin
X
X#######
X# Man pages
X
X# Directory for super(1) man page
XMANDIR=/usr/local/man/man1
X
X# Extension on the super(1) man page
XMANEXT=1
X
X# Directory for the man page describing the format of the super.tab file.
X# Note: file formats usually go into section 4 (SysV) or section 5 (BSD).
XMAN_FMT_DIR=/usr/local/man/man4
X
X# Extension on the super.tab format man page.
XMAN_FMT_EXT=4
X
X#######
X# CC
XCC=cc
X
X#######
X# CFLAGS
X
X# 1. USE_NETGROUP: This indicates whether the hostname "+xxx" should mean
X# any host in netgroup "xxx"; e.g. `joe@+xyz' means allow
X# joe to execute the command from any host in NIS netgroup xyz.
X# If USE_NETGROUP is not defined, `joe@+xyz' means joe at host `+xyz'.
X# (Netgroups are typically defined in /etc/netgroup on your NIS master.)
X
X# 2. USE_SYSLOG: This indicates whether the error-message printing routine
X# should be compiled with the option of calling syslog(). You should
X# define this if you have syslog() available. The actual printing to
X# syslog at runtime is controlled by the global option syslog=y|n.
X
X# 3. 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 ok 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# 4. SUPERFILE: if you want the superfile to be other than
X# /usr/local/lib/super.tab, add -DSUPERFILE=\"FullPathName\"
X
X# 5. SAFE_PATH: this is the setting of the PATH variable for any command
X# executed by super. The default is "/bin:/usr/bin:/usr/ucb".
X# If you prefer a different path, add -DSAFE_PATH=\"MyPreferredPath\",
X# and change the description of PATH in the man page to suit.
X
X# 6. TIMESTAMP_DIR: This is the directory in which timestamp files are
X# kept for commands requiring passwords to be entered. The default
X# is "/usr/local/lib/superstamps". If you prefer a different path,
X# add -DTIMESTAMP_DIR=\"/My/Preferred/Directory\".
X
X# 7. L14: define this if your filenames can only be 14 characters long.
X# Timestamp filenames usually look like hostname/user; with this option,
X# the hostname part is truncated to 14 characters.
X
X# AIX
X# CFLAGS= -O -DUSE_NETGROUP -DUSE_SYSLOG
X
X# HP-UX 9.01:
X# Note: unpatched HP-UX 9.01 c89 has a bug, so that compiling with -O
X# doesn't work. Patched version is OK.
XCFLAGS= -DAa -D_HPUX_SOURCE -DUSE_NETGROUP -DMAXFD="sysconf(_SC_OPEN_MAX)-1" -DUSE_SYSLOG
X
X# IRIX v4.0.5:
X# CFLAGS= -g -DUSE_NETGROUP -DSAFE_PATH=\"/bin:/usr/bin:/usr/bsd\" -DUSE_SYSLOG
X
X# Solaris 2.2, 2.3; SunOS 5.2, 5.3:
X# CFLAGS= -O -DUSE_NETGROUP -DMAXFD="sysconf(_SC_OPEN_MAX)-1" -DSUNOS5 -DUSE_SYSLOG
X
X# SunOS 4.1.3:
X# CFLAGS= -O -DUSE_NETGROUP -DUSE_SYSLOG
X
X#######
X# LDFLAGS,LDLIBS
X
X# AIX
X# LDFLAGS=
X# LDLIBS=
X
X# HP-UX 9.01:
XLDFLAGS=
XLDLIBS=
X
X# IRIX v4.0.5: -lsun is for the NIS stuff (so that you can use
X# netgroup entries). -lmalloc avoids a problem with the
X# default malloc routine that causes a core dump.
X# LDFLAGS =
X# LDLIBS = -lmalloc -lsun
X
X# Solaris 2.2, 2.3: don't use -Bstatic or -static: it's supposedly not needed
X# for security (see LD_LIBRARY_PATH in ld(1)), and the groupname-lookup
X# routines are for some reason only available in shared libs (see getgrnam(3)).
X# LDFLAGS=
X# LDLIBS= -lnsl
X
X# SunOS 4.1.3: use -Bstatic with cc; use -static with gcc
X# LDFLAGS= -static
X# LDLIBS=
X
X####################################################################
X# You shouldn't have to modify anything below this line.
X####################################################################
X
XSRC= super.c strqtok.c regex.c re_fail.c braces.c error.c wildmat.c version.h
XOBJ= super.o strqtok.o regex.o re_fail.o braces.o error.o wildmat.o
XALL= README WhatsNew Makefile super.1 super.5 sample.tab sample.cdmount $(SRC)
X
Xall: super
X
Xsuper: $(OBJ)
X $(CC) $(LDFLAGS) -o super $(OBJ) $(LDLIBS)
X
X# NOTICE NOTICE NOTICE!
X# You must install super as "super", and not use any other program name.
X# This is because at run-time, the program assumes that if it's invoked
X# with a different name, it must have been invoked via a symlink, and
X# it treats
X# % myothernameforsuper args
X# as if you typed
X# % super myothernameforsuper args
X# If you insist on installing under another name, then you must edit
X# super.c and change the #define ONETRUENAME from "super" to your other
X# name, and of course edit the documentation to match.
Xinstall:
X cp super $(BINDIR)/super
X chmod 04755 $(BINDIR)/super
X cp super.1 $(MANDIR)/super.$(MANEXT)
X cp super.5 $(MAN_FMT_DIR)/super.$(MAN_FMT_EXT)
X
Xclean:
X rm -f super *.o
X
X$(ALL):
X co $@
X
Xshar: $(ALL)
X ( echo "#include <stdio.h>" ; \
X echo '#include "version.h"' ; \
X echo 'main() {printf("%s.%s\\n",Version,Patchlevel);exit(0);}') > temp.c
X cc temp.c
X V=super-`a.out`; mkdir $$V && cp $(ALL) $$V && \
X cp Makefile $$V/Makefile.orig && chmod +w $$V/Makefile && \
X shar -n $$V -s wi...@astro.caltech.edu -a -c $$V > $$V.shar && \
X rm -rf $$V
X rm a.out temp.c
END_OF_FILE
if test 5077 -ne `wc -c <'super-3.4.5/Makefile'`; then
echo shar: \"'super-3.4.5/Makefile'\" unpacked with wrong size!
fi
# end of 'super-3.4.5/Makefile'
fi
if test -f 'super-3.4.5/Makefile.orig' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'super-3.4.5/Makefile.orig'\"
else
echo shar: Extracting \"'super-3.4.5/Makefile.orig'\" \(5077 characters\)
sed "s/^X//" >'super-3.4.5/Makefile.orig' <<'END_OF_FILE'
X
X#######
X# Where the executable goes
XBINDIR=/usr/local/bin
X
X#######
X# Man pages
X
X# Directory for super(1) man page
XMANDIR=/usr/local/man/man1
X
X# Extension on the super(1) man page
XMANEXT=1
X
X# Directory for the man page describing the format of the super.tab file.
X# Note: file formats usually go into section 4 (SysV) or section 5 (BSD).
XMAN_FMT_DIR=/usr/local/man/man4
X
X# Extension on the super.tab format man page.
XMAN_FMT_EXT=4
X
X#######
X# CC
XCC=cc
X
X#######
X# CFLAGS
X
X# 1. USE_NETGROUP: This indicates whether the hostname "+xxx" should mean
X# any host in netgroup "xxx"; e.g. `joe@+xyz' means allow
X# joe to execute the command from any host in NIS netgroup xyz.
X# If USE_NETGROUP is not defined, `joe@+xyz' means joe at host `+xyz'.
X# (Netgroups are typically defined in /etc/netgroup on your NIS master.)
X
X# 2. USE_SYSLOG: This indicates whether the error-message printing routine
X# should be compiled with the option of calling syslog(). You should
X# define this if you have syslog() available. The actual printing to
X# syslog at runtime is controlled by the global option syslog=y|n.
X
X# 3. 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 ok 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# 4. SUPERFILE: if you want the superfile to be other than
X# /usr/local/lib/super.tab, add -DSUPERFILE=\"FullPathName\"
X
X# 5. SAFE_PATH: this is the setting of the PATH variable for any command
X# executed by super. The default is "/bin:/usr/bin:/usr/ucb".
X# If you prefer a different path, add -DSAFE_PATH=\"MyPreferredPath\",
X# and change the description of PATH in the man page to suit.
X
X# 6. TIMESTAMP_DIR: This is the directory in which timestamp files are
X# kept for commands requiring passwords to be entered. The default
X# is "/usr/local/lib/superstamps". If you prefer a different path,
X# add -DTIMESTAMP_DIR=\"/My/Preferred/Directory\".
X
X# 7. L14: define this if your filenames can only be 14 characters long.
X# Timestamp filenames usually look like hostname/user; with this option,
X# the hostname part is truncated to 14 characters.
X
X# AIX
X# CFLAGS= -O -DUSE_NETGROUP -DUSE_SYSLOG
X
X# HP-UX 9.01:
X# Note: unpatched HP-UX 9.01 c89 has a bug, so that compiling with -O
X# doesn't work. Patched version is OK.
XCFLAGS= -DAa -D_HPUX_SOURCE -DUSE_NETGROUP -DMAXFD="sysconf(_SC_OPEN_MAX)-1" -DUSE_SYSLOG
X
X# IRIX v4.0.5:
X# CFLAGS= -g -DUSE_NETGROUP -DSAFE_PATH=\"/bin:/usr/bin:/usr/bsd\" -DUSE_SYSLOG
X
X# Solaris 2.2, 2.3; SunOS 5.2, 5.3:
X# CFLAGS= -O -DUSE_NETGROUP -DMAXFD="sysconf(_SC_OPEN_MAX)-1" -DSUNOS5 -DUSE_SYSLOG
X
X# SunOS 4.1.3:
X# CFLAGS= -O -DUSE_NETGROUP -DUSE_SYSLOG
X
X#######
X# LDFLAGS,LDLIBS
X
X# AIX
X# LDFLAGS=
X# LDLIBS=
X
X# HP-UX 9.01:
XLDFLAGS=
XLDLIBS=
X
X# IRIX v4.0.5: -lsun is for the NIS stuff (so that you can use
X# netgroup entries). -lmalloc avoids a problem with the
X# default malloc routine that causes a core dump.
X# LDFLAGS =
X# LDLIBS = -lmalloc -lsun
X
X# Solaris 2.2, 2.3: don't use -Bstatic or -static: it's supposedly not needed
X# for security (see LD_LIBRARY_PATH in ld(1)), and the groupname-lookup
X# routines are for some reason only available in shared libs (see getgrnam(3)).
X# LDFLAGS=
X# LDLIBS= -lnsl
X
X# SunOS 4.1.3: use -Bstatic with cc; use -static with gcc
X# LDFLAGS= -static
X# LDLIBS=
X
X####################################################################
X# You shouldn't have to modify anything below this line.
X####################################################################
X
XSRC= super.c strqtok.c regex.c re_fail.c braces.c error.c wildmat.c version.h
XOBJ= super.o strqtok.o regex.o re_fail.o braces.o error.o wildmat.o
XALL= README WhatsNew Makefile super.1 super.5 sample.tab sample.cdmount $(SRC)
X
Xall: super
X
Xsuper: $(OBJ)
X $(CC) $(LDFLAGS) -o super $(OBJ) $(LDLIBS)
X
X# NOTICE NOTICE NOTICE!
X# You must install super as "super", and not use any other program name.
X# This is because at run-time, the program assumes that if it's invoked
X# with a different name, it must have been invoked via a symlink, and
X# it treats
X# % myothernameforsuper args
X# as if you typed
X# % super myothernameforsuper args
X# If you insist on installing under another name, then you must edit
X# super.c and change the #define ONETRUENAME from "super" to your other
X# name, and of course edit the documentation to match.
Xinstall:
X cp super $(BINDIR)/super
X chmod 04755 $(BINDIR)/super
X cp super.1 $(MANDIR)/super.$(MANEXT)
X cp super.5 $(MAN_FMT_DIR)/super.$(MAN_FMT_EXT)
X
Xclean:
X rm -f super *.o
X
X$(ALL):
X co $@
X
Xshar: $(ALL)
X ( echo "#include <stdio.h>" ; \
X echo '#include "version.h"' ; \
X echo 'main() {printf("%s.%s\\n",Version,Patchlevel);exit(0);}') > temp.c
X cc temp.c
X V=super-`a.out`; mkdir $$V && cp $(ALL) $$V && \
X cp Makefile $$V/Makefile.orig && chmod +w $$V/Makefile && \
X shar -n $$V -s wi...@astro.caltech.edu -a -c $$V > $$V.shar && \
X rm -rf $$V
X rm a.out temp.c
END_OF_FILE
if test 5077 -ne `wc -c <'super-3.4.5/Makefile.orig'`; then
echo shar: \"'super-3.4.5/Makefile.orig'\" unpacked with wrong size!
fi
# end of 'super-3.4.5/Makefile.orig'
fi
if test -f 'super-3.4.5/README' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'super-3.4.5/README'\"
else
echo shar: Extracting \"'super-3.4.5/README'\" \(8896 characters\)
sed "s/^X//" >'super-3.4.5/README' <<'END_OF_FILE'
XSuper(1) is a setuid-root program that offers
X
X o restricted setuid-root access to executables, adjustable
X on a per-program and per-user basis;
X
X o a relatively secure environment for scripts, so that well-written
X scripts can be run as root (or some other uid/gid), without
X 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 restrict which users, on which hosts, may execute a
X setuid-root program.
X
X - to call a script that allows users to send STOP/CONT
X signals to certain jobs, but not others.
X
X--------------------
X
XSuper and sudo
X
XI have received some enquiries regarding the difference between super
Xand sudo, another program designed to give restricted access to certain
Xcommands.
X
XSudo --
X Sudo allows a permitted user to execute a command as the superuser.
X I think its central design philosophy is that each user can be
X trusted when executing certain commands. This is implemented
X by allowing each user to execute the restricted commands for
X which s/he is trusted, without giving access to other restricted commands.
X
XSuper --
X The design philosophy behind super is two-fold:
X (a) some users can be trusted when executing certain commands;
X (b) there are some commands, such as a script to mount CDROM's,
X which you'd like to be safely executable even by users who
X are NOT trusted. Although setuid-root scripts are insecure,
X a good setuid-root wrapper around a sensible non-setuid script
X can be hard to break, and super provides that wrapper so that
X even a non-trusted user can use the scripts.
X
XIn my view, the main differences to the administrator are:
X
X (1) the files that specify valid user/command combinations have
X a different look and feel.
X
X (2) super provides a safe wrapper for scripts, so that a
X well-written script can be run safely by ordinary
X users without having to actually trust them.
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
Xe.g.
X cdmount /usr/local/bin/cdmount {harry,sally}@kaa tom@surya
X cdumount /usr/local/bin/cdumount {harry,sally}@kaa tom@surya
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
XEach entry in the super.tab file can contain a variety of options, which
Xinclude such things as setting the real uid and/or gid to something other
Xthan root, requiring the user's password before executing the command,
Xand so on.
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. By default, the effective uid
Xis set to 0 (root) before executing the 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
Xany characters other than [0-9], they are discarded. To these are
Xadded reasonable values for IFS, PATH, USER and HOME (USER and HOME
Xare set to the username and login directory, respectively, of the
Xreal uid under which the command is executed by super). LOGNAME is
Xset to the same as USER. SUPERCMD is set to the <command>. ORIG_USER,
XORIG_LOGNAME, and ORIG_HOME are set to the USER, LOGNAME, and HOME of
Xthe user who invoked super. All descriptors excepting 0,1,2 are closed.
XSignals are all reset to have default handling.
X
X--------------------
X
XAcknowledgements
X
XThis program uses the following extremely useful code from others:
X
X o Ozan Yigit's regex routines
X o Rich $alz's sh-style pattern-matching routines.
X o The BSD brace-expansion code.
X
XThe following people contributed ideas, code, and/or fixes to various
Xsuper versions (my apologies to anybody I've inadvertently left out):
X
X Geoffrey A. Lowney (geo...@vallista.ca.boeing.com)
X - provided bugfixes for debug initialization & help listing.
X - suggested the symlink hack (a la rsh).
X - suggested the -H / -h (long help / short help) option.
X Pat Myrto (rwing!p...@rutgers.edu)
X - portability modifications.
X H.C.den Harink (Ha...@electron.ms.philips.nl)
X - group id fixes, Makefile fixes.
X (ees...@ee.surrey.ac.uk)
X - fix for checking TERM.
X Pedro Antonio Acebes Bayon (pac...@cozuelos.tid.es)
X - noted failure to match some TERM patterns, logging error,
X portability problems.
X Valter V. Cavecchia (val...@itnsg1.cineca.it)
X - modifications for logging usage; suggested -h should show
X commands only valid for that user; inspired options for
X setting uid, gid, env, fd, extended SAFE_PATH.
X Gary Duncan (gdu...@penguin.pts.philips.oz.au)
X - beta-test sufferer in extremis.
X Amar Vadlamudi (na...@src.umd.edu)
X - pointed out problems with network-wide logging, and workaround.
X Olle Wikstrom (ere...@ere.ericsson.se)
X - pointed out that I screwed up TERM checking yet again!
X Gerry Singleton (Gerry.S...@canada.sun.com)
X - ported to Solaris 5.3.
X
X Many others for beta-testing various versions...
X
X--------------------
X
XMaking and installation:
X
XThe Makefile is short. 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 number of file descriptors. In that case, use
X-DMAXFD=nnn when compiling, where nnn is the largest valid file descriptor;
Xalternatively, replace nnn with any C expression to return the
Xmaximum valid descriptor.
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
XYou can also use the -d option (debug), which will show you what super will
Xdo, but never actually execute a command:
X
X % super -d 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 invoke 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 ${1+"$@"}
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. Generally, csh
X scripts should _always_ contain the -f flag on the exec line to
X prevent the .cshrc file from being sourced:
X #!/bin/csh -fb
X
X4. Better still, avoid csh scripts entirely -- they are harder to
X write safely than Bourne-shell scripts.
X
X5. 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
X6. By default, super only changes the effective uid. Some programs
X (e.g. exportfs under SunOS 4.1.x) require the real uid to be root.
X In that case, either your super script will have to change the real
X uid to root before executing the command, or the super.tab file
X should set "uid=root".
X
X7. 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 Be leery of using "env=var,..." to increase the list of preserved
X environment variables -- some programs can be tricked into
X executing commands embedded in certain environment variables.
X
X--------------------
X
X/* Will Deich
X * Caltech 105-24, Pasadena, CA 91125
X * Internet: wi...@astro.caltech.edu
X */
END_OF_FILE
if test 8896 -ne `wc -c <'super-3.4.5/README'`; then
echo shar: \"'super-3.4.5/README'\" unpacked with wrong size!
fi
# end of 'super-3.4.5/README'
fi
if test -f 'super-3.4.5/WhatsNew' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'super-3.4.5/WhatsNew'\"
else
echo shar: Extracting \"'super-3.4.5/WhatsNew'\" \(6179 characters\)
sed "s/^X//" >'super-3.4.5/WhatsNew' <<'END_OF_FILE'
X-------------------
XWhat's new for version 3.4.5:
X
X A. Improved parsing for arguments that super supplies to commands.
X Previously, only whitespace would separate arguments. Version
X 3.4.5 parses the FullPathName with recognition of embedded
X quotes and backslashes.
X
X For example, suppose the super.tab file contains a line like
X
X cmd "FullPath -a -b 2\ 3 -xrm 'r s t \
X u v w' ..." SuperOptions... validusers...
X
X The "FullPath..." string (containing the arguments -a, -b, ...)
X ... is parsed using Bourne-shell-like rules for backslashes
X and quotes, and the line is parsed as
X argv[0] FullPath
X argv[1] -a
X argv[2] -b
X argv[3] 2 3
X argv[4] -xrm
X argv[5] r s t u v w
X
X (Use "super -d cmd" to check that your args are being parsed
X as expected before you unleash a new command on your users.)
X
X-------------------
XWhat's new for version 3.4.4:
X
X A. The timestamp file of us...@host.name.dom is now by default
X stored in
X TIMESTAMP_DIR/hostname/user
X with the old behavior (TIMESTAMP_DIR/user) being an option
X controlled with global option timestampbyhost=y|n.
X
X The timestampuid=xxx option has been added to allow timestamp
X files to be created under a particular uid.
X This allows for a cross-mounted timestamp directory on hosts
X that map NFS root accesses to nobody, but still keeps the
X password entries distinct on different hosts.
X
X B. TIMESTAMP_DIR is now documented in the Makefile, so that it
X is easily configured at compile time.
X
X-------------------
XWhat's new for version 3.4.3:
X
X A. Option -H has been added to give the long-winded help information
X that has been the only thing printed until now.
X
X The -h option now prints a short help listing:
X
X Command Comments
X ------- --------
X cmd1 help info for cmd1
X cmd2 help info for cmd2
X ...
X
X-------------------
XWhat's new for version 3.4.2:
X
X A. If "xyz" is a symlink to super, then
X % xyz args...
X is treated by super just like
X % super xyz args...
X
X Super detects such symlinks by the rule that argv[0] must
X match "*/super" or "super"; otherwise, it's assumed to be
X a symlink. Therefore, a symlink named "super" won't work --
X super won't recognize it's being invoked via symlink.
X
X-------------------
XWhat's new for version 3.4.1:
X
X A. When printing debug info, there was misformatted output
X of any extra file descriptors being held open. (The
X problem only occurred in 3.4.0, not earlier versions.)
X
X-------------------
XWhat's new for version 3.4:
X
X A. Added nargs=[mmm-]nnn to limit user-entered args.
X
X-------------------
XWhat's new for version 3.3.2:
X
X A. Allow "@hostname", without any user or groupname part.
X
X-------------------
XWhat's new for version 3.3.1:
X
X A. Fixed SunOS 5.x to use "getspnam()" when looking up passwords.
X
X-------------------
XWhat's new for version 3.3:
X
X A. Added mail="....." global option.
X
X B. Fixed goofy error in parsing password=n.
X
X C. Added syslog=y|n option.
X
X-------------------
XWhat's new for version 3.2:
X A. Added options password=y|n, timeout=n, renewtime=y|n to require
X passwords on specific commands (or all commands).
X
X B. A bugfix: if the super.tab file tried to pass options to the
X executable command, the exec would fail.
X
X-------------------
XWhat's new for version 3.1:
X A. Added global option loguid=xxx to allow the logfile to be opened
X under a uid other than root. This allows the logfile to be
X shared across a network over which root doesn't have write access.
X
X-------------------
XWhat's new for version 3.0:
X
X A. Command logging -- you can specify a file to receive a log
X of super uses and attempts.
X
X B. More environment variables -- for each entry, the super.tab file
X can specify environment variables that should be passed to
X the command instead of discarded. You simply add entries like
X env=TZ,TAPE
X to the super.tab file, to keep TZ and TAPE in addition to
X the default list. Of course one has to use this with
X caution.
X
X C. Set uid/gid -- for each entry, the super.tab file can specify
X the uid and/or gid (the default is to only change the effective
X uid to root). To use this, you add entries like
X uid=xxx gid=yyy
X to the super.tab file, or u+g=xxx to set the uid
X to xxx and the gid to xxx's login gid.
X
X As a supplement to the setuid/setgid ability, super defines
X some extra environment variables so that the invoked command
X can know the username and home directory of the user who
X invoked the command, as well as that of the uid under which
X the command is executing.
X
X D. Open file descriptors -- for each entry, the super.tab file
X can specify a list of file descriptors that should not be
X closed (in addition to the default 0,1,2).
X
X E. Initial arguments -- for each entry, the super.tab file can
X specify a set of initial arguments that are put into argv[]
X ahead of the arguments the user typed on the command line.
X
X F. super -h now only prints the commands that may be executed by the
X user; and the super.tab file can specify a line of explanatory
X text to be printed with each command.
X
X G. A bugfix: The TERM environment variable can now contain "-+_.:/"
X in addition to [a-zA-Z0-9].
X
X H. The super.tab file can be configured with either Bourne-shell style
X or regex (ed-style) valid-user patterns; the default is regex.
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
END_OF_FILE
if test 6179 -ne `wc -c <'super-3.4.5/WhatsNew'`; then
echo shar: \"'super-3.4.5/WhatsNew'\" unpacked with wrong size!
fi
# end of 'super-3.4.5/WhatsNew'
fi
if test -f 'super-3.4.5/braces.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'super-3.4.5/braces.c'\"
else
echo shar: Extracting \"'super-3.4.5/braces.c'\" \(6334 characters\)
sed "s/^X//" >'super-3.4.5/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"
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 6334 -ne `wc -c <'super-3.4.5/braces.c'`; then
echo shar: \"'super-3.4.5/braces.c'\" unpacked with wrong size!
fi
# end of 'super-3.4.5/braces.c'
fi
if test -f 'super-3.4.5/error.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'super-3.4.5/error.c'\"
else
echo shar: Extracting \"'super-3.4.5/error.c'\" \(8380 characters\)
sed "s/^X//" >'super-3.4.5/error.c' <<'END_OF_FILE'
X#include <stdio.h>
X#include <errno.h>
X#include <string.h>
X#include <pwd.h>
X#include <unistd.h>
X
X#ifdef USE_SYSLOG
X#include <syslog.h>
X#endif
X
X#ifdef __STDC__
X#include <stdarg.h>
X#else
X#include <varargs.h>
X#endif
X
X#ifdef SUNOS5
X#include <sys/time.h>
X#include <sys/systeminfo.h>
Xextern int sysinfo();
X#define gethostname(buf, lbuf) (sysinfo(SI_HOSTNAME, (buf), (lbuf)))
X#undef NSIG
X#define NSIG _sys_nsig
X#else
Xextern int gethostname();
X#include <time.h>
X#endif
X
XFILE *error_logfile = NULL;
Xchar *error_prog = NULL;
Xchar *error_log_prog = NULL;
Xchar *error_command = NULL;
X
Xint error_syslog = 0;
X
X#ifdef USE_SYSLOG
Xint error_priority = LOG_ERR;
Xint openlog_done = 0;
X#else
Xint error_priority = 0;
X#endif
X
Xstatic int uid = -1;
Xstatic char user[128] = "";
Xstatic char hostname[1024] = "";
X
X/* Error -- print error message, then optionally die.
X
X * Usage: Error(show_perror, die, format, args... );
X * Print error message according to format & args.
X * If show_perror != 0 && errno != 0, follow error message with perror("").
X * (If show_perror !=0, but errno==0, follow error msg with "\n").
X * If die != 0, exit with exit(die).
X
X * If error_prog != NULL, then the message to stderr is preceded
X * with "<error_prog>: ".
X
X * If error_logfile != NULL, then the message is also directed to
X * that file, preceded with
X * error_log_prog: user@hostname timestamp
X * If error_log_prog is NULL, it isn't printed. We keep separate
X * error_prog and error_log_prog so that the program name can be
X * printed on stderr (where there might otherwise be confusion to the
X * user about which program it is) but optionally not printed in the
X * logfile, which is typically unique to the program, so that the
X * program name is redundant.
X
X * If error_command != NULL and *error_command != '\0', then error_command
X * is popen'd and the message is piped in. If the popen fails, Error()
X * is silent about the problem. NOTE: the command is executed separately
X * for each call to this routine.
X
X * If compiled with USE_SYSLOG defined, and the caller sets error_syslog != 0,
X * then the message is passed to syslog(), at priority error_priority.
X * In this case, the fmt string and the printf output must be less
X * than 1000 characters each. This is because syslog() accepts a
X * printf-style variadic argument list, but it doesn't have a va_list
X * version. Therefore we print into a string and pass that onto syslog().
X * As a side effect, you can't use syslog-specific "%m" in the fmt.
X * If the error_prog string is non-null, then just before the first
X * call to syslog, openlog is called with an ident string = error_prog.
X * Note that this is done just once: you can't change error_prog
X * between messages.
X
X * Return code is -1, so you can print error messages and return an error
X * code with return Error(...);
X */
X
X#ifdef __STDC__
X/* VARARGS3 */
Xint
XError(
X int show_perror, /* If errno != 0, follow msg with perror("") */
X int die, /* If !0, exit with exit(die) */
X char *fmt, /* Print rest of args with fprintf(stderr, fmt, ...) */
X ... )
X{
X va_list ap;
X int error = errno;
X FILE *error_cmd = NULL;
X
X /* Program name */
X if (error_prog)
X (void) fprintf(stderr, "%s: ", error_prog);
X
X if (error_command && *error_command)
X error_cmd = popen(error_command, "w");
X
X if (error_log_prog) {
X if (error_logfile)
X (void) fprintf(error_logfile, "%s: ", error_log_prog);
X if (error_cmd)
X (void) fprintf(error_cmd, "%s: ", error_log_prog);
X }
X
X if (error_logfile || error_syslog || error_command) {
X if (getuid() != uid || *user == '\0') {
X struct passwd *pw;
X pw = getpwuid((uid=getuid()));
X if (pw) (void) strcpy(user, pw->pw_name);
X }
X }
X
X if (error_logfile || error_cmd) {
X /* user@hostname & timestamp */
X char *s;
X time_t tptr;
X if (error_log_prog) {
X if (error_logfile)
X fprintf(error_logfile, "%s: ", error_log_prog);
X if (error_cmd)
X fprintf(error_cmd, "%s: ", error_log_prog);
X }
X if (*hostname == '\0')
X (void) gethostname(hostname, sizeof(hostname));
X (void) time(&tptr);
X s = ctime(&tptr);
X s[strlen(s) - 1] = '\0';
X if (error_logfile)
X (void) fprintf(error_logfile, "%s@%s %s\t", user, hostname, s);
X if (error_cmd)
X (void) fprintf(error_cmd, "%s@%s %s\t", user, hostname, s);
X }
X
X /* User's msg */
X va_start(ap, fmt);
X (void) vfprintf(stderr, fmt, ap);
X va_end(ap);
X
X if (error_logfile) {
X /* User's msg */
X va_start(ap, fmt);
X (void) vfprintf(error_logfile, fmt, ap);
X va_end(ap);
X }
X
X if (error_cmd) {
X /* User's msg */
X va_start(ap, fmt);
X (void) vfprintf(error_cmd, fmt, ap);
X va_end(ap);
X }
X
X if (show_perror) {
X if (error) {
X errno = error;
X perror("");
X if (error_logfile) {
X (void) fprintf(error_logfile, "Error %d\n", error);
X /* (void) fprintf(error_logfile, "%s\n", strerror(error)); */
X }
X if (error_cmd) {
X (void) fprintf(error_cmd, "Error %d\n", error);
X /* (void) fprintf(error_cmd, "%s\n", strerror(error)); */
X }
X } else {
X (void) fputc('\n', stderr);
X if (error_logfile)
X (void) fputc('\n', error_logfile);
X if (error_cmd)
X (void) fputc('\n', error_cmd);
X }
X }
X
X#ifdef USE_SYSLOG
X if (error_syslog) {
X char newfmt[1000], buf[1000];
X if (!openlog_done && error_prog) {
X openlog(error_prog, 0, LOG_USER);
X openlog_done = 1;
X }
X sprintf(newfmt, "(%s) ", user);
X strcat(newfmt, fmt);
X va_start(ap, fmt);
X (void) vsprintf(buf, newfmt, ap);
X va_end(ap);
X syslog(error_priority, buf);
X }
X#endif
X
X if (die)
X (void) exit(die);
X
X if (error_cmd)
X pclose(error_cmd);
X
X return -1;
X
X}
X#else
X
X/* VARARGS3 */
Xint
XError( va_alist )
Xva_dcl
X{
X va_list ap;
X int die, show_perror;
X char *fmt;
X int error = errno;
X FILE *error_cmd;
X
X /* Program name */
X if (error_prog)
X (void) fprintf(stderr, "%s: ", error_prog);
X
X if (error_command && *error_command)
X error_cmd = popen(error_command, "w");
X
X if (error_log_prog) {
X if (error_logfile)
X (void) fprintf(error_logfile, "%s: ", error_log_prog);
X if (error_cmd)
X (void) fprintf(error_cmd, "%s: ", error_log_prog);
X }
X
X if (error_logfile || error_syslog || error_command) {
X if (getuid() != uid || *user == '\0') {
X struct passwd *pw;
X pw = getpwuid((uid=getuid()));
X if (pw) (void) strcpy(user, pw->pw_name);
X }
X }
X
X if (error_logfile || error_cmd) {
X /* user@hostname & timestamp */
X char *s;
X time_t tptr;
X if (error_log_prog) {
X if (error_logfile)
X fprintf(error_logfile, "%s: ", error_log_prog);
X if (error_cmd)
X fprintf(error_cmd, "%s: ", error_log_prog);
X }
X if (*hostname == '\0')
X (void) gethostname(hostname, sizeof(hostname));
X (void) time(&tptr);
X s = ctime(&tptr);
X s[strlen(s) - 1] = '\0';
X if (error_logfile)
X (void) fprintf(error_logfile, "%s@%s %s\t", user, hostname, s);
X if (error_cmd)
X (void) fprintf(error_cmd, "%s@%s %s\t", user, hostname, s);
X }
X
X /* User's msg */
X va_start(ap);
X show_perror = va_arg(ap, int);
X die = va_arg(ap, int);
X fmt = va_arg(ap, char *);
X (void) vfprintf(stderr, fmt, ap);
X va_end(ap);
X
X if (error_logfile) {
X /* User's msg */
X va_start(ap);
X show_perror = va_arg(ap, int);
X die = va_arg(ap, int);
X fmt = va_arg(ap, char *);
X (void) vfprintf(error_logfile, fmt, ap);
X va_end(ap);
X }
X
X if (error_cmd) {
X /* User's msg */
X va_start(ap);
X show_perror = va_arg(ap, int);
X die = va_arg(ap, int);
X fmt = va_arg(ap, char *);
X (void) vfprintf(error_cmd, fmt, ap);
X va_end(ap);
X }
X
X if (show_perror) {
X if (error) {
X errno = error;
X perror("");
X if (error_logfile)
X (void) fprintf(error_logfile, "Error %d\n", error);
X if (error_cmd)
X (void) fprintf(error_cmd, "Error %d\n", error);
X } else {
X (void) fputc('\n', stderr);
X if (error_logfile)
X (void) fputc('\n', error_logfile);
X if (error_cmd)
X (void) fputc('\n', error_cmd);
X }
X }
X
X#ifdef USE_SYSLOG
X if (error_syslog) {
X char newfmt[1000], buf[1000];
X va_start(ap);
X show_perror = va_arg(ap, int);
X die = va_arg(ap, int);
X fmt = va_arg(ap, char *);
X if (!openlog_done && error_prog) {
X openlog(error_prog, 0, LOG_USER);
X openlog_done = 1;
X }
X sprintf(newfmt, "(%s) ", user);
X strcat(newfmt, fmt);
X (void) vsprintf(buf, newfmt, ap);
X va_end(ap);
X syslog(error_priority, buf);
X }
X#endif
X
X if (die)
X (void) exit(die);
X
X if (error_cmd)
X pclose(error_cmd);
X
X return -1;
X
X}
X#endif
END_OF_FILE
if test 8380 -ne `wc -c <'super-3.4.5/error.c'`; then
echo shar: \"'super-3.4.5/error.c'\" unpacked with wrong size!
fi
# end of 'super-3.4.5/error.c'
fi
if test -f 'super-3.4.5/super.1' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'super-3.4.5/super.1'\"
else
echo shar: Extracting \"'super-3.4.5/super.1'\" \(6913 characters\)
sed "s/^X//" >'super-3.4.5/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.br
X.I command
X[
X.I args
X]
X.SH DESCRIPTION
X.I Super
Xallows specified users to execute scripts (or other commands) as if they
Xwere root; or it can set the uid and/or gid on a per-command basis
Xbefore executing the command.
XIt is intended to be a secure alternative to making scripts setuid root.
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
XSome commands can only be run after the user enters his or her password.
XThe command can then be run multiple times until some expiration time,
Xat which point the password needs to be re-entered.
XThe list of password-requiring commands and the password durations
Xare set in the same file that records the valid users for each command.
X.PP
XIf
X.I command
Xis a link to the
X.I super
Xprogram,
Xthen typing
X.ti +.5i
X% \fIcommand args\fP
X.br
Xis equivalent to typing
X.ti +.5i
X% super \fIcommand args\fP
X.br
XIf the
X.I command
Xbegins with a directory path, the directory parts are stripped off
Xand the \fIcommand\fP is used as above.
XThe
X.I command
Xmust not be
X.BR super ,
Xor
X.I super
Xwill not recognize that it's being invoked via a link.
X.PP
X.I Super
Xwithout any arguments will display the list of commands that may be executed
Xby the user.
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\-zA\-Z0\-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 associated with the
Xreal uid of the program running under
X.IR super ;
X.HP
XHOME: set to the login directory
Xof the user running
X.IR super ;
X.HP
XORIG_USER, ORIG_LOGNAME, ORIG_HOME: set to the values of
XUSER, LOGNAME, and HOME for the user who invoked
X.I super
X(these are normally the same values as USER, LOGNAME, and HOME;
Xthey only differ if the super command changes uid or gid
Xbefore executing the program);
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.HP
Xadditional environment variables as specified in the
X.I super.tab
Xfile (see below).
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 \-d
XThis enables debug mode, in which case (a) debugging information
Xis printed while checking a user for validity, and (b) the command
Xisn't actually executed. Useful to check if a new entry in the
Xsuper.tab file (see below) has been handled properly.
XIt
X.I must
Xbe the first argument on the command line.
X.HP
X.BR \-h \ |\ \-?
XIf no arguments are given, or if the first argument is ``\-h'' or ``\-?'',
X.I super
Xprints a brief summary of the commands available to the present user.
X.HP
X.BR \-H
XIf the first argument is ``\-H'',
X.I super
Xprints a verbose listing of the commands available to the user.
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.sp
XWithin the super.tab file, additional options can be specified
Xto
X.I (a)
Xexecute the command with a different uid and/or gid;
X.I (b)
Xpreserve additional environment variables, beyond the set described above; and
X.I (c)
Xkeep additional file descriptors open.
X.HP
X.I /usr/local/lib/superstamps/\fRusername\fP
X\(em used as a timestamp for the last time that the user entered
Xhis or her password.
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, make sure your script doesn't quietly
Xinvoke the user's
X.I .cshrc
Xor similar file.
XOr, 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 \fB#!\ \fIinterpreter-path\fR.
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
XSimilarly, if your super.tab file starts a shell such as csh or tcsh, you
Xmay want to include the \-b option in the super.tab file, so that you
Xdon't have to remember to type it on the command line every time;
Xuse a line like the following in the super.tab file:
X.ti +.5i
XSHELL "/usr/bin/csh \-fb" some_priv_user
X.br
XN.B. This is by way of example only; it's not a very good idea to
Xreally let somebody become root without any password check.
X.HP
X3. Better still, avoid csh scripts entirely -- they are harder to
Xwrite safely than Bourne-shell scripts.
X.HP
X4. 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`
Xtest "$SUPERCMD" = "$prog" ||
X exec /usr/local/bin/super $prog ${1+"$@"}
X.fi
X.in -.5i
X.HP
X5. 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
X6. By default, \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, you should put
Xan option like ``\fIuid=root\fP'' or ``\fIu+g=root\fP''
Xinto the super.tab file.
X.SH "SEE ALSO"
X.I super(5)
X.SH AUTHOR
XWill Deich
X.br
Xw...@astro.caltech.edu
X.SH BUGS
XThere is a race condition when using password-requiring commands,
Xbut it doesn't affect security: if a user is running two copies
Xof \fIsuper\fR simultaneously, and both processes try to update the
Xuser's password timestamp file at the same time, then it is possible
Xfor one of the \fIsuper\fR commands to fail.
XWorkaround: a single user shouldn't execute two password-requiring
X\fIsuper\fR programs simultaneously.
END_OF_FILE
if test 6913 -ne `wc -c <'super-3.4.5/super.1'`; then
echo shar: \"'super-3.4.5/super.1'\" unpacked with wrong size!
fi
# end of 'super-3.4.5/super.1'
fi
if test -f 'super-3.4.5/wildmat.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'super-3.4.5/wildmat.c'\"
else
echo shar: Extracting \"'super-3.4.5/wildmat.c'\" \(5005 characters\)
sed "s/^X//" >'super-3.4.5/wildmat.c' <<'END_OF_FILE'
X/* $Revision: 1.1 $
X**
X** Do shell-style pattern matching for ?, \, [], and * characters.
X** Might not be robust in face of malformed patterns; e.g., "foo[a-"
X** could cause a segmentation violation. It is 8bit clean.
X**
X** Modified 3 Mar 1993 by Will Deich, wi...@surya.caltech.edu:
X** A leading ^ means to complement the match; that is,
X** wildmat(string, "^"pat) returns TRUE if string does NOT
X** match the pattern.
X** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
X** Rich $alz is now <rs...@osf.org>.
X** April, 1991: Replaced mutually-recursive calls with in-line code
X** for the star character.
X**
X** Special thanks to Lars Mathiesen <tho...@diku.dk> for the ABORT code.
X** This can greatly speed up failing wildcard patterns. For example:
X** pattern: -*-*-*-*-*-*-12-*-*-*-m-*-*-*
X** text 1: -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1
X** text 2: -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1
X** Text 1 matches with 51 calls, while text 2 fails with 54 calls. Without
X** the ABORT code, it takes 22310 calls to fail. Ugh. The following
X** explanation is from Lars:
X** The precondition that must be fulfilled is that DoMatch will consume
X** at least one character in text. This is true if *p is neither '*' nor
X** '\0'.) The last return has ABORT instead of FALSE to avoid quadratic
X** behaviour in cases like pattern "*a*b*c*d" with text "abcxxxxx". With
X** FALSE, each star-loop has to run to the end of the text; with ABORT
X** only the last one does.
X**
X** Once the control of one instance of DoMatch enters the star-loop, that
X** instance will return either TRUE or ABORT, and any calling instance
X** will therefore return immediately after (without calling recursively
X** again). In effect, only one star-loop is ever active. It would be
X** possible to modify the code to maintain this context explicitly,
X** eliminating all recursive calls at the cost of some complication and
X** loss of clarity (and the ABORT stuff seems to be unclear enough by
X** itself). I think it would be unwise to try to get this into a
X** released version unless you have a good test data base to try it out
X** on.
X*/
X
X#define TRUE 1
X#define FALSE 0
X#define ABORT -1
X
X
X/* What character marks an inverted character class? */
X#define NEGATE_CLASS '^'
X
X/* Is "*" a common pattern? */
X#define OPTIMIZE_JUST_STAR
X
X/* Do tar(1) matching rules, which ignore a trailing slash? */
X#undef MATCH_TAR_PATTERN
X
X
X/*
X** Match text and p, return TRUE, FALSE, or ABORT.
X*/
Xstatic int
XDoMatch(text, p)
X register char *text;
X register char *p;
X{
X
X register int last;
X register int matched;
X register int reverse;
X
X for ( ; *p; text++, p++) {
X if (*text == '\0' && *p != '*')
X return ABORT;
X switch (*p) {
X case '\\':
X /* Literal match with following character. */
X p++;
X /* FALLTHROUGH */
X default:
X if (*text != *p)
X return FALSE;
X continue;
X case '?':
X /* Match anything. */
X continue;
X case '*':
X while (*++p == '*')
X /* Consecutive stars act just like one. */
X continue;
X if (*p == '\0')
X /* Trailing star matches everything. */
X return TRUE;
X while (*text)
X if ((matched = DoMatch(text++, p)) != FALSE)
X return matched;
X return ABORT;
X case '[':
X reverse = p[1] == NEGATE_CLASS ? TRUE : FALSE;
X if (reverse)
X /* Inverted character class. */
X p++;
X matched = FALSE;
X if (p[1] == ']' || p[1] == '-')
X if (*++p == *text)
X matched = TRUE;
X for (last = *p; *++p && *p != ']'; last = *p)
X /* This next line requires a good C compiler. */
X if (*p == '-' && p[1] != ']'
X ? *text <= *++p && *text >= last : *text == *p)
X matched = TRUE;
X if (matched == reverse)
X return FALSE;
X continue;
X }
X }
X
X#ifdef MATCH_TAR_PATTERN
X if (*text == '/')
X return TRUE;
X#endif /* MATCH_TAR_PATTERN */
X
X return *text == '\0';
X}
X
X
X/*
X** User-level routine. Returns TRUE or FALSE.
X*/
Xint
Xwildmat(text, p)
X char *text;
X char *p;
X{
X#ifdef OPTIMIZE_JUST_STAR
X if (p[0] == '*' && p[1] == '\0')
X return TRUE;
X#endif /* OPTIMIZE_JUST_STAR */
X
X return (p[0] == '^') ? DoMatch(text,p+1) != TRUE : DoMatch(text, p) == TRUE;
X}
X
X
X
X#if defined(TEST)
X#include <stdio.h>
X
X/* Yes, we use gets not fgets. Sue me. */
Xextern char *gets();
X
X
Xint
Xmain()
X{
X char p[80];
X char text[80];
X
X printf("Wildmat tester. Enter pattern, then strings to test.\n");
X printf("A blank line gets prompts for a new pattern; a blank pattern\n");
X printf("exits the program.\n");
X
X for ( ; ; ) {
X printf("\nEnter pattern: ");
X (void)fflush(stdout);
X if (gets(p) == NULL || p[0] == '\0')
X break;
X for ( ; ; ) {
X printf("Enter text: ");
X (void)fflush(stdout);
X if (gets(text) == NULL)
X exit(0);
X if (text[0] == '\0')
X /* Blank line; go back and get a new pattern. */
X break;
X printf(" %s\n", wildmat(text, p) ? "YES" : "NO");
X }
X }
X
X exit(0);
X /* NOTREACHED */
X}
X#endif /* defined(TEST) */
END_OF_FILE
if test 5005 -ne `wc -c <'super-3.4.5/wildmat.c'`; then
echo shar: \"'super-3.4.5/wildmat.c'\" unpacked with wrong size!
fi
# end of 'super-3.4.5/wildmat.c'
fi
echo shar: End of archive 3 \(of 3\).
cp /dev/null ark3isdone
Attached is patch01 to Super-3.4.5.
The problem fixed was:
If you:
- compile super without -DUSE_NETGROUP, and
- use a hostname in a super.tab entry
then:
super will improperly complain that you've got a wrong-format hostname.
(The complete source for the patched super is available for anonymous ftp
from: phobos.caltech.edu:users/will/super-3.4.6.shar )
-------------------
Super(1) is a setuid-root program that offers
o restricted setuid-root access to executables, adjustable
on a per-program and per-user basis;
o a relatively secure environment for scripts, so that well-written
scripts can be run as root (or some other uid/gid), without
unduly compromising security.
-------------------
-Will
Will Deich
Caltech 105-24, Pasadena, CA 91125
Internet: wi...@astro.caltech.edu
---------------------------------> cut here <---------------------------------
*** super.c Mon May 9 13:44:55 1994
--- super.c.new Mon May 9 13:44:39 1994
***************
*** 1178,1185 ****
return -1; /* hostname is not in this netgroup */
}
#else
! return taglines(Error(0, 0,
! "hostnames may not begin with `+' since this super()\n\
was compiled without -DUSE_NETGROUP.\n"));
#endif
--- 1178,1186 ----
return -1; /* hostname is not in this netgroup */
}
#else
! if (hostpat && *hostpat == '+') /* Disallow +name */
! return taglines(Error(0, 0,
! "hostnames may not begin with `+' since this super()\n\
was compiled without -DUSE_NETGROUP.\n"));
#endif
*** version.h Mon May 9 13:45:00 1994
--- version.h.new Mon May 9 13:44:44 1994
***************
*** 1,2 ****
#define Version "3.4"
! #define Patchlevel "5"
--- 1,2 ----
#define Version "3.4"
! #define Patchlevel "6"