Cover V14, i08

Article

aug2005.tar

Tuning Your SELinux Policy with Audit2allow

Kevin Fenzi

Fedora Core 3 Linux has been shipping with Security Enhanced Linux (SELinux) enabled by default for about six months now. SELinux allows privileges to be separated much more finely than the typical approach of having users and groups and the all-powerful root "superuser". The default SELinux configuration is fine for some uses, but the SELinux configuration files make sendmail.cf look easy. In this article, I will show you step-by-step how to tune your SELinux policy to your specific needs using the audit2allow tool.

What Is SELinux?

SELinux is a kernel patch (which was merged into the main kernel.org kernel in the 2.6.0-test series) that provides the hooks needed to detect, log, and enforce Mandatory Access Controls on processes. The rules that control what is allowed and disallowed constitute a "policy". This policy includes rules specifying which things are managed under the SELinux framework.

The traditional permissions model consists of users and groups and Unix file permissions. Using this model, you can restrict which users and groups of users can read, write, and execute files. SELinux provides a richer set of permissions with users, roles, and types.

For example, under a traditional permissions model, you must give root (superuser) access to processes that wish to listen on privileged (less than 1024) ports. Once these processes have that access, they can perform any of the actions that the root user can perform. Under a SELinux model, you can grant the specific server permission to open its specific port and nothing else.

Which Linux Versions Provide SELinux Support?

At least the following distributions now include support for SELinux:

  • Fedora Core 2
  • Fedora Core 3
  • Red Hat Enterprise Linux 4
  • CentOS 4
  • Debian unstable (kernel support)
  • Hardened Gentoo project
  • SUSE 9.x (kernel support)
  • Ubuntu

There may well be additional distributions/variants that contain SELinux support. Consult your system documentation.

Why SELinux?

Using SELinux can enhance the security of your Linux server. Being able to specify fine-grained controls on processes prevents vulnerable software from being exploited in many cases. For example, suppose you have a generic daemon process. It needs to be able to listen on a specfic (privileged) tcp port, process requests by saving the input to a file, read a configuration file, then parse the input file and return a result to the socket.

Under the traditional permissions model, the daemon must be started with root permissions to open the tcp port and listen at it (although it can drop those privileges after the socket is set up). It typically would then run as a user created only to run the daemon. It would have a directory owned by the user, where it could read or write any files, and a parsing process running as that user. If there were issues with the setup as root, the process could do anything the root user could do. If there were problems with the read/write code, the process might be able to leak information to the Internet about other files the user could read, or the process might be tricked into overwriting files from other processes.

If there were problems with the parsing process, the process might be made to run a shell as the process user (allowing remote access to the server) or run some other command that could be used to compromise the server.

Now let's consider the same example under a SELinux model. You would set up a policy for your generic daemon, which would be allowed to open the specific tcp port needed and no others. It would never need to run as root. It could only read and write the specific files of the type allowed. The parsing process could only read and write files of its specific type and could never execute anything like a shell command. The server and parser could never read or write files in /tmp or the like.

Thus, the exposure caused by a programming error or configuration problem is reduced when using a SELinux model. Of course, even with SELinux enabled, you should stay current with system updates and other good security practices. SELinux is just one of many things that can be done to help secure a system.

Is It Safe?

SELinux has been merged into the main Linux kernel and has been reviewed by many people. The source is released under the GPL, and all source code is available for review. Some users have expressed concern that the NSA may have done this as a way to provide backdoors into systems. Thus far, no backdoors have been found.

Files, Processes, and Users

SELinux needs to associate a "security context" with files and processes. It does this with files by writing an extended filesystem attribute to the filesystem. This means that your filesystem must support extended attributes. In the Fedora/Red Hat distributions, the ls command has a new option -- -Z to show the security context of a file. For example:

  ls -lZ /etc/selinux

-rw-r--r--  root    root   system_u:object_r:selinux_config_t config
drwxr-xr-x  root    root   system_u:object_r:selinux_config_t targeted
Here we see a file and a directory in /etc/selinux. Their security context is owned by the "system_u" user, the "object_r" role, and they are type "selinux_config_t". By checking this user/role/type against the current SELinux policy, the system determines what is allowed or denied.

Processes are also assigned a security context when they are started, which you can see using the -Z flag to the ps command. In process rules, you can specify, for example, that the squid Web proxy daemon can open a specific port but no others:

root@example# ps axZ | grep squid
user_u:system_r:squid_t          3912 ?        Ss     0:00 squid -D
user_u:system_r:squid_t          3915 ?        S      9:10 (squid) -D
user_u:system_r:squid_t          3916 ?        Ss     0:01 (unlinkd)
In this example, we can see the security context of the squid processes. They are user type "user_u", role "system_r", and their type is "squid_t".

Users are assigned a "role", which defines what that user can do. With a role, you can define things like whether a user is allowed to run the ping command or execute a Web server startup. For example, the id command for root on a targeted SELinux system would be:

root@example# id
uid=0(root)
gid=0(root)groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel)
context=root:system_r:unconfined_t
Here we see that the root user has a context of "system_r" and a type of "unconfined_t".

Policies

Red Hat and Fedora distributions use a /etc/sysconfig/selinux file to control whether SELinux is enabled and the mode in which it is started. Other distributions may use a different configuration.

The /etc/sysconfig/selinux file from Fedora Core 3:

# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#       enforcing - SELinux security policy is enforced.
#       permissive - SELinux prints warnings instead of enforcing.
#       disabled - SELinux is fully disabled.
SELINUX=enforcing
# SELINUXTYPE= type of policy in use. Possible values are:
#       targeted - Only targeted network daemons are protected.
#       strict - Full SELinux protection.
SELINUXTYPE=targeted
The "SELINUX" setting is pretty simple. It controls whether SELinux is enforcing policy decisions, is simply logging what it would have done if it had enforced the policy, or is disabled. In this example, the "enforcing" state is set, so the policy decisions will be enforced. In other words, processes will be denied if the policy denies them some action.

The "SELINUXTYPE" setting selects which policy is active. Fedora Core 3 offers two choices of policies. The "targeted" policy has policy decisions only on some key processes and allows anything not listed as outside the scope of the policy. The "strict" policy has policy setup for many more processes and will deny anything not explicitly allowed. For servers running few services or services that are not changed much from defaults, the "strict" policy might be an option, otherwise the targeted policy provides at least some of the benefits of SELinux. In this example, the "targeted" mode is enabled, meaning that those processes enumerated in the policy will be checked by SELinux, and those not listed will be allowed.

You can change from "enforcing" to "permissive" mode via the setenforce command; use "setenforce 0" for permissive and "setenforce 1" for enforcing mode. You can also pass a "selinux=0" in the kernel boot command to totally disable SELinux on boot.

To make changes to your policy, you must load the policy sources packages. In Fedora Core 3, for example, those packages are "selinux-policy-targeted-sources" and "selinux-policy-strict-sources". These packages can be installed with the command:

yum install selinux-policy-targeted-sources selinux-policy-strict-sources
Logging

SELinux logs to the system log when it denies (and in some cases when it allows) an action on the part of a process. You can use these logs to modify your policy to allow some action that the default policy is denying.

Here is an SELinux log entry (which is logged by default to /var/log/messages):

kernel: audit(1114070701.193:0): avc:  denied  { read } for  pid=24216
exe=/usr/libexec/mysqld name=mysql dev=cciss/c0d0p6 ino=16408
scontext=user_u:system_r:mysqld_t tcontext=root:object_r:var_lib_t
tclass=dir
This tells us the following information:

  • A "read" request was denied.
  • The process trying the read had a process ID of 24216.
  • The process was /usr/libexec/mysqld.
  • The target of the action was running from device /dev/cciss/c0d0p6.
  • The inode of the target of the action was 16408.
  • The SELinux context for the process was that of a user, mysqld type.
  • The file it was trying to read was a root-owned file of var_lib_t type.

Let's take a closer look at the fields in a SELinux log entry:

audit(timestamp) -- This field states that it's an audit message from SELinux and that it was logged at timestamp time (in seconds since Jan. 1st, 1970).

avc -- This message was from the SELinux access vector cache. Pretty much every message you are likely to see is from this cache.

denied | accepted -- This field indicates whether the action was denied or accepted. You may see logs of accepted messages in some cases (like reloading the policy).

{ read | write | unlink | ... } -- This field shows the type of action that was attempted, such as reading a file, writing, unlinking, loading policy, etc.

for pid=<pid> -- This is the process ID that attempted the action.

exe=<executable> -- This is the path to the executable that started the process.

name=<name> -- This is the name of the target on which the action was attempted.

dev=<device> -- This is the device on which the target file is located.

ino=<inode-number> -- This is the inode of the target of the action.

scontext=<security context> -- This is the process's security context. This contains user, role, and type.

tcontext=<target context> -- This is the security context of the target of this action, for example, the file, directory, etc.

tclass=<target class> -- This is the class of the target object, such as directory, file, device node, or something else.

Here is another example:

kernel: audit(1114800386.039:0): avc:  granted  { load_policy } for
pid=12589 exe=/usr/sbin/load_policy
scontext=root:system_r:unconfined_t
tcontext=system_u:object_r:security_t tclass=security
In this example, we can see a SELinux log message showing that the policy was reloaded by root (probably from a make load command). Note that it shows the message as "granted". Here's another example log message:

kernel: audit(1114800360.543:0): avc:  denied  { unlink } for
pid=12253 exe=/usr/sbin/named name=example.com dev=hda3 ino=505301
scontext=root:system_r:named_t tcontext=root:object_r:named_zone_t
tclass=file
Here we can see that SELinux denied named unlinking for a file called "example.com". It was on a /dev/hda3 device and is inode 505301.

Audit2allow

So, in the event that we want mysqld to be able to read the file it's trying to read, how do we add to the policy? There is a wonderful tool called "audit2allow" that will take an audit message as above and convert it into an allowed policy line.

Audit2allow is contained in the policycoreutils package in Fedora or Red Hat-based systems. This package must be installed before you can do the following steps in the article. You can install this package using "yum install policycoreutils".

So, if we echo the above line into the audit2allow utility, it prints:

allow mysqld_t var_lib_t:dir read;
This policy line says to allow any process with the "mysqld_t" type to read directories of the "var_lib_t" type.

Now that we have a policy line, we need to load it into the Linux kernel so it knows to allow that action. SELinux policy sources are located under /etc/selinux/<policytype>/src/policy. For example, if we are using the "targeted" policy, the policy source would be located in /etc/selinux/targeted/src/policy/.

Under that directory, you can see various parts of the policy, along with a Makefile. For local policy additions, edit /etc/selinux/targeted/src/policy/domains/misc/local.te and add the line that audit2allow gave us above. Then, run make load to compile, check, and load the policy into your kernel.

You should see a line in your system logs similar to the following:

  Apr 21 14:34:29 linuxmachine kernel: audit(1114115669.205:0): avc:
granted  { load_policy } for  pid=7648 exe=/usr/sbin/load_policy
scontext=root:system_r:unconfined_t
tcontext=system_u:object_r:security_t tclass=security
  Apr 21 14:34:29 linuxmachine kernel: security:  3 users, 4 roles,
320 types, 23 bools
  Apr 21 14:34:29 linuxmachine kernel: security:  53 classes, 10952 rules
Other Audit2allow Tricks

Audit2allow has several other nice options. If you run audit2allow as audit2allow -l -i /var/log/messages, it will look for the last time your policy was reloaded and only look at messages since that time. With audit2allow -d, the messages will be read from your Linux kernel dmesg buffer.

If you have a complex application or configuration, you could run in "permissive" SELinux mode and gather all the avc messages and run audit2allow on them, or run in enforcing mode and check the output of audit2allow -l after each run.

Upgraded Policies

When updates come out for your policy, you should check your changes against the update to see whether adjustments are needed. Sometimes that will result in a removal of a custom rule that has been merged into the main policy.

If you have made your changes to the local.te file only, you can check whether they are in your new policy by grep'ing the policy.conf file just before your make load.

Cautionary Notes

Be careful to add rules only for those items you specifically need. Blindly adding rules to allow anything that you see as denied could result in allowing something that an attacker could exploit down the road. As policies are further tuned for typical uses, less customization will be needed.

Summary

Audit2allow is a useful tool for tuning your SELinux policy to fit your specific needs. You can use it to generate new policy lines from denied policy log messages without having to write new policy lines.

References

Home of the SELinux project -- http://www.nsa.gov/selinux/

The Un-Official SELinux FAQ -- http://www.crypt.gen.nz/selinux/faq.html

SELinux link zoo -- http://www.crypt.gen.nz/selinux/links.html

Ubuntu Linux SELinux pages -- https://www.ubuntulinux.org/wiki/SELinux

Kevin Fenzi, co-author of the Linux Security HOWTO, is a senior member of tummy.com's team. He has been working as a Unix and Linux Systems Administrator for more than 15 years. His passion is security and all of the steps needed to ensure that systems are kept safe.