Everything gets a label
In natural language, the term "context" can be used in a sentence for example, "it depends on the context". Well, with SELinux, it does depend on the context! The context of a process is what identifies the process to SELinux. SELinux has no notion of Linux process ownership and frankly doesn't care how the process is called, which process ID it has and under which account the process runs. All it wants to know is what the context is of that process. Let's look at an example context: the context of the current user (try it out yourself if you are on a SELinux enabled system):
$ id -Z unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
The id
command, which returns information about the current user, with the -Z
switch (a commonly agreed switch for displaying SELinux information) shows us the context of the current user (actually the context of the id process itself when it was executing). As we can see, the context is a string representation, and looks like it has five fields (it doesn't, it has four fields. The last field just happens to contain a ':').
SELinux developers decided to use contexts (strings) instead of real process metadata as well as contexts on resources (often called labels) for its access controls. This is different to MAC systems such as AppArmor which use the path of the binary (and thus the process name) and the paths of the resources to handle permission checks. The decision to make SELinux a label-based mandatory access control was taken for various reasons, which are as follows:
- Using paths might be easier for administrators, but this doesn't allow to keep the context information close to the resource. If a file or directory is moved, remounted, or a process has a different namespace view on the files, the access controls might behave differently. With contexts, this information is retained and the system keeps controlling the resource properly.
- Contexts reveal the context of the process very well. The same binary application can be launched in different contexts depending on how it got started. The context value (for example, the one shown in the
id -Z
output earlier on) is exactly what the administrator needs. With it, he knows what the rights are of each of the running instances, but he can also deduce from it how the process might have been launched. - Contexts also make abstraction of the object itself. We are talking now about processes and files, but this is also applicable to less tangible resources, for example: pipes (inter-process communication) or database objects. Path-based identification only works as long as you can write a path.
As an example, consider the following two sentences:
- Allow the
httpd
processes to bind to the TCP port 80 - Allow the processes labeled with "
httpd_t
" to bind to TCP ports labeled with "http_port_t
"
In the first example, we cannot easily reuse this policy when the web server process isn't using the httpd
binary (perhaps because it was renamed, or it isn't Apache but another web server), or when we want to have HTTP access on a different port. With the labeled approach, the binary can be called "apache2
" or "MyWebServer.py
"; as long as the process is labeled httpd_t
then the policy applies. The same with the port definition, you can label port 8080 with http_port_t
and thus allow the web servers to bind to that port as well.
The context fields
To come to a context, SELinux uses at least three, and sometimes four values. Let us look at the context of the Apache web server as an example:
$ ps -eZ | grep httpd system_u:system_r:httpd_t:s0 511 ? 00:00:00 httpd
As we can see, the process is assigned a context which is made up of the following fields:
system_u
- represents the SELinux usersystem_r
- represents the SELinux rolehttpd_t
- represents the SELinux type (also known as domain in case of a process)s0
- represents the sensitivity
The roles can be depicted as follows:
When we work with SELinux, contexts are all that we need. In the majority of cases, it is the third field (called the domain or type) that is most important as the majority of SELinux policy rules (over 99 percent) consists of rules related to the interaction between two types (without mentioning roles, users, or sensitivities).
SELinux types
As mentioned, SELinux is a label-based access control mechanism. In most SELinux literature, this is fine-tuned to say that SELinux is a type enforcement mandatory access control system. This is because the type of a process (called the domain) defines the fine-grained access controls of that process with respect to itself or other types (which can be processes, files, sockets, network interfaces, and more). When some access attempts are denied, the fine-grained access controls on the type level are most likely to blame.
With type enforcement, SELinux is able to control what an application is allowed to do based on how it got executed in the first place: a web server that is launched interactively by a user will run with a different type than a web server executed through the init
system, even though the process binary and path are the same. The web server launched from the init
system is most likely trusted (and thus allowed to do whatever web servers are supposed to do), whereas a user launched web server is less likely to be of "normal behavior" and as such will have different privileges.
For instance, look at the following dbus-daemon
processes:
# ps -eZ | grep dbus-daemon system_u:system_r:system_dbusd_t 4531 ? 00:00:00 dbus-daemon staff_u:staff_r:staff_dbusd_t 5266 ? 00:00:00 dbus-daemon
In the preceding example, one dbus-daemon
process is the system D-Bus daemon
running with the aptly named system_dbusd_t
type, whereas another one is running with the staff_dbusd_t
type assigned to it. Even though their binaries are completely the same, they both serve a different purpose on the system and as such have a different type assigned. SELinux then uses this type to govern the actions allowed by the process towards other types, including how system_dbusd_t
can interact with staff_dbusd_t
.
SELinux types are by convention suffixed with "_t
", although this is not mandatory.
SELinux roles
SELinux roles - the second part of a SELinux context, enable SELinux to support role-based access controls. Although type enforcement is the most used (and known) part of SELinux, role-based access control is vital in order to keep a system secure, especially from malicious user attempts. SELinux roles are used to define which types a user processes can be in. As such, SELinux roles help define what a user can and cannot do.
On most SELinux enabled systems, the following roles are made available to be assigned to users. By convention, SELinux roles are defined with an "_r
" suffix. Some of the roles and their descriptions are as follows:
Other roles might be supported as well, such as guest_r
and xguest_r
(Fedora). It is wise to consult the distribution documentation for more information about the supported roles.
SELinux users
A SELinux user is different from a Linux user. Unlike the Linux user information which can change while the user is working on the system (through tools such as sudo
or su
), the SELinux policy will enforce that the SELinux user remains the same even when the Linux user itself has changed. Because of the immutable state of the SELinux user, specific access controls can be implemented to ensure that users cannot work around the (limited) set of permissions granted to them, even when they get privileged access. An example of such an access control is the UBAC (User Based Access Control) feature that some Linux distributions (optionally) enable, not allowing access to files of different SELinux users.
But the most important feature of SELinux users is that SELinux user definitions restrict which roles the (Linux) user is allowed to be in. Once a user is assigned a SELinux user, he cannot switch to a role that he isn't meant to be in. This is the role-based access control implementation of SELinux.
SELinux users are, by convention, defined with a "_u
" suffix, although this is not mandatory. The SELinux users that most distributions have available are named after the role they represent, but instead of ending with "_r
" they end with "_u
". For instance, for the sysadm_r
role, there is a sysadm_u
SELinux user.
Sensitivity labels
Although not always present (some Linux distributions by default do not enable sensitivity labels), the sensitivity labels are needed for the MLS (Multi-Level Security) support within SELinux. Sensitivity labels allow classification of resources and restriction of access to those resources based on a security clearance. These labels consists of two parts: a confidentiality value (prefixed with "s") and a category value (prefixed with "c").
In many organizations and companies, documents are labeled internal, confidential, or strictly confidential. SELinux can assign processes a certain clearance level towards these resources. With MLS, SELinux can be configured to follow the Bell-LaPadula model, a security model that can be characterized by "no read up, no write down": based on a process' clearance level, that process cannot read anything with a higher confidentiality level nor write to (or communicate otherwise with) any resource with a lower confidentiality level. SELinux by itself, does not use the "internal", "confidential", and other labels. Instead, it uses numbers from 0
(lowest confidentiality) to whatever the system administrator wants to be as the highest value (this is configurable and set when the SELinux policy is built).
Categories allow for resources to be tagged with one or more categories on which access controls are also possible. The idea behind categories is to support multi-tenancy (for example, as systems hosting applications for multiple customers) within a Linux system, by having processes and resources belonging to one tenant to be assigned a particular category whereas the processes and resources of another tenant getting a different category. When a process does not have proper categories assigned, it cannot do anything with resources (or other processes) that have other categories assigned.
In that sense, categories can be seen as tags, allowing access to be granted only when the tags of the process and the target resource match.