Chapter 10. Handling SELinux-aware Applications
In this chapter, we will cover handling of SELinux-aware applications through the following recipes:
Controlling D-Bus message flows
Restricting service ownership
Understanding udev's SELinux integration
Using cron with SELinux
Checking the SELinux state programmatically
Querying SELinux userland configuration in C
Interrogating the SELinux subsystem code-wise
Running new processes in a new context
Reading the context of a resource
For most applications, the SELinux subsystem in the Linux kernel is capable of enforcing security controls without further interaction with other applications and components. However, there are actions that cannot be handled by the SELinux subsystem autonomously. Some applications execute commands for specific users, but the target domain cannot be deduced from the path of the application that is itself being executed, making type transitions based on the label impossible.
One solution for this problem is to make the application SELinux-aware, having the application interrogate the SELinux subsystem as to what should be the context of the newly executed application. Once the context is obtained, the application can then instruct the SELinux subsystem that this context can be assigned to the process that will be launched next.
Of course, it isn't only about deciding what context a process should be in. Applications can also check the SELinux policy and act on the policy themselves...
Controlling D-Bus message flows
D-Bus implementation on Linux is an example of an SELinux-aware application, acting as a user space object manager. Applications can register themselves on a bus and can send messages between applications through D-Bus. These messages can be controlled through the SELinux policy as well.
Before looking at the SELinux access controls related to message flows, it is important to focus on a D-Bus service and see how its authentication is done (and how messages are relayed in D-Bus) as this is reflected in the SELinux integration.
Go to /etc/dbus-1/system.d/
(which hosts the configuration files for D-Bus services) and take a look at a configuration file. For instance, the service configuration file for dnsmasq
looks like the following:
Restricting service ownership
Applications that register themselves on the bus own a service name. The uk.org.thekelleys.dnsmasq
service name is an example of this. The D-Bus policy, declared in the busconfig
XML file at /etc/dbus-1/system.d/
(or session.d/
if the service is for the session bus instead of system bus) provides information for D-Bus to decide when taking ownership of a particular service is allowed.
Thanks to D-Bus' SELinux integration, additional constraints can be added to ensure that only authorized applications can take ownership of a particular service.
To restrict service ownership through the SELinux policy, follow the ensuing set of steps:
Inside the D-Bus configuration file of the service, make sure that the own
permission is properly protected. For instance, make sure only the root
Linux user can own the service:
If the runtime service account can differ, it is possible...
Understanding udev's SELinux integration
The udev device manager is responsible for handling device files inside the /dev/
structure whenever changes occur. As many device files have different contexts, without any SELinux awareness, the udev policy would need to be enhanced with many, many named file transitions. Such a named file transition, for a device /dev/mydevice
towards the mydevice_t
type, would look like the following code:
However, when /dev/mydevice1
, /dev/mydevice2
, and so on need to be labeled as well, then each possible name would need to be iterated in the policy (named file transitions do not support regular expressions). Luckily, udev is SELinux-aware, making it unnecessary to create policy enhancements for every device file.
This recipe shows us when additional policy enhancements are needed and when not.
To understand how udev's SELinux integration works, the following decision criteria can be followed:
Another example of an SELinux-aware application is cron. Well, actually a set of cron implementations, as there is not a single cron application. Examples of cron implementations are vixie-cron, cronie, and fcron.
The cron implementations invoke commands for (and as) a particular Linux user. As these commands are not set in stone (the main purpose of cron is to allow any command to be run for a particular user or even for the system itself), it is not possible to easily create a policy that is sufficiently fine-grained to accommodate all features provided by cron. After all, for SELinux itself, there is no difference between cron calling a command for one user or another: all that is involved is the cron domain (crond_t
) and the target type of the command (such as bin_t
).
For this reason, many cron implementations are made SELinux-aware, allowing the cron implementation to select the proper target context.
To properly interact with an SELinux-aware cron...
Checking the SELinux state programmatically
If the need arises to make an SELinux-aware application, then several languages can be used. The libselinux
package usually provides bindings for multiple programming and scripting languages. In the next set of recipes, the C programming language will be used as an example implementation.
The first step to support SELinux in an application is to check the SELinux state. In this recipe, we will show how to create an application that links with the libselinux
library and checks the state of SELinux.
As we are going to update a C application, this set of recipes will assume basic knowledge of C programming. An example C application that uses all the input from this (and other) recipes can be found in the download pack of this book.
In order to link with libselinux
and to check the current SELinux state, the following set of steps can be used:
Create a C application code file and refer to the SELinux header files through a compiler...
Querying SELinux userland configuration in C
In this recipe, we will be querying the SELinux userland to obtain the default context for a given user based on the context of the current process. The process is responsible for gathering the Linux username of the user upfront.
Query the SELinux configuration as follows:
Get the current context of the process:
Take the Linux username (assumed to be in the name
variable) and get the SELinux user:
Now, get the default context based on the obtained SELinux user (sename
) and current context (which...
Interrogating the SELinux subsystem code-wise
In order to query the SELinux policy, we have seen the use of the sesearch
command and other SELinux utilities. Code-wise, SELinux policies can be queried using the security_compute_av_flags
method.
The curcon
and newcon
variables can be filled in through methods such as getcon()
(for the current context) or get_default_context()
as we have seen in the previous recipe.
As an example, we want to query the transition permission between two process domains. To accomplish this, the following method is used:
First of all, call the security_compute_av_flags()
method:
Now read the response:
Check whether the current context is a permissive domain or not:
Running new processes in a new context
Sometimes, it isn't possible to force a particular domain upon invocation of a new task or process. The default transition rules that can be enabled through the SELinux policy are only applicable if the source domain and file context (of the application or task to execute) are unambiguously decisive for the target context.
In applications that can run the same command (or execute commands with the same context) for different target domains, SELinux-awareness is a must.
This recipe will show how to force a particular domain for a new process.
The newcon
variable that is used in this recipe can be filled in through methods such as get_default_context()
as we have seen in a previous recipe.
To launch a process in a specific context, go through the following steps:
Tell SELinux what the new context should be:
Fork and execute the command. For instance, to...
Reading the context of a resource
It is, of course, also important to obtain the context of a resource if the application is SELinux-aware. This could be for logging purposes or to decide which domain to transition to (based on the resource context, current context, username, and so on).
To read the context of a resource, the following methods are available:
Given a file path, the following call to getfilecon()
will provide the context of the file:
To get the context of a process, assuming the pid
variable (of the pid_t
type) has the proper process ID in it, the following code is used:
The SELinux library has various methods for obtaining...