Calling refpolicy interfaces

Writing up SELinux policies entirely using the standard language constructs offered by SELinux is doable, but it is prone to error and not manageable in the long term. To support a more simple language construct, the reference policy project uses a set of M4 macros that are expanded with the underlying SELinux policy statements when the policy is built.

The API that policy developers can use can be consulted online, but most systems also have this information available onsite at /usr/share/doc/selinux-*/. Finding proper interfaces requires some practice though, which is why one of the functions that we installed earlier (as part of the development environment) simplifies this for us.

In this recipe, we are going to edit the mylogging.te file we generated earlier with the right reference policy macro.

How to do it…

To use reference policy interfaces in an SELinux policy module, the following approach can be taken:

  1. Use the sefinddef function to find permission groups or patterns to write to files:
    ~$ sefinddef 'file.*write'
    define(`write_files_pattern',`
     allow $1 $3:file write_file_perms;
    
    define(`write_file_perms',`{ getattr write append lock ioctl open }')
    
    
  2. Use the seshowdef function to show the full nature of the write_files_pattern definition:
    ~$ seshowdef write_files_pattern
    define(`write_files_pattern',`
     allow $1 $2:dir search_dir_perms;
     allow $1 $3:file write_file_perms;
    ')
    
  3. Use the sefindif function to find the interface that will allow writing to named_conf_t:
    ~$ sefindif 'write_files_pattern.*named_conf_t'
    contrib/bind.if: interface(`bind_write_config',`
    contrib/bind.if: write_files_pattern($1, named_conf_t, named_conf_t)
    
  4. Now, update the mylogging.te file to use this function. The file should now look like the following:
    policy_module(mylogging, 0.2)
    gen_require(`
      type syslogd_t;
    ')
    bind_write_config(syslogd_t)

    Note

    Note the use of the backtick (`) and single quote ('). Whenever definitions are used, they need to start with a backtick and end with a single quote.

  5. Rebuild and reload the policy module, and then rerun the tests we did earlier to verify that this still allows us to write to the named_conf_t labeled file.

How it works…

One of the principles behind the build system of the reference policy is that an SELinux policy module should not directly mention SELinux types that are not related to that module. Whenever a policy module needs to define rules against a type that is defined by a different module, interfaces defined by that different module should be used instead.

In our example, we need the interface used by the BIND SELinux policy (which handles the BIND-named daemon policy rules); this interface allows us to write to the BIND DNS server configuration file type (named_conf_t). We can check the online API, the API documentation in /usr/share/doc/selinux-*, or just guess the interface name. However, in order to be certain that the interface does what we need, we need to query the interface definitions themselves.

That is where the sefinddef, seshowdef, sefindif, and seshowif functions come into play. These functions are not part of any SELinux user space—they are provided through the functions.sh file we installed earlier and are simple awk/grep/sed combinations against the SELinux policy files.

With sefinddef (the SELinux find definition), we can search through the support macros (not related to a particular SELinux policy module) for any definition that matches the expression given to it. In this recipe, we gave file.*write as the expression to look for. The seshowdef (SELinux show definition) function shows us the entire definition of the given pattern.

The sefindif (SELinux find interface) function then allows us to find an interface that the SELinux policy provides. In this recipe, we used it to search for the interface that allows a domain to write to the BIND DNS server configuration files. There is also a seshowif (SELinux show interface) function that shows us the entire interface definition like the following:

~$ seshowif bind_write_config
interface(`bind_write_config',`
 gen_require(`
 type named_conf_t;
 ')
 write_files_pattern($1, named_conf_t, named_conf_t)
 allow $1 named_conf_t:file setattr_file_perms;
')

This example interface nicely shows how interfaces are handled by the SELinux reference policy build system. Whenever such an interface is called, one or more arguments are given to the interface. In our case, we passed on syslogd_t as the first (and only) argument.

The build system then substitutes every $1 occurrence in the interface with the first argument, effectively expanding the call to the following code:

write_files_pattern(syslogd_t, named_conf_t, named_conf_t)
allow syslogd_t named_conf_t:file setattr_file_perms;

The call to write_files_pattern is then expanded with the definition we saw earlier.

For the policy developer, this is all handled transparently. The sources of the SELinux policy file stay well-formatted and only call the interfaces. It is at build time that the expansion of the various interfaces is done. This allows developers to have nicely segregated, compartmentalized policy definitions.

See also