[comp.bugs.sys5] Guide to writing secure setuid programs?

riddle@woton.UUCP (Prentiss Riddle ) (03/09/88)

There's been a recent flurry of discussion in comp.bugs.sys5 about a
few specific security pitfalls to avoid in writing setuid programs.  I
get the feeling that this is just the tip of the iceberg. 

Can anyone point us to a more comprehensive guide to how to write good
setuid programs?  If you've got something on-line, please consider
posting it; if you know of good book or journal references, please mail
them to me and I will summarize. 

And if nothing of this sort exists, perhaps it's time to write one. 
Thanks. 

-- Prentiss Riddle ("Aprendiz de todo, maestro de nada.")
-- Opinions expressed are not necessarily those of my employer.
-- riddle%woton.uucp@im4u.utexas.edu  {ihnp4,uunet}!ut-sally!im4u!woton!riddle

scl@virginia.acc.virginia.edu (Steve Losen) (03/12/88)

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@virginia.edu
University of Virginia Academic Computing Center

blarson@skat.usc.edu (Bob Larson) (03/14/88)

In article <700@virginia.acc.virginia.edu> scl@virginia.acc.Virginia.EDU (Steve Losen) writes:
>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?

So what?  It depends a lot on what you are printing.  When someone
modifies the batch of checks waiting to be printed or gives the
confedintial information you were printing to a compeditor I doubt
your boss wold say "So what?".
-- 
Bob Larson	Arpa: Blarson@Ecla.Usc.Edu	blarson@skat.usc.edu
Uucp: {sdcrdcf,cit-vax}!oberon!skat!blarson
Prime mailing list:	info-prime-request%fns1@ecla.usc.edu
			oberon!fns1!info-prime-request

jbs@fenchurch.MIT.EDU (Jeff Siegal) (03/14/88)

In article <7616@oberon.USC.EDU> blarson@skat.usc.edu (Bob Larson) writes:
>In article <700@virginia.acc.virginia.edu> scl@virginia.acc.Virginia.EDU (Steve Losen) writes:
>>	[...].  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?
>So what?  It depends a lot on what you are printing.  When someone
>modifies the batch of checks waiting to be printed or gives the
>confedintial information you were printing to a compeditor I doubt
>your boss wold say "So what?".

Setting the directory mode to 777 by itself doesn't let anyone modify
or read anything.  All it allows people do is:

	1. List the file names in the directory
	2. Access files in the dirctory _according_to_their_modes.
	3. Remove files from the directory.

#1 can be prevented, if necessary by setting the directory mode to 733
rather than 777.  This effectively prevents #2 and #3 if you use
obscure file names.  #2 can be absolutely prevented in any case by
setting the mode of each file in the directory appropriately.  #3 can
be absolutely prevented on BSD 4.3 systems by setting the sticky bit
(mode 1733) on the directory.

The security risk is minimal, since all that can be done is removing
the files, not reading or writing them. Even this can be effectively
prevented by using the messures described above.

Jeff Siegal

wesommer@athena.mit.edu (William Sommerfeld) (03/14/88)

In article <8468@eddie.MIT.EDU> jbs@eddie.MIT.EDU (Jeff Siegal) writes:
>#1 [listing the directory] can be prevented, if necessary by setting the
>directory mode to 733 rather than 777.  This effectively prevents
>#2 [reading and/or writing files in the directory] and #3 [deleteing
>files] if you use obscure file names.  #2 can be absolutely prevented
>in any case by setting the mode of each file in the directory
>appropriately.  #3 can be absolutely prevented on BSD 4.3 systems by
>setting the sticky bit (mode 1733) on the directory.

Of course, this implies that the daemon which reads this queue runs as
root, which may not be desirable either.

Secure systems should be able to withstand attacks when the attackers
have complete access to all design documentation, source, and object
code.  "Security through obscurity" is not security at all.

 - How do you seed the random number generator used to generate the
100-character `obscure' filename such that knowing, for example, the
approximate starting time and process ID of a process which dropped
something interesting in the queue doesn't make things any easier.

A much better approach would be to have a pseudo-user for for whatever
facility you were creating, and a _short_, _auditable_ setuid program,
without shell escapes and other similar nonsense, to deposit things in
the spool directory.  If you need to put a fancy user-interface on it,
build the user interface as a separate process and have it fire up a
setuid backend process to do the `real work'.

					- Bill

friedl@vsi.UUCP (Stephen J. Friedl) (03/14/88)

In article <8468@eddie.MIT.EDU>, jbs@fenchurch.MIT.EDU (Jeff Siegal) writes:
> Setting the directory mode to 777 by itself doesn't let anyone modify
> or read anything.  All it allows people do is:
> 
> 	1. List the file names in the directory
> 	2. Access files in the dirctory _according_to_their_modes.
> 	3. Remove files from the directory.

You missed at least two:

	4. Rename files
	5. Add new files

What if you see a job ready to print.  You know payroll will be printing
checks soon so you make up a file of your own checks.  When you see it
in the queue you remove theirs and insert yours.

Another one: your system's laser printer has usage accounting built
into the spooler.  You make up your own spooler files and stick them
in the directory directly.  The despooler never knows the difference.

The set-sticky-bit-on-directory fix will be available for SVR3.2
from AT&T soon.  If this is done, you only can only unlink files
if you own the file or own the directory.  This largely fixes the
above problems in the manner of BSD.

-- 
Steve Friedl, KA8CMY      ARPA/CSNet:  friedl@vsi.uu.net       *Hi Mom*
uucp email : { kentvax, uunet, attmail, ihnp4!amdcad!uport }!vsi!friedl
"Too bad we judge others by their actions and ourselves by our motives"

davidsen@steinmetz.steinmetz.UUCP (William E. Davidsen Jr) (03/15/88)

In article <700@virginia.acc.virginia.edu> scl@virginia.acc.Virginia.EDU (Steve Losen) writes:
| 
| 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.
  While I agree with much of what you said, I totally disagree on this
point. I can't belive that this is workable of any system available to
hackers (ie. public access, campus machines, etc), and would not really
provide benefits on even a machine loaded with well behaved users.

  While setuid leaves problems, I doubt the proper way to solve them is
to let people do things with system queues without using setuid. I have
spent a great deal of time setting my system so that it is secure (one
of the systems is public access). Here are a few things I've done:

  Scan the entire filesystems with find and identify every program in
the system with setuid on. Any of these which are owned by system uids
are examined carefully for problems.

  I have all my system queues set to require access via setuid, just the
opposite of your method. I have all my lp and uucp stuff locked up,
accessed by a small number of trusted programs. Other programs which
access the queues may be setuid, but are not executable by anyone but
the user and root. When creating files setuid prevents files from having
dubious ownership, such as root. If you leave a file owned by root, it
can be modified.

  I have logins just for working on various subsystems, most commonly
uumaint (uucp) and lpmaint (lp). Logins which access uucico run under
another uid, not uucp. All files and queues are accessable by uucp
(unless other access is needed). No program which does a "system" call
or starts a shell in any way is setuid.

  I have accounting on, running to a write only device. This allows me
to analize system behavior as needed. It has helped me spot and correct
problems, and is checked by a cron script daily and by me when I get a
bad feeling.

  I don't allow users to execute many of the uucp programs, particularly
uucico. I'm not going to post the hole here, but only logins can run the
program on my system.

  I hope that this offers another viewpoint on this area.
-- 
	bill davidsen		(wedu@ge-crd.arpa)
  {uunet | philabs | seismo}!steinmetz!crdos1!davidsen
"Stupidity, like virtue, is its own reward" -me

scl@virginia.acc.virginia.edu (Steve Losen) (03/15/88)

In article <391@vsi.UUCP> friedl@vsi.UUCP (Stephen J. Friedl) writes:
>In article <8468@eddie.MIT.EDU>, jbs@fenchurch.MIT.EDU (Jeff Siegal) writes:
>> Setting the directory mode to 777 by itself doesn't let anyone modify
>> or read anything.  All it allows people do is:
>> 
>> 	1. List the file names in the directory
>> 	2. Access files in the dirctory _according_to_their_modes.
>> 	3. Remove files from the directory.
>
>You missed at least two:
>
>	4. Rename files
>	5. Add new files
>
>What if you see a job ready to print.  You know payroll will be printing
>checks soon so you make up a file of your own checks.  When you see it
>in the queue you remove theirs and insert yours.

Sorry I started such  a  controversy  here.  I  must  agree  that  in  many
situations  you  need  a  secure  print  spooler.  My  intent was to simply
illustrate  that  in  some  situations  setuid  hassles  can   be   avoided
altogether.  One should always balance the risks of opening up permisssions
on certain files/directories with  the  possibly  hidden  risks  of  poorly
designed setuid software.

For  the  record,  the  spooler  I wrote was a shell script and we all know
setuid shell scripts are either  unsupported  (sysV)  or  a  security  hole
(BSD).  This  script  emulates  a  "spool"  command  that runs on our Prime
systems that can print to sites all over the grounds (campus). The  spooler
shell  script  puts files in a directory for a daemon to kermit (ugh!) to a
Prime system for printing. The users of this system are all academic  types
who  understand the risks, drawbacks (slowness), etc., but who nevertheless
have successfully and happily printed their jobs with  this  Rube  Goldberg
monstrosity for the past two years.

I  admit  that  this  system  was  cobbled  up  quite  hastily, but we were
expecting to get TCP/IP on the Primes real soon and well, uh, ... you  know
how these things go.
-- 
Steve Losen     scl@virginia.edu
University of Virginia Academic Computing Center

hansen@pegasus.UUCP (Tony L. Hansen) (03/16/88)

<	3. Remove files from the directory.
<
< #3 can be absolutely prevented on BSD 4.3 systems by setting the sticky
< bit (mode 1733) on the directory.

This enhancement was also put into System V release 3.2. Also, the /tmp and
/usr/tmp directories are shipped as mode 1777. This was just one of the
modifications made in that release to make the UNIX System more secure.

					Tony Hansen
				ihnp4!pegasus!hansen, attmail!tony

jack@cwi.nl (Jack Jansen) (03/17/88)

Well, some things I check a set-uid program for (most of them
learned in the time I wasn't interested yet in writing secure
setuid program but in find bugs in setuid programs:-)

- Never take for granted that the user will not do this-or-that. She
  will.
- Check, double-check and triple-check *each* library or system
  call. Try to think of devious ways of making it do things it
  shouldn't. Hints: think of IFS, links, symbolic links, running
  in removed directories(!), environment variables in general,
  job control, etc etc etc.
- Never take for granted that the user will not do this-or-that. He
  will.
- Always be aware of the time-sharing aspect of unix. Never write
  code like
  if( stat(file, &sb) < 0 ) {
      /* Now we're sure the file doesn't exist so it'll be owned
      ** by us.
      */
      fd = creat(file, mode);
  }
- Never take for granted that the user will not do this-or-that. She
  will.
- Never think that setgid or setuid to bin is less dangerous than
  setuid to root.
- Never take for granted that the user will not do this-or-that. He
  will.
- Never allow interrupt routines, *not even with a signal catcher*
  to clean up in a critical section. 'ulimit stacksize' can be
  very useful at times..........
- The difficult one: never trust any other machine. Given control
  over one machine on an ethernet, for instance, I can impersonate
  *any* other machine by first crashing the original, trusted, machine
  and then impersonating it.

And, of course, the general rule is not to write setuid programs
in the first place, but that has been handled by other people.
-- 
	Jack Jansen, jack@cwi.nl (or jack@mcvax.uucp)
	The shell is my oyster.

jc@heart-of-gold (John M Chambers x7780 1E342) (03/18/88)

> And, of course, the general rule is not to write setuid programs
> in the first place, but that has been handled by other people.

One question I have:  To my knowledge, there does not actually exist a way
to "write a setuid program".

Yes, of course, I know how to type:
	chmod 6755 foo
I claim that this does not constitute "writing a setuid program".  What it
does is take an existing non-setuid program (that is already written and
compiled) and make it into a setuid program.  But when the programmer wrote
it, it wasn't setuid.

Lest people think I am being facetious, I'd like to point out that there
is an important point at work here.  When writing a program, I don't know
whether it will be setuid.  So how can I follow the above advice? (Obviously, 
by not writing any programs! :-)  When I write a line of code, how do I 
determine whether it is in a setuid program?

There is one sense in which I could conceive of actually writing a setuid
program.  If there were a way to test at run time whether the program (well,
actually the process, but you know what I mean) is setuid:

	if (setuid(getpid())) {
		<<Don't do a bunch of stuff>>
	} else {
		<<Go ahead and do them>>
	}

Can anyone show me the source for setuid()?  I suspect that you can't,
but I'd like someone to explain how stupid I'm being and how easy it is.

For a further argument, consider the alternative:

	#if SETUID
		<<Don't do a bunch of stuff>>
	#else
		<<Go ahead and do them>>
	#endif

I contend that there is no way for the C preprocessor to correctly 
implement the above #if command.  I'd be very interested in being
proved incorrect.

matt@oddjob.UChicago.EDU (Schizophrenic Solipsist) (03/18/88)

jc@heart-of-gold (John M Chambers x7780 1E342) asks:

)   If there were a way to test at run time whether the program (well,
) actually the process, but you know what I mean) is setuid:
) 
) 	if (setuid(getpid())) {
) 		<<Don't do a bunch of stuff>>
) 	} else {
) 		<<Go ahead and do them>>
) 	}
) 
) Can anyone show me the source for setuid()?  I suspect that you can't,
) but I'd like someone to explain how stupid I'm being and how easy it is.

int
i_am_setuid()
{
	return getuid() != geteuid();
}

/* I *think* it is done in SysV as it is in BSD.  Amen. */
________________________________________________________
Matt	     University		matt@oddjob.uchicago.edu
Crawford     of Chicago     {astrovax,ihnp4}!oddjob!matt

geoff@desint.UUCP (Geoff Kuenning) (03/18/88)

In article <8468@eddie.MIT.EDU> jbs@eddie.MIT.EDU (Jeff Siegal) writes:

> Setting the directory mode to 777 by itself doesn't let anyone modify
> or read anything.  All it allows people do is:
> 
> 	1. List the file names in the directory
> 	2. Access files in the dirctory _according_to_their_modes.
> 	3. Remove files from the directory.

You forgot one:

	4. Create files in the directory.

In practice, this allows modifying, though not reading, any file.  Simply
remove the original and replace it by your favorite Trojan horse.  On
System V (the system under discussion), you can even give the file away
to the original owner and group.  With the utime(2) call, you can even
put in the original access and modification (though not i-node change)
times.
-- 
	Geoff Kuenning   geoff@ITcorp.com   {uunet,trwrb}!desint!geoff

pdb@sei.cmu.edu (Patrick Barron) (03/19/88)

In article <127@heart-of-gold> jc@heart-of-gold (John M Chambers x7780 1E342) writes:
>Lest people think I am being facetious, I'd like to point out that there
>is an important point at work here.  When writing a program, I don't know
>whether it will be setuid.  So how can I follow the above advice? (Obviously, 
>by not writing any programs! :-)  When I write a line of code, how do I 
>determine whether it is in a setuid program?

   On the contrary, normally when one writes a program that is going to
have the set-uid bit set, one knows that for a fact before starting.  Taking
random programs that you know little or nothing about, and making them
set-uid, is an exceptionally bad idea.

>Can anyone show me the source for setuid()?  I suspect that you can't,

  No, I can't show you the source for setuid(), but only because my Ultrix
license agreement prohibits it.... :-)  Seriously, there really is a setuid()
system call.  It doesn't do what you want, though.

--Pat.

gwyn@brl-smoke.ARPA (Doug Gwyn ) (03/20/88)

In article <3098@pegasus.UUCP> hansen@pegasus.UUCP (XT1554000-Tony L. Hansen;LZ 3B-315;6243) writes:
>This enhancement was also put into System V release 3.2. Also, the /tmp and
>/usr/tmp directories are shipped as mode 1777. This was just one of the
>modifications made in that release to make the UNIX System more secure.

How well does this work in practice?  Due to the large number of
utilities that fail to properly clean up their tmp files, often
the system administrator ends up having to periodically clean out
/tmp and /usr/tmp.  With this new scheme, he'll have to become
superuser to do this, unless a privileged operator-executable
cleanup utility is provided (or the system is rebooted and does
this on each reboot).

jc@minya.UUCP (John Chambers) (03/21/88)

> A much better approach would be to have a pseudo-user for for whatever
> facility you were creating, and a _short_, _auditable_ setuid program,
> without shell escapes and other similar nonsense, to deposit things in
> the spool directory.  

A program that does exactly this was posted to one of the sources group
a couple of years back, under the name "append.c".  Perhaps it's time
to post it again.  Or is it archived in one or the source newsgroups?

It was also a Unix implementation of a Multics security feature.  It's
also a good counter-example to the frequent claims that all setuid programs
are Bad Things.

-- 
John Chambers <{adelie,ima,maynard,mit-eddie}!minya!{jc,root}> (617/484-6393)

cudcv@daisy.warwick.ac.uk (Rob McMahon) (03/27/88)

In article <8468@eddie.MIT.EDU> jbs@eddie.MIT.EDU (Jeff Siegal) writes:
|In article <7616@oberon.USC.EDU> blarson@skat.usc.edu (Bob Larson) writes:
|>about setuid lp programs.
|Setting the directory mode to 777 by itself doesn't let anyone modify
|or read anything.  All it allows people do is:
|
|	1. List the file names in the directory
|	2. Access files in the dirctory _according_to_their_modes.
|	3. Remove files from the directory.

	4. Add files (or links) to the directory.

If you're not careful Joe User can get files printed out which he has no
read permission to by making links, symbolic links, into this directory.

Rob
-- 
UUCP:   ...!mcvax!ukc!warwick!cudcv	PHONE:  +44 203 523037
JANET:  cudcv@uk.ac.warwick.cu          ARPA:   cudcv@cu.warwick.ac.uk
Rob McMahon, Computing Services, Warwick University, Coventry CV4 7AL, England