Skip to content

CVE-2015-5602 and SELinux?

How is SELinux helpful?

That is one of the most common questions that we get when a new CVE (Common Vulnerabilities and Exposures) appears. We explain SELinux as a technology for process isolation to mitigate attacks via privilege escalation.

A real example of this attack can be seen in CVE-2015-5602 known as Unauthorized Privilege Escalation in sudo. Under certain conditions, this security issue allows you to modify any file on your system. From there it follows that you are able to modify the /etc/shadow file, containing secure user account data. To demonstrate how SELinux could help you here we would remind a SELinux feature called SELinux Confined Users.

SELinux confined users

On Fedora systems, the default Targeted security policy is enforced to confine commonly used applications/services to mitigate attacks on a system. With this policy, Linux users are unconfined by default. It means there are no restrictions for attacks coming from these users. CVE-2015-5602 is such an example. Fortunately, you can configure SELinux to confine also Linux users how it is described in Confining users with SELinux in RHEL and Confining Users on Fedora as a part of process isolation for Linux users.

I personally use SELinux confined users by default to take all advantages of process isolation for Linux users on my Fedora system.

In my case mgrepl Linux user is mapped to staff_u SELinux user

# semanage login -l |grep mgrepl

Login Name SELinux User MLS/MCS Range
mgrepl staff_u s0-s0:c0.c1023

who is supposed to be a SELinux login user with common administrative permissions and he is able to run sudo in the dedicated SELinux domain.

type_transition staff_t sudo_exec_t : process staff_sudo_t;

It tells me if staff_u SELinux user executes sudo then there is a SELinux transition to staff_sudo_t domain. With configured sudoers we can see

$ sudo -e ~/test.txt
$ ps -efZ | grep sudo
staff_u:staff_r:staff_sudo_t:s0-s0:c0.c1023 root 5390 4925 0 23:04 pts/3 00:00:00 sudo -e /home/mgrepl/test.txt

CVE-2015-5602 vs. confined SELinux users

With followed steps to reproduce of CVE-2015-5602 and with defined SELinux confinement for this Linux user using semanage utility

# semanage login -a -s staff_u usr
$ ssh usr@localohst
[usr@localhost ~]$ ln -s /etc/shadow ~/temp/test.txt
[usr@localhost ~]$ id -Z

we can try to edit ~/temp/test.txt file to access /etc/shadow

[usr@localhost ~]$ sudo -e ~/temp/test.txt
sudoedit: /home/usr/temp/test.txt: Permission denied
[usr@localhost ~]$ getenforce

That’s it.


And the following log event is generated for this denied.

type=AVC msg=audit(1446584115.930:558): avc: denied { read } for pid=3098 comm="sudoedit" name="shadow" dev="dm-1" ino=1049344 scontext=staff_u:staff_r:staff_sudo_t:s0 tcontext=system_u:object_r:shadow_t:s0 tclass=file permissive=0

Are you now thinking about SELinux confined users?

I would like to thank Daniel Kopeček <> for a heads-up and co-authoring this post.

SELinux insides – Part2: Neverallow assertions

Usually if we describe how to create a local policy, how to generate a new policy, how to add additional rules, we always talk about ALLOW rules and sometimes about DONTAUDIT rules. But we have another Access Vector (AV) rules – AUDITALLOW and NEVERALLOW.

ALLOW allows defined rules
DONTAUDIT stops the auditing of denial messages
AUDITALLOW audits events defined by a rule
NEVERALLOW specifies that an allow rule must not be generated for the operation

In this blog post, I would like to describe more details about NEVERALLOW rules on real examples and announce that we turn them back on in Fedora 23/Rawhide.

But why do we need to have NEVERALLOW rules/assertions? The answer is pretty easy. We need to be sure that we do not allow any unwanted/unsecure/dangerous actions. For example, we do not want to allow ordinary services to access /etc/shadow and NEVERALLOW assertions give us this ability. In the policy, we declare rules like

neverallow ~can_read_shadow_passwords shadow_t:file read;

It ensures that the policy will not contain rules allowing any domain without can_read_shadow_passwords attribute read access to /etc/shadow (otherwise the policy won’t compile).

What does it mean in practice? We can demonstrate it with the following rules on a system where NEVERALLOW assertion checks are enabled.

$ cat neverallow_test.cil
(allow sssd_t shadow_t (file (read)))

$ sudo semodule -i neverallow_test.cil
Neverallow found that matches avrule at line 310 of /var/lib/selinux/targeted/tmp/modules/100/authlogin/cil
Binary policy creation failed at line 1 of /var/lib/selinux/targeted/tmp/modules/400/neverallow_test/cil
Failed to generate binary


$ cat neverallow_test.cil
(typeattributeset can_read_shadow_passwords (sssd_t))
(allow sssd_t shadow_t (file (read)))

$ sudo semodule -i neverallow_test.cil
$ sesearch -A -s sssd_t -t shadow_t
allow sssd_t shadow_t : file read ;

In the first case, we were not able to define ALLOW rule because of existing NEVERALLOW rule in the policy. In the second case, we assigned can_read_shadow_passwords attribute to sssd_t to pass this NEVERALLOW rule.

As I mentioned above we turned this assertion check back on in Fedora 23/Rawhide with a new 2.4 userspace release which contains some optimalization in libsepol. Before that it took a long time to get a build of Fedora distribution policy with enabled NEVERALLOW check.

SEMOD_EXP="/usr/bin/semodule_expand -a"

was a part of our Fedora selinux-policy.spec file.

$ man semodule_expand
-a Do not check assertions. This will cause the policy to not check any neverallow rules.

Together with that we also changed to not check policy assertions during load, by setting expand-check=0 in /etc/selinux/semanage.conf. This option affects our practice examples.

So for a long time we did not have this check and we needed to be really careful with a rules which could conflict with defined assertions in the policy. With the latest Fedora SELinux userspace and policy packages, we no longer use “-a” option in selinux-policy.spec file and we modified semanage.conf to contain expand-check=1.

CIL – Part2: Module priorities

In my previous blog, I talked about CIL performance improvements. In this blog post, I would like to introduce another cool feature called module priorities. If you check the link, you can see a nice blog post published by Petr Lautrbach about this new feature.

With new SELinux userspace, we are able to use priorities for SELinux policy modules. It means you can ship own ipa policy module, which is based on distribution policy module, with additional rules and load it with higher priority. No more different names for policy modules and higher priority wins.

# semodule --list-modules=full | grep ipa
400 ipa pp
100 ipa pp

Of course, you can always say you want to use distro policy module and add just additional fixes. Yes, it works fine for some cases when you add just minor fixes which you are not able to get to distro policy for some reasons. Actually you can also package this local policy how Lukas Vrabec wrote in his blog.

Another way how to deal with this case is a fact you can ship SELinux policy for your application at all and don’t be a part of distro policy. Yes, we can see these cases.

For example

# semodule --list-modules=full | grep docker
400 docker pp

But what are disadvantages with this way?

* you need to know how to write SELinux policy
* you need to maintain this policy and reflect the latest distro policy changes
* you need to do “hacks” in your policies if you need to have some interfaces for types shipped by distro policy
* you would get your policy to upstream and check if there is no conflict with distribution policy if they do a merge with the same upstream

From Fedora/RHEL point of view, this was always a problem how to deal with policies for big projects like Cluster, Gluster, OpenShift and so on. We tried to get these policies out of distro policy but it was really hard to do a correct rpm handling and then we faced my above mentioned points.

So is there any easy way how to deal with it? Yes, it is. We ships a policy for a project in our distribution policy and this project takes this policy, adds additional fixes, creates pull requests against distribution policy and if there will be different timelines then it will be shipped by this project. And that’s it! It can be done easily using module priorities.

For example, we have Gluster policy in Fedora by default.

# semodule --list-modules=full | grep gluster
100 glusterd pp

And now, Gluster team needs to do a new release but it causes some SELinux issues. Gluster folks can take distribution policy, add additional rules and package it.

Then we will see something like

# semodule --list-modules=full | grep gluster
100 glusterd pp
400 glusterd pp

In the mean time, Gluster folks can do pull requests with all changes against disitribution policy and they can still ship the same policy. The Gluster policy is a part of distribution policy, it can be easily usptream-able and moreover, it can be disabled in distribution policy by default.

# semodule --list-modules=full | grep gluster
400 gluster cil
100 glusterd pp disabled

$ matchpathcon /usr/sbin/glusterfsd
/usr/sbin/glusterfsd system_u:object_r:glusterd_exec_t:s0

This model is really fantastic and give us answers for lot of issues.

CIL – Part1: Faster SELinux policy (re)build

As you probably know we shipped new features related to SELinux policy store migration in Fedora 23. If you check the link, you can see more details about this change. You can read some technical details, benefits and examples how to test it. In this blog series, called CIL, I would like to introduce you this new feature and show you benefits which CIL brings.

One of the most critical part of SELinux usability are time-consuming SELinux operations like policy installations or loading new policy modules for example. I guess you know what about I am talking. For example, you want to create own policy module for your application and test it on your virtual machine. It means you attempt to execute

semodule -i myapps.pp

and you are waiting, waiting, waitng and waiting.

The same you can see if you try to disable a module

semodule -d rhcs

and you are waiting, waiting, waitng and waiting.

It directly depends on used policy language and on the amount of policy rules which need to be rebuilt if SELinux policy modules are managed. You can read more info about policy modules and kernel policy in my previous blog.

And at this point, CIL brings really big performance improvements. Just imagine, no more “waiting waiting waiting” on a policy installation. No more “waiting waiting waiting” if you load your own policy module.

But no more words and let show you some real numbers.


You can see really big differences for chosen SELinux operations between a regular system with old SELinux userspace without CIL and with a new SELinux userspace with CIL.

It means we can really talk about ~75% speed-up for tools/apps which access to manage SELinux policy.

Note: These numbers come from Fedora 23 virtual machine and all these actions require a policy rebuild.

And it is not only about SELinux tools but we have also SELinux aware applications – systemd for example which loads Fedora distribution policy on boot process. And you get also big improvements on this boot process.

CIL: systemd[1]: Successfully loaded SELinux policy in 91.886ms.
REGULAR: systemd[1]: Successfully loaded SELinux policy in 172.393ms.

I believe you are now really excited to test this new feature and get own numbers and see how much faster SELinux tools like semodule, semanage are if they manipulate with a policy.

SELinux insides – Part1: Policy module store, policy modules and kernel policy.

As you probably know, we are working to get the latest SELinux userspace which supports a new location for a policy store together with CIL support to Fedora 23.

There is a Fedora feature page about this change. We talk about a policy store, policy modules and about a binary policy (or kernel policy).

“SELinux security policy is located in /etc/selinux directory together with configuration files. In Fedora, we use a modular policy. It means the policy is not one large source policy but it can be built from modules. These modules together with a base policy (contains the mandatory information) are compiled, linked and located in a policy store where can be built into a binary format and then loaded into the security server. This binary policy is located in /etc/selinux/<SELINUXTYPE>/policy/policy.29 for example.”

But how are these compiled policy modules created?

If a policy writer starts with a new policy, he creates source policy files (.te, .if, .fc). These files are compiled using checkmodule into an intermediate format .mod. This policy object file contains Type Enforcement (TE) rules together with expanded rules defined by interface files (*.if). Then semodule_package is called (with a file context file) to create a SELinux policy module .pp.

In this phase, semodule is used to manage the policy store (installing, loading, updating, removing modules) and also builds the binary policy file – policy.29 for example.

Policy module creation

How does CIL come into the game?


As you can see, the current compiled *.pp policy modules are converted to CIL code using /usr/libexec/selinux/hll/pp binary. And all these CIL files are compiled into a binary policy file – policy.29. With the userspace (v2.4), we call .pp file as a High Level Language (HLL) file. So we could define own HLLs to convert these HLL files to CIL format.

Where are these policy modules located?

With installed SELinux, we describe the following directory locations

/sys/fs/selinux The SELinux filesystem.
/etc/selinux Location for SELinux configuration files and policies.
/etc/selinux/<SELINUXTYPE>/module Location for policy module store and additional configuration files.

The default location for policy modules is changed from /etc/selinux/<SELINUXTYPE>/module to /var/lib/selinux/<SELINUXTYPE>/module with the 2.4 userspace. Also the following options are added by libsepol (v2.4) with CIL support to semanage.conf.

store-root = <path>
compiler-directory = <path>
ignore-module-cache = true|false
target-platform = selinux | xen

“store-root” option can be changed from the default /var/lib/selinux to a custom location according to distribution requirements.

Are you getting dac_override AVC message?

Some time ago, Dan Walsh wrote “Why doesn’t SELinux give me the full path in an error message?” blog related to DAC_OVERRIDE capability.

“According to SELinux By Example. DAC_OVERRIDE allows a process to ignore Discretionary Access Controls including access lists.”

In Fedora 22, we have still a quite large number of DAC_OVERRIDE allowed by default. You can check it using

$ sesearch -A -p dac_override -C |grep -v ^DT |wc -l

So the question is if they are still needed. Basically most of them have been added because of a bad ownership of files/directories located in /var/lib, /var/log, /var/cache directories. But as you probably realize, we just “mask” bugs in applications and open backdoors in the Fedora SELinux policy.

For this reason, we want to introduce a new Fedora 23 feature to remove these capabilities where it is possible.

Let’s test it on the following real example:

$ sesearch -A -s psad_t -t psad_t -c capability
Found 1 semantic av rules:
allow psad_t psad_t : capability { dac_override setgid setuid net_admin net_raw } ;

$ ls -ldZ /var/lib/psad /var/log/psad /var/run/psad /etc/psad/
drwxr-xr-x. 3 root root system_u:object_r:psad_etc_t:s0 4096 May 26 12:40 /etc/psad/
drwxr-xr-x. 2 root root system_u:object_r:psad_var_lib_t:s0 4096 May 26 12:35 /var/lib/psad
drwxr-xr-x. 4 root root system_u:object_r:psad_var_log_t:s0 4096 May 26 12:47 /var/log/psad
drwxr-xr-x. 2 root root system_u:object_r:psad_var_run_t:s0 100 May 26 12:44 /var/run/psad

$ ps -efZ |grep psad
system_u:system_r:psad_t:s0 root 25461 1 0 12:44 ? 00:00:00 /usr/bin/perl -w /usr/sbin/psad
system_u:system_r:psad_t:s0 root 25466 1 0 12:44 ? 00:00:00 /usr/sbin/psadwatchd -c /etc/psad/psad.con

which looks correct. So is dac_override really needed for psad_t? How could I check it?

On my Fedora 23 system, I run with

$ cat dacoverride.cil
(typeattributeset cil_gen_require domain)
(auditallow domain self (capability (dac_override)))

policy module which audits all dac_override as granted in /var/log/audit/audit.log if they are needed.

For example I see

type=AVC msg=audit(1432639909.704:380132): avc: granted { dac_override } for pid=28878 comm="sudo" capability=1 scontext=staff_u:staff_r:staff_sudo_t:s0-s0:c0.c1023 tcontext=staff_u:staff_r:staff_sudo_t:s0-s0:c0.c1023 tclass=capability

which is expected. But I don’t see it for psad_t if I try to use it. So this is probably a bug in the policy and dac_override should be removed for psad_t. Also we should ask psad maintainers for their agreement.

And what happens if you go with the following ownership change

$ ls -ldZ /var/log/psad/
drwxr-xr-x. 4 mgrepl mgrepl system_u:object_r:psad_var_log_t:s0 4096 May 26 13:53 /var/log/psad/

? You get

type=AVC msg=audit(1432641212.164:380373): avc: granted { dac_override } for pid=30333 comm="psad" capability=1 scontext=system_u:system_r:psad_t:s0 tcontext=system_u:system_r:psad_t:s0 tclass=capability



How to create a new initial policy using sepolicy-generate tool?

I have a service running without own SELinux domain and I would like to create a new initial policy for it.

How can I create a new initial policy? Is there a tool for it?

We get these questions very often. And my answer is pretty easy. Yes, there is a tool which can help you with this task.

Let’s use a real example to demonstrate how to create own initial policy for the running lttng-sessiond service on my system.

I see

$ ps -efZ |grep lttng-sessiond
system_u:system_r:unconfined_service_t:s0 root 29186 1 0 12:31 ? 00:00:00 /usr/bin/lttng-sessiond -d

unconfined_service_t tells us the lttng-sessiond service runs without SELinux confinement.

Basically there is no problem with a service running as unconfined_service_t if this service does “everything” or this service is a third party software. A problem occurs if there are another services with own SELinux domains and they want to access objects created by your service.

Then you can see AVCs like

type=AVC msg=audit(1431724248.950:1003): avc: denied { getattr } for pid=768 comm="systemd-logind" path="/dev/shm/lttng-ust-wait-5" dev="tmpfs" ino=25832 scontext=system_u:system_r:systemd_logind_t:s0 tcontext=system_u:object_r:tmpfs_t:s0 tclass=file permissive=0

In that case, you want to create  SELinux policy from the scratch to get objects created by your service with the specific SELinux labeling to see if you can get a proper SELinux confinement.

Let’s start.

1. You need to identify an executable file which is used to start a service. From

$:s0 root 29186 1 0 12:31 ? 00:00:00 /usr/bin/lttng-sessiond -d

you can see /usr/bin/lttng-sessiond is used. Also

$ grep ExecStart /usr/lib/systemd/system/lttng-sessiond.service
ExecStart=/usr/bin/lttng-sessiond -d

is useful.

2. Run sepolicy-generate to create initial policy files.

sepolicy generate --init -n lttng /usr/bin/lttng-sessiond
Created the following files:
/home/mgrepl/Devel/RHEL/selinux-policy/lttng.te # Type Enforcement file
/home/mgrepl/Devel/RHEL/selinux-policy/lttng.if # Interface file
/home/mgrepl/Devel/RHEL/selinux-policy/lttng.fc # File Contexts file
/home/mgrepl/Devel/RHEL/selinux-policy/lttng_selinux.spec # Spec file
/home/mgrepl/Devel/RHEL/selinux-policy/ # Setup Script

3. Run

# sh


# ls -Z /usr/bin/lttng-sessiond
system_u:object_r:lttng_exec_t:s0 /usr/bin/lttng-sessiond
# systemctl restart lttng-sessiond
# ps -eZ |grep lttng-sessiond
system_u:system_r:lttng_t:s0 root 29850 1 0 12:50 ? 00:00:00 /usr/bin/lttng-sessiond -d
# auseaarch -m avc -ts recent
... probably you see a lot of AVCs ...

Now you have created/loaded own initial policy for your service. In this point, you can work on AVCs, you can ask us to help you with these AVCs.