Gentoo Logo

Disclaimer : This document is not valid and is not maintained anymore.

[ << ] [ < ] [ Home ] [ > ] [ >> ]

5. Modifying the Gentoo Hardened SELinux Policy


5.a. SELinux Policy Language


By default, Gentoo provides a generic, yet tightly controlled policy which is deemed a good start policy for the majority of users. However, the purpose behind a Mandatory Access Control system is to put the security administrator in control. As such, a handbook on SELinux without information on how to write policies wouldn't be complete.

In this chapter, we'll talk a bit about the language behind SELinux policies and give some pointers on how to create your own policies, roles, etc.

Building a SELinux Module

First, before we go into the art of SELinux policy writing, let's first make a small SELinux module with a rule we can test, build the module and see if things work. Although these steps are fairly easy, they are important nonetheless. Modifying the SELinux policy as offered by Gentoo is best done through additional SELinux policy modules. Only when the core policy (the base policy) is not to your liking should you see on using a totally different policy.

Let's start with a skeleton for a policy module we'll call testmod. You should use simple names for the modules as the build infrastructure is quite sensitive to special constructs. Use only letters a-z and numbers, and never start a module name with a number.

Code Listing 1.1: Policy module skeleton

policy_module(testmod, 1.0.0)

Yes, that's it. But as you can see, it is fairly empty. So let's add a rule that allows a regular user (in the user_t domain) to read ebuild files (of type portage_ebuild_t).

Code Listing 1.2: Policy module testmod

policy_module(testmod, 1.0.0)

require {
  type user_t;
  type portage_ebuild_t;
  class file { read open getattr };
  class dir { read search open getattr };

allow user_t portage_ebuild_t:file { read open getattr };
allow user_t portage_ebuild_t:dir { read search open getattr };

As you can see, something as simple as allowing a user to read a file requires quite a few privileges. The directory privileges are needed to allow a user to navigate through the Portage tree structure whereas the file privileges are needed for a user to be able to access and open the ebuilds. Save this file as testmod.te.

To build the policy and convert it into the binary module that we can load into the SELinux policy store, we can use the Makefile available in /usr/share/selinux/strict/include (substitute strict with the SELinux policy type you are using).

Code Listing 1.3: Building a binary policy module

$ make -f /usr/share/selinux/strict/include/Makefile testmod.pp

The filename (testmod.pp) is the destination binary SELinux module name. The Makefile will automatically look for the testmod.te file you have in the working directory.

As a result, you should now have a file called testmod.pp. This module file can now be loaded in the SELinux policy store as follows:

Code Listing 1.4: Loading a binary module

# semodule -i /path/to/testmod.pp

Congratulations! You have now build your first SELinux policy module. If you want to disable it, remove it through semodule -r testmod.

This method of building a policy (using the Makefile and semodule) is something that you will need to do every time you want to update the SELinux policy on your system. The contents of the policy however does change as we will see in the rest of this document.

Getting the SELinux Policy Interfaces

To streamline policy development, the SELinux policy based on the reference policy uses interfaces to access privileges within a module. If you have built selinux-base-policy with USE="doc" then this information is available at /usr/share/doc/selinux-base-policy-<version>/html. It is recommended to have this information at hand, since most policy development/updates will be done through the interfaces offered by the policy.

If you are just interested, you can also find these interface definitions online. Mind you though, the online resource is only the reference policy and might differ a bit from the policy available within Gentoo.

Using Policy Interfaces

Using the policy interfaces allows you to update the policy with more readable functions. For instance, to allow the user_t domain to call and use Portage applications, the module could look like so:

Code Listing 1.5: Example policy to allow user_t to use portage

policy_module(testmod, 1.0.0)

require {
  type user_t;
  role user_r;

portage_run(user_t, user_r)

Of course, this makes the user_t domain much more privileged than the previously defined rules to read ebuild files: it allows the user to call portage, update the system, etc. Of course, the user still requires the proper regular Linux permissions (so he needs to be part of the portage group or become root). Needless to say, we do not recommend to grant this to a regular user ;-)

5.b. Full SELinux Policy Modules

Checking Out an Isolated Module

With the above in mind, we can now go one step further and investigate a full policy module, with both the type enforcement rules (.te file), file contexts (.fc) and interfaces (.if).

You should know that writing a module requires you to get intimate with the application. It isn't a matter of just hoping for the best: as a security administrator, you will be responsible for defining what accesses are allowed and which not. If you forget one, the application might break under the users' hands. But if you add too much, you might grant privileges that can be abused later on. And it will be a lot more difficult to track and remove privileges later as you will be hesitating if the privilege is needed or not.

In this section, we will not divulge in how to write one. We have an excellent Gentoo Hardened SELinux Development resource that guides you in that. However, we will look into such a full module to explain the other aspects of policy development.

Type Enforcement File

The .te file we wrote earlier is a type enforcement file. Its purpose is to define the access rules related to the module that you are building, but also - and more importantly - define new types (or even roles).

The example below is a snippet from a module for the skype application.

Code Listing 2.1: Snippet from skype.te

policy_module(skype, 1.0.0)

type skype_t;
type skype_exec_t;
application_domain(skype_t, skype_exec_t)

type skype_home_t;

manage_dirs_pattern(skype_t, skype_home_t, skype_home_t)
manage_files_pattern(skype_t, skype_home_t, skype_home_t)

In the above example, three new types are declared: skype_t (which will be used for the application), skype_exec_t (which is the label given to the application binary) and skype_home_t (which will be used for the users' ~/.Skype location). Also, the skype_t domain is given some privileges with respect to the skype_home_t label (manage directories and files).

File Context File

In the .fc file (which stands for file context file) the module's resources (files, directories, sockets, ...) are defined. Once the module is loaded, these rules are added so that file system relabeling will put the correct context on the files.

The example below is a snippet from the skype modules' file context file.

Code Listing 2.2: Snippet from skype.fc

HOME_DIR/\.Skype(/.*)?    gen_context(system_u:object_r:skype_home_t,s0)
/opt/skype/skype       -- gen_context(system_u:object_r:skype_exec_t,s0)
/usr/bin/skype         -- gen_context(system_u:object_r:skype_exec_t,s0)

The format of the file context file has the following syntax:

  1. The regular expression that matches the file(s) and directorie(s) affected by that line
  2. An optional identifier to differentiate the type of files (file, directory, socket, symbolic link, ...)
  3. A gen_context line that contains the context to assign to the file(s) and directorie(s)

Interface File

In the .if file (for interface file) interfaces are declared which can be used by other modules. It is through interfaces that a nicely defined policy can be built on top of other, existing policy modules.

One interface could be to allow users to call and execute an application. For instance, the following interface can be found in the skype module.

Code Listing 2.3: Snippet from skype.if

                type skype_t, skype_exec_t, skype_tmpfs_t, skype_home_t;

        role $1 types skype_t;

        domtrans_pattern($2, skype_exec_t, skype_t)

        allow $2 skype_t:process { ptrace signal_perms };

        manage_dirs_pattern($2, skype_home_t, skype_home_t)
        manage_files_pattern($2, skype_home_t, skype_home_t)
        manage_lnk_files_pattern($2, skype_home_t, skype_home_t)

        relabel_dirs_pattern($2, skype_home_t, skype_home_t)
        relabel_files_pattern($2, skype_home_t, skype_home_t)
        relabel_lnk_files_pattern($2, skype_home_t, skype_home_t)

        ps_process_pattern($2, skype_t)

Through this skype_role, we can then allow users to call skype, as can be found in the unprivuser.te file (which defines the user_t domain):

Code Listing 2.4: Snippet from unprivuser.te to call skype

	skype_role(user_r, user_t)

The following table shows a few common interfaces that could be in use. We seriously recommend to look at the available interfaces when enhancing or creating your own modules - and be sure to pick the interface that adds just what you need, nothing more.

Suffix Example Description
_template virt_domain_template(prefix) Not really an interface, templates create additional domains based on the information given to them. This is usually done for fine-grained policy templates with a common (sub)set of privileges.
Suffix Example Description
miscfiles_cert_type(resource) Transformation interfaces generally add specific attributes to resources or domains. Attributes "transform" the given resource into something more. In the given example, the miscfiles_cert_type(resource) assigns the cert_type attribute to the resource (and also marks it as a file). Interfaces, like miscfiles_read_all_certs work on these attributes.
Access interfaces
Suffix Example Description
_<access>_<resource> mta_getattr_spool(domain) Grant the specified domain access towards the shown resource. The resource usually defines the type too (like kudzu_getattr_exec_files: grant getattr on the kudzu_exec_t files) unless it is obvious from the name, or when the resource is a more specific term towards the domain. It can also include dontaudit (like mta_dontaudit_getattr_spool).
_exec dmesg_exec(domain) Grant one domain the right to execute the given domains' executable file (in the example, allow "domain" to execute dmesg_exec_t files), but without implying that the domains transition. In other words, dmesg gets executed but still confined by the privileges of the source domain.
_domtrans dmesg_domtrans(domain) Grant one domain execute and transition privileges towards the new domain. This interface is most commonly used to allow application domains to transition to another. In the given example, dmesg is ran with the privileges of the dmesg_t domain.
_run netutils_run(domain, role) Grant a given role and domain the rights to execute and transition towards the given domain. This is usually granted to (existing) user roles and domains and gives them the set of privileges needed to interact safely with the new (interactive) domain (such as terminal access).
_role xserver_role(role, domain) Allow the given role and domain the necessary permissions to transition and interact with the given domain. This interface is enhanced with the privileges to interact with the domain (and its underlying files) more thoroughly, and is usually assigned to newly created users or roles within the policy (rather than enhance existing user domains and roles).
_admin aide_admin(domain) Grant the given domain the rights to administer the target domains' environment. This usually involves privileges to manage and relabel all affiliated files, directories, sockets, etc.

5.c. Using audit2allow


When reading online resources on SELinux, you will notice that there are many references to a tool called audit2allow. This tools' purpose is to read AVC denial messages from the audit log file and transform them into a policy module that you can load. The advantage is that it makes it a lot easier to write policies. The downside is that the output (unless you use the -R option) is not usable for the Makefile we used earlier to build modules.

Another disadvantage is that the tool does not intelligently cope with changes. It blindly accepts denials and treats them as if they need to be allowed, rather than investigate if no other context should be given to the file, etc.

Using audit2allow

Using audit2allow is pretty straightforward. You send it the denials you want to fix and store the result in a .te file. You then convert it into an intermediary format which can then be translated into a .pp file for final loading by semodule.

For instance, to catch all denials and transform them into allowed statements from firefox-related denials:

Code Listing 3.1: Generate a new policy using audit2allow

# grep firefox /var/log/avc.log | audit2allow -m firefoxmod > firefoxmod.te
# checkmodule -m -o firefoxmod.mod firefoxmod.te
# semodule_package -o firefoxmod.pp -m firefoxmod.mod
# semodule -i firefoxmod.pp

Keep the module name (given through the -m option) simple: only use characters ([a-z]) and numbers ([0-9]), and start the module name with a character.

5.d. Using selocal


Sometimes we need to just make a small adjustement here, an addition there, all over time. If we use audit2allow, this would result in several separate modules over time - mypolicy1, mypolicy2, ... After a while, we do not know which rules are added where and for what reason.

To support this in the future, Gentoo is providing a tool that allows you to add in small rule sets over time, documenting the reasons why and simplifying the management of these rules.

Using selocal

With policycoreutils-2.1.13-r11 onwards, a new command called selocal is available. This command allows users to easily add in additional SELinux policy rules to the local policy without having go through the hassle of building and maintaining their own .te files. Instead, this command does that for you.

Rules that are added to the local policy (hence the name, selocal) can be accompanied with a small comment to allow users to describe why a change was added (or to refer to a bug id on Gentoo's bugzilla).

Code Listing 4.1: Adding a rule to the local policy

# selocal -a "rpcbind_stream_connect(sysadm_t)" -c "Be able to call exportfs (NFS)"
# selocal --build --load

With --list you can view the currently added local policy rules, and with --delete they can be removed from the local policy. When you want to have the changes take effect, run selocal --build --load to build the new local policy and load it in memory.

5.e. Creating Additional Roles

Create the Role and Domain

If you want to create additional roles on the system, you first need to create a SELinux module that creates that role, together with its main domain. Below is a very simple example for a database administration role, which we will offer through the sec-policy/selinux-dbadm package later:

Code Listing 5.1: Example dbadm_r and dbadm_t definition

policy_module(dbadm, 1.0.0)

# Declarations

role dbadm_r;

# Create a user domain (dbadm_t) 

# dbadm local policy

allow dbadm_t self:capability { dac_override dac_read_search sys_ptrace };





	# Grant the user admin rights on PostgreSQL (process-wise)
	postgresql_admin(dbadm_t, dbadm_r)

Build and load the module and verify that the role is now available:

Code Listing 5.2: Build and verify

# make -f /usr/share/selinux/strict/include/Makefile dbadm.pp
# semodule -i dbadm.pp
# seinfo -r | grep dbadm

Assign Users the Role

Next, assign users the role you just created. You can do this to existing SELinux users, but here we'll create an additional SELinux user for the role.

Code Listing 5.3: Grant a user the role

(Create a new SELinux user called dbadm_u and grant it the dbadm_r role)
# semanage user -a -R "staff_r dbadm_r system_r" dbadm_u

Next map the user(s) to this SELinux user.

Code Listing 5.4: Mapping a set of users to the dbadm_u user

# semanage login -a -s dbadm_u david
# semanage login -a -s dbadm_u laura

Also load in the following policy to support transitioning to the dbadm_r role for the staff_r role:

Code Listing 5.5: Allow transitioning to dbadm_r


In the first semanage command, where we create the dbadm_u user, you might have noticed that we grant it the system_r role. This is because the user is granted the postgresql_admin role, allowing the user to manipulate the postgresql init script:

Code Listing 5.6: User restarting postgresql service

$ ls -Z /etc/init.d/postgresql
system_u:object_r:postgresql_initrc_exec_t    /etc/init.d/postgresql
$ sudo -r dbadm_r -t dbadm_t /etc/init.d/postgresql restart

Thanks to the SELinux support by sudo, you can also mention the role and type in the sudoers file:

Code Listing 5.7: Using the sudoers file for specific roles

david ALL=(ALL) ROLE="dbadm_r" TYPE="dbadm_t" NOPASSWD: /etc/init.d/postgresql [a-z]*

With this setting, the user can just run sudo /etc/init.d/postgresql restart.

By using sudo correctly, you can grant the user full administrative rights over a daemon (including editing files) with a much lower risk of the user elevating his rights to a more system-wide administrative role.

The idea behind this approach is that the user stays within its main domain (like staff_t) but is allowed to perform administrative actions through sudo which allow him to (temporarily) transition to the dbadm_r role.

[ << ] [ < ] [ Home ] [ > ] [ >> ]


View all

Page updated July 7, 2013

Summary: Gentoo Hardened offers a default policy, but this might not allow what you want (or allows too much). In this chapter we tell you how you can tweak Gentoo's policy, or even run your own.

Chris PeBenito

Sven Vermeulen

Chris Richards

Donate to support our development efforts.

Copyright 2001-2014 Gentoo Foundation, Inc. Questions, Comments? Contact us.