Cover V12, I03

Article
Figure 1
Listing 1
Listing 2
Listing 3
Listing 4

mar2003.tar

SELinux

Kerry Thompson

Security Enhanced Linux (SELinux) is an extension to the standard Linux kernel that has been designed to enforce strict access controls. SELinux lets you confine processes to the minimum amount of privilege they require. In this article, I will cover the ideas behind SELinux and show how to install, configure, and manage an SELinux system. As an example of configuring a security policy, I'll show how to configure a BIND-based DNS server with an example security policy that restricts the DNS server to accessing only those files it requires for operation.

Introduction and History

SELinux was released late in 2000 by the U.S. National Security Agency (NSA) and was developed with cooperation from such security heavyweights as NAI Labs, Secure Computing Corporation, and MITRE Corporation. The NSA Information Assurance Research Office continues to guide SELinux development; it is this office that is responsible for carrying out research and development of solutions to achieve a high level of information security critical to government and industry.

Following the initial release of SELinux, the Linux community soon realized that the standard kernel needed to be extended to provide more flexibility for security add-ons. From this came the Linux Security Module (LSM) version of the Linux kernel, which provides for the modular addition of security extensions to the standard Linux kernel. SELinux was then changed to be built as an LSM module, and I will cover the LSM implementation in this article.

The full source code for SELinux was released to the open source community with the aim of creating a viable, secure operating system. With the assistance of open source developers worldwide, SELinux is quickly becoming accepted as a mainstream operating system that can provide a high level of security through mandatory access control.

Models for Access Control

Security experts use a number of models to describe security access control systems. Probably the most common of these is Discretionary Access Control (DAC). DAC is the model commonly used by UNIX systems -- it describes how each user has complete control over the files that they own and the programs that they use, and programs run by a user will have all of the rights that the user has. A user can allow others access to her objects at her discretion and, under such a model, the level of system security is left to the discretion of the applications running on it. For instance, if a program running as the root user is compromised (e.g., through a buffer overflow or a misconfiguration), then the attacker could gain root privileges, and the security of the entire system is compromised.

Another security model is Mandatory Access Control (MAC). If you've configured rules for a firewall system, then you will know what a MAC model is like: the strict security access controls are defined by an administrator and cannot be changed by anybody else. A system with a MAC model operates under very strictly defined rules, the key one of which is usually "that which is not expressly permitted, is denied".

Implementing a MAC model on a UNIX system would typically be very difficult; defining rules for every user to use every program to access every object would result in an enormous set of rules. To make things easier, we can use the concept of Role-Based Access Control (RBAC). Under RBAC, an administrator can define roles and allow certain users access to those roles. For example, an accountant would want read and write access to the accounts database but nothing else, and a system auditor should be able to read all of the system logs and configuration files, but never be able to write to them. By defining roles in a system, defining which objects certain roles can access, and allowing various users to assume various roles, the task of defining a mandatory access security policy is greatly simplified.

There are very few operating systems available with such a level of access control. The main goal of the release of SELinux is to demonstrate that such highly secure operating systems are practical and viable in the world outside of the government and military.

SELinux is a practical implementation of a Mandatory Access Control model based on the Linux kernel. Put simply, the administrator of an SELinux system has the ability to set up a security policy on the system to define which programs can access which files. You could think of it as having the ability to place a firewall around every user and process on the system. To do this, SELinux implements a mechanism to tag each and every process and file with a security context to which it belongs, and it adds a security enforcement module into the Linux kernel to permit or deny all accesses to objects such as files and devices. The security policy defined by the administrator is accessed by the kernel through a security server process. This process runs as a part of the kernel and decides which subjects (processes, users) can access which objects (files, devices). In SELinux, this control mechanism is called Type Enforcement (TE).

If a process that is running as the root user on an SELinux system is compromised (e.g., through a buffer overflow, etc.), then the damage is limited to whatever that process can access as defined by the policy (see Figure 1).

Having the security policy definition external to the kernel allows for easier management and dynamic changing of the policy at run time. SELinux also includes RBAC controls and has the ability to assign roles to users and processes and to control the switching of roles.

SELinux also has the ability to implement the Multi-Level Security model (MLS). Under this model, objects such as files in the system can be classed with layers of security, such as "Top Secret", "Secret", "Confidential", and "Unrestricted". The theory is that information can only be passed from a lower level to an upper level and never flow the other way. MLS is typically used only in military and government multi-user systems, which demand an extremely high level of security. I won't cover MLS in this article. And, that is enough theory -- it's time to look at how SELinux actually works.

Installing SELinux

Begin by installing a Linux system. For my research, I used Red Hat Linux 7.3, which has been reliable and stable. Other distributions known to work include Debian, SuSE, and Mandrake. Any Linux with a standard configuration should work fine. When installing the Linux system, be sure to do the following:

  • Use ext2 or ext3 type filesystems.
  • Install the kernel development kit.
  • Install the gcc compiler -- I used GCC v2.96.
  • Use Grub (or Lilo) as your bootloader.

Install any other packages that you want to run, such as BIND DNS, Apache, Samba, etc.

Also, I recommend configuring the system without a GUI login configured. It may become difficult to log in on the GUI console once SELinux has been installed. Edit /etc/inittab and set the default run-level to 3 to disable the X server startup, just to be sure.

With a Linux system installed, it's time to download SELinux. You can get the latest from the NSA Web site at:

http://www.nsa.gov/selinux/src-disclaim.html
Be sure you download the "2.4 LSM-based prototype stable, recommended" version. This version is based on the 2.4.xx kernel with LSM (Linux Security Module) support. There are several packages that you can download; I chose the "Everything on One Part" package. This is a single download and includes a Linux kernel already patched with the LSM code and all of the SELinux files you need for an installation. Other downloadable packages include patch files that you can apply to the normal kernel source to create an SELinux kernel.

The SELinux kit consists of the following parts:

  • A standard Linux kernel
  • The Linux Security Module (LSM) add-on
  • SELinux kernel modules
  • An extra kernel patch for SELinux, which needs to be applied
  • SELinux management programs to tag files, build the policy, etc.
  • A standard set of policy files for RBAC and TE policies
  • Some SELinux-aware programs to replace standard ones: cp, find, id, ls, mkdir, etc.

There are two ways to install SELinux, and they are both clearly documented in the distribution README file. The first and easiest is to run the command make quickinstall as root; this will perform most of the build and installation automatically. Alternatively, you can follow the step-by-step installation instructions covered in the README file.

During the installation, be sure that you enable both "NSA SELinux Support" and "NSA SELinux Development Support" in the kernel configuration under the "Security options" menu. The development support enables you to run your system in a permissive mode, which simply logs policy violations without blocking them. Once things are set up and working, you can use the avc_toggle command to enter enforcement mode, which actively blocks policy violations.

Using SELinux

At the end of the installation, you should have all of the SELinux support and management software in place. If your PATH is set properly, you should be able to run the SELinux specialized programs. For example, the SELinux ps command can show the security context of running processes:

[root@xena /]# ps -e --context
  PID   SID CONTEXT                        COMMAND
    1     7 system_u:system_r:init_t       init
    2     1 system_u:system_r:kernel_t     [keventd]
    3     1 system_u:system_r:kernel_t     [kapmd]
    4     1 system_u:system_r:kernel_t     [ksoftirqd_CPU0]
    5     1 system_u:system_r:kernel_t     [kswapd]
    6     1 system_u:system_r:kernel_t     [bdflush]
    7     1 system_u:system_r:kernel_t     [kupdated]
    8     7 system_u:system_r:init_t       [khubd]
    9     7 system_u:system_r:init_t       [kjournald]
  515   274 system_u:system_r:syslogd_t    syslogd -m 0
  520   283 system_u:system_r:klogd_t      klogd -x
  706   295 system_u:system_r:ntpd_t       /usr/sbin/ntpd -U ntp -g
  757   296 system_u:system_r:named_t      /usr/local/sbin/named -u named
  778   297 system_u:system_r:sshd_t       /usr/sbin/sshd
  910   305 system_u:system_r:gpm_t        gpm -t ps/2 -m /dev/mouse
  928   306 system_u:system_r:crond_t      crond
  982   310 system_u:system_r:xfs_t        xfs -droppriv -daemon
 1018   311 system_u:system_r:atd_t        /usr/sbin/atd
 1319   312 system_u:system_r:getty_t      /sbin/mingetty tty2
 1620   312 system_u:system_r:getty_t      /sbin/mingetty tty1
 2726   297 system_u:system_r:sshd_t       /usr/sbin/sshd
 2728   323 root:user_r:user_t             -bash
 2920   323 root:user_r:user_t             ps -e --context
[root@xena /]#
The output from ps here shows two extra columns giving the SID (Security IDentifier tag) and the associated user, role, and type for the process being displayed. Similarly, the ls command also shows the context tags for files:

[root@xena /]# ls -l --context
drwxr-xr-x  root   root     system_u:object_r:bin_t          bin
drwxr-xr-x  root   root     system_u:object_r:boot_t         boot
drwxr-xr-x  root   root     system_u:object_r:device_t       dev
drwxr-xr-x  root   root     system_u:object_r:etc_t          etc
drwxr-xr-x  root   root     system_u:object_r:file_t         initrd
drwxr-xr-x  root   root     system_u:object_r:lib_t          lib
drwx------  root   root     system_u:object_r:lost_found_t   lost+found
drwxr-xr-x  root   root     system_u:object_r:file_t         misc
drwxr-xr-x  root   root     system_u:object_r:file_t         mnt
dr-xr-xr-x  root   root     system_u:object_r:proc_t         proc
drwxr-x---  root   root     system_u:object_r:sysadm_home_dir_t root
drwxr-xr-x  root   root     system_u:object_r:sbin_t         sbin
drwxrwxrwt  root   root     system_u:object_r:tmp_t          tmp
drwxr-xr-x  root   root     system_u:object_r:usr_t          usr
drwxr-xr-x  root   root     system_u:object_r:var_t          var
[root@xena /]#
Note that system_u user is assigned to all files at installation time. For files created after installation, the user context will be assigned the user identity of the creating process. Since the notion of a role is irrelevant for files, all files are assigned the object_r role.

If you've configured your kernel with the SELinux Development Support, then your system will be running in permissive mode. In other words, instead of blocking those functions not permitted by the security policy, it will simply be logging the illegal activities and allowing them to proceed. Examining messages in /var/log/messages should show a number of these. By running the avc_toggle command, you can switch the kernel into the enforcement mode, where illegal functions will actually be blocked. Even if you're logged in as root, after entering enforcement mode, you may find you can't do some things. Consider the following example of the SELinux avc_toggle command:

[root@xena /]# id
uid=0(root) gid=0(root) groups=0(root) context=root:user_r:user_t sid=323
[root@xena /]# avc_toggle
enforcing
[root@xena /]# tail /var/log/messages
tail: /var/log/messages: Permission denied
[root@xena /]# avc_toggle
avc_toggle: Permission denied
[root@xena /]#
In this example, I'm logged in as root and I switched the kernel into enforcing mode with avc_toggle. Unfortunately, under SELinux there is no such thing as the all-powerful root user, as demonstrated when I try to display the tail of the messages file or even switch enforcement mode off. It's not very often that you see "Permission denied" on a UNIX system when logged in as root! I need to have access to the systems administrator role to be able to do this under SELinux. So I switch roles to sysadm_r using the SELinux newrole command, and things work better:

[root@xena /]# newrole -r sysadm_r
Authenticating root.
Password: <rootpassword>
[root@xena /]# tail /var/log/messages
Jan 20 10:53:22 xena kernel: avc:  denied  { avc_toggle } for  pid=12592
  exe=/usr/local/selinux/bin/avc_toggle scontext=root:user_r:user_t
  tcontext=system_u:system_r:kernel_t tclass=system
Jan 20 10:53:30 xena kernel: avc:  denied  { read } for  pid=12593
  exe=/usr/bin/tail path=/var/log/messages dev=03:01 ino=23230
  scontext=root:user_r:user_t tcontext=system_u:object_r:var_log_t tclass=file
Jan 20 10:53:35 xena kernel: avc:  denied  { avc_toggle } for  pid=12594
  exe=/usr/local/selinux/bin/avc_toggle scontext=root:user_r:user_t
  tcontext=system_u:system_r:kernel_t tclass=system
[root@xena /]# avc_toggle
permissive
[root@xena /]#
In the tail of the messages file here, you can see three denials of access. The first is the first avc_toggle that I ran while in permissive mode; it got logged and was performed anyway, switching the kernel into enforcement mode. The second is when I tried to run tail while logged in as root, and being in enforcement mode, the tail command was actively denied access to the messages file. The third was an attempt to get out of enforcement mode before I switched to the sysadm_r role. Next, I will look at how the SELinux security policy is defined and managed.

Managing the SELinux Security Policy

Defining security policies on SELinux is a large topic in itself, and there are other articles on the subject, so I will cover only the minimum topics that you will need to get things working in a simple manner.

By default, the installation will install the policy definition files into directories under /etc/security/selinux/src -- if it hasn't, then copy everything in the selinux/policy directory from the distribution package into /etc/security/selinux/src.

The "users" file contains user and role definitions. If you want your users to be able to switch roles, they should have entries in this file.

The directory "file_contexts" contains the definitions for the labeling of files on the system. In file_contexts/program, there are file context definitions for each type of program on the system. For example, file_contexts/program/ntpd.fc contains labeling definitions for ntpd programs (see Listing 1). The first column contains a regular expression to match against file names. The second column contains the context with which the matching files are to be labeled. The command make relabel will read all of the file context definitions and apply them to all local filesystems -- this can take 10 minutes or more to run.

The core of the security policy definition lies under the directory "domains". There are a number of files in the "domains/program" directory that contain m4 macro definitions for many common programs. The command make load will rebuild and reload the kernel security policy from these files.

Configuring a Policy for a BIND DNS Server

SELinux is compatible with the normal Linux kernel so that an SELinux system is capable of running any software compiled to run on Linux. To evaluate the mandatory access control features of SELinux, I installed the latest copy of the BIND DNS server on a SELinux system. Although the BIND server can be secured by running it in a chroot "jail", I'll run it as a normal service on my test system.

I downloaded the latest BIND (9.2.2rc1 in my case) and ran the usual configure and installation:

wget ftp://ftp.isc.org/isc/bind9/9.2.2rc1/bind-9.2.2rc1.tar.gz
gzip -cd <bind-9.2.2rc1.tar.gz | tar xvf -
cd bind-9.2.2rc1
./configure
make
make install
I then set up my /etc/named.conf configuration file as shown in Listing 2. I also set up the named.ca and example.zone zone files in /var/named. Note that I've got dynamic updates enabled for the example.com domain.

Next, I need to label the files that the "named" process will be using. SELinux comes with a predefined configuration for named, but this needs to be changed to the slightly different file locations that are on my system. Editing the SELinux policy file file_contexts/program/named.fc, I set up labeling for the new files as shown in Listing 3.

Note that the ordering of entries in the named.fc file is significant. The directory itself will be labeled with system_u:object_r:named_zone_t; all files within this directory will be labeled as system_u:object_r:named_conf_t, except for *.zone zone files, *.jnl journal files, and the named.pid file. I label named.pid the same as a zone file because named reads and writes to the file the same way it does for a zone or jnl file.

Next, I edit the Type Enforcement rules in domains/program/named.te. Again, SELinux comes with a suitable file, which needs little editing. Listing 4 contains an excerpt from the file covering the main areas of interest. There are basically two types of statements in a TE policy file: type statements and allow statements. Type statements simply define a context type that will be assigned to file(s). Allow statements define which operations are allowed; these are of the form:

allow <sources> <targets>:object_classes permissions;
For example, from named.te, we see the line:

allow named_t resolv_conf_t:file { getattr read };
This entry allows the named process to perform reads and get attributes on files tagged with resolv_conf_t. Other entries in the file are m4 macros. For example:

rw_dir_create_file(named_t, named_zone_t)
is a macro to allow processes running in the named_t context (the named process) to read, write, and create files in directories labeled with named_zone_t.

Once the file contexts and policy have been defined for named, we can label the files and load the policy. To label the files, simply run make relabel from the policy directory. Then check that the files are labeled correctly with the ls --context command. Once the files have been labeled, the policy can be loaded by running make load from the policy directory. This collects all of the m4 TE files together into the policy.conf file and compiles the policy into the running SELinux kernel. The full policy is defined in the policy.conf file, which is a huge 150,000 lines on my system. This shows how big a job it can be to define a mandatory security policy even on a relatively simple system.

A useful utility for writing policy rules is the newrules.pl Perl program, which is in the scripts directory of the SELinux release. By processing "denied" lines from the messages file, newrules.pl can generate rules to allow those functions, which were denied. For example, if we take the three denied lines from the messages file listed previously, the following rules will be generated:

[root@xena selinux]# tail /var/log/messages | ./scripts/newrules.pl
allow user_t kernel_t:system { avc_toggle };
allow user_t var_log_t:file { read };
[root@xena selinux]#
Of course, the output from newrules.pl should be checked to be sure you're not permitting too much access when you add the rules to your policy.

With a policy configured for the named process, we can now start it up and monitor its progress with the system in permissive mode. Security violations will be logged into syslog (in the /var/log/messages file), and we can use these logs to get our policy correct before switching the system into enforcement mode.

Summary

SELinux is a practical implementation of mandatory access control being applied to a real-world operating system. By deploying SELinux, it is possible to create systems with a very strong level of security, systems that can even resist being attacked through vulnerabilities in programs running at the highest levels of system privilege.

In this article, I've given a quick overview of what SELinux is, how it works, and how to manage and configure it. A large amount of additional information is available on the Internet, and there is an active community continuing to develop SELinux. For example, there are plans to release an SELinux-specific Linux distribution and to implement more advanced policy management systems based on XML. And finally, does the NSA itself use SELinux? Not surprisingly, the NSA does not comment on such operational issues.

Kerry Thompson is a CISSP-certified security consultant with more than 12 years experience in systems administration and security. He is based in Auckland, New Zealand and can be contacted at: kerry@crypt.gen.nz.