Security-Enhanced Linux (SELinux) is a security enhancement to Linux which allows users and administrators more control over access levels. Access can be constrained on such variables as which users and applications can access which resources. Conversely, SELinux access controls are determined by a policy loaded on the system which may not be changed by careless users or misbehaving applications.
SELinux also adds finer granularity to access controls. Instead of only being able to specify who can read, write or execute a file, SELinux lets you specify who can unlink, append only, move a file and so on. Additionally SELinux allows you to specify access to many resources other than files as well, such as network resources and interprocess communication (IPC).
SELinux enforces the idea that programs should be limited to what files they can access and what actions they can take.
SELinux is a kernel security extension, which can be used to guard against misconfigured or compromised programs. It comes with Mandatory Access Control (MAC) system that improves the traditional UNIX/Linux DAC (Discretionary Access Control) model.
SELinux can be any one of the following state:
The type of policies that can be used for the SELinux include:
SELinux is an implementation of a mandatory access control mechanism in the Linux kernel, checking for allowed operations after standard discretionary access controls are checked. It was created by the National Security Agency and can enforce rules on files and processes in a Linux system, and on their actions, based on defined policies.
When using SELinux, files, including directories and devices, are referred to as objects. Processes, such as a user running a command or an application, are referred to as subjects. Most operating systems use a Discretionary Access Control (DAC) system that controls how subjects interact with objects, and how subjects interact with each other. On operating systems using DAC, users control the permissions of files (objects) that they own. For example, on Linux operating systems, users could make their home directories world-readable, giving users and processes (subjects) access to potentially sensitive information, with no further protection over this unwanted action.
Relying on DAC mechanisms alone is fundamentally inadequate for strong system security. DAC access decisions are only based on user identity and ownership, ignoring other security-relevant information such as the role of the user, the function and trustworthiness of the program, and the sensitivity and integrity of the data. Each user typically has complete discretion over their files, making it difficult to enforce a system-wide security policy. Furthermore, every program run by a user inherits all of the permissions granted to the user and is free to change access to the user's files, so minimal protection is provided against malicious software. Many system services and privileged programs run with coarse-grained privileges that far exceed their requirements, so that a flaw in any one of these programs could be exploited to obtain further system access.
SELinux adds Mandatory Access Control (MAC) to the Linux kernel, and is enabled by default in Red Hat Enterprise Linux. A general purpose MAC architecture needs the ability to enforce an administratively-set security policy over all processes and files in the system, basing decisions on labels containing a variety of security-relevant information. When properly implemented, it enables a system to adequately defend itself and offers critical support for application security by protecting against the tampering with - and bypassing of - secured applications.
MAC provides strong separation of applications that permits the safe execution of untrustworthy applications. Its ability to limit the privileges associated with executing processes limits the scope of potential damage that can result from the exploitation of vulnerabilities in applications and system services. MAC enables information to be protected from legitimate users with limited authorization as well as from authorized users who have unwittingly executed malicious applications.
SELinux is a Linux security module that is built into the Linux kernel. SELinux is driven by loadable policy rules. When security-relevant access is taking place, such as when a process attempts to open a file, the operation is intercepted in the kernel by SELinux. If an SELinux policy rule allows the operation, it continues, otherwise, the operation is blocked and the process receives an error.
It is envisaged that there should be minimal resource overhead should SELinux be deployed, in terms of system performance.
SELinux decisions, such as allowing or disallowing access, are cached. This cache is known as the Access Vector Cache (AVC). Caching decisions decrease how often SELinux policy rules need to be checked, which increases performance. SELinux policy rules have no effect if DAC rules deny access first.
The SELinux Policy is the set of rules that guide the SELinux security engine. It defines types for file objects and domains for processes. It uses roles to limit the domains that can be entered, and has user identities to specify the roles that can be attained. In essence, types and domains are equivalent, the difference being that types apply to objects while domains apply to processes.
A type is a way of grouping items based on their similarity from a security perspective. This is not necessarily related to the unique purpose of an application or the content of a document. For example, a file can have any type of content and be for any purpose, but if it belongs to a user and exists in that user's home directory, it is considered to be of a specific security type, user_home_t.
These object types are considered alike because they are accessible in the same way by the same set of subjects. Similarly, processes tend to be of the same type if they have the same permissions as other subjects. In the targeted policy, programs that run in the unconfined_t domain have an executable file with a type such as sbin_t. From an SELinux perspective, this means they are all equivalent in terms of what they can and cannot do on the system.
For example, the binary executable file object at /usr/bin/postgres has the type postgresql_exec_t. All of the targeted daemons have their own *_exec_t type for their executable applications. In fact, the entire set of PostgreSQL executables such as createlang, pg_dump, and pg_restore have the same type, postgresql_exec_t, and they transition to the same domain, postgresql_t, upon execution.
The SELinux policy defines various rules which determine how each domain may access each type. Only what is specifically allowed by the rules is permitted. By default, every operation is denied and audited, meaning it is logged in the $AUDIT_LOG file. In Red Hat Enterprise Linux, this is set to /var/log/messages. The policy is compiled into binary format for loading into the kernel security server, and each time the security server makes a decision, it is cached in the AVC to optimize performance.
The policy can be defined either by modifying the existing files or by adding local Type Enforcement (TE) and File Context (FC) files to the policy tree. These new policies can be loaded into the kernel in real time. Otherwise, the policy is loaded during the boot process by /sbin/init. SELinux plays an important role during the early stages of system start-up. Because all processes must be labeled with their correct domain, init performs some essential operations early in the boot process to maintain synchronization between labeling and policy enforcement.
The default SELinux policy should be usable on almost any machine with minimal configuration and a small amount of system administrator training.
This policy should prevent system services - including most of the common network-visible services such as mail servers, ftp servers, and DNS servers - from accessing files which those services have no valid reason to access. This action alone prevents a huge amount of possible damage from network attacks against services, from trojaned software, and so forth.
Almost every service that listens on a network, such as sshd or httpd, is confined. Also, most processes that run as the Linux root user and perform tasks for users, such as the passwd application, are confined. When a process is confined, it runs in its own domain, such as the httpd process running in the httpd_t domain. If a confined process is compromised by an attacker, depending on SELinux policy configuration, an attacker's access to resources and the possible damage they can do is limited.
Under the targeted policy, every subject and object runs in the unconfined_t domain except for the specific targeted daemons. Objects that are in the unconfined_t domain have no restrictions and fall back to using standard Linux security, that is, DAC. The daemons that are part of the targeted policy run in their own domains and are restricted in every operation they perform on the system. This way processes that are exploited or compromised in any way are contained and can only cause limited damage.
Unconfined processes run in unconfined domains, for example, init programs run in the unconfined initrc_t domain, unconfined kernel processes run in the kernel_t domain, and unconfined Linux users run in the unconfined_t domain. For unconfined processes, SELinux policy rules are applied, but policy rules exist that allow processes running in unconfined domains almost all access.
Processes running in unconfined domains fall back to using DAC rules exclusively. If an unconfined process is compromised, SELinux does not prevent an attacker from gaining access to system resources and data, but of course, DAC rules are still used. SELinux is a security enhancement on top of DAC rules - it does not replace them.
In other words, any process that is not specifically covered by a policy is allowed to run unfettered, e.g. third party applications. This policy allows these to be able to be deployed with minimal support and operational overhead.
There are two ways to configure SELinux under Red Hat Enterprise Linux: using the Security Level Configuration Tool (system-config-selinux), or manually editing the configuration file (/etc/sysconfig/selinux).
The /etc/sysconfig/selinux file is the primary configuration file for enabling or disabling SELinux, as well as for setting which policy to enforce on the system and how to enforce it.
NOTE:
The /etc/sysconfig/selinux file contains a symbolic link to the actual configuration file, /etc/selinux/config.
The following explains the full subset of options available for configuration:
[1] This is useful for debugging and troubleshooting purposes. In permissive mode, more denials are logged because subjects can continue with actions that would otherwise be denied in enforcing mode. For example, traversing a directory tree in permissive mode produces avc: denied messages for every directory level read. In enforcing mode, SELinux would have stopped the initial traversal and kept further denial messages from occurring.
Actions made while SELinux is disabled may result in the file system no longer having the correct security context. That is, the security context defined by the policy. The best way to relabel the file system is to create the flag file /.autorelabel and reboot the machine. This causes the relabel to occur very early in the boot process, before any processes are running on the system. Using this procedure means that processes cannot accidentally create files in the wrong context or start up in the wrong context.
It is possible to use the fixfiles relabel command prior to enabling SELinux to relabel the file system. This method is not recommended, however, because after it is complete, it is still possible to have processes potentially running on the system in the wrong context. These processes could create files that would also be in the wrong context.
The opposite of the targeted policy is the strict policy. In the strict policy, every subject and object exists in a specific security domain, and all interactions and transitions are individually considered within the policy rules.
The strict policy is a much more complex environment and Security Engineering would recommend that the SELINUXTYPE variable be set to targeted. Should it be set to strict mode, all processes would be operating in a “deny by default” mode and would require policies to be written for each and every process that operates on the system, e.g. policies would need to be written for all third party applications. While this would be a noble goal, it would require significant resource to deploy, support and maintain.
The /etc/selinux/ directory is the primary location for all policy files as well as the main configuration file.
The following example shows sample contents of the /etc/selinux/ directory:
-rw-r--r-- 1 root root 448 Sep 22 17:34 config drwxr-xr-x 5 root root 4096 Sep 22 17:27 strict drwxr-xr-x 5 root root 4096 Sep 22 17:28 targeted
The two subdirectories, strict/ and targeted/, are the specific directories where the policy files of the same name (that is, strict and targeted) are contained.
The following are some of the commonly used SELinux utilities:
/usr/sbin/setenforce — Modifies in real-time the mode in which SELinux runs.
For example:
setenforce 1 — SELinux runs in enforcing mode. setenforce 0 — SELinux runs in permissive mode.
To disable SELinux, you need to either specify the appropriate setenforce parameter in /etc/sysconfig/selinux or pass the parameter selinux=0 to the kernel, either in /etc/grub.conf or at boot time.
/usr/sbin/sestatus -v — Displays the detailed status of a system running SELinux. The following example shows an excerpt of sestatus -v output:
SELinux status: enabled SELinuxfs mount: /selinux Current mode: enforcing Mode from config file: enforcing Policy version: 21 Policy from config file: targeted Process contexts: Current context: user_u:system_r:unconfined_t:s0 Init context: system_u:system_r:init_t:s0 /sbin/mingetty system_u:system_r:getty_t:s0
Refer to the setools or policycoreutils package contents for more information on all available binary utilities.
Inside the “/etc/selinux” folder have the “targeted” folder, which contains the source & Binary policies.
If we intend to make any changes to the Policy we have to install the targeted source policy.
The default installation will be the Binary policy which is unchangeable.
For changing the policy we have to install the targeted source policy.
Requirements to install the source policy include:
To install the source policy, we first need to determine the currently installed policy. To achieve this, run the command
rpm -qa |grep -i selinux
This will show the selinux-policy-targeted package. This is responsible for the binary policy installation. For installation of the selinux source policy download the source package of the policy named selinux-policy-targeted-sources.version.rpm. The installation creates the folder “/etc/selinux/targeted/src”
Install the package
rpm -ivh selinux-policy-targeted.version.rpm # cd /etc/selinux/targeted/src
Beneath the /etc/selinux/targeted/src we have a directory called “policy” which contains all of the files and directories that relate to the targeted policy.
These can then be edited and changed to suit particular goals.
There may be circumstances that require amending policies that are in force. As an example, there may be a requirement to change the allowed ports that the web server runs on.
By design and default, web servers generally run upon two ports, 80(http) and 443(https). Accordingly, the RHEL SELinux policies mandate that the web server can only bind to these ports. However, in the following example we will change the following self-explanatory variable in the /etc/httpd/conf/httpd.conf
“Listen 80” -> “Listen 12345”
This will force the web server to listen on port 12345, however when we attempt to start the web server an error message is displayed.
Starting httpd: (13)Permission denied: make_sock: could not bind to address [::]:12345 (13)Permission denied: make_sock: count not bind to address 0.0.0.0:12345 no listening sockets available, shutting down unable to open logs [FAILED]
Fix this:
semanage port -a -t http_port_t -p tcp 12345
result:
Starting httpd: [OK]
As the above shows, a further command – semanage – was run to allow this change to be implemented.
The semanage command is part of the “policycoreutils-python” package, and should be installed. However, there may be cases where an administrator needs to be able to track down the source of an unexplained error, and the first step may well be to investigate if SELinux is causing the issue.
The sealert command can be used to parse messages and provide useful information to help in this task.
For example, when the above command returned an error which we were not expecting, running the command
sealert –a /var/log/audit/audit.log
displays the output which advises on a possible course of action:
SELinux is preventing /usr/sbin/httpd from name_bind access on the tcp_socket. ... Suggests ... semanage port -a -t http_port_t -p tcp 12345
RHEL6 also comes with utilities to support administrators when investigating issues which may be attributed to SELinux.
Of particular recommendation is “setroubleshoot”.
The purpose behind setroubleshoot is to let users know when access has been denied, help them resolve it if necessary, and to reduce overall frustration while working through tight security restrictions in the default SELinux policies.
The GUI frontend for setroubleshoot is useful for developers and administrators who run Linux desktops for testing, or those who run servers with a display attached.
To install setroubleshoot, run the following command:
yum install setroubleshoot{-server,-plugins,-doc}
Once installed, email alerting can provide sight over the estate. This can be configured via the /etc/setroubleshoot/setroubleshoot.conf file.
By default, users are not constrained under SELinux by default on Redhat Enterprise Linux (and derivitives), and it is believed that this is due to the extra administrative burden that may be caused. However, constraining users can provide additional controls over access and would be recommended for deployment in high risk environments where enhanced security is required.
As previously mentioned, most of the coreutils have an additonal “-Z” flag which provides additional information about a users' SELinux domain. Below shows the output from a default SELinux enabled system (i.e. no explicitly confined users)
As a standard user, e.g. peter:
id && sudo -i
result:
uid=1234(peter) gid=1234(peter) groups=1234(peter), 10)(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0:c0.c1023
Now as root
id
result:
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0:c0.c1023
A number of confined SELinux user roles are available. Each Linux user is mapped to an SELinux user via SELinux policy, allowing Linux users to inherit the restrictions on SELinux users, for example (depending on the user), not being able to: run the X Window System; use networking; run setuid applications (unless SELinux policy permits it); or run the su and sudo commands to become the Linux root user. This helps protect the system from the user.
The following shows a new user being added to the system (note the added “-Z user_u” portion of the command), and being placed into the user_u mapping.
NOTE: The “semange” command is not installed by default, and should be installed via the “policycoreutils-python” package.
Create a new test user named peter_se.
useradd -Z user_u peter_se
Check all SELinux login permissions.
semanage login -l
result:
Login Name SELinux User MLS/MCS Range
__default__ unconfined_u s0-s0:c0.c1023
root unconfined_u s0-s0:c0.c1023
peter_se user_u s0
system_u system_u s0-s0:c0.c1023
Set a password for the test user:
passwd peter_se
Run as root with this test user account:
su - peter_se
Check the SELinux permissions for the user.
id -Z
result:
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
Now logout.
logout
Remote into the account as the test user.
ssh peter_se@127.1
Check the SELinux permissions again for the user.
id -Z
result:
user_u:user_r:user_t:s0
su -
result:
Try to run as root again using the test user. This wont work.
-bash su: command not found
Try to run the su command directly.
/bin/su
result:
-bash /bin/su: Permission denied
Now logout.
logout
Remote into the account as a normal user.
su - peter
and check the SELinux permissions:
id -Z
result:
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
This shows that this user has unconfined access, i.e. all access.
Delete the test account
userdel -r peter_se
Linux users mapped to the SELinux user_u user run in the user_t domain. In this domain, Linux users are unable to run setuid applications unless SELinux policy permits it (such as passwd), and cannot run su or sudo, preventing them from becoming the Linux root user with these commands.
Note however that the root user was able to “su” to the user peter_se, and that the peter_se user was placed into an unconfined_u user – why? This occurred because the domains are in fact inherited, and as can be seen in the first few lines of the screenshot, the user root is in fact part of the unconfined_u domain. However, as can be then seen, if the user peter_se logs in over SSH (in essence, creating a new session) then they are appropriately placed into the user_u class.
Further, as can be seen, the test user cannot execute the “su” command, and cannot elevate privileges. This might be useful in scenarios where the users are considered semi-trusted or to protect application specific accounts, where application accounts should never need to be able to elevate privileges.
As shown above, a new user was created which confined a user to the user_u class. However there may be cases where pre-existing users need to be confined where they had not been previously. By using the “semanage” command, it is possible to add an existing used to an SELinux user group. The following shows the user “peter” being added to the “user_u” group:
Check the SELinx permissions:
semanage login -l
result:
Login Name SELinux User MLS/MCS Range
__default__ unconfined_u s0-s0:c0.c1023
root unconfined_u s0-s0:c0.c1023
system_u system_u s0-s0:c0.c1023
Now modify the SELinux permissions for the user peter.
login -a -s user_u peter
Check the SELinx permissions again:
Login Name SELinux User MLS/MCS Range
__default__ unconfined_u s0-s0:c0.c1023
root unconfined_u s0-s0:c0.c1023
peter_se user_u s0
system_u system_u s0-s0:c0.c1023
In the table below, you can see examples of basic confined domains for Linux users in Red Hat Enterprise Linux 6:
User | Domain | X Window System | su or sudo | Execute in home directory and /tmp/ (default) | Networking |
---|---|---|---|---|---|
sysadm_u | sysadm_t | yes | su and sudo | yes | yes |
staff_u | staff_t | yes | only sudo | yes | yes |
user_u | user_t | yes | no | yes | yes |
guest_u | guest_t | no | no | no | yes |
xguest_u | xguest_t | yes | no | no | Firefox only |
In certain circumstances it may be useful to control users ability to load and execute programs. This may help stop certain attacks, such as buffer overflow exploits, from operating correctly and help mitigate entire classes of attack, which rely on a user being able to execute code from their home directory.
Linux users in the guest_t and xguest_t domains can not execute applications in their home directories or /tmp/; however, by default, Linux users in the user_t and staff_t domains can.
Booleans are available to change this behavior, and are configured with the setsebool command. The setsebool command must be run as the Linux root user. The setsebool -P command makes persistent changes. Do not use the -P option if you do not want changes to persist across reboots:
The following screenshot shows (in the left pane) the command that is used to disable “allow_user_exec_content” type. In the right hand pane, the user is creating a simple C program (hello world!) and then attempting to execute it. Note how other commands on the system are still able to run freely – only those in /tmp and the users home directory are constrained.
setsebool -P allow_user_exec_content off
Then try to run something from the user's home directory. It wont work.
Polyinstantiation is the concept of creating a user or process specific view of a shared resource, i.e. Process A cannot affect process B by writing malicious code to a shared resource, such as UNIX directory /tmp.
The /tmp and /var/tmp directories are normally used for temporary storage by all programs, services, and users.
Such setup, however, makes these directories vulnerable to race condition attacks, or an information leak based on file names. SELinux offers a solution in the form of polyinstantiated directories. This effectively means that both /tmp and /var/tmp are instantiated, making them appear private for each user.
When instantiation of directories is enabled, each user's /tmp and /var/tmp directory is automatically mounted under /tmp-inst and /var/tmp/tmp-inst.
Security Engineering is currently aware of several issues when configuring these options, and do not currently recommend it.