Nuts and Bolts SELinux Lead image: Lead Image © Amy Walters, 123RF.com
Lead Image © Amy Walters, 123RF.com
 

Writing SELinux modules

Creative Security

Much has happened in the field of SELinux in the last few years, including the development of new usability features. The current release makes it easier to write SELinux policy modules yourself. By Thorsten Scherf

SELinux struggles to cast off its image as difficult to maintain and the cause of potential application problems. Yet in recent years, much has changed for the better, especially with regard to usability. For example, modules have replaced its monolithic set of rules. If you want to develop a new SELinux module, three files are typically necessary for this purpose.

Three Files for an SELinux Module

A type enforcement (.te) file stores the actual ruleset. To a large extent, it consists of m4 macros, or interfaces. For example, if you want to access a particular service's resources, such as the logfiles, the service provides a corresponding interface for this purpose. If you want your own application to access these resources, you can draw this on the service's interface without having to deal with the logfile details. For example, you do not need to know the logfile's security label, because the interface abstracts access.

In addition to the actual ruleset, a second file is necessary. This file, known as the file context (.fc) file, defines the security label of your own application for which you are developing a policy. Optionally, interfaces can be made available as necessary. This is always helpful if other applications or services need to access their own resources. The m4-based interfaces are saved in a file with the .fc suffix.

To create a policy module from these files, you first have to create a file in .mod format. The checkmodule tool is used for this purpose. As a result, you receive a file containing the complete ruleset from the .te file. The m4 macros have already been resolved, but the information from the .fc file is missing for the final policy module. You can add this using the semodule package, resulting in the finished policy module, then load it in the Linux kernel's security server. To simplify the process, the SELinux development tools provide a makefile with which you can create a complete policy module in a single operation.

Listing 1 shows an example. Here, the certmonger service needs access to the directory of the Pegasus CIM broker. This does not require a file or interface file. Armed with the makefile, you can easily create the policy module and load it in the security server. With the help of the sesearch tool, you can then verify that the kernel is actually aware of this new rule:

Listing 1: SELinux Module

01 require{
02     type certmonger_t;
03     type pegasus_conf_t;
04 }
05 allow certmonger_t pegasus_conf_t:file {
06     ioctl read getattr lock open
07 } ;
08 # make -f /usr/share/selinux/devel/Makefile mypegasus.pp
09 # semodule -i mypegasus.pp
# sesearch --allow -s certmonger_t -t pegasus_conf_t
Found 1 semantic av rules: allow certmonger_t pegasus_conf_t : file {
    ioctl read getattr lock open
  } ;

What actually happens in the background is this: The kernel itself doesn't use the module you created, but instead accesses a policy.XX file. The XX suffix stands for the SELinux version that the kernel supports. This file is based on a basic policy, and all policy modules that are available and active on the system. The file is stored below /etc/selinux:

# ls -lh /etc/selinux/targeted/policy/total 3.6M
-rw-r--r--. 1 root root 3.6M Mar 3 13:28 policy.29

The modules are available in a policy store below /var/lib/selinux/targeted/active/modules / (as of libselinux version 2.4). On older systems, they resided below /etc/selinux/targeted/modules/active/.

Compiling is Quicker

Since each change requires you to create a new policy file (policy.XX), it takes several minutes to compile the module.

The developers addressed this issue with libselinux version 2.4. Policy modules can now be developed in a new language, the Common Intermediate Language (CIL). A compiler translates existing modules (*.pp files) into CIL, and they can be loaded directly into the security server. Although the individual modules again need to be merged to a single policy file, the process will be much faster than with the pp modules used previously. In testing, the CIL format modules load up to 75 percent faster. Listing 2 shows an example of how to convert the newly created policy module to the CIL format. If necessary, the generated file can be modified, or loaded directly into the security server:

# semodule -X 500 -i mypegasus.cil

Listing 2: Module in CIL Format

pp | tee mypegasus.cil
(roleattributeset cil_gen_require system_r)
(typeattributeset cil_gen_require certmonger_t)
(typeattributeset cil_gen_require pegasus_conf_t)
(allow certmonger_t pegasus_conf_t (file (ioctl read getattr lock open)))

The -X option sets a priority for the module. This is also a new feature of the latest version of libselinux. For example, you can load modules of the same type in the server to override the distributor's module:

# semodule --list=full | grep mypegasus
500 mypegasus cil
400 mypegasus pp

If necessary, you can also remove (semodule -r) or temporarily disable (semodule -d) the old module.

In addition to the improved performance when loading and unloading the SELinux modules in CIL format, there is also another advantage. The previously used pp modules were all based on a high-level language, which can be compiled into CIL format with the appropriate compiler. This allows other frameworks to contribute their own rulesets in any language. As long as you have a compiler that translates to CIL format, the SELinux security server can easily use any of these rulesets (Figure 1). Of course, you can write your policy module directly in CIL format. For information about the language and its syntax, see the SELinux wiki [1].

Policy modules can now be loaded into the security server in CIL format.
Figure 1: Policy modules can now be loaded into the security server in CIL format.

Conclusions

The new CIL format not only boosts the performance of SELinux modules, but improves their readability and comprehensibility. Thanks to the new module priorities, it is easily possible to extend existing rulesets. The module with the highest priority simply overrides the instructions from another module with the same name, but with a lower priority. This is interesting for upstream projects where a new policy exists that is not part of the Linux distribution. Users can thus download the upstream policy and load it with a higher priority than the distribution policy. This certainly helps to facilitate the handling of SELinux policies.