16.5 Using chroot( )

You can enhance the security of your programs by using the chroot( ) system call. The chroot( ) call changes the root directory of a process to a specified subdirectory within your filesystem. This change essentially gives the calling process a private world from which it cannot escape.[19] Several widely-used network daemons, such as the BIND nameserver, are written so they can run in a chroot( ) environment.

[19] Modern BSD systems have an even more powerful version of chroot( ) called jail( ) that can provide significantly better isolation to jailed processes.

For example, if you have a program that only needs to listen to the network and write into a log file that is stored in the directory /usr/local/logs, then you could execute the following code to restrict the program to that directory:

assert(chdir("/usr/local/logs") == 0);
assert(chroot("/usr/local/logs") == 0);
assert(chdir("/") == 0);

There are several issues that you must be aware of when using the chroot( ) system call that are not immediately obvious:

  1. It is imperative that you successfully chdir( ) into the chroot area before doing anything important (and best if you chdir( ) there before you call chroot( )). chroot( ) does not change the working directory, and a privileged program can break out of a chroot area if its working directory is outside the area.

  2. With some systems, it is also critical that you set the current working directory to be "/" after the chdir is executed. Otherwise, it is possible to break out of the chroot( ) system in some cases.

  3. If your operating system supports shared libraries and you are able to statically link your program, you should be sure that your program is statically linked. On some systems, static linking is not possible. On these systems, you should make certain that the necessary shared libraries are available within the restricted directory (as copies).

  4. You should not give other users write access to the chroot( )ed directory.

  5. If you intend to log with syslog( ), you should call the openlog( ) function before executing the chroot( ) system call, or make sure that a /dev/log device file exists within the chroot( ) directory.

  6. chroot( )ed processes should run with a UID that is not used by any programs outside of the chroot( ) area. This prevents the processes from using debugger hooks to manipulate outside processes and potentially subvert the jail.

  7. Do not allow root-owned processes to run inside the chroot area. As soon as your program successfully chroots, it should immediately setgid( ) and setuid( ) to give up its superuser privileges. Likewise, where possible, restrict the occurance of SUID programs and devices within the chroot environment.

Many versions of Unix provide a program called chroot that can be used to execute an arbitrary command in a chrooted environment, like this:

# chroot /path/to/directory /chrooted/path/to/command arguments

This will cause a chroot to the specified directory (which must be set up as described earlier), and then run the given command, which must be in the chrooted area (along with any necessary shared libraries, etc.) already.

Note that under some versions of Unix, a user with a root shell and the ability to copy compiled code into the chrooted environment may be able to "break out." The same applies to an SUID program (or other program running as root) that has not dropped its privileges. Thus, don't put all your faith in this mechanism.

    Part VI: Appendixes