Chapter 9. SELinux systemd Access Control

In Red Hat Enterprise Linux 7, system services are controlled by the systemd daemon. In previous releases of Red Hat Enterprise Linux, daemons could be started in two ways:
  • At boot time, the System V init daemon launched an init.rc script and then this script launched the desired daemon. For example, the Apache server, which was started at boot, got the following SELinux label:
    system_u:system_r:httpd_t:s0
  • An administrator launched the init.rc script manually, causing the daemon to run. For example, when the service httpd restart command was invoked on the Apache server, the resulting SELinux label looked as follows:
    unconfined_u:system_r:httpd_t:s0
When launched manually, the process adopted the user portion of the SELinux label that started it, making the labeling in the two scenarios above inconsistent. With the systemd daemon, the transitions are very different. As systemd handles all the calls to start and stop daemons on the system, using the init_t type, it can override the user part of the label when a daemon is restarted manually. As a result, the labels in both scenarios above are system_u:system_r:httpd_t:s0 as expected and the SELinux policy could be improved to govern which domains are able to control which units.

9.1. SELinux Access Permissions for Services

In previous versions of Red Hat Enterprise Linux, an administrator was able to control, which users or applications were able to start or stop services based on the label of the System V Init script. Now, systemd starts and stops all services, and users and processes communicate with systemd using the systemctl utility. The systemd daemon has the ability to consult the SELinux policy and check the label of the calling process and the label of the unit file that the caller tries to manage, and then ask SELinux whether or not the caller is allowed the access. This approach strengthens access control to critical system capabilities, which include starting and stopping system services.
For example, previously, administrators had to allow NetworkManager to execute systemctl to send a D-Bus message to systemd, which would in turn start or stop whatever service NetworkManager requested. In fact, NetworkManager was allowed to do everything systemctl could do. It was also impossible to setup confined administrators so that they could start or stop just particular services.
To fix these issues, systemd also works as an SELinux Access Manager. It can retrieve the label of the process running systemctl or the process that sent a D-Bus message to systemd. The daemon then looks up the label of the unit file that the process wanted to configure. Finally, systemd can retrieve information from the kernel if the SELinux policy allows the specific access between the process label and the unit file label. This means a compromised application that needs to interact with systemd for a specific service can now be confined by SELinux. Policy writers can also use these fine-grained controls to confine administrators. Policy changes involve a new class called service, with the following permissions:
class service
{
       start
       stop
       status
       reload
       kill
       load
       enable
       disable
}
For example, a policy writer can now allow a domain to get the status of a service or start and stop a service, but not enable or disable a service. Access control operations in SELinux and systemd do not match in all cases. A mapping was defined to line up systemd method calls with SELinux access checks. Table 9.1, “Mapping of systemd unit file method calls on SELinux access checks” maps access checks on unit files while Table 9.2, “Mapping of systemd general system calls on SELinux access checks” covers access checks for the system in general. If no match is found in either table, then the undefined system check is called.

Table 9.1. Mapping of systemd unit file method calls on SELinux access checks

systemd unit file method SELinux access check
DisableUnitFiles disable
EnableUnitFiles enable
GetUnit status
GetUnitByPID status
GetUnitFileState status
Kill stop
KillUnit stop
LinkUnitFiles enable
ListUnits status
LoadUnit status
MaskUnitFiles disable
PresetUnitFiles enable
ReenableUnitFiles enable
Reexecute start
Reload reload
ReloadOrRestart start
ReloadOrRestartUnit start
ReloadOrTryRestart start
ReloadOrTryRestartUnit start
ReloadUnit reload
ResetFailed stop
ResetFailedUnit stop
Restart start
RestartUnit start
Start start
StartUnit start
StartUnitReplace start
Stop stop
StopUnit stop
TryRestart start
TryRestartUnit start
UnmaskUnitFiles enable

Table 9.2. Mapping of systemd general system calls on SELinux access checks

systemd general system call SELinux access check
ClearJobs reboot
FlushDevices halt
Get status
GetAll status
GetJob status
GetSeat status
GetSession status
GetSessionByPID status
GetUser status
Halt halt
Introspect status
KExec reboot
KillSession halt
KillUser halt
ListJobs status
ListSeats status
ListSessions status
ListUsers status
LockSession halt
PowerOff halt
Reboot reboot
SetUserLinger halt
TerminateSeat halt
TerminateSession halt
TerminateUser halt

Example 9.1. SELinux Policy for a System Service

By using the sesearch utility, you can list policy rules for a system service. For example, calling the sesearch -A -s NetworkManager_t -c service command returns:
allow NetworkManager_t dnsmasq_unit_file_t : service { start stop status reload kill load } ; 
allow NetworkManager_t nscd_unit_file_t : service { start stop status reload kill load } ; 
allow NetworkManager_t ntpd_unit_file_t : service { start stop status reload kill load } ; 
allow NetworkManager_t pppd_unit_file_t : service { start stop status reload kill load } ; 
allow NetworkManager_t polipo_unit_file_t : service { start stop status reload kill load } ;