An SElinux module (4): building and debugging

Having written the type enforcemnt, interface and file context files the next step is to put them together into a single policy module. This is done by "compiling" the three files into a single policy file that can be inserted into the complete SElinux policy. Once inserted, the process of debugging the module begins.

Building the lighttpd module

When using eclipse with the SLIDE plugin building the module can happen automatically (this is actually the process I use). Here, I will outline how to do it by hand as it is just as simple and is more general.

As part of the selinux-policy-devel rpm a "make" file is provided (in fedora, /usr/share/selinux/devel/Makefile). To build the single combined policy file (that has the extension of .pp) all we need to do is run the make command using the -f /usr/share/selinux/devel/Makefile argument to direct it to the SElinux makefile. The make command needs to be run in the directory containing the lighttpd.te, lighttpd.fc and lighttpd.if files.

#make -f /usr/share/selinux/devel/Makefile Compiling targeted lighttpd module /usr/bin/checkmodule: loading policy configuration from tmp/lighttpd.tmp /usr/bin/checkmodule: policy configuration loaded /usr/bin/checkmodule: writing binary representation (version 6) to tmp/lighttpd.mod Creating targeted lighttpd.pp policy package rm tmp/lighttpd.mod tmp/lighttpd.mod.fc

This creates the combined policy file lighttpd.pp that is ready to be inserted into the running SElinux targeted policy. If there are errors in the source files the make process will fail and an error message describing the error should be printed.

Inserting the lighttpd module

The compiled module, lighttpd.pp, can be inserted into the running SElinux policy using the semodule command. This command allows modules to be inserted into, removed from or updated in the current running policy. If the lighttpd daemon is already running it should be stopped so that it makes the transition to the correct type (lighttpd_t) when it is next started once the policy module is installed. This would not be necessary if the lighttpd daemon already had the correct type (e.g. if we were "updating" the module). Stopping the daemon can be done through the init scripts (/etc/init.d/lighttpd stop). To insert the module we use the -i argument to the semodule command

#semodule -i lighttpd.pp

Given this generates no errors, the module is now inserted into the running SElinux policy. However, the task of inserting the module is not yet complete. We must ensure that all the files that should be labelled with lighttpd specific contexts are correctly labelled. By looking at the lighttpd executable file, we can see that it has not yet been labelled with the lighttpd_exec_t type:

#ls -Z /usr/sbin/lighttpd -rwxr-xr-x root root system_u:object_r:sbin_t /usr/sbin/lighttpd

All of the files must be labelled with contexts as we specified in the file context file. This can be done by using the restorecon command. As our lighttpd module is now part of the running policy, this command will know how to correctly label the files we tell it to label.

#restorecon -r /usr/sbin/lighttpd #restorecon -r /var/log/lighttpd #restorecon -r /usr/lib/lighttpd #restorecon -r /etc/lighttpd

Note, by using the the -r option restorecon will recursively descend into directories labelling everything correctly. We could have used the command "restorecon -r /" and relabelled the entire file system to match the changed SElinux policy, but it is quicker to target the parts of the file system you know need to have new or altered labels.

Debugging the module

After restarting the lighttpd daemon using the init scripts (note, it is important to use the init scripts as that is how the transition rule was designed to ensure the process gets assigned the correct type), we can check it is running correctly with the ps command:

#ps auxZ | grep lighttpd user_u:system_r:lighttpd_t lighttpd 9748 0.0 0.0 4604 564 ? S 14:30 0:00 /usr/sbin/lighttpd -f /etc/lighttpd/lighttpd.conf

This shows that the lighttpd daemon is running with the lighttpd_t type and is thus restricted by the module type enforcement rules.

The first step in the process of debugging the module is to see what access violations the module may be generating. The setroubleshoot facility is one of the best ways of monitoring and being notified of access violations. Looking at the logs directly is also useful in the initial stages when a new module or policy is likely to generate lots of violations. Fedora uses the audit system to collect system and SElinux logs. This provides useful tools for searching through and accessing the log entries.

After observing the process for some time, one of the access violations that this module generates is revealed in the audit logs:

#ausearch -ts 14:00 | grep avc ... type=AVC msg=audit(1179653298.741:67): avc: denied { read } for pid=3795 comm="prelink" name="" dev=dm-0 ino=200000 scontext=system_u:system_r:prelink_t:s0-s0:c0.c1023 tcontext=system_u:object_r:lighttpd_modules_t:s0 tclass=file ...

(Note, I used the -ts 14:00 option, which shows all logs since 2pm, as I knew the entries happened just after that time). These violations are not generated by the lighttpd process, rather, another component of the running fedora system — prelink. prelink is a process that accesses shared objects and potentially makes them more efficient for startup/loading (see the man page for more information). What is happening is that prelink tries to access the lighttpd shared object module files which are now labelled with the lighttpd_modules_t type and it can nolonger read them. This demonstrates the complexity of the SElinux module system. Introducing our module and its associated types has "broken" a completely different process on the system.

The first thought that comes to mind is "does the SElinux prelink policy provide an interface for our module to use?". Before we follow this approach, lets look at one of the particularly useful analysis tools available. The audit2allow tool turns access vector cache (avc) error logs into type enforcement rules that would solve or prevent those violations. It can act as a unix pipe so we can feed it the error logs and see what rules it comes up with for us:

#ausearch -ts 14:00 | grep avc | audit2allow #============= prelink_t ============== allow prelink_t lighttpd_modules_t:file read;

The audit2allow tool has produced a nice, neat and correct rule that would prevent such access violations in the future. The rule simply says processes with the domain type prelink_t have access to read files with the type lighttpd_modules_t. The option -R causes audit2allow to generate output suitable for policy and interface writing:

#ausearch -ts 14:00 | grep avc | audit2allow -R require { type prelink_t; type lighttpd_modules_t; class file read; } #============= prelink_t ============== allow prelink_t lighttpd_modules_t:file read;

In this example, this option only added the "require" block (which specifies dependencies in a similar manner to the gen_require macro used in our interfaces). In some cases it may suggest interfaces or macros that may suit our needs, but it often doesn't get the correct solution so, in general, audit2allow output is only a guide. The rule it is suggesting should not be used in our lighttpd module as it directly references a type not declared in our module. It would be better if the prelink module provided an interface for our module to use. Looking through the prelink interface file (provided as part of the devel rpm) we find the interface prelink_object_file which "Makes the specified file type prelinkable". Adding this interface to the lighttpd.te file solves this access violation issue:

# use interface to allow prelink to prelink the object modules (admin/prelink.if) # "Make the specified file type prelinkable" prelink_object_file(lighttpd_modules_t)