Skip to content

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?

policy_store_2

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.

Advertisements

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
387

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
f

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

system_u:system_r:unconfined_service_t
$: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/lttng.sh # Setup Script

3. Run

# sh lttng.sh

4. YOU ARE DONE. CHECK YOUR RESULTS.

# 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.

How would tools like “paster” work with SELinux? … “Thin story”

I have “recently” written

How would tools like “paster” work with SELinux?

blog about projects which use/used a tool like the paster to create own application servers. Now let’s talk about other specific example – Thin (Ruby web server).

Previously, we had all services using Thin running in the thin_t SELinux domain type. But we did not realize there are many projects which use Thin in different ways and we could not treat all of them just by one SELinux domain type. So we re-wrote the thin policy to provide

thin_domain_template()

But how to use this interface? I will show you on AEOLUS-CONFIGSERVER example.

AEOLUS-CONFIGSERVER scenario

# yum install aeolus-configserver
# rpm -qi aeolus-configserver
Description :
The Aeolus Config Server, a service for storing and retrieving VM configurations

At First (read the previous blog for more details) we need to add a helper script which calls the thin binary from this helper script instead of either an init script or a systemd unit file.

# rpm -ql aeolus-configserver |grep wrapper
/usr/bin/aeolus-configserver-thinwrapper
# cat /usr/bin/aeolus-configserver-thinwrapper

#!/bin/bash

/usr/bin/thin start -c $CONFIG_SERVER_DIR -l $THIN_LOG \
-P $THIN_PID -a $THIN_IP -e $RACK_ENV \
–user $AEOLUS_USER –group $AEOLUS_GROUP \
-d –prefix=${PREFIX} -R $CONFIG_SERVER_RACKUP -p $CONFIG_SERVER_PORT

Now the following steps are needed to get an initial policy.

1. Create a new policy file.

# echo “policy_module(aeolus_configserver,1.0)” > aeolus_configserver.te

2. Use the template.

# echo “thin_domain_template(aeolus_configserver)” >> aeolus_configserver.te

You can check the thin_domain_template() on

http://git.fedorahosted.org/cgit/selinux-policy.git/tree/thin.if?h=master_contrib

3. Compile/load it.

# make -f /usr/share/selinux/devel/Makefile aeolus_configserver.pp
# semodule -i aeolus_configserver.pp

4. What will we get?

The following basic types will be created

# seinfo -t |grep configserver
thin_aeolus_configserver_exec_t
thin_aeolus_configserver_t

by “thin_domain_template(aeolus_configserver)“.

* thin_aeolus_configserver_exec_t type is an executable type for the aeolus-configserver-thinwrapper helper script.

# ls -Z /usr/bin/aeolus-configserver-thinwrapper
-rwxr-xr-x. root root system_u:object_r:thin_aeolus_configserver_exec_t:s0 /usr/bin/aeolus-configserver-thinwrapper

* thin_aeolus_configserver_t domain type is a domain type for an aeolus-configserver thin process.

With these types we can see the following transition

initrc_t @ thin_aeolus_configserver_exec_t –> thin_aeolus_configserver_t @ thin_exec_t –> thin_aeolus_configserver_t

But without this helper script you would see

initrc_t @ thin_exec_t –> thin_t

which means your project runs in the thin domain type defaultly and probably would not work. We have limited rules for the thin_t domain type because we want to know about this fact.

Now back to the thin_aeolus_configserver_t domain type. This type also got the thin_domain attribute. Basic rules are defined by the policy for all thin domains which are covered by this attribute.

FOREMAN start point

If you call

thin_domain_template(foreman)

you will get

# seinfo -t |grep foreman
thin_foreman_exec_t
thin_foreman_t

and thin_foreman_t will have the thin_domain attribute with the following basic rules

http://git.fedorahosted.org/cgit/selinux-policy.git/tree/thin.te?h=master_contrib

Check the thin_domain local policy section.

Note: You can also see the policy which has been created for aeolus-configserver.

Help for us …. “template.te”

From time to time I get questions how to write|compile|load own local policy module. We wrote a lot of blogs about using ausearch, audit2allow tools to generate a policy from AVC messages. We also mentioned the sepolgen tool to generate a new policy for services, applications, roles.

Note: there is a new tool for this called “sepolicy”.

But sometimes either you or I need to create a local test policy without these tools. Basically we start with

# vim mypol.te

and add needed declarations for mypol.te policy file. For example

# cat mypol.te
policy_module(mypol,1.0)
require{
type ping_t;
type openshift_initrc_t;
}
dontaudit ping_t openshift_initrc_t:fifo_file write;

Then we just compile/load this policy module.

# make -f /usr/share/selinux/devel/Makefile mypol.pp
# semodule -i mypol.pp

Based on this example we came up with a new VIM template for *.te policy file which would help us so we do not have to remember all these statements nor write them over and over. You can download this template from

http://mgrepl.fedorapeople.org/template.te

and execute

# cp template.te /usr/share/vim/vimfiles/

After that just add the line to your /etc/vimrc file.

autocmd BufNewFile *.spec 0r /usr/share/vim/vimfiles/template.spec
+ autocmd BufNewFile *.te 0r /usr/share/vim/vimfiles/template.te
augroup END

Now you can try to create a new local policy.

# vim mypolicy.te

What do you think? Would you like to add some additional examples or comments?

How would tools like “paster” work with SELinux?

There are many projects which use/used a tool like the paster to create own application servers. For example, piranha, TurboGears2 and others. But what is a problem here?

The paster tool is a python script. The script is called either from an init script or a systemd unit file by a project. This means we can have multiple calling of this script to create running daemons. We have the following transition

initrc_t @ bin_t -> initrc_t

Probably you know this is wrong. We should confine daemons running in the initrc_t domain type. We had this issue with the piranha package. I did not know everything about the paster tool. I added the piranha_web_exec_t label for the paster python script and we got the following transition

initrc_t @ piranha_web_exec_t -> piranha_web_t

which is what we wanted. But this change caused TurboGears2 application servers running with piranha_web_t domain type. It was obviously wrong.

What happened?

turbogears init script @ paster -> TurboGears2 application server
initrc_t @ piranha_web_exec_t -> piranha_web_t

You see the problem. How could we solve issues like this? The solution is pretty easy because we know how SELinux and transitions work. We can just do

initrc_t @ $1_exec_t ->$1_t @ bin_t -> $1_t

What is “?_exec_t” in this case? It can be a script which is called from either an init script or a systemd unit file and this script then calls the pastor python script with arguments. Then we get what we want

initrc_t @ piranha_web_exec_t -> piranha_web_t @ bin_t -> piranha_web_t
initrc_t @ turbogears_exec_t -> turbogears_t @ bin_t -> turbogears_t

and we can leave the pastor python script labeled as bin_t.

We can apply this solution for other projects.

How do we do selinux-policy updates?

Sometimes I get questions how we do selinux-policy updates. How does the process go?

We go through all new bugs every day in the morning. So there are two periods per day because Dan is from USA and I am from Czech Republic.

We add appropriate fixes to the selinux-policy repo on fedorahosted.org first and then we commit changes also to the selinux-policy git repo on fedoraproject.org. But this does not mean we do a new build immediately. The main reason is we want to cover as much bugs by a build as possible. So we do a new build either at the end of the day or the next day. Of course if a build is required we do it when is needed.

Also we are not able to do a new update with an each build. In this case, you can easily download new builds from koji and install them. So if you see

“Fixed in selinux-policy-<version>”

as a comment in a bug, you get a new build very soon. If not, then I have overslept and just ping me. I would like to thank all for testing/using a new builds from koji.