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.
|