Guide to writing secure setuid programs?

Steve Losen scl at virginia.acc.virginia.edu
Sat Mar 12 02:40:40 AEST 1988


I have no guide for writing setuid programs, but here are a few
ideas that come to mind:

1)	There's a big difference between setuid to root and setuid to
	an ordinary user.  This should be obvious because of the many
	operations that only root can do.  One should always endeaver to
	make a program setuid to some non-root user if at all possible.
	If a program is setuid to a non-root user it should revert back
	to the real user whenever possible so that the program can access
	all of the user's files.  We all know what a pain it is that lp(1)
	can't open any files that user "lp" can't access (which may
	include files that the real user can access.)

2)  Avoid setuid if you can.  I once wrote a very simple print spooler
	that puts files in a directory where they are picked up periodically
	by a daemon to be printed.  I made the directory 777 instead of using
	setuid-to-lp fraud.  Sure a malicious user can remove files in the
	print queue. So what?  To my knowlege, no one has ever done that in
	the two years since I put the system up.  Ask yourself, "Does this
	really need to be setuid?"  For example, I would *never* make a program
	setuid just so it could write to a protected logfile.  To hell with it.
	make the logfile 622.

3)	Have the setuid program grab all necessary resources at the beginning
	and revert back to the real user.  For example, once you open a file
	you can revert back to the real user before reading or writing.
	This little trick can be very handy for instructors who want their
	students to turn in assignments to the instructor's protected directory
	from the student's protected directory.  Sound like job for root?
	Nope! Write a setuid-to-instructor program that creat(2)'s a file in
	the instructors protected directory.  Now revert back to the real user
	(the student), open the assignment, and copy it to the fd that was
	creat(2)'ed earlier.  As a matter of fact, I heard a horror story
	about another such turn-un program.  The program was setuid to
	root and it prompted for source and destination file names.
	The source name was appended to the student's home directory
	while the destination name was appended to the instructor's "turn-in"
	directory and the copy was done.  Unfortunately, the programmer
	forgot about "../../.." and students quickly learned that they could
	copy any file anywhere with this program.
	(This was taken from comp.risks, a newsgroup I highly recommend.)

	Another thing you can do is set up a system so that all of its
	sensitive files and directories are within a single subtree.
	Make the directory at the top of the subtree 700 to keep ordinary
	users out.  But within the subtree loosen the permissions.  Make
	directories where users will create files 777.  Make logfiles
	622.  Each program that manipulates this system is setuid to the
	owner of the protected directory.  Upon invocation, the process
	chdirs below the protected directory and reverts back to the real
	user.  Now the process can access both the user's files and the
	sensitive system files.  This sort of design would have solved the
	problem with lp(1).

4)	Instead of making a whole program setuid, identify the portions that
	need special privilege and write small, very specific setuid programs
	to do those tasks.  Fork and/or exec the small programs from the main
	program.  We once had a program that needed to signal another process
	that was owned by a different user.  Instead of making the first
	process setuid to root, we wrote a small setuid to root program to
	send the signal.  To keep normal users from running the little program
	we put it in a special group with mode 4110 and made the first program
	setgid to the special group.

5)	If you MUST write a setuid to root program be VERY careful when
	checking any input or arguments from a user.  Realize that root
	can do amazing things that even an administrator would never do.
	Take unlink(2) in SysV, for example.  Root can unlink a directory,
	disconnecting from the file system any files that were
	in the directory.  Fsck is the only way to relink those files.
	So even if you do an access(2) and find out that "yes, the real user
	can remove file foo" be sure that "foo" is not a directory.
	Root can also unlink "." and ".." .  Another example is
	mount(2).  You have to be root to mount a floppy on a 3b2.  If you
	write a setuid program for your users to mount floppies, you better
	check carefully where the user wants to mount or she could put her
	own "passwd" file on the floppy and mount it on /etc.
	
	In a similar vein, when running in "root" mode, realize that
	environment variables are easy to fake.  Never use the PATH
	environment variable; always fully specify the pathnames of
	any process you exec.  If you exec a process as root, you might
	want to clear the environment first.  Use getpwent instead of $HOME.
	

Well this is certainly a lot more than I first intended to say.
-- 
Steve Losen     scl at virginia.edu
University of Virginia Academic Computing Center



More information about the Comp.unix.wizards mailing list