Bugtraq mailing list archives
Re: Wiping out setuid programs
From: djb () CR YP TO (D. J. Bernstein)
Date: Mon, 11 Jan 1999 04:19:49 -0000
1. Managing a privileged daemon connection is no more difficult than managing any other privileged open file. For example, there's a library routine today that opens /etc/shadow, inside a root utility that calls setuid() and then runs a user program. Does this mean that user programs have access to /etc/shadow? Do we have to change read() to check the caller's euid, instead of preserving the privileges sneakily obtained by the euid that called open()? Of course not. The library sets the close-on-exec bit. The privileged descriptor is closed before the user program starts. End of story. (Except for systems like Solaris. See my previous note on setuid().) The library routine might be changed tomorrow to open a connection to a daemon through a UNIX-domain socket accessible only to root. Or it might be changed to open a connection to a daemon that grants access based on getpeereuid(). Yes, the resulting file descriptors are privileged. No, that's not a problem. That's how file descriptors have always worked. You can think of a world-writable socket with getpeereuid() as a giant farm of sockets, with one socket writable by each user. Or you can think of it as a restricted socket, with a setuid helper utility that makes a connection, writes getuid(), drops privileges, and runs a user program; such utilities have been available for years. In any case, it's obvious how to use it correctly. 2. I'm not saying that sendmsg() uids are completely useless. Alan Cox gives the example of a root web server running scripts as various users, and logging some packets of data from each script. Today this server needs to create at least one pipe per user. With sendmsg() uids, all the scripts could share a single open file for logging. However, whether or not we have sendmsg() uids, we need connect() uids, so that connection-oriented daemons can account for all resource use. Implementing streams on top of datagrams is an unnecessary roadblock. (Implementing datagrams on top of streams is no big deal.) 3. It's certainly true that serious configuration errors may prevent a daemon from starting. But the same is true of setuid programs. It's also true that serious programming errors may cause a daemon to crash. That's why there are monitoring tools that automatically restart dead daemons. This feature is built into init under System V. What about daemons that insist on running in the background? No problem: #!/bin/sh thebackgrounddaemon 5>&1 | cat >/dev/null This works for typical daemons that close descriptors 0, 1, and 2. Steven M. Bellovin writes:
and, on many platforms, the utterly buggy implementation of UNIX domain sockets and (especially) file descriptor passing.
I agree that fd passing is a fertile ground for kernel bugs, up to and including panics. But I've never had any problems with basic I/O through UNIX-domain stream sockets.
Look at all of the remote attacks on sendmail, or all the buffer overflows!
I see from http://pobox.com/~djb/docs/maildisasters/sendmail.html that, between sendmail 8.6 in 1993 and sendmail 8.8.7 in 1997, there were eight bugs that allowed attackers to run programs as unauthorized uids: 8.8.3: Local attack. Would have been impossible if local delivery had been run under the target user's uid. 8.8.2: Local attack. Would have been impossible if the daemon hadn't been setuid. 8.8.0/8.8.1: Remote attack---the MIME 7-to-8 buffer overflow. 8.7.5: Local attack. Would have been impossible on a well-managed system if the queue runner hadn't been setuid. (This is one of the security holes I found; the real problem is an obvious flaw in the UNIX getpw*() API.) 8.7.5: Local attack. Would have been impossible if the program building a user's From line hadn't been setuid. 8.7.3/8.6.12: Remote attack, as I recall---the bizarre-PTR problem. 8.6.6: Local attack. Would have been impossible if the argv-parsing code hadn't been setuid. 8.6.5: Local attack. Would have been impossible if local delivery had been run under the target user's uid. Eliminating setuid would have stopped four of these holes. Reducing the local delivery privileges in a straightforward way would have stopped two more. Running header conversions in a controlled playground would have stopped the other two.
We can use string-safe languages like Java, and we can store temporary files in some place that isn't world-writable -- and then we can trip over bizarre host names, inappropriate code paths that allow for command execution in the wrong spot, etc.
And we can develop better programming tools that reduce your error rate even more. For example, text0-format files would have stopped the bizarre-PTR problem. But I'm talking about something much more fundamental: reducing the amount of code in which bugs can lead to security holes. Yes, there's a lot of buggy code, but most of that code can be taken out of the loop. What's left is small enough to carefully review. ---Dan P.S. I'm curious whether the mailing list and web archives can handle RFC 822-endorsed b bo ol ld df fa ac ce e, without MUA Q-P corruption this time.
Current thread:
- Re: Wiping out setuid programs Steve Bellovin (Jan 07)
- Re: Wiping out setuid programs Gene Spafford (Jan 08)
- <Possible follow-ups>
- Re: Wiping out setuid programs D. J. Bernstein (Jan 09)
- Re: Wiping out setuid programs Alan Cox (Jan 09)
- Re: Wiping out setuid programs Nick Maclaren (Jan 10)
- Bind 8.* bug. Alan Brown (Jan 11)
- Re: Wiping out setuid programs Neale Banks (Jan 11)
- Re: Wiping out setuid programs Steven M. Bellovin (Jan 09)
- Re: Wiping out setuid programs der Mouse (Jan 09)
- Re: Wiping out setuid programs D. J. Bernstein (Jan 10)
- Re: Wiping out setuid programs Niall Smart (Jan 12)