[comp.sources.misc] v05i063: Solving the setuid script problem

dg@lakart.UUCP (David Goodenough) (12/03/88)

Posting-number: Volume 5, Issue 63
Submitted-by: "David Goodenough" <dg@lakart.UUCP>
Archive-name: secure

[Any setuid gurus want to say whether this is actually secure or not? ++bsa]

After all the discussion about the lack of security of setuid shell scripts,
I put the following together. It runs shell scripts, but is a little more
careful as to what it will run. There's no makefile - just compile this,
copy it to /bin/secure, chown it to root, and chmod it 4755. secure.8
describes the layout of /etc/secure - it's just a line for each safe shell
script, containing the full pathname first, and the numeric uid the run the
script.
-- 
	dg@lakart.UUCP - David Goodenough		+---+
							| +-+-+
	....... !harvard!xait!lakart!dg			+-+-+ |
AKA:	dg%lakart.uucp@harvard.harvard.edu	  	  +---+

--- cut here --- cut here --- cut here --- cut here --- cut here ---
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the '#! /bin/sh' line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (NOT csh) to create the files:
#	A: secure.c
#	B: secure.8
#
# Unwrapping with no options prevents overwriting files that already exist;
# giving a '-c' switch causes files to be overwritten regardless.
#
# wrapped by dg@lakart(David Goodenough) on Wed Nov 23 10:13:18 EST 1988
#
if test -f secure.c -a x$1 != x-c
then
    echo shar: file secure.c already exists
else
    echo shar: extracting secure.c
sed "s/^X//" <<\##__EOF..secure.c__## >secure.c
X#include	<stdio.h>
X
X#define		SECURE		"/etc/secure"
X
Xmain(n, a)
Xchar **a;
X {
X    int uid;
X    FILE *fp;
X    char program[100];
X
X    if (strcmp(a[0], "SEC-URE"))/* I'm damned if I know why this is */
X     {				/* necessary, but it is */
X	(void) strcpy(program, a[0]);
X	a[0] = "SEC-URE";
X	execv(program, a);	/* re exec ourselves so setuid bits work */
X	exit(1);		/* this should never happen */
X     }
X    if ((fp = fopen(a[1], "r")) == (FILE *) NULL)
X      exit(1);			/* file not found */
X    (void) fclose(fp);
X    if (a[1][0] != '/')
X      exit(1);			/* only pass an absolute pathname to /bin/sh */
X    if ((fp = fopen(SECURE, "r")) == (FILE *) NULL)
X      exit(1);			/* can't find the file of secure programs */
X    while (fscanf(fp, "%s %d", program, &uid) == 2)
X     {
X	if (strcmp(program, a[1]) == 0)
X	 {			/* aha ..... we found our program */
X	    (void) fclose(fp);
X	    (void) unsetenv("IFS");
X				/* tweak the environment for added safety */
X	    (void) setenv("PATH", "/bin:/usr/bin", 1);
X	    (void) setuid(uid);	/* set the uid */
X	    a[0] = "-sh";
X	    execv("/bin/sh", a);
X	    exit(1);		/* this should never happen */
X	 }
X     }
X    exit(1);			/* come here if we didn't find the program */
X }
##__EOF..secure.c__##
fi
if test -f secure.8 -a x$1 != x-c
then
    echo shar: file secure.8 already exists
else
    echo shar: extracting secure.8
sed "s/^X//" <<\##__EOF..secure.8__## >secure.8
X.\" dg@lakart - David Goodenough Wed Nov 23 09:47:12 EST 1988
X.\"
X.TH SECURE 8 "Nov 23, 1988"
X.UC 4
X.SH NAME
X.B secure
X\- run setuid shell scripts safely
X.SH SYNOPSIS
X.B #! /bin/secure
X.br
X.SH DESCRIPTION
X.B Secure
Xis never normally executed from a shell. Instead it can be used
Xas the interpreter for shell scripts that need to be run setuid someone
Xelse: this is done by making the first line of the script
X.PP
X.ti+5n
X#! /bin/secure
X.PP
Xrather than the usual
X.PP
X.ti+5n
X#! /bin/sh
X.PP
X.B Secure
Xdetermines if a script can be safely run by looking in
X/etc/secure. This is a list of secure shell scripts, and the numeric
Xuser id that the script is to be run with. A typical line from
X/etc/secure might be
X.PP
X.ti+5n
X/usr/script		0
X.PP
Xwhich would mean that /usr/script should be run as root.
X.PP
XBecause
X.B secure
Xwill only
Xexec a pathname beginning with '/', that comes from /etc/secure, it
Xcannot be used to illegally gain root access.
X.PP
XIn addition, for the sake of security,
X.B secure
Xsets the PATH environment variable back to a simple default, and deletes
Xthe IFS environment variable.
X.SH "SEE ALSO"
X.BR sh (1),
##__EOF..secure.8__##
fi
# end of shell archive
exit 0