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:
Distribution |
Policy store name |
MLS? |
deny_unknown |
Unconfined domains? |
UBAC? |
---|---|---|---|---|---|
Gentoo |
|
No |
|
No |
Yes (configurable) |
Fedora 19 |
|
Yes (only |
|
Yes, but limited |
No |
Gentoo |
|
No |
|
Yes |
Yes (configurable) |
Fedora 19 |
|
Yes (only |
|
Yes |
No |
Gentoo |
|
Yes (only |
|
Yes (configurable) |
Yes (configurable) |
Gentoo |
|
Yes |
|
Yes (configurable) |
Yes (configurable) |
Fedora 19 |
|
Yes |
|
Yes |
No |
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.
Version |
Linux kernel |
Description |
---|---|---|
12 |
It is the "Old API" for SELinux, now deprecated | |
15 |
2.6.0 |
It is the "New API" for SELinux |
16 |
2.6.5 |
It provides conditional policy extensions |
17 |
2.6.6 |
It provides IPv6 support |
18 |
2.6.8 |
It adds fine-grained netlink socket support |
19 |
2.6.12 |
It provides enhanced multi-level security |
20 |
2.6.14 |
It doesn't access vector table size optimizations”, the version (20) improved the access vector table size (it is a performance optimization). |
21 |
2.6.19 |
It provides object classes in range transitions |
22 |
2.6.25 |
It provides policy capabilities (features) |
23 |
2.6.26 |
It provides per-domain permissive mode |
24 |
2.6.28 |
It provides explicit hierarchy (type bounds) |
25 |
2.6.39 |
It provides filename based transition support |
26 |
3.0 |
It provides role transition support for non-process classes |
27 |
3.5 |
It supports flexible inheritance of user and role for newly created objects |
28 |
3.5 |
It supports flexible inheritance of type for newly created objects |
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).