Project:SELinux/Development

When planning to help Gentoo Hardened in the development of SELinux policies, or when trying to debug existing policies, this document should help you get acquainted with the necessary resources, trips and tricks to get along.

About this document...
Dealing with Mandatory Access Control is never easy. SELinux might be available by default with Linux, enabling it can provide serious headaches - let alone developing policies for it. Within Gentoo Hardened, we strive to offer a default policy that is flexible enough to match the requirements of most of you (our users) yet remain manageable by the limited number of developers that we have. To ensure that the policy we offer is up to date, we definitely need help from end users and other developers, because developing policies requires intimate knowledge of the products they are written for. With over several thousand packages, this is just not feasible for a handful of us. Hence, this Gentoo Hardened SELinux Development guide.

Within this document, we will try to explain how to set up an environment ready to build policies yourself and provide patches to Gentoo Hardened. We also cover how to deal with malfunctioning domains and even how to create your own, new domains from scratch (if we need to). Further down, we give an overview of the guidelines that we try to follow during the policy developments and finally talk about how to properly create patches and submit them to our bugzilla service.

For those who want to run Gentoo Hardened with their own policies, we've also added a chapter on just that. We know that our policy does not match everyone's requirements, so we definitely want to help you run your own too.

Intended audience
This document is a must-read for everyone willing to provide patches or develop the Gentoo Hardened SELinux policies.

Other SELinux advanced users might find this document interesting as well.

What you need to know
This document does assume prior knowledge on SELinux policies and the way the reference policy works. For those that need a quick recap, here are the highlights...


 * SELinux uses domains and types to differentiate its various security objects. A domain is usually referred to as the security context of a process (or group of processes) whereas a type is usually referred to as the label given to a particular resource (file, directory, network interface, socket, network port, ...).
 * SELinux policies describe what interaction is allowed between a domain and the other domains and types it needs to work with. If no policy allows for a particular activity, then the activity is denied.
 * The structure in which policies are written are called SELinux policy modules which contain three parts: a type enforcement file (with suffix ) that contains the intra-module permissions, an interface file (with suffix  ) that contains the inter-module permissions and a file contexts file (with suffix  ) that contains the file context definitions for all file resources that are labeled with the type or types defined in the module
 * Inter-domain privileges must be declared through functions in the interface file which can then be called by other modules. This includes the necessary permissions to allow domain transitions
 * SELinux uses attributes to make multiple domains manageable. Domains can have certain permissions against all domains or types that are given a particular attribute. Be aware of this when you start assigning attributes to your own types or domains.

Patching the reference policy
Gentoo Hardened builds its policy upon the reference policy as provided by Tresys and managed through a reasonably active community. For Gentoo, the policy is based on a git repository which you can clone and work on. This repository is started with a fresh reference policy snapshot and then updated accordingly. When the reference policy makes a new release, we will rebuild the changes on top of the new release. Although this might be resource intensive, it gives us reasonable assurance that our repository doesn't deviate too much from the reference policy, and it also urges developers to send patches upstream as soon as possible.

Let's create the workplace in which you can develop policies:

As result, you now have a directory called in which you'll find the policies  currently in use by Gentoo Hardened. Changes in this repository are reflected in patches loaded into a. We currently generate (support) two types of patches:

You don't need to do anything specific for these - the Gentoo developers will update any diffs or patches they receive to be compliant to the above. But at least you now know why some developmers might have edited your patch before committing ;-)
 * 1) The majority of patches are directly loaded into an archive (called  ). This archive of patches is applied to the reference policy download (used by our ebuilds) to get it to the state of Gentoo's policy repository.
 * 2) When a patch is needed on the permissions inside a  file, or on the context inside a  file, but no immediate release of a new patchbundle is scheduled, the patch might be applied to a single ebuild. Unlike the patches for the patchbundle, which are relative to the root of the repository, patches on a single module are taken inside the  location.

Navigating the policy workspace
The main location you will work with is. This location is subdivided in categories:

The categorization is arbitrary and serves little purpose other than keeping the modules a bit separated. Each module must have a unique name, regardless of the category! The location has a specific meaning when committing patches to the reference policy as it is a subrepository. However, for Gentoo's this isn't the case.

Inside the categories, the modules are available using their three files

Building a module
To build a module, go to the location where the module code is. You might want to make a copy and work in the copy as the following step will add files and perhaps even directories in the work location. If you don't, then using can help you not commit any unneeded files.

Next, run  with the development Makefile offered by your installation.

You now have a file available which you can load (using   ).

Building the base policy
If you want to build the base policy, run.

The result should be a file that you can load using. However, if you intend to do a bit more than just test this base policy quickly, it is seriously recommended to create your own Gentoo overlay for your own and install that one as installing a base policy is not only about the policy module itself, but also about the include files that will then be stored in.

Introduction
The most likely problem that you are hitting is that a domain does exist in Gentoo Hardened SELinux, but that it isn't functioning as it should. To solve this problem, it is adviseable to use the following sequence of investigations:


 * 1) Is it really SELinux that is restraining your system?
 * 2) Is the problem related to wrong resource labels / security contexts?
 * 3) Is the problem related to intra-module permissions?
 * 4) Is the problem related to inter-module permissions?

Check if SELinux is to blame
Make sure that the problem you are seeing is a SELinux-triggered problem. An easy way to find out is to run SELinux in permissive mode and try again:

This only works if the problem is not to do with a SELinux-aware application (unlike  or   which are linked to the libselinux library). SELinux-aware applications might alter their behavior if SELinux is set on the system regardless of it running in permissive mode or not. A prime example is  (as can be seen in bug #257111 ). But for applications that are not SELinux aware, this is the easiest method to find out if SELinux is to blame or not.

If running your system in permissive mode works around the problem, read on. If it doesn't, check the regular permissions (  'ing the application might be a good idea too) as the problem is most likely not SELinux related.

Get the proper AVC denials
Assuming that we now know that SELinux is to blame, we need to make sure that we get the proper AVC denials. Either locate the proper denials in (or  ) around the time that you encountered the issue, or run   and reproduce the problem.

Analyzing the meaning of the AVC denial is covered by Reading Audit Logs in the Gentoo Hardened SELinux handbook. The denial should give you a pointer where to look for. However, it is possible that no denial is occurring, or at least no relevant ones.

SELinux supports a dontaudit statement, which tells the policy not to log a denial if it occurs. These statements are used by SELinux policy developers when they believe a privilege is attempted by software which isn't needed. To make sure users aren't confused, these denials are hidden from regular sight.

You can disable all dontaudit statements easily though:

Retry getting the proper AVC denials.

The moment you get the denials you are looking for, isolate them and then undo the changes you made earlier (to ensure that your audit or avc log isn't filled with these cosmetic denials):

If you still do not see any denials, then check out the  output for other problems. It is possible that SELinux is not even getting to the point of the policy, which you will not notice by looking at the AVC denials alone. However, the chance of this to happen is very slim - most of the time, you'll find the AVC denials you are looking for.

Deducing the correct security contexts
The next step is to see if we are dealing with the right security contexts. This does require a bit of insight in how both the application (that is failing) and the policy relate to each other. In essence, you want to make sure that the process is running in the right domain and is trying to work on the right target type.

Say you are having issues with SELinux (re)labeling and you notice the following AVC denial:

In this case,  is running in the  domain even though it should run in. So check the security context of the  binary as well as the transition rules:

In the above (forced) situation, the problem is with the security context of the binary - it should have been instead of. Usually, entry points are named similarly (like or  ). If you are not certain about which domain it should be, use

The  utility is extremely powerful to query the SELinux policy (which is currently in memory). I also advise you to use the  switch to see which rules are trigged by certain SELinux booleans:

In the above example, the domain only has write privileges on files labeled  if the  boolean is set (which it currently isn't, otherwise the line would stat with ET instead of DT).

To gain a bit of insight in the various, available domains, use  :

To gain a bit of insight in the (current) file context rules, use  :

Most of the time, fixing domain issues is a matter of relabeling files (or updating the configuration to match the contexts already defined - both work).

Intra-module permissions are missing
It is possible that you get a denial between correct security contexts, but that the permission is just never granted. In this case, you can choose between two things:


 * 1) Enhance the module so that the particular permission is granted, or
 * 2) Enhance the module with an additional type where the permission is granted, and assign this type/label to the related resources

In both cases you will need to edit the module files (most likely the file), build the module, load it, perhaps even relabel the files or the package and retry. It is also a good idea to take a look at upstream (latest refpolicy repository or the repositories of Fedora and co) and see if they have already solved this problem or not.

Granting additional permissions between existing domains is the easiest, but might introduce additional problems: if this permission is only needed in a particular case yet you grant it for all files and resources related to those domains, then you are opening up the policy beyond what is necessary. Often, creating an additional domain or type can be beneficial.

A noticeable example is Portage' support for CVS/SVN/GIT ebuilds (the so-called live ebuilds). These ebuilds get their repository and store it in the location, which was by default labeled  with only read-access for the  domain. However, with those live ebuilds, the domain also needs write privileges to this location. Rather than allowing write privileges to , a new type was created called  for just this location and the rights are copied and enhanced on this additional type.

Inter-module permissions are needed
If the solution for the problem requires permissions between modules, then you need to create the proper interface functions in the target domain and call these functions from the source domain.

Interface functions are the APIs that a module provides towards other SELinux modules when they need to interact with the domains. For instance, the module provides, amongst other functions, the   interface:

mysql_stream_connect interface

The interface declares that the domain passed on as its first (and only) argument gets the rights offered by , which is a macro (defined in  ) that looks like so:

stream_connect_pattern

Modules that need to interact with MySQL through a Unix domain stream socket will need the proper permissions to work with the target type. Modules cannot just set allow statements towards as they do not know this type. Instead, they call the  interface, like the  file does:

Postfix module calling mysql_stream_connect

If the change you need is adding statements (towards existing interface calls) in the file, then you should be able to test it easily by building the changed module and loading it. However, if you need to change the interface of your (or another) module itself (in the file) you will eventually need to rebuild the base policy and even provide and install a new  package as this package is responsible for placing the interfaces in.

This is one of the reasons why the package in Gentoo Hardened is often updated. Updates on existing interface files (i.e. for already existing modules) always need to go through this package.

Reuse existing domains
If you are facing problems because you run an application which has no domain itself (and hence is probably running in the,  or  domains - or even tries to run in the  domain), you will need to create one. But before we do that, it might be possible that the application can work within the domain definition of a different application.

One example here is lighttpd. This lightweight HTTPd service "uses" the definitions offered by the module. By marking its executable file it runs in the  domain and uses the same policy like Apache. By labeling the files according to the definitions (but now for lighttpd) it might Just Work (TM).

Reusing existing domains requires that you at least consider the following aspects:


 * Will the application run on the same system as the application for which the domain is originally intended? If so, then both might run in the same domain (and as such have more privileges towards each other than intended) which might not be what you want.
 * Do you need to enhance (read: add additional privileges) the master domain? If so, make sure that you don't add more privileges than the original domain would ever need to the extend that these privileges become a security risk.

(Do Not) Copy from existing domains
If reusing existing domains introduces too many risks, you'll need to create a new domain for the application. Many people would be inclined to copy the domain definition of a similar application and work from there. Although this is a viable approach, it is considered a bad practice because you start by providing privileges to the domain that are never needed, and removing privileges from a domain later is very difficult. Even more, if you are not the author of the modules, most developers will not even try to remove them as they assume that the author of the domain had a good reason to add it in the first place. This is one of the reasons why upstream takes great care in accepting patches - they must be properly documented before they are accepted.

Instead, create a domain from scratch but take a close eye on the domain you belief is very similar. Issues that arise during the module development might be quickly resolved by looking at how the original domain is defined.

Starting from scratch
To start the development of a new module from scratch, first identify the domain(s) you want to have. An application that, in its entire lifespan only constitutes of a single process, will most likely only have one domain. For instance, the Skype client will have just. However, applications that have multiple processes running might need multiple domains too. For instance, the Postfix application runs a master, queue manager and pickup service , but depending on the commands you execute, it will also have (short-lived) processes running as  ,  , etc.) It is considered a best practice to start with a fine-grained model for domains and only later decide if merging multiple domains into one is beneficial. Splitting domains later is more difficult. Don't forget to look at the client-side aspect too!

Next, define the types that each domain interacts with. This of course includes the binary (like ) but do not forget resources like


 * The configuration file(s) in (f.i.  )
 * PID files (f.i. )
 * Spool files (f.i. )
 * Variable data files (f.i. )
 * Log files (f.i. )
 * Cache files (f.i. )
 * (User) content files (f.i. and  )

Also, try to separate types that are used by other domains as well. This way, the other domains can only interact with those files or resources that are labeled accordingly, rather than interact with a broad spectrum of files. The distinction that the module makes between system-provided content (like phpmyadmin files) and user-provided content (in the  directory in the users' home directories) seems (and is) very logical, but one could wrongly say that for Apache itself, the access controls are the same. Although that might be true, both types are clearly used in different ways so this mandates the use of different domains.

Once you have defined those types too, start writing down the intra-domain permissions. Right now is a good time to look at other modules to see how they do things. Start with defining the accesses towards the domains.

Snippet from the spamassassin module

This small snippet defines many things. The first two lines just mention the new types (the domain and  type). The  interface marks  as an application domain type (it gets the  and  attributes and a few default permissions (like allowing that it sends SIGCHLD and SIGNULL to init). It also marks  as an applications' executable type (  and  attributes) so that it can be executed by regular users (these domains have execute rights against all resources that have the  attribute set. Finally, it marks the domain as a constrained domain for user-based access controls. In other words, if SELinux users and  launch the application in  domains, then the domains are segregated from each other (the intra-domain rules inside  are only valid for communication within the same SELinux user, not between SELinux users).

Attributes are an important aspect in SELinux policy development. They make managing the domains easier, but you should always consider the implications when you add an attribute to one of your types. It usually means that a whole lot of permissions are suddenly granted between other domains and yours.

Next, set the proper intra-domain permissions. For instance, allow your domain to read its configuration files as well as more access inside its own location:

Snippet from openca module

The majority of work in developing SELinux policy modules is using and choosing the right interfaces. Having a few functions available to browse through all the available information is always interesting, so you might want to use the following function definitions (definitely not mandatory - this is only to help people skim through the policy definitions):

SELinux policy development function definitions

These functions can then be used to find the information / interfaces you are looking for. Perhaps you need the application to read the postfix configuration files, so look for potential interfaces that use the postfix_etc_t type:

Same thing if you want to look for the correct macro definition (usually, if you notice something but you cannot find it as an interface, then it is most likely a macro):

Suppose you need to read, write, connect, ... to a socket:

To see what the ps_process_pattern is about:

As we strive to bring most of our patches upstream, please do consider the contribution guidelines of the reference policy project. The project has a documented style guide, naming convention and an online API reference (for the various interfaces).

Note that, the moment you create a new module, you'll need to create the proper role interfaces (if it is an application that is directly called from a user domain). Take a look at  and how it is used in the  and  role definitions.

Testing new modules
When you test your application, test it in as many ways as possible. If your application is a command-line application, run it both from a regular terminal (tty) as well as a virtual one (in an xterm). See if it still works if you run it in a screen session. Try out all functions and features that the application supports.

This rigorous testing is necessary because SELinux denies everything that isn't explicitly allowed. If you do not test certain features, chances are that the module does not provide the necessary permissions and as such, users will be impacted.

To test out a new module, load it and relabel the files affiliated with the application (either through   or using   ). Consider the following testing activities if applicable (not all domains are interactive domains, so please read the activities with your domain definition in mind):


 * Sending signals to the application (if you need to be able to kill it, try killing it)
 * Run it both as a regular user as well as administrative users (if applicable). If your domain needs to support unconfined domains/users, run it from an unconfined user domain too.
 * Run it from a terminal, console, screen, sudo, ...
 * Change the applications' configuration file (including rendering it useless with syntax errors) and look at the applications' behavior. Especially syntax failures as that might trigger the application to log things at places that you haven't discovered earlier.

Gentoo Hardened SELinux policy
To streamline the policy development efforts, Gentoo Hardened has a SELinux Policy document explaining the principles used during policy development and the implementation guidelines we strive to follow during development.

Such a policy is important because we want to have a consistent security policy that users and developers can relate to. By following the policy, we hope that other developers can quickly jump in and work on it further.

File context patches
If you are able to fix a problem by adding the proper file contexts (using  ), please consider the following:


 * If the location for which you set the context deviates from the standard location as either intended by the project or Gentoo itself, it might be best to document it in the forums or elsewhere. We will not change file contexts to match every ones configuration, unless the file context change is apparent for each installation.
 * Developers might not immediately push file context changes in new policy module packages to keep the amount of policy module changes low. Instead, these changes can be stacked and pushed when other changes occur as well.

If you believe that the change is needed for everyone using Gentoo Hardened with SELinux, create a bugreport and assign it to. In the bugreport, mention the file context you think is necessary and why.

Policy patches
Policy patches are best generated through the  approach.

Edit the file(s), like openct.te

Attach this patch to the bugreport explaining why it is needed. If you think the patch itself is not obvious, make sure that the necessary comments are in place inside the patch for future reference. If your patch contains many steps, it might be best to use separate commits in the repository so that the logic of the patch can be easily followed.

Please have a separate patch file per module (do not combine multiple modules in a single patch).

Fully Running Your Own Policy
To run a different policy than the one Gentoo Hardened providers, there are two methods to do so.

The first method is to use a different git repository and use live ebuilds. In this case, you point to your own git repository that contains the SELinux policy (which still has to be a reference policy clone) and use the live ebuilds without further adjustements.

The second method is to build versioned packages, overriding the Gentoo ones.

Using a different repository
To use a different repository, point the  variable to your git repository.

If you use different branches, set the  to the branch name you want to use.

Next, make sure that your system uses live ebuilds. This is accomplished by setting the appropriately:

Creating a local overlay
If you want to use your own policy rather than Gentoo's, we seriously recommend to use a local overlay which uses the same package names and constructs. This allows your policy to integrate properly with the other Gentoo packages (which might depend on the SELinux packages). For instance, when you install openldap, it will still properly depend on the package even if you provide it completely.

To do so, first create a local overlay and copy the content of the category inside it.

Next, tell Portage to not synchronise the category of the main tree anymore. To do so, create the file with the following content:

Rsync exclusion information

Finally, add your current overlay by editing :

Editing make.conf

From now onwards, Gentoo Portage will only use your local overlay (you can remove if you don't want Portage to even reuse the current set of packages.

Updating module packages
To create or update a module package, you can use the following skeleton for the ebuilds:

Skeleton for ebuilds, example for postfix

The patch(es) that you can put in the location (and referred to in the   ) should be made as defined earlier in this document. You can put multiple patches in this variable if you want.

Don't forget to run  with every change, and run   to check for potential mistakes.

Updating base package
To provide updates on the base policy, it is recommended to keep all patches you made centrally in a directory (say ). When you want to create a new and  release, create a patchbundle from your patch directory, put the bundle online somewhere, create the updated ebuild and try it out.

However, the moment you modify the base package(s), you will also need to change the file and the base policy ebuild. This is because, by default, the ebuilds will look for their files in the Gentoo download locations instead of wherever you would like to store the patches and sources.

Put a copy of the eclass inside your overlay and edit the  definition to point to your new location. Then edit the and  ebuilds (inside your overlay again) and change their   too.

Once this is done, you can go onwards with building a new base policy package.

Add the patches you want to include, cfr Submitting Patches

Then, create a new patch bundle

Finally, bump the revision of the ebuilds in the overlay:

Update references to the patchbundle and dependencies (like BASEPOL):

Don't forget to run  and. You can then install and test it out.

What is a Third Party Policy?
Most users who are responsible for handling the SELinux policies on one or more systems will most likely be writing their own policies too. Be it because the policy offered by Gentoo doesn't provide a policy for the package yet, or because the policy is needed for a local, non-Gentoo-provided package, it is still important for the administrator to easily manage these added policies on the systems.

An administrator can perfectly build and load the policy once and then let it be. After all, once a SELinux policy is loaded, it will be reloaded after every (re)boot. But within Gentoo, we support simple ebuilds that install the policy on the system for you.

Creating an Ebuild
Assuming you already have an overlay, all you need to do is create a package for your third party policy. For instance, a policy for an Oracle database might be called so you would then create a package.

Then create the following ebuild:

Third party policy ebuild

Finally, put the three policy-related files (assuming you have three) in the directory of the package.

With this in place, you can now install the package and it will install the SELinux policy module. If the package provided an interface, it will also be added to the location (or  or whatever your SELINUXTYPE is).