22.2. Minimizing the Opportunity for Attack
One of the best strategies for making a program secure against attempts to exploit their privileges is to make the parts of a program that can be attacked as simple as possible. While this strategy can be difficult to employ for network programs and system daemons, programs that must be run with special permissions (via the setuid or setgid bits, or run by the root user) can usually use a few common mechanisms to limit their areas of vulnerability.
22.2.1. Giving Up Permissions
Many programs that need special privileges use those privileges only at startup time. For example, many networking daemons need to be run by the root user so they can listen() on a reserved port, but they do not need any special permissions after that. Most web servers use this technique to limit their exposure to attack by switching to a different user (normally a user called nobody or apache) right after they open TCP/IP port 80. While the server may still be subject to remote exploits, at least those exploits will no longer give the attacker access to a process running as root. Network clients who need reserved ports, such as rsh, can employ a similar strategy. They are run as setuid to root, which allows them to open the proper port. Once the port is open, there is no longer any need for root privileges, so they can drop those special abilities.
One or more of setuid(), setgid(), and setgroups() need to be used to reset the processes permissions. This technique is effective only if the real, effective, file system, and saved uids (or gids) are all set to their proper values. If the program is running setuid (or setgid), the process probably wants to set those uids to its saved uid. System daemons that are changing to a different user after being run by root need to change their user and group ids, and should also clear their supplemental group list. For more information on how a process can change its credentials, see page 108.
22.2.2. Getting a Helping Hand
If a program needs special permissions during more than just its initial startup, helper programs may provide a good solution. Rather than making the entire application run with elevated privileges, the main program runs as the normal user who invoked it, and runs another, very small program that has the proper credentials to perform the task that requires them. By architecting the application in this way, the complexity of the code that can be attacked is dramatically reduced. This reduction makes the code much easier to get correct and to audit for any mistakes. If there are problems in the main application that allow the user to perform arbitrary actions, those actions can be performed only with the user's normal credentials, rendering any attack useful only against that user, not the user with elevated capabilities.
Using small helper programs in this way has become quite popular in the Linux community. The utempter library, discussed on page 340, uses a setgid helper program to update the utmp database. The helper is very careful to validate its command-line arguments and to ensure that the application calling it is allowed to update the utmp database. By providing this service through a helper application, programs that use utempter do not need to have any special permissions themselves; before this library was written, any program that used pseudo ttys needed to be setgid to the group that owned the utmp database.
Another example of a helper program is the unix_chkpwd program used by PAM (PAM, or Pluggable Authentication Modules, is discussed in detail starting on page 635). Passwords on most Linux systems are stored in a file that is readable only by the root user; this prevents dictionary attacks on the users' encrypted passwords. Some programs want to make sure the person currently at the computer is the one who is logged in (xscreensaver, which can be used to lock a system's screen until the user returns, is a common program that does this), but do not normally run as root. Rather than make those programs setuid root so they can validate the user's password, PAM's normal Unix-style authentication calls unix_chkpwd to validate the password for it, so that only unix_chkpwd needs to be setuid root. Not only does this remove the need for xscreensaver to be written as a privileged program, but it also means that any vulnerabilities in the X11 libraries it depends on do not allow local exploits.
 A dictionary attack is a brute force method of discovering passwords where an automated program runs through a large list of common passwords, such as words in a dictionary, until one works.
Using helper programs in this way is a very good way of eliminating the possibility of security problems in applications. Writing these helpers is normally quite straightforward, and their correctness is relatively simple to determine. There are a couple of things to watch out for in their design, however.
Quite often, confidential data is passed between the main application and the helper program. For unix_chkpwd, the user's unencrypted password must be supplied for the helper program to validate. Some care needs to be taken in how that information is passed; while it is tempting to use a command-line argument, that would allow any user who runs ps at just the right time to see a user's unencrypted passwords. If a pipe is used to transmit the data instead (normally set as the helper program's stdin), then the data is transmitted without other programs being able to see it.
The helper program also needs to carefully ensure that the program calling it is allowed to perform the action it is requesting. The unx_chkpwd helper does not let a program validate the passwords of any user other than the one running it. It uses its own real uid to validate that the program that calls it is allowed to check the password of the user it has requested. The utempter helper does similar checks to make sure that programs cannot remove terminals from the utmp database unless it is appropriate to do so.
22.2.3. Restricting File System Access
One more way of keeping coding mistakes from providing the potential for an attack is to limit the set of files to which a program has access by using the chroot() system call. As discussed on page 296, chroot() followed by a chdir() call changes the root directory of the process, limiting the set of files that process is able to access. This does not prevent an exploit, but it can sometimes contain the damage. If a network server running as a user other than root is remotely exploited, it becomes much more difficult for that remote user to use that server as the base of a local exploit if it cannot access any setuid files (the most common programs local exploits can take advantage of).
Anonymous ftp servers are the most common programs that take advantage of the chroot() mechanism. In recent years it has become more popular in other programs, and many system administrators have used the chroot command to force system daemons into running in a restricted environment as a precaution against intruders.