home *** CD-ROM | disk | FTP | other *** search
- From decwrl!ucbvax!pasteur!ames!xanth!nic.MR.NET!hal!ncoast!allbery Thu Dec 29 19:07:04 PST 1988
- Article 773 of comp.sources.misc:
- Path: granite!decwrl!ucbvax!pasteur!ames!xanth!nic.MR.NET!hal!ncoast!allbery
- From: maart@cs.vu.nl.UUCP (Maarten Litmaath)
- Newsgroups: comp.sources.misc
- Subject: v05i097: setuid.c -- another approach to setuid shell scripts
- Message-ID: <8812231209.aa08936@piraat.cs.vu.nl>
- Date: 29 Dec 88 01:54:43 GMT
- Sender: allbery@ncoast.UUCP
- Reply-To: maart@cs.vu.nl.UUCP (Maarten Litmaath)
- Lines: 292
- Approved: allbery@ncoast.UUCP
-
- Posting-number: Volume 5, Issue 97
- Submitted-by: "Maarten Litmaath" <maart@cs.vu.nl.UUCP>
- Archive-name: setuid
-
- [I don't care how "secure" you make them, I still don't trust them.... ++bsa]
- [As usual, any setuid gurus want to comment on the security or lack of same
- in this one? ++bsa]
-
- Instead of patching David's original idea, I tried a different approach.
- Using David's solution one must maintain a `database' of valid setuid shell
- scripts and ordinary users cannot create new entries unless there's an update
- program accessible to them too. It's /bin/secure who's setuid. Using my
- method it's again the shell script itself who's got the setuid bit.
- /bin/setuid (notice slightly different name :-) is a mere executable DOING
- THE RIGHT THINGS. Read the manual, examine the source, be convinced :-)
- Regards,
- Maarten Litmaath @ VU Amsterdam:
- maart@cs.vu.nl, mcvax!botter!maart
-
- : This is a shar archive. Extract with sh, not csh.
- : This archive ends with exit, so do not worry about trailing junk.
- : --------------------------- cut here --------------------------
- PATH=/bin:/usr/bin:/usr/ucb
- echo Extracting 'setuid.8'
- sed 's/^X//' > 'setuid.8' << '+ END-OF-FILE ''setuid.8'
- X.\" maart@cs.vu.nl - Maarten Litmaath Fri Dec 23 10:13:51 MET 1988
- X.\"
- X.TH SETUID 8 "Dec 23, 1988"
- X.UC 4
- X.SH NAME
- X.B setuid
- X\- run setuid shell scripts safely
- X.SH SYNOPSIS
- X.B #! /bin/setuid
- X.br
- X.SH DESCRIPTION
- X.B setuid
- Xis never normally executed from a shell. Instead it can be used
- Xas the interpreter for shell scripts that need to be run setuid to someone
- Xelse: this is done by making the first line of the script
- X.PP
- X.ti+5n
- X#! /bin/setuid
- X.PP
- Xrather than the usual
- X.PP
- X.ti+5n
- X#! /bin/sh
- X.PP
- X.B setuid
- Xtries to open the script for reading. If successful it discards the first
- Xline (containing the \fB#! /bin/setuid\fR) and tries to read a line
- Xformatted as follows:
- X.br
- X.br
- X.PP
- X.ti+5n
- X#? absolute\-path\-of\-interpreter arguments
- X.PP
- XExample:
- X.PP
- X.ti+5n
- X#? /bin/csh -bf /usr/etc/setuid_script
- X.PP
- XLinking to the script in order to gain root privileges is useless, because
- Xthe script contains its own invocation command sequence, including its
- Xabsolute path: the real name of the link cannot be found in the ultimate
- Xcommand.
- X.B setuid
- Xwill only
- Xexec a pathname beginning with a '/', to improve security.
- XOf course
- X.B setuid
- Xcan be `fooled' by supplying dubious arguments to the interpreter, like
- Xrelative pathnames. Furthermore it is a mistake to let the last directory
- Xcomponent of the ultimate path be writable by others. In our example /usr/etc
- Xshould not be writable for ordinary users.
- X.PP
- XIn addition, for the sake of security,
- X.B setuid
- Xsets the PATH environment variable back to a simple default, and deletes
- Xthe IFS environment variable.
- X.SH AUTHOR
- XMaarten Litmaath @ VU Informatika Amsterdam (maart@cs.vu.nl)
- X.SH "SEE ALSO"
- X.BR sh (1), csh (1)
- X.SH BUGS
- XThe maintenance of setuid scripts is a bit annoying: if a script is moved,
- Xone must not forget to change the path mentioned in the script. Possibly
- Xthe editing causes the setuid bit to get turned off.
- + END-OF-FILE setuid.8
- chmod 'u=rw,g=r,o=r' 'setuid.8'
- set `wc -c 'setuid.8'`
- count=$1
- case $count in
- 1867) :;;
- *) echo 'Bad character count in ''setuid.8' >&2
- echo 'Count should be 1867' >&2
- esac
- echo Extracting 'setuid.c'
- sed 's/^X//' > 'setuid.c' << '+ END-OF-FILE ''setuid.c'
- Xchar sccsid[] = "@(#) setuid.c 1.0 88/12/24 Maarten Litmaath";
- X
- X/*
- X * setuid.c
- X * execute setuid shell scripts safely
- X * see setuid.8
- X */
- X
- X#include <stdio.h>
- X
- X
- X#define COMMENT '#'
- X#define MAGIC '?'
- X#define MAXARGV 1024
- X#define MAXLEN 256
- X#define SEC_ARGC 1
- X#define SEC_OPEN 2
- X#define SEC_FMT 3
- X#define SEC_READ 4
- X#define SEC_EXEC 5
- X
- X
- Xtypedef int bool;
- X
- X
- Xchar E_argc[] = "%s: argument expected\n",
- X E_open[] = "%s: cannot open ",
- X E_fmt[] = "%s: format error in %s\n",
- X E_read[] = "%s: read error in ",
- X E_exec[] = "%s: cannot execute ",
- X Defaultpath[] = "/bin:/usr/bin",
- X *interpreter,
- X *newargv[MAXARGV];
- X
- X
- Xmain(argc, argv)
- Xint argc;
- Xchar **argv;
- X{
- X FILE *fp;
- X int c;
- X char *prog, *strrchr();
- X
- X
- X if (!(prog = strrchr(argv[0], '/')))
- X prog = argv[0];
- X else
- X ++prog;
- X
- X if (argc != 2) {
- X fprintf(stderr, E_argc, prog);
- X exit(SEC_ARGC);
- X }
- X
- X if (!(fp = fopen(argv[1], "r"))) {
- X fprintf(stderr, E_open, prog);
- X perror(argv[1]);
- X exit(SEC_OPEN);
- X }
- X
- X while ((c = getc(fp)) != '\n' && c != EOF) /* skip #! line */
- X ;
- X
- X
- X if (!getparams(fp)) {
- X if (ferror(fp)) {
- X fprintf(stderr, E_read, prog);
- X perror(argv[1]);
- X exit(SEC_READ);
- X }
- X fprintf(stderr, E_fmt, prog, argv[1]);
- X exit(SEC_FMT);
- X }
- X
- X#ifdef DEBUG
- X for (c = 0; newargv[c]; c++)
- X printf("newargv[%d]='%s'\n", c, newargv[c]);
- X#endif DEBUG
- X
- X (void) unsetenv("IFS");
- X (void) setenv("PATH", Defaultpath, 1);
- X
- X execv(interpreter, newargv);
- X
- X fprintf(stderr, E_exec, prog);
- X perror(interpreter);
- X exit(SEC_EXEC);
- X}
- X
- X
- Xstatic bool getparams(fp)
- XFILE *fp;
- X{
- X char buf[MAXLEN], *skipblanks(), *skiptoblank(), *strrchr();
- X register char *p;
- X register int i = 0;
- X
- X
- X for (;;) {
- X if (!fgets(buf, sizeof buf, fp) ||
- X buf[strlen(buf) - 1] != '\n')
- X return 0;
- X
- X p = skipblanks(buf);
- X
- X switch (*p++) {
- X case '\n':
- X continue;
- X case COMMENT:
- X break;
- X default:
- X return 0;
- X }
- X
- X if (*p++ == MAGIC)
- X break;
- X }
- X
- X p = skipblanks(p);
- X
- X if (*(interpreter = p) != '/')
- X return 0;
- X
- X p = skiptoblank(p);
- X
- X if (*p == '\n')
- X return 0;
- X
- X *p++ = '\0';
- X
- X newargv[0] = strrchr(interpreter, '/') + 1;
- X
- X while (i < MAXARGV - 1) {
- X p = skipblanks(p);
- X
- X if (*p == '\n')
- X break;
- X
- X newargv[++i] = p;
- X
- X p = skiptoblank(p);
- X
- X if (*p == '\n')
- X break;
- X
- X *p++ = '\0';
- X }
- X
- X newargv[++i] = 0;
- X
- X /*
- X * we expect 2 args at least: interpreter + file
- X * furthermore we expect p to point to end of line by now
- X */
- X
- X if (i <= 1 || *p != '\n')
- X return 0;
- X
- X *p = '\0';
- X return 1;
- X}
- X
- X
- Xstatic char *skipblanks(p)
- Xregister char *p;
- X{
- X while (*p == ' ' || *p == '\t')
- X ++p;
- X
- X return p;
- X}
- X
- X
- Xstatic char *skiptoblank(p)
- Xregister char *p;
- X{
- X while (*p != ' ' && *p != '\t' && *p != '\n')
- X ++p;
- X
- X return p;
- X}
- + END-OF-FILE setuid.c
- chmod 'u=rw,g=r,o=r' 'setuid.c'
- set `wc -c 'setuid.c'`
- count=$1
- case $count in
- 2671) :;;
- *) echo 'Bad character count in ''setuid.c' >&2
- echo 'Count should be 2671' >&2
- esac
- exit 0
-
-
-