Things that can affect the setuid program
Let's consider some ways the calling user could affect the behavior of the setuid process. I'll divide the things to consider in three groups: 1) the program itself, 2) the input to the program, and 3) the environment it runs in.
The binary:
If the unprivileged user can modify the binary that will be run, that would be a simple way to change the privileged process. This includes cases where the path to the program file goes through user-writable directories and symlinks. Making sure the program file and the path to it is not writable by unauthorized users is mandatory. (And sudo
doesn't seem to check.)
Setuid binaries are actually more immune to modification in this way, since the setuid bit is a property of the inode, not the path, so symlink tricking will not work. (Also, it seems that some Linuxes drop the setuid bit if another user writes to the file, but I wouldn't count on that.)
Input issues include all the input the program needs and uses to function, be it data from standard input, command line arguments or something else.
If the program needs any input from the user (instead of doing exactly one thing, always), we can't avoid this. Network services need to be careful with their input in the same way. (Witness SQL injections etc. for an example.) I think input handling is especially difficult for things like shell scripts where all data is text and all text is one quote away from turning into a command, so to say.
If the input is sensitive, we might consider issues like command line arguments being visible to other users on the system.
The environment is a wider issue. Obviously it includes environment variables, with stuff like PATH
(which can change which subprocess runs), LC_*
and POSIXLY_CORRECT
(which might change the output format of shell commands), HOME
(which might be used to access files), and LD_*
(which modify the behaviour of the dynamic loader) among others. Those are the easy things.
But consider something like resource limits, Linux cgroups, chroots, SELinux contexts, namespaces and who knows what else. Setting, say a tight limit for stack use or for the number of processes might make the privileged process crash at an unexpected point or fail to launch subprocesses. The unexpected crash might happen after the process has locked some resource but before it has a chance to commit and unlock it...
(Though, many of the latter cannot be changed by non-privileged users, so they might not be a significant problem. A setuid executable that is accessible from a limited environment might still need to properly deal with the limited environment.)
What to do about it
At the very least you should
- Make sure the executable is writable only by authorized users, including both the file itself and the path leading to it. Making it non-readable is not really necessary, unless the program contains embedded secrets.
- Reset the environment when starting, including setting
PATH
to known value. Using absolute paths feels safe, but I can't tell what effect it would have if the search path is known. env_reset
in sudo
should do it.
- (
LD_*
variables are processed by the dynamic linker before you get a chance to unset them, but the linker should ignore them for setuid processes.)
- Validate its input. Of course. Though that may also be easier said than done, especially if you are considering giving user input to a shell script.
- Take sensitive input (passwords) through a pipe or stdin.
- Reset resource limits or take the risk of failing at an unexpected point.
In theory, it should be possible to make a "safe" setuid or sudo-ran program in a traditional environment, but the less-common system specific features might be harder to validate.
(And incidentally, I might say that a setuid program will not always easily open a security breach, since many systems have stuff like passwd
, su
, and sudo
itself that are setuid, yet are not considered relatively safe.)
Alternative to setuid
The other possibility besides sudo or setuid, is to talk to the privileged process through a pipe (or socket). This has the advantage that the running binary is easily secured, and its execution environment is known and the unprivileged user has no way of affecting it.
However, with pipes input from all writers is stuck together without separation. You could define requests to be separated by newlines, but someone could still write a partial line to the pipe, prepending data to the next request from another process. Also, pipes don't easily allow bidirectional communication, so getting a status reply from the privileged process would not be easy.
Sockets don't have this problem, but they can't be accessed by the usual file system functions either. Instead, they need to be accessed via socket functions. (i.e. echo foo > /my/socket
will not work. socat
might be useful in scripts).
iptables
command to allow it to port 21. Point is that web developers can unlock access to FTP by simply visiting certain url, of course HTTPS one... – Miloš Đakonović Jul 12 '16 at 20:54sudo
. That's true even when run bywww-data
. I've just updated it to say that it should quote all its variables. – cas Jul 13 '16 at 04:59suid
and recommendssudo
. Almost all described shouldn't be possible becausesudo
resets environment (5th bullet in my question) and I know that I should use only absolute paths. Anyway thanks - very very serious subject. – Miloš Đakonović Jul 13 '16 at 19:00mycommand
sudo given towww-data
, which strictly limits usage ofiptables
, you are giving freedom towww-data
to executeiptables
whateverwww-data
likes. It would be horrible mistake I think. – Miloš Đakonović Jul 18 '16 at 14:35/etc/sudoers
to validate a particular subset of data? (It's close to impossible, which is why validation in the script itself is to be preferred.) – Chris Davies Jul 19 '16 at 22:07CAP_NET_ADMIN
capability, not full root. – derobert Jul 20 '16 at 16:13