SELinux Cookbook
上QQ阅读APP看书,第一时间看更新

Building a simple SELinux module

Now that we have a development environment, it is time to create our first SELinux policy module. As its purpose does not matter at this point, we will focus on a privilege that is by default not allowed (and rightfully so) yet easy to verify, as we want to make sure that our policy development approach works. The privilege we are going to grant is to allow the system logger to write to a logfile labeled named_conf_t (the type used for the configuration of the BIND DNS server—known as named).

Note

Building SELinux policy modules is to extend the existing policy with more rules that allow certain accesses. It is not possible to create a policy module that reduces the allowed privileges for a domain. If this is needed, the policy module needs to recreate and substitute the existing policy (and thus, a distribution-provided policy will need to be removed from the system).

Getting ready

Before we get started, we first need to make sure that we can test the outcome of the change. A simple method would be to change the context of the /var/log/messages file (or another general logfile that the system logger is configured to use) and send messages through the system logger using the logger command:

~$ logger "Just a simple log event"
~$ tail /var/log/messages

Verify that the message has indeed been delivered by looking at the last lines shown through the tail command. Then, change the context and try again. The event should not be shown, and a denial should be logged by the audit daemon:

~# chcon -t named_conf_t /var/log/messages
~$ logger "Another simple log event"
~$ tail /var/log/messages
~# ausearch -m avc -ts recent

With this in place, we can now create our first simple SELinux module.

How to do it…

Building a new SELinux policy is a matter of going through the following steps:

  1. Create a file called mylogging.te inside ${DEVROOT}/local with the following content:
    policy_module(mylogging, 0.1)
    gen_require(`
      type syslogd_t;
      type named_conf_t;
     ')
    # Allow writing to named_conf_t files
    allow syslogd_t named_conf_t:file { getattr append lock ioctl open write };
  2. Copy or link the Makefile file available in /usr/share/selinux/devel/ or /usr/share/selinux/mcs/include/ (the exact location is distribution specific) to the current directory:
    ~$ ln –s /usr/share/selinux/devel/Makefile
    
  3. Build the SELinux policy module through this Makefile. The target is to name the (target) policy module with the .pp suffix:
    ~$ make mylogging.pp
    
  4. Switch to the root user, and if we are logged on through an unprivileged SELinux domain/role, switch to the sysadm_r or secadm_r role (this is not needed if the current user domain is already sysadm_t or unconfined_t):
    ~$ sudo –r sysadm_r –t sysadm_t -s
    
  5. Now, load the SELinux policy module (which will immediately activate the newly defined SELinux policy):
    ~# semodule –i mylogging.pp
    
  6. Verify that the newly defined SELinux policy is active by generating a new log event and by looking at the logfile to see if it has indeed been registered.
  7. Commit the newly created files to the repository:
    ~$ cd ${DEVROOT}/local
    ~$ git add mylogging.te Makefile
    ~$ git commit –m 'Adding mylogging.te which allows the system logger to write to the named configuration file type named_conf_t'
    

    When verified, reset the context of the logfile using restorecon /var/log/messages and remove the policy module from the subsystem using semodule -r mylogging. After all, we do not want this rule to stay active.

How it works…

There are three important, new aspects of SELinux policy development that we got in touch with in this recipe:

  • A policy source file called mylogging.te was created
  • A generated, binary policy module called mylogging.pp was built
  • The binary policy file, mylogging.pp, is added to the active policy store on the system

At the end, we committed the file to our local repository. Using version control on policy files is recommended in order to track changes across time. A good hint would be to tag new releases of the policies—if users ever report issues with the policy, you can then ask them for the SELinux policy module version (through semodule –l) and use the tags in the repository to easily find rules for that particular policy module.

In the remainder of this book, we will no longer use git add/commit so that we can focus on the SELinux recipes.

The policy source file

In the recipe, we created a policy source file called mylogging.te, which contains the raw SELinux policy rules. The name, mylogging, is not chosen at random; it is a common best practice to name custom modules starting with my and followed by the name of the SELinux policy module whose content we are enhancing (in our case, the logging module that provides the SELinux policy for everything that is system-logging related). The .te suffix is not just a convention (referring to the type enforcement part of SELinux); the build system requires the .te suffix.

The policy module rules start with the policy_module(…) call, which tells the build system that the file will become a loadable SELinux policy module with the given name and version. This name and version will be displayed by the semodule command if we ask it to list all the currently loaded SELinux policy modules:

~# semodule –l
aide 1.8.0
alsa 1.13.0

mylogging 0.1

The best practice is to keep all rules for a single domain within a policy module. If rules for multiple unrelated domains are needed, it is recommended that you create multiple modules, as this isolates the policy rules and makes modifications more manageable.

In this simple example, we did not follow this best practice (yet). Instead, we told the SELinux subsystem that the policy is going to be enhanced with an access vector for syslogd_t. The access vector here is to allow this domain a set of permissions against files that are labeled as named_conf_t.

The binary policy module

When we called the Makefile file, the underlying scripts built a loadable binary SELinux policy module. Such files have the .pp suffix and are ready to be loaded into the policy store. The Makefile file called might not be installed by default; some distributions require specific development packages to be installed (such as selinux-policy-devel in Fedora).

There is no nice way of retrieving the sources of a policy if we are only given the .pp file. Sure, there are commands such as semodule_unpackage and sedismod available, but these will only give a low-level view of the rules, not the original .te code. So, make sure to have backups, and as we saw in the example, use a versioning system to control changes across time.

Loading a policy into the policy store

To load the newly created policy into the policy store, we called the semodule command. This application is responsible for managing the policy store (which is the set of all SELinux policy modules together with the base policy module) and linking or unlinking the modules together into a final policy.

This final policy (which can be found at /etc/selinux/mcs/policy) is then loaded into the SELinux subsystem itself and enforced.

There's more...

Throughout this book, the build system used is based on the reference policy build system. This is a collection of M4 macros and affiliated scripts that make the development of SELinux policies easier. This is, however, not the only way of creating SELinux policies.

When visiting online resources, you might come across SELinux policy modules whose structure looks like the following:

module somename 1.0;
require {
  type some_type_t;
  type another_type_t;
}
allow some_type_t another_type_t:dir { read search };

Tip

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

These are policy files that do not use the reference policy build system. To build such files, we first create an intermediate module file with checkmodule, after which we package the module file towards a loadable binary SELinux policy with semodule_package:

~$ checkmodule -M –m –o somename.mod somename.te
~$ semodule_package –m somename.mod –o somename.pp

To keep things simple, we will stick to the reference policy build system.

See also

Many topics and areas have been touched upon in this recipe. More information can be found at the following resources: