home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / unix / volume26 / super < prev    next >
Encoding:
Text File  |  1992-07-02  |  25.0 KB  |  908 lines

  1. Newsgroups: comp.sources.unix
  2. From: will@surya.caltech.edu (Will Deich)
  3. Subject: v26i062: super - execute limited command set as root
  4. Sender: unix-sources-moderator@pa.dec.com
  5. Approved: vixie@pa.dec.com
  6.  
  7. Submitted-By: will@surya.caltech.edu (Will Deich)
  8. Posting-Number: Volume 26, Issue 62
  9. Archive-Name: super
  10.  
  11. Super(1) is a small program that allows users to execute other programs
  12. (particularly scripts) as root, without unduly compromising security.
  13.  
  14. Sample uses:
  15.     -  to call a script that allows users to use mount(8) on
  16.     cdrom's or floppy disks, but not other devices.
  17.  
  18.     -  to call a script that allows users to send STOP/CONT
  19.     signals to certain jobs, but not others.
  20.  
  21.     -  to restrict which users may execute a setuid-root program.
  22.  
  23.     /* Will Deich
  24.      * Caltech  105-24, Pasadena, CA 91125
  25.      * Internet: will@surya.caltech.edu
  26.      */
  27.  
  28. #! /bin/sh
  29. # This is a shell archive.  Remove anything before this line, then unpack
  30. # it by saving it into a file and typing "sh file".  To overwrite existing
  31. # files, type "sh file -c".  You can also feed this as standard input via
  32. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  33. # will see the following message at the end:
  34. #        "End of shell archive."
  35. # Contents:  README Makefile approve.c getlogdir.c ingroup.c sample.tab
  36. #   super.1 super.c word.c
  37. # Wrapped by vixie@cognition.pa.dec.com on Fri Jul  3 11:05:52 1992
  38. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  39. if test -f 'README' -a "${1}" != "-c" ; then 
  40.   echo shar: Will not clobber existing file \"'README'\"
  41. else
  42. echo shar: Extracting \"'README'\" \(3178 characters\)
  43. sed "s/^X//" >'README' <<'END_OF_FILE'
  44. Super(1) is a small program that allows users to execute other programs
  45. X(particularly scripts) as root, without unduly compromising security.
  46. X
  47. X
  48. Sample uses:
  49. X    -  to call a script that allows users to use mount(8) on
  50. X    cdrom's or floppy disks, but not other devices.
  51. X
  52. X    -  to call a script that allows users to send STOP/CONT
  53. X    signals to certain jobs, but not others.
  54. X
  55. X    -  to restrict which users may execute a setuid-root program.
  56. X
  57. A "super.tab" file names each command that super is willing to execute, and
  58. says who can use it. It contains lines like:
  59. X
  60. X    command   fullpathname    valid-user/group ed-type patterns
  61. X
  62. To execute a super command, type
  63. X
  64. X    % super command [args...]
  65. X
  66. If <command> is "-h" or "-?", or missing, super prints its current
  67. list of allowed commands, but nothing is executed.
  68. X
  69. If a user is allowed to execute a given <command>, the <fullpathname>
  70. is exec'd, with <command> as argv[0].  The superuser is always
  71. allowed to execute any super command.
  72. X
  73. XFor security, the environment variables are discarded, save for
  74. TERM, LINES, and COLUMNS.  To these are added reasonable values for
  75. IFS, PATH, USER and HOME (USER and HOME are set to the username and
  76. login directory, respectively, of the person who who runs super).
  77. LOGNAME is set to the same as USER.
  78. SUPERCMD is set to the <command>.
  79. All descriptors excepting 0,1,2 are closed.
  80. Signals are all reset to have default handling.
  81. X
  82. X--------------------
  83. X
  84. Making and installation:
  85. X
  86. The makefile is only 21 lines long.  Modify it to suit yourself.
  87. You have to be root to install super, as it must run setuid root.
  88. X
  89. By default, super expects to find its table of valid commands+users/groups
  90. in /usr/local/lib/super.tab.  If you change this (it's #define'd in
  91. super.c), you must also change the documentation.
  92. X
  93. A sample super.tab file is found in sample.tab.
  94. X
  95. X--------------------
  96. Notes on super scripts:
  97. X
  98. X1.  Scripts run via super(1) must start "#!/bin/sh" (or whatever interpreter
  99. X    is being used).
  100. X
  101. X2.  It's nice to be able to type something like
  102. X    % cdmount
  103. X    instead of
  104. X    % super cdmount
  105. X
  106. X    You can make your script automatically execute super on
  107. X    itself by starting a script in the following manner:
  108. X
  109. X    #!/bin/sh
  110. X    prog=`basename $0`
  111. X    if [ X$SUPERCMD != X$prog ] ; then
  112. X        exec /usr/local/bin/super $prog "$@"
  113. X    fi
  114. X
  115. X
  116. X3.  Some variants of csh will not run setuid scripts unless the -b flag
  117. X    (force a "break" from option processing) is set:
  118. X    #!/bin/csh -fb
  119. X
  120. X4.  A brief security comment:
  121. X    You must be exceedingly careful when writing scripts for super.
  122. X    A surprising variety of seemingly-ordinary commands can, when
  123. X    run setuid-root, be exploited to nasty purpose.  Always make your
  124. X    scripts do as little as possible, and give the user as few options
  125. X    as possible.
  126. X
  127. X    Think twice about side-effects and alternative uses of these
  128. X    scripts.  For instance, you might write a script to allow users to
  129. X    mount cd-rom's by executing mount(8).  But if you don't write it
  130. X    carefully, a user could mount a floppy disk containing, say, a
  131. X    setuid-root shell.
  132. X
  133. X--------------------
  134. X
  135. X/* Will Deich
  136. X * Caltech  105-24, Pasadena, CA 91125
  137. X * Internet: will@surya.caltech.edu
  138. X */
  139. END_OF_FILE
  140. if test 3178 -ne `wc -c <'README'`; then
  141.     echo shar: \"'README'\" unpacked with wrong size!
  142. fi
  143. # end of 'README'
  144. fi
  145. if test -f 'Makefile' -a "${1}" != "-c" ; then 
  146.   echo shar: Will not clobber existing file \"'Makefile'\"
  147. else
  148. echo shar: Extracting \"'Makefile'\" \(442 characters\)
  149. sed "s/^X//" >'Makefile' <<'END_OF_FILE'
  150. X
  151. BINDIR=/usr/local/bin
  152. MANDIR=/usr/man/manl
  153. MANEXT=l
  154. CFLAGS= -g
  155. X
  156. SRC= super.c approve.c ingroup.c word.c getlogdir.c
  157. OBJ= super.o approve.o ingroup.o word.o getlogdir.o
  158. X
  159. super: $(OBJ)
  160. X    $(CC) $(CFLAGS) -o super $(OBJ)
  161. X
  162. install:
  163. X    install -m 04755 -o root super $(BINDIR)
  164. X    install super.1 $(MANDIR)/super.$(MANEXT)
  165. X
  166. shar: README Makefile $(SRC) super.1 sample.tab
  167. X    shar README Makefile $(SRC) super.1 sample.tab > super.shar
  168. X
  169. lint:
  170. X    lint $(SRC)
  171. END_OF_FILE
  172. if test 442 -ne `wc -c <'Makefile'`; then
  173.     echo shar: \"'Makefile'\" unpacked with wrong size!
  174. fi
  175. # end of 'Makefile'
  176. fi
  177. if test -f 'approve.c' -a "${1}" != "-c" ; then 
  178.   echo shar: Will not clobber existing file \"'approve.c'\"
  179. else
  180. echo shar: Extracting \"'approve.c'\" \(5045 characters\)
  181. sed "s/^X//" >'approve.c' <<'END_OF_FILE'
  182. X#include <stdio.h>
  183. X#include <string.h>
  184. X#include <ctype.h>
  185. X#include <pwd.h>
  186. X
  187. X#include "version.h"
  188. X
  189. static char SccsId[] = "%W%\t%G%";
  190. X
  191. static FILE *fp;
  192. X
  193. extern errno;
  194. X
  195. X#define MIN(a,b) ((a) < (b) ? (a) : (b))
  196. X
  197. extern char *user;
  198. extern char *prog;
  199. X
  200. X/* Returns:
  201. X *    - NULL ptr if error:
  202. X *        a) username not found;
  203. X *        b) superfile can't be opened for reading;
  204. X *        c) no such command as usrcmd in superfile;
  205. X *        d) user not allowed to execute this command.
  206. X *    - ptr to empty string if all ok, but no program should be executed.
  207. X *    - ptr to path of file to exec, if user allowed to do so.
  208. X *    Any error also generates a message to stderr.
  209. X
  210. X * New calls to approve() overwrite the buffer containing the returned path.
  211. X */
  212. X
  213. X#define GROUPSEP ':'    /* ok users are named as "user<GROUPSEP>group" */
  214. X
  215. char *
  216. approve(superfile, usrcmd)
  217. char *superfile;    /* file of approved commands+users */
  218. char *usrcmd;        /* command we're to check on.
  219. X             * If command is one of:
  220. X             *    "" | "-h" | "-?"
  221. X             *    then a list of super commands is printed
  222. X             *    instead of attempting to execute anything.
  223. X             */
  224. X{
  225. X    int uid;
  226. X    struct passwd *usrpw;
  227. X    /* has to be static: holds the returned path used by the caller. */
  228. X    static char buf[1024];
  229. X    char chkbuf[1024];
  230. X    char *allusers, *line, *command, *path, *okuser, *okgroup;
  231. X    char *msg, *word(), *re_comp();
  232. X    int re_exec(), ingroup();
  233. X    char *sep;
  234. X    int i, n, givehelp, commandfound;
  235. X    char *nil = (char *) NULL;
  236. X    static char *empty = "";
  237. X    void anchor();
  238. X
  239. X    uid = getuid();
  240. X    usrpw = getpwuid(uid);
  241. X    if (!usrpw) {
  242. X    (void) fprintf(stderr,
  243. X        "%s - approve(): Couldn't get your password entry: ", prog);
  244. X    perror("");
  245. X    return NULL;
  246. X    }
  247. X    user = usrpw->pw_name;
  248. X
  249. X    if ((fp = fopen(superfile, "r")) == NULL) {
  250. X    (void) fprintf(stderr, "%s: Couldn't open valid-users text file `%s': ",
  251. X                superfile);
  252. X    perror("");
  253. X    return NULL;
  254. X    }
  255. X
  256. X    sep = " \t\n";        /* How to split fields on input lines */
  257. X
  258. X    /* Do we just give help or really match a command with this user? */
  259. X    givehelp = (usrcmd == nil) ||
  260. X            strcmp(usrcmd, "-h")==0 || strcmp(usrcmd, "-?")==0;
  261. X
  262. X    if (givehelp) {
  263. X    (void) fprintf(stderr, "%s version %s patchlevel %s\n",
  264. X                prog, Version, Patchlevel);
  265. X    (void) fprintf(stderr, "Use:\n\t%s command [args]\n\n", prog);
  266. X    (void) fprintf(stderr, "%-16s  %-24s  %s\n",
  267. X        "Command", "Full Path           ", "Valid User:Group Patterns");
  268. X    (void) fprintf(stderr, "%-16s  %-24s  %s\n",
  269. X        "-------", "--------------------", "-------------------------");
  270. X    }
  271. X
  272. X    commandfound = 0;
  273. X    for (n=1; fgets(buf, sizeof(buf), fp); n++) {
  274. X    if (!*buf || *buf == '#')    /* Skip empty lines  and comments */
  275. X        continue;
  276. X    line = strtok(buf, "#");        /* Strip comments */
  277. X
  278. X    allusers = word(buf, " \t\v", 3);    /* Find the okusernames before
  279. X                         * carving up the line.
  280. X                         */
  281. X
  282. X    command = strtok(line, sep);        /* get the command... */
  283. X    path = strtok(nil, sep);        /* ...and its full path */
  284. X    if (!command || !path)
  285. X        continue;                /* Skip improper lines */
  286. X
  287. X    if (givehelp) {
  288. X        (void) fprintf(stderr, "%-16s  %-24s  %s", command, path, allusers);
  289. X        continue;
  290. X    }
  291. X    if (strcmp(command, usrcmd) != 0)
  292. X        continue;                /* Skip non-matching commands */
  293. X    commandfound++;
  294. X
  295. X    /* Check our user against all valid user patterns */
  296. X    if (uid == 0)
  297. X        return path;            /* root is always legit */
  298. X    for (okuser = strtok(nil, sep); okuser; okuser = strtok(nil, sep)) {
  299. X        /* Split into user,group */
  300. X        okgroup = strchr(okuser, GROUPSEP);
  301. X        if (okgroup && *(okgroup+1)) {
  302. X        /* pat was "uuu:ggg or ":ggg" */
  303. X        if (okuser == okgroup)
  304. X            okuser = "^.*$";    /* pat was ":ggg" */
  305. X        *okgroup++ = '\0';
  306. X        } else {
  307. X        /* pat was "uuu" or "uuu:" */
  308. X        if (okgroup)
  309. X            *okgroup = '\0';    /* pat was "uuu:" */
  310. X        okgroup = "^.*$";
  311. X        }
  312. X        /* Force all matches to be anchored: prepend/append
  313. X         * ^, $ if they haven't been put in by user
  314. X         */
  315. X        anchor(okuser, chkbuf);
  316. X        if ((msg = re_comp(chkbuf)) != NULL) {
  317. X        (void) fprintf(stderr,
  318. X            "%s: bad pattern `%s' on line %d in %s: %s\n",
  319. X                prog, okuser, n, superfile, msg);
  320. X        continue;
  321. X        }
  322. X        if (re_exec(user) != 1)
  323. X        continue;            /* Skip non-matched user */
  324. X
  325. X        anchor(okgroup, chkbuf);
  326. X        i = ingroup(user, chkbuf);
  327. X        if (i == -1) {
  328. X        (void) fprintf(stderr,
  329. X                "%s: bad pattern `%s%c%s' on line %d in %s\n",
  330. X                prog, okuser, GROUPSEP, okgroup, n, superfile);
  331. X        continue;
  332. X        }
  333. X        if (i == 1)
  334. X        return path;            /* Matched cmd+user+group */
  335. X    }
  336. X    }
  337. X    if (givehelp)
  338. X    return empty;
  339. X    else if (!commandfound)
  340. X    (void) fprintf(stderr, "%s: No such super command as `%s'.\n",
  341. X        prog, usrcmd);
  342. X    else
  343. X    (void) fprintf(stderr, "%s %s: Permission denied to user %s\n",
  344. X        prog, usrcmd, user);
  345. X    return NULL;
  346. X}
  347. X
  348. void
  349. anchor(in, out)
  350. char *in;
  351. char *out;
  352. X{
  353. X    /* Copies in to out, prefixing with "^" and suffixing with "$"
  354. X     * if these are missing.
  355. X     */
  356. X    int i;
  357. X    i = (*in != '^');
  358. X    if (i)
  359. X    out[0] = '^';
  360. X    (void) strcpy(out+i, in);
  361. X    i = strlen(out);
  362. X    if (out[i-1] != '$')
  363. X    out[i++] = '$';
  364. X    out[i] = '\0';
  365. X}
  366. END_OF_FILE
  367. if test 5045 -ne `wc -c <'approve.c'`; then
  368.     echo shar: \"'approve.c'\" unpacked with wrong size!
  369. fi
  370. # end of 'approve.c'
  371. fi
  372. if test -f 'getlogdir.c' -a "${1}" != "-c" ; then 
  373.   echo shar: Will not clobber existing file \"'getlogdir.c'\"
  374. else
  375. echo shar: Extracting \"'getlogdir.c'\" \(935 characters\)
  376. sed "s/^X//" >'getlogdir.c' <<'END_OF_FILE'
  377. X#include <string.h>
  378. X#include <pwd.h>
  379. X
  380. static char SccsId[] = "@(#)getlogdir.c    1.1\t12/13/91";
  381. X
  382. X#ifndef NULL
  383. X#define NULL (char *) 0
  384. X#endif
  385. X
  386. int getlogdir(user, buf)
  387. char *user;
  388. char *buf;
  389. X{
  390. X    /* Gets the login directory of the named user, and puts it into buf.
  391. X     * If user==NULL || *user == '\0', the current user is obtained.
  392. X     * Best if buf is MAXPATHLEN long.
  393. X     * 0 is returned on success; -1 on error.
  394. X     */
  395. X
  396. X    struct passwd *pass;
  397. X    char *p;
  398. X    char *getlogin();
  399. X
  400. X    buf[0] = '\0';
  401. X    if (user != NULL && *user != '\0') {
  402. X    /* Name given; use getpwnam */
  403. X    pass = getpwnam(user);
  404. X    } else if ((p = getlogin()) != NULL) {
  405. X    /* No name given; use current login name */
  406. X    pass = getpwnam(p);
  407. X    } else {
  408. X    /* No user given && getlogin() returned NULL; use current uid */
  409. X    pass = getpwuid(getuid());
  410. X    }
  411. X
  412. X
  413. X    if (pass == (struct passwd *) NULL)
  414. X    return -1;
  415. X
  416. X    (void) strcpy(buf, pass->pw_dir);
  417. X
  418. X    return 0;
  419. X}
  420. END_OF_FILE
  421. if test 935 -ne `wc -c <'getlogdir.c'`; then
  422.     echo shar: \"'getlogdir.c'\" unpacked with wrong size!
  423. fi
  424. # end of 'getlogdir.c'
  425. fi
  426. if test -f 'ingroup.c' -a "${1}" != "-c" ; then 
  427.   echo shar: Will not clobber existing file \"'ingroup.c'\"
  428. else
  429. echo shar: Extracting \"'ingroup.c'\" \(1802 characters\)
  430. sed "s/^X//" >'ingroup.c' <<'END_OF_FILE'
  431. X#include <grp.h>
  432. X
  433. static char SccsId[] = "@(#)ingroup.c    1.3\t12/21/91";
  434. X
  435. X/* Use:
  436. X *    ingroup(user, gp_pat)
  437. X * Returns:
  438. X *    1 if the user is in a group matching the regex pattern gp_pat.
  439. X *    0 if the user isn't in a group matching the pattern.
  440. X *    -1 if pattern failed to compile.
  441. X
  442. X * SIDE-EFFECT: uses re_comp! -- messes up caller's use of same!
  443. X
  444. X * Uses re_{comp,exec} routines to compare.
  445. X
  446. X * Examples:
  447. X *    ingroup("joe", "xyz")
  448. X * returns !0 if user joe is in group "xyz".
  449. X *    ingroup("joe", "xy.*")
  450. X * returns !0 if user joe is in any group matching "xy.*".
  451. X
  452. X
  453. X * If compiled with -DPROGRAM, then a small program is compiled; use is:
  454. X *    % ingroup username pat...
  455. X * Prints name of first matching pattern; prints nothing on no matches.
  456. X */
  457. X
  458. ingroup(user, gp_pat)
  459. char *user;
  460. char *gp_pat;    /* pattern to match */
  461. X{
  462. X    char *re_comp();
  463. X    int re_exec();
  464. X    struct group *gp;
  465. X    char **mem;
  466. X    void setgrent();
  467. X
  468. X    if (re_comp(gp_pat) != (char *)0 )
  469. X    return -1;
  470. X
  471. X    /* Search group file for groups user's in.
  472. X     * Test for group matches wherever user belongs.
  473. X     */
  474. X    setgrent();
  475. X    for (gp = getgrent(); gp; gp = getgrent()) {
  476. X    for (mem = gp->gr_mem; *mem; mem++)
  477. X        if (strcmp(*mem, user) == 0)
  478. X        break;
  479. X    if (!*mem)
  480. X        continue;            /* not in group */
  481. X
  482. X    if (re_exec(gp->gr_name) == 1)    /* in group; compare gp name with pat */
  483. X        return 1;
  484. X    }
  485. X    return 0;
  486. X}
  487. X
  488. X#ifdef PROGRAM
  489. X#include <stdio.h>
  490. main(argc, argv)
  491. int argc;
  492. char **argv;
  493. X{
  494. X    char **pat;
  495. X    int i;
  496. X
  497. X    if (argc < 3) {
  498. X    (void) fprintf(stderr, "Use: %s user pat...\n", argv[0]);
  499. X    (void) exit(1);
  500. X    }
  501. X    for (pat = &argv[2]; *pat; pat++) {
  502. X    if ((i=ingroup(argv[1], *pat)) == -1)
  503. X        (void) fprintf(stderr, "Invalid re_comp pattern ``%s''\n", *pat);
  504. X    else if (i) {
  505. X        puts(*pat);
  506. X        (void) exit(0);
  507. X    }
  508. X    }
  509. X}
  510. X#endif
  511. END_OF_FILE
  512. if test 1802 -ne `wc -c <'ingroup.c'`; then
  513.     echo shar: \"'ingroup.c'\" unpacked with wrong size!
  514. fi
  515. # end of 'ingroup.c'
  516. fi
  517. if test -f 'sample.tab' -a "${1}" != "-c" ; then 
  518.   echo shar: Will not clobber existing file \"'sample.tab'\"
  519. else
  520. echo shar: Extracting \"'sample.tab'\" \(917 characters\)
  521. sed "s/^X//" >'sample.tab' <<'END_OF_FILE'
  522. X# This file lists commands that super(1) will execute for you as root.
  523. X
  524. X# The format for data lines in this file is
  525. X
  526. X#    commandname   fullpathname      ed patterns for valid users/groups.
  527. X
  528. X# The format for a users/groups entry is
  529. X#    user[:]   or  :group   or  user:group
  530. X#    
  531. X# All patterns are "anchored"; i.e. they are forced to match the entire
  532. X# username or groupname.
  533. X
  534. X
  535. X# Example entry:
  536. X
  537. X#    doit    /usr/local/bin/doit    me you  ja.*:ok_j  :goodguys
  538. X
  539. X# ...allows users "me", "you", any users named "ja.*" in group "ok_j",
  540. X# and anybody in group "goodguys" to run /usr/local/bin/doit setuid root,
  541. X# by typing
  542. X#    % super doit [ doit args ]
  543. X
  544. X# ---------------------------------------------------------------------------
  545. X# Cmd    Full Path        Valid-User/Group Patterns
  546. X
  547. timeout    /usr/local/bin/timeout    :operator :wheel gv phillips srk
  548. restart    /usr/local/bin/restart    :operator :wheel gv phillips srk
  549. cdmount    /usr/local/bin/cdmount    .*
  550. END_OF_FILE
  551. if test 917 -ne `wc -c <'sample.tab'`; then
  552.     echo shar: \"'sample.tab'\" unpacked with wrong size!
  553. fi
  554. # end of 'sample.tab'
  555. fi
  556. if test -f 'super.1' -a "${1}" != "-c" ; then 
  557.   echo shar: Will not clobber existing file \"'super.1'\"
  558. else
  559. echo shar: Extracting \"'super.1'\" \(2869 characters\)
  560. sed "s/^X//" >'super.1' <<'END_OF_FILE'
  561. X.TH SUPER 1 local
  562. X.SH NAME
  563. super \- execute commands setuid root.
  564. X.SH SYNOPSIS
  565. X.B super
  566. X.I command
  567. X[
  568. X.I args
  569. X]
  570. X.SH DESCRIPTION
  571. X.I Super
  572. allows users to execute scripts (or other commands) as if they were root.
  573. It
  574. is intended to be a secure alternative to making scripts setuid root.
  575. X.PP
  576. X.I Super
  577. consults a file to see if the user is allowed to execute the requested
  578. X.IR command .
  579. If so,
  580. X.I super
  581. will exec
  582. X.IR command\  [\  args\  ].
  583. Root is always permitted to execute any command in the
  584. super file.
  585. X.PP
  586. X.I Super
  587. without any arguments will display its list of commands and their allowed users.
  588. X.PP
  589. XFor security, the following precautions are taken before exec'ing:
  590. X.HP
  591. X\fI(a)\fP    all descriptors save 0,1,2 are closed;
  592. X.HP
  593. X\fI(b)\fP    all of the user's environment variables are
  594. discarded, save for TERM, LINES, and COLUMNS.  To these
  595. are added reasonable values for:
  596. X.RS
  597. X.HP
  598. USER and LOGNAME: both are set to the username
  599. of the real user running
  600. X.IR super ;
  601. X.HP
  602. HOME: set to the login directory
  603. of the real user running
  604. X.IR super ;
  605. X.HP
  606. IFS: set to blank, tab, newline;
  607. X.HP
  608. PATH: set to \fI/bin:/usr/bin:/usr/ucb\fP.
  609. X.HP
  610. SUPERCMD: set to \fIcommand\fP.
  611. X.RE
  612. X.in -.5i
  613. X.HP
  614. X\fI(c)\fP    all signal handling is reset to the default.
  615. X.SH OPTIONS
  616. X.HP
  617. X.BR \-h \ |\  \-?
  618. If no arguments are given, or if the first argument is ``\-h'' or ``\-?'',
  619. X.I super
  620. prints a usage line and lists all the available commands.
  621. X.SH FILES
  622. X.HP
  623. X.I /usr/local/lib/super.tab
  624. X\(em contains the list of commands that
  625. X.I super
  626. may execute, along with the names of the users who may execute each
  627. command.
  628. X.SH CREATING SUPER SCRIPTS
  629. You must be exceedingly careful when writing scripts for
  630. X.IR super .
  631. A surprising variety of ordinary commands can, when
  632. run setuid-root, be exploited for nasty purposes.  Always make your
  633. scripts do as little as possible, and give the user as few options
  634. as possible.
  635. X.PP
  636. Think twice about side-effects and alternative uses
  637. of these scripts.  For instance, you might write a script to allow
  638. users to mount cd-rom's by executing
  639. X.IR mount(8) .
  640. But if you don't write it carefully, a user could mount a floppy
  641. disk containing, say, a setuid-root shell.
  642. X.PP
  643. Security issues aside, here are some hints on creating super scripts:
  644. X.HP
  645. X1.    Scripts must begin with 
  646. X.BI #! interpreter-path . 
  647. X.HP
  648. X2.    Some variants of csh will not run setuid scripts unless the \-b flag
  649. X(force a "break" from option processing) is set:
  650. X.ti +.5i
  651. X#!/bin/csh -fb
  652. X.br
  653. X.HP
  654. X3.    It's nice to make the
  655. X.I super
  656. call transparent to users, so that they can type
  657. X.ti +.5i
  658. X% cdmount \fIargs\fP
  659. X.br
  660. instead of
  661. X.ti +.5i
  662. X% super cdmount \fIargs\fP
  663. X.br
  664. You can make a script
  665. X.I super
  666. itself by beginning the script in the following way:
  667. X.in +.5i
  668. X.nf
  669. X#!/bin/sh
  670. prog=`basename $0`
  671. if [ X$SUPERCMD != X$prog ] ; then
  672. X    exec /usr/local/bin/super $prog "$@"
  673. fi
  674. X.fi
  675. X.in -1i
  676. X.SH AUTHOR
  677. Will Deich
  678. X.br
  679. will@surya.caltech.edu
  680. END_OF_FILE
  681. if test 2869 -ne `wc -c <'super.1'`; then
  682.     echo shar: \"'super.1'\" unpacked with wrong size!
  683. fi
  684. # end of 'super.1'
  685. fi
  686. if test -f 'super.c' -a "${1}" != "-c" ; then 
  687.   echo shar: Will not clobber existing file \"'super.c'\"
  688. else
  689. echo shar: Extracting \"'super.c'\" \(3952 characters\)
  690. sed "s/^X//" >'super.c' <<'END_OF_FILE'
  691. X#include <stdio.h>
  692. X#include <string.h>
  693. X#include <signal.h>
  694. X#include <sys/param.h>
  695. X
  696. X#include "version.h"
  697. X
  698. static char SccsId[] = "%W%\t%G%";
  699. X
  700. X#ifndef SUPERTAB
  701. X#define SUPERTAB "/usr/local/lib/super.tab"
  702. X#endif
  703. X
  704. X/*
  705. X * Super allows users to execute other programs (particularly
  706. X * scripts) as root, without unduly compromising security.
  707. X * 
  708. X * Use:
  709. X * 
  710. X *     $0 commandname args...
  711. X * 
  712. X * If the commandname is "-h" or "-?", or missing, super prints its current
  713. X * list of allowed commands, but nothing executed.
  714. X * 
  715. X * The super.tab file names each command that super will execute, and
  716. X * says who can use it. It contains lines like:
  717. X * 
  718. X *     commandname   fullpathname    valid-user/group ed-type patterns
  719. X * 
  720. X * See sample.tab for how to specify user & group patterns.
  721. X * If a user is allowed to execute a given <commandname>, the <fullpathname>
  722. X * is exec'd, with <commandname> as argv[0].
  723. X * 
  724. X * For security, the environment variables are discarded, save for
  725. X * TERM, LINES, and COLUMNS.  To these are added reasonable values for
  726. X * IFS, PATH, USER and HOME (USER and HOME are set to the username and
  727. X * login directory, respectively, of the person who who runs super).
  728. X * LOGNAME is set to the same as USER.
  729. X * Finally, SUPERCMD is set to the name of the super command
  730. X * being executed.  All descriptors excepting 0,1,2 are closed.
  731. X * Signals are all reset to have default handling.
  732. X */
  733. X
  734. static char *SAFE_IFS = "IFS= \t\n";
  735. static char *SAFE_PATH = "PATH=/bin:/usr/bin:/usr/ucb";
  736. X
  737. char *prog;    /* This program */
  738. char *user;    /* who's invoking prog */
  739. X
  740. main(argc, argv)
  741. int argc;
  742. char **argv;
  743. X{
  744. X    char *s, *path, *approve();
  745. X    char **buttonup(), **envp;
  746. X    void exit();
  747. X
  748. X    s = strchr(argv[0], '/');
  749. X    prog = (s && *(s+1)) ? s+1 : argv[0];
  750. X
  751. X    if ((path = approve(SUPERTAB, argv[1])) == NULL)
  752. X    (void) exit(1);
  753. X    else if (*path == '\0')
  754. X    (void) exit(0);
  755. X
  756. X    /* Button up for security, and get a modified environment */
  757. X    envp = buttonup(argv[1]);
  758. X
  759. X    if (execve(path, &argv[1], envp) == -1) {
  760. X    (void) fprintf(stderr, "%s: Couldn't exec %s (%s): ",
  761. X                    prog, argv[1], path);
  762. X    perror("");
  763. X    (void) exit(1);
  764. X    }
  765. X    return 0;
  766. X}
  767. X
  768. char **
  769. buttonup(cmd)
  770. char *cmd;    /* name of command being started */
  771. X{
  772. X    /* Closes all descriptors save 0,1,2.
  773. X     * Resets all signal-handling to SIG_DFL.
  774. X     * Discards all env. variables save for TERM, LINES, and COLUMNS.
  775. X     * To these are added reasonable values for IFS, PATH, USER, and HOME.
  776. X     * LOGNAME is set to the same as USER, and SUPERCMD is set to cmd.
  777. X     * Returned:
  778. X     *    a pointer to the modified environment list.
  779. X     */
  780. X    int i, fd, n;
  781. X    char *Getenv();
  782. X    int getlogdir();
  783. X    static char *env[10];
  784. X    static char User[100];        /* USER */
  785. X    static char Logname[100];        /* LOGNAME (alias for USER) */
  786. X    static char Home[MAXPATHLEN+5];    /* HOME */
  787. X    static char Cmd[1200];        /* SUPERCMD */
  788. X    void (*signal())();
  789. X
  790. X    n = getdtablesize(); 
  791. X    for (fd=3; fd < n; fd++)
  792. X    (void) close(fd);
  793. X    
  794. X    for (i=0; i<NSIG; i++)
  795. X       (void) signal(i, SIG_DFL);
  796. X
  797. X    (void) sprintf(User, "USER=%s", user);
  798. X    (void) sprintf(Logname, "LOGNAME=%s", user);
  799. X    (void) sprintf(Cmd, "SUPERCMD=%s", cmd);
  800. X    (void) strcpy(Home, "HOME=");
  801. X    (void) getlogdir(user, Home+5);
  802. X    i = 0;
  803. X    env[i] = Getenv("TERM");    if (env[i]) i++;
  804. X    env[i] = Getenv("LINES");    if (env[i]) i++;
  805. X    env[i] = Getenv("COLUMNS");    if (env[i]) i++;
  806. X    env[i++] = SAFE_IFS;
  807. X    env[i++] = SAFE_PATH;
  808. X    env[i++] = User;
  809. X    env[i++] = Logname;
  810. X    env[i++] = Cmd;
  811. X    env[i] = (char *) NULL;
  812. X
  813. X    return &env[0];
  814. X}
  815. X
  816. char *
  817. Getenv(s)
  818. char *s;
  819. X{
  820. X    /* Like getenv(), but returns ptr to the <name> in "name=xxxx",
  821. X     * not just the xxxx.
  822. X     */
  823. X    char **envp; 
  824. X    int l;
  825. X    extern char **environ;
  826. X
  827. X    if (!s)
  828. X    return (char *) NULL;
  829. X    l = strlen(s);
  830. X    for (envp=environ; *envp ; envp++)
  831. X    if (strncmp(*envp, s, l) == 0  &&  *(*envp+l) == '=')
  832. X        return *envp;
  833. X    return (char *) NULL;
  834. X}
  835. END_OF_FILE
  836. if test 3952 -ne `wc -c <'super.c'`; then
  837.     echo shar: \"'super.c'\" unpacked with wrong size!
  838. fi
  839. # end of 'super.c'
  840. fi
  841. if test -f 'word.c' -a "${1}" != "-c" ; then 
  842.   echo shar: Will not clobber existing file \"'word.c'\"
  843. else
  844. echo shar: Extracting \"'word.c'\" \(1225 characters\)
  845. sed "s/^X//" >'word.c' <<'END_OF_FILE'
  846. X#include <ctype.h>
  847. X
  848. static char SccsId[] = "@(#)word.c    1.1\t12/4/91";
  849. X
  850. X/* Returns a pointer to the start of the k'th word in s.
  851. X * Numbering is that k=1 for the first word.
  852. X * A pointer to a null string is returned if there is no k'th word.
  853. X */
  854. X
  855. X/*
  856. X * Compile with -DPROGRAM to get a standalone program that's used as
  857. X *     $0  k  string
  858. X * and prints the k'th whitespace-separated word of the string on stdout.
  859. X */  
  860. X
  861. char *
  862. word(s, sep, k)
  863. register char *s;
  864. register char *sep;    /* word separators */
  865. int k;
  866. X{
  867. X    int i;
  868. X    
  869. X    /* Skip leading separators */
  870. X    s += strspn(s, sep);
  871. X
  872. X    /* Skip words before the k'th word */
  873. X    for (i=1; i<k; i++) {
  874. X    if (*s) s += strcspn(s, sep);    /* Skip a word... */
  875. X    if (*s) s += strspn(s, sep);    /* ...and the trailing separators */
  876. X    }
  877. X    return s;
  878. X}    
  879. X
  880. X#ifdef PROGRAM
  881. X#include <stdio.h>
  882. X#include <string.h>
  883. main(argc, argv)
  884. int argc;
  885. char **argv;
  886. X{
  887. X    char *prog, *word();
  888. X    int atoi();
  889. X
  890. X    prog = strrchr(argv[0], '/');
  891. X    prog = (prog && *(prog+1)) ? prog+1 : (prog) ? prog : argv[0];
  892. X    if (argc != 3) {
  893. X    (void) fprintf(stderr, "Usage: %s  k  \"string\"\n", prog);
  894. X    (void) exit(1);
  895. X    }
  896. X    printf("%s\n", word(argv[2], " \t\v", atoi(argv[1])));
  897. X    return 0;
  898. X}
  899. X#endif
  900. END_OF_FILE
  901. if test 1225 -ne `wc -c <'word.c'`; then
  902.     echo shar: \"'word.c'\" unpacked with wrong size!
  903. fi
  904. # end of 'word.c'
  905. fi
  906. echo shar: End of shell archive.
  907. exit 0
  908.