Introduction
Similar to any other software development, having a well-functioning development environment is essential to successfully create and manage SELinux policies. Such an environment should not only support version control, but also be able to quickly search through the sources or show definitions.
With SELinux, this means that the policy module sources (which are all readable text files) should be stored in a structured manner, the upstream project that provides SELinux policies should be readily accessible, and the necessary functions or scripts to query and search through the policies should be available.
Adventurous users might want to take a look at the SELinux Policy IDE (SLIDE) as offered by Tresys Technology (http://oss.tresys.com/projects/slide). In this book, we do not focus on this IDE; instead, we use whatever file editor the user wants (such as VIM, Emacs, or Kate) and enhance the environment through the necessary shell functions and commands.
Before we cover the setup of the development environment, let's do a quick recapitulation of what SELinux is.
About SELinux
The Security Enhanced Linux (SELinux) project is the result of projects initiated and supported by the US National Security Agency (NSA) and came to life in December 2000. It is the implementation of a security system architecture with a flexible, policy-driven configuration approach. This architecture is called the Flux Advanced Security Kernel (Flask), and its related resources are still an important read for everyone involved with SELinux.
Most papers are linked through the Flask website at http://www.cs.utah.edu/flux/fluke/html/flask.html. The following are some examples of these papers:
- The paper called The Inevitability of Failure: The Flawed Assumption of Security in Modern Computing Environments is still a very topical paper on why mandatory access controls are needed in operating systems
- The NSA publication, Implementing SELinux as a Linux Security Module, available at http://www.nsa.gov/research/_files/publications/implementing_selinux.pdf, goes deeper into how SELinux is implemented
Nowadays, SELinux can be best seen as an additional layer of security services on top of the existing Linux operating system. It is part of the mainstream Linux kernel and uses the Linux Security Modules (LSM) interfaces to hook into the interaction between processes (user space) and resources. It manages various access services (such as the reading of files, getting directory attributes, binding to domain sockets, connecting to TCP sockets, and gaining additional capabilities) through a powerful approach called type enforcement.
The following diagram displays the high-level functional position of the SELinux subsystem. Whenever a subject (in the drawing, this is the Application) wants to perform an action against a resource, this action is first checked by the Discretionary Access Control mechanism that the Linux kernel provides. After the action is checked and allowed by the DAC mechanism, the LSM implementation (against which SELinux is registered) calls the hooks that the SELinux subsystem has provided. SELinux then checks the policy (through the cache, and if the access is not registered in the cache yet, it checks in the entire policy) and returns whether the access should be allowed or not.
SELinux is a Mandatory Access Control system in which the governed activities on the system are defined in rules that are documented through a policy. This policy is applicable to all processes of the system and enforced through the SELinux subsystem, which is part of the Linux kernel. Anything that is not allowed by the policy will not be allowed at all—security is not left at the discretion of the user or correctness of the application. Unlike Linux DAC restrictions, enforcement itself (the SELinux code) is separate from the rules (the SELinux policy). The rules document what should be considered as acceptable behavior on the system. Actions that do not fit the policy will be denied by the SELinux subsystem.
In SELinux, a set of access control mechanisms are supported. The most visible one is its type enforcement in which privileges of a subject (be it the kernel or a Linux process) towards an object (such as a file, device, system capability, or security control) are granted based on the current security context of that subject. This security context is most often represented through a readable string such as staff_u:staff_r:staff_t:s0:c0,c3
. This string represents the SELinux user (staff_u
), SELinux role (staff_r
), SELinux type/domain (staff_t
), and optionally, the SELinux sensitivity level or security clearance, which provides both the sensitivity (s0
) as well as assigned categories (c0,c3
).
Alongside type enforcement, SELinux has several other features as well. For instance, it provides a role-based access control system by assigning valid domains (which are SELinux types assigned to running processes) to roles. If a role is not granted a particular domain, then that role cannot execute tasks or applications associated with that domain. SELinux also supports user-based access controls, thus limiting information flow and governing data sharing between SELinux users.
Another stronghold within SELinux is its support for sensitivities (which SELinux displays as integers, but these integers can very well be interpreted as public, internal, confidential, and so on) as well as access categories. Through the constraints that SELinux can impose in its policy, systems can be made to largely abide by the Bell-LaPadula model (https://en.wikipedia.org/wiki/Bell-LaPadula_model). This model supports information flow restrictions such as no read up (lower sensitivities cannot read information from higher sensitivities) and no write down (higher sensitivities cannot leak information to lower sensitivities).
The role of the SELinux policy
The SELinux policy itself is a representation of what the security administrator (the role that is usually mentioned as being the owner of what is and isn't allowed on a system) finds acceptable, expected, and normalized behavior:
- Acceptable: Application and user behavior will be acceptable because it will be allowed on the system by the policy
- Expected: Application and user behavior will be expected as the policy usually doesn't (or shouldn't) contain access vectors (a permission assigned to a subject towards a particular object) that are not applicable to the system, even if it would be acceptable on other systems in the environment
- Normalized: Application and user behavior will be normalized not in the sense of database normalization, but as in normality—something that is consistent with the most common behavior of the process
As a policy represents these behaviors, correct tuning and development of the policy is extremely important. This is why the SELinux Cookbook will focus on policy development and policy principles.
A policy that is too restrictive will cause applications to malfunction, often in ways that its users will find unexpected. It will not be surprising to the security administrator of course, as he knows that the policy dictates what is allowed, and he is (or at least should be) perfectly aware of what the policy says.
However, a policy that is too broad will not result in such behavior. On the contrary, everything will work as expected. Sadly, when nonstandard or abnormal behavior is triggered, the (too) broadly defined policy might still allow this nonstandard or abnormal behavior to happen. When this abnormal behavior is an exploited vulnerability, then SELinux—as powerful as it can be—has nothing to deter the exploit, as the policy itself has granted the access. Another example of this principle would be a network firewall, whose policy can be too open as well.
Through the packaged approach that policies provide (SELinux policies are like loadable kernel modules, but then for the SELinux subsystem), administrators can push the policies to one or more systems, usually through the package management system or centralized configuration management system of choice. Unlike Linux DAC controls, which need to be applied on the files themselves, SELinux policies are much easier to handle and are even versionable—a trait much appreciated by administrators in larger environments.
The example
Throughout this book, we will often come across settings that are optional or whose value is heavily dependent on the choices made by the system administrator. In order to not repeat documenting and explaining when a setting or value is configurable, we will use the following configuration settings:
- The SELinux type (which is configured in
/etc/selinux/config
) will be MCS in this book. It uses an MLS-enabled, single-sensitivity policy definition. This means that contexts will always have a sensitivity level or security clearance assigned when displayed, and the location of the SELinux policy configuration will always be shown in/etc/selinux/mcs/
. Make sure to substitute this path with your own if the policy store on your environment is named differently. For instance, a Red Hat or Fedora installation defaults to/etc/selinux/targeted/
. - Operations will be documented as they run through restricted users, which are aptly named
user
(for an unprivileged end user assigned theuser_r
role),staff
(for a user that might perform administrative tasks and is assigned thestaff_r
andsysadm_r
roles), androot
(which is mapped to thesysadm_r
role). Distributions might have users associated with theunconfined_r
role. Whenever a step can be skipped for unconfined users, it will be explicitly mentioned.