Using chroot()


 
Network Programming with Perl
By Lincoln  D.  Stein
Slots : 1
Table of Contents
Chapter  14.   Bulletproofing Servers

    Content

Another common technique for protecting the system against buggy servers involves the chroot() call. chroot() takes a single argument containing a directory path and changes the current process so that this path becomes the top-level directory (" / "). The effects of chroot() are irrevocable. Once the new top-level directory has been established, the program cannot see outside it or affect files or directories above it. This is a very effective technique for insulating the script from sensitive system files and binaries.

chroot() does not change the current working directory. Ordinarily you will want to chdir() into part of the restricted space before calling chroot() . chroot() can be called only when the program is running with root privileges and is available only on UNIX systems. It is most frequently used by programs that need to run a lot of external commands or are particularly powerful. For example, the FTP daemon can be configured to allow anonymous users access to a restricted part of the filesystem. To enforce this restriction, FTP calls chroot() soon after the anonymous user logs in, changing the top-level directory to the designated restricted area.

Adding chroot() to the Psychotherapist Server

It is simple enough to add support for chroot() to the psychotherapist server, but we have to be a little careful about what we're doing and why we're doing it. We do not want to run the entire server in a chroot() environment, because then it would not be able to see and unlink its PID file on normal termination. Instead, we want to change to a restricted directory before we begin interacting with the remote user. This is best done by the child process in the main loop, just before relinquishing its root privileges.

Figure 14.5 shows how this is done. It is a slight enhancement to the root psychotherapist server: Just before calling interact() , the child process invokes a new subroutine called prepare_child(). prepare_child() regains root access by swapping the real and effective UIDs (line 15), making root the effective user ID. This is done in a local() statement within a block; when the block is done, the UIDs are swapped again. We call chroot() to reassign the root directory (line 19). The last statement assigns the effective UID to the real UID, permanently relinquishing root privileges.

Figure 14.5. A redesign psychotherapist main loop calls chroot()

graphics/14fig05.gif

For the purposes of this example, we use /home/ftp as the directory to chroot() to. This is the same directory used for anonymous FTP on Linux systems and is unlikely to contain confidential material or vulnerable files.

After calling chroot() , the script is quite effectively sealed off from the rest of the system. Like an explorer entering an undeveloped wilderness, your script must bring with it everything it needs, including configuration files, external utilities, and Perl libraries. These need to be placed in the chroot() destination directory, and all hard-coded path names in your script have to be adjusted to reflect what the filesystem will look like after the destination directory becomes top level. For example, the file that lived at /home/ftp/bin/ls before chroot() becomes /bin/ls after a chroot() to the /home/ftp directory.

If the script launches other programs during its operation, they too will be subject to the chroot() restrictions. This means that any dependencies that they have, including configuration files and dynamically linked libraries, must be copied into the chroot() directory.

As a concrete example of this, when I first ran the program with this modification, everything seemed to be fine until the Chatbot::Eliza module tried to issue a warning message, at which point a message appeared in the system log warning me that Perl couldn't load Carp::Heavy, an internal component of the Carp module. Apparently this module isn't loaded automatically when you use Carp but is loaded dynamically the first time that Carp is needed. However, because the Perl library tree became unavailable as soon as chroot() was called, it could not be loaded. The solution I chose was to explicitly use Carp::Heavy in the Daemon module thereby preloading it. Another solution would have been to copy this file into the appropriate location under /home/ftp/lib .

Watch for this, particularly if you use Perl's Autoloader facility. Autoloader's strategy of delaying compilation of .pm files until needed means that all Autoloader- processed .al files must be accessible to the script within its chroot() environment.


   
Top


Network Programming with Perl
Network Programming with Perl
ISBN: 0201615711
EAN: 2147483647
Year: 2000
Pages: 173

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net