v05i097: setuid.c -- another approach to setuid shell scripts
Maarten Litmaath
maart at cs.vu.nl.UUCP
Thu Dec 29 11:54:43 AEST 1988
Posting-number: Volume 5, Issue 97
Submitted-by: "Maarten Litmaath" <maart at 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 at 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 at 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 at 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
More information about the Comp.sources.misc
mailing list