Policies – the ultimate dictators

Enabling SELinux does not automatically start enforcement of access, if SELinux is enabled and it cannot find a policy, it will refuse to start. That is because the policy defines the behavior of the system (what should SELinux allow). Because SELinux is extremely flexible, its policy developers already started differentiating one policy implementation from another through what it calls a policy type or policy store.

A policy store contains a single policy, and only a single policy can be active on a system at any point in time. Administrators can switch a policy, although this requires the system to be rebooted, and might even require relabeling the entire system (relabeling is the act of resetting the contexts on all files and resources available on that system). The active policy on the system can be queried using sestatus (SELinux status) as follows:

# sestatus | grep "Loaded policy"
Loaded policy name: targeted

In the preceding example, the currently loaded policy is named targeted. The policy name that SELinux will use upon its next reboot is defined in the /etc/selinux/config configuration file as the SELINUXTYPE parameter.

Most SELinux supporting distributions base their policy on the reference policy [http://oss.tresys.com/projects/refpolicy/], a fully functional SELinux policy set managed as a free software project. This allows distributions to ship with a functional policy set rather than having to write one themselves. Many project contributors are distribution developers, trying to push changes of their distribution to the reference policy project itself, where the changes are peer-reviewed to make sure no rules are brought into the project that might jeopardize the security of any platform.

SELinux policy store names and options

The most common SELinux policy store names are strict, targeted, mcs, and mls. None of the names assigned to policy stores are fixed though, so it is a matter of convention. Hence, it is recommended to consult the distribution documentation to verify what should be the proper name of the policy. Still, the name often gives some information about the options that are enabled on the system.

MLS status

One of the options is MLS support that can either be enabled or disabled. If disabled, then the SELinux context will not have a fourth field with sensitivity information in it, making the contexts of processes and files look as follows:

staff_u:sysadm_r:sysadm_t

To check if MLS is enabled, it is sufficient to see if the context indeed doesn't contains such a fourth field, but it can also be acquired from the Policy MLS status line in the output of sestatus:

# sestatus | grep MLS
Policy MLS Status: disabled

Another method would be to look into the pseudo file, /sys/fs/selinux/mls. a value of 0 means disabled, whereas a value of 1 means enabled:

# cat /sys/fs/selinux/mls
0
Dealing with unknown permissions

Permissions (such as read, open, and lock) are defined both in the Linux kernel and in the policy itself. However, sometimes newer Linux kernels support permissions that the current policy doesn't.

A recently introduced one is block_suspend (to be able to block system suspension) and when that occurs, SELinux has to take the decision: as the policies are not aware of this new permission, how should it deal with the permission when triggered? SELinux can allow (assume everything is allowed to perform this action), deny (assume no one is allowed to perform this action), or reject (stop loading the policy at all and halt the system) the request. This is configured through the deny_unknown value.

To see the state for unknown permissions, look for the Policy deny_unknown status line in sestatus:

# sestatus | grep deny_unknown
Policy deny_unknown status: denied

Administrators can set this for themselves in the /etc/selinux/semanage.conf file through the handle-unknown key (with allow, deny, or reject).

Supporting unconfined domains

A SELinux policy can be written as very strict, limiting applications as close as possible to their actual behavior, but it can also be written to be very liberal in what applications are allowed to do. One of the concepts available in many SELinux policies is the idea of unconfined domains. When enabled, it means that certain SELinux domains (process contexts) are allowed to do almost anything they want (of course within the boundaries of the regular Linux DAC permissions which still hold) and only a few selected are truly confined (restricted) in their actions.

Unconfined domains have been brought forward to allow SELinux to be active on desktops and servers where administrators do not want to fully restrict the entire system, but only a few of the applications running on it.

With other MAC systems, for example, AppArmor, this is inherently part of the design of the system. However, SELinux was designed to be a full mandatory access control system and thus needs to provide access control rules even for those applications that shouldn't need any. By marking these applications as unconfined, almost no additional restrictions are imposed by SELinux.

We can see if unconfined domains are enabled on the system through seinfo:

# seinfo -tunconfined_t
 unconfined_t
# seinfo -tunconfined_t
ERROR: could not find datum for type unconfined_t

Most distributions that enable unconfined domains call their policy targeted, but this is just a convention that is not always followed. Hence, it is always best to consult the policy using seinfo to make this sure.

User-based access control

When UBAC is enabled, certain SELinux types will be protected by additional constraints. This will ensure that one SELinux user cannot access files (or other specific resources) of another user. UBAC gives some additional control on information flow between resources, but is far from perfect. In its essence, it is made to isolate SELinux users from one another.

Many Linux distributions disable UBAC. Gentoo allows users to select if they want UBAC or not through a Gentoo USE flag (which is enabled by default).

Policies across distributions

As mentioned, policy store names are not standardized. What is called targeted in Fedora is not targeted in Gentoo. Of the options mentioned previously, the following table shows us how some of the policy stores are implemented across these two distributions:

Other distributions might even have different names and implementation details.

Yet, besides the naming differences, many of the underlying settings can be changed by the administrator. For instance, even though Fedora does not have a strict policy, it does have a documented approach on running Fedora without unconfined domains. It would be wrong to state that Fedora as such does not support fully confined systems.

MCS versus MLS

In the feature table, we notice that for MLS, some policies only support a single sensitivity (s0). When this is the case, we call the policy an MCS (Multi Category Security) policy. The MCS policy enables sensitivity labels, but only with a single sensitivity while providing support for multiple categories (and hence the name).

With the continuous improvement made in supporting Linux in PaaS (Platform as a Service) environments, implementing proper multitenancy requires proper security isolation as a vital part of its offering.

Policy binaries

While checking the output of sestatus, we see that there is also a notation of policy versions:

# sestatus | grep version
Max kernel policy version: 28

As the output states, 28 is the highest policy version the kernel supports. The policy version refers to the supported features inside the SELinux policy: every time a new feature is added to SELinux, the version number is increased. The policy file itself (which contains all the SELinux rules loaded at boot time by the system) can be found in /etc/selinux/targeted/policy (where targeted refers to the policy store used, so if the system uses a policy store named strict, then the path would be /etc/selinux/strict/policy).

If multiple policy files exist, we can use the output of seinfo to find out which policy file is used:

# seinfo
Statistics for policy file: /etc/selinux/targeted/policy/policy.27
Policy Version & Type: v.27 (binary, mls)
...

The next table gives the current list of policy feature enhancements and the Linux kernel version in which that feature is introduced. Many of the features are only of concern to the policy developers, but knowing the evolution of the features gives us a good idea on the evolution of SELinux.

By default, when a SELinux policy is built, the highest supported version as defined by the Linux kernel and libsepol (the library responsible for building the SELinux policy binary) is used. Administrators can force a version to be lower using the policy-version parameter in /etc/selinux/semanage.conf.

SELinux policy modules

Initially, SELinux used a single, monolithic policy approach: all possible access control rules are maintained in a single, binary policy file that the SELinux utilities then load. It quickly became clear that this is not manageable in long term, and thus the idea of developing a modular policy approach was born.

Within the modular approach, policy developers can write isolated policy sets for a particular application (or set of applications), roles, and so on. These policies then get built and distributed in their own policy modules. Platforms that need access controls for that particular application load the SELinux policy module that defines the access rules.

On some Linux distributions, these SELinux policy modules are stored inside /usr/share/selinux, usually within a subdirectory named after the policy store (such as "targeted" or "strict"). The policy modules that are currently loaded are always available in /etc/selinux/targeted/modules/active and its modules subdirectory.

Of all the *.pp files in these locations, the base.pp one is special. This policy module file contains core policy definitions. The remaining policy module files are "isolated" policy modules, providing the necessary rules for the system to handle applications and roles related to the module itself. For instance, the screen.pp module contains the SELinux policy rules for the GNU screen (and also tmux) application.

Once those files are placed on the system, the distribution package manager usually calls the semodule command to load the policy modules. This command then combines the files into a single policy file (for example, policy.28) and loads it in memory.

On Fedora, the SELinux policies are provided by the selinux-policy-targeted (or -minimum or -mls) package. On Gentoo, they are provided by the various sec-policy/selinux-* packages (Gentoo uses separate packages for each module, reducing the amount of SELinux policies that are loaded on an average system).