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