initCommon(); $template->displayHeader(); ?>

7. Using Other Types To Lock Down CGI Scripts

In the section Section 6, “Virtual Hosting, CGI scripts and suEXEC” you saw that a user's CGI script running in the httpd_sys_script_t domain can read and write all Web content. In many scenarios, this is undesirable. For example, the Pyblosxom blog software (http://roughingit.subtlehints.net/pyblosxom/) reads in a number of plain text blog entry files, and renders them to HTML on the fly to the client. For this, it only needs read access to the blog entry files. Ideally, if Pyblosxom was compromised or had a bug, it would not have the ability to modify or delete your Web log entries.

SELinux allows you to achieve this by defining more types. You grant httpd_sys_script_t the access to those types that you desire. The default Apache HTTP policy defines a number of other types by default.

User-controllable types defined by the SELinux policy

httpd_sys_script_ro_t

A CGI script may only read files and directories with this type.

httpd_sys_script_ra_t

Like the previous type, except a CGI script may also append more data (useful for log files, etc).

httpd_sys_script_rw_t

Content that a CGI script may change in any way, including deletion.

httpd_sys_content_t

See below.

httpd_sys_script_exec_t

The type for CGI executables. httpd_t cannot execute other types such as httpd_sys_script_rw_t.

The Fedora Core 3 release introduces a new policy boolean called httpd_unified. If this boolean is enabled, it means that httpd_sys_content_t (the type used for everything up until this point) is treated as the union of all the other types above. If this boolean is disabled, it means that (for example) in order for a CGI script to be executed by httpd_t, it must be labeled with the httpd_sys_script_exec_t type. And it also means that in order for a CGI script to write data, the target file must be labeled with httpd_sys_script_rw_t, as above.

The httpd_unified boolean was introduced so that one could, for the most part, use a typical Apache HTTP installation without a good grasp on file labeling and security contexts. However, disabling this boolean grants far greater security, and is highly recommended.

Now, examine how Bob might set up a Pyblosxom installation:

# cd /var/www/example.com
# ls -aZ blog
drwxr-xr-x  bob  bob  user_u:object_r:httpd_sys_content_t .
drwxr-xr-x  bob  bob  user_u:object_r:httpd_sys_content_t ..
-rwxr-xr-x  bob  bob  user_u:object_r:httpd_sys_script_exec_t index.cgi
drwxr-xr-x  bob  bob  user_u:object_r:httpd_sys_script_ro_t pyblosxom
# cd pyblosxom
# ls -aZ
drwxr-xr-x  bob  bob  user_u:object_r:httpd_sys_script_ro_t .
drwxr-xr-x  bob  bob  user_u:object_r:httpd_sys_content_t ..
-rw-r--r--  bob  bob  user_u:object_r:httpd_sys_script_ro_t comment-form.rss
-rw-r--r--  bob  bob  user_u:object_r:httpd_sys_script_ro_t comment-story.rss
-rw-r--r--  bob  bob  user_u:object_r:httpd_sys_script_ro_t comment.rss
-rw-r--r--  bob  bob  user_u:object_r:httpd_sys_script_ro_t config.py
-rw-r--r--  bob  bob  user_u:object_r:httpd_sys_script_ro_t date_head.html
-rw-r--r--  bob  bob  user_u:object_r:httpd_sys_script_ro_t firstpost.txt
...

Note that the index.cgi executable is marked as httpd_sys_script_exec_t, and the rest of the data is marked as httpd_sys_script_ro_t. This protects all the blog data from a compromised, misconfigured, or buggy Pyblosxom.

One other important thing to note about these types is that by default, httpd_t cannot access any of them except for httpd_sys_content_t. In other words, by default Apache HTTP cannot directly access content intended for your CGI scripts.

Consider the case of a small intranet, accessed via a CGI script, which has some public content, and some password-protected content. It stores its data in a large database file. In this case, you would label the database file as httpd_sys_script_rw_t so that the intranet CGI could write to it. But if you allow httpd_t access to httpd_sys_script_rw_t, then if Apache HTTP was compromised or simply misconfigured, it could gain access to the database directly, bypassing the access control that the CGI script was doing for the password-protected content.

displayFooter('$Date: 2005/03/30 17:47:23 $'); ?>