The D-Bus daemon provides an inter-process communication channel between applications. Unlike traditional IPC methods, D-Bus is a higher-level communication channel that offers more than simple signaling or memory sharing. Applications that want to chat over D-Bus link with one of the many D-Bus-compatible libraries, such as those provided by the libdbus, sd-bus (part of systemd), GDBus, and QtDBus applications.
The D-Bus daemon is part of the systemd application suite.
- The system-wide D-Bus is the main instance used for system communication. Many services or daemons will associate themselves with the system D-Bus to allow others to communicate with them through D-Bus.
- The session-specific D-Bus is an instance running for each logged-in user. It is commonly used by graphical applications to communicate with each other within a user session.
Both D-Bus instances are provided through the
dbus-daemon application. The system-wide D-Bus will run with the
--system option, whereas a session-specific instance will run with the
Applications register themselves against D-Bus through a namespace. Conventionally, this namespace uses the domain name of the project. For instance, systemd declares the
org.freedesktop.systemd1 namespace, whereas D-Bus is at
The currently associated applications can be queried using Python easily:
# python3.6 >>> import dbus >>> for service in dbus.SystemBus().list_names(): ... print(service) org.freedesktop.DBus org.freedesktop.login1 org.freedesktop.systemd1 org.freedesktop.PolicyKit1 com.redhat.tuned :1.10 :1.11 org.freedesktop.NetworkManager ...
Each application then provides objects on the bus that can be reached by other objects (other applications)—of course, assuming they have the privileges to do so. These objects are represented through a path-like syntax and generally also use the domain of the project as a prefix.
For instance, to list the objects currently associated with
org.freedesktop.systemd1, we can use the
gdbus command. To facilitate its use, we first enable auto-completion support, after which we can use the Tab key to easily add the appropriate values:
# source /usr/share/bash-completion/completions/gdbus # gdbus call --system --dest <TAB><TAB> # gdbus call --system --dest org.freedesktop.systemd1 --object-path /org/freedesktop/systemd1<TAB><TAB> Display all 220 possibilities? (y or no) /org/freedesktop/systemd1 /org/freedesktop/systemd1/job /org/freedesktop/systemd1/unit ...
Applications can trigger methods on these objects, or send messages to the applications bound to these objects through these methods.
For instance, to get the state of the
sshd.service unit through D-Bus, we invoke the
org.freedesktop.systemd1.Manager.GetUnitFileState method on the
org.freedesktop.systemd1 object reachable through the
/org/freedesktop/systemd1 path, and with the
sshd.service argument, like this:
# gdbus call --system \ --dest org.freedesktop.systemd1 \ --object-path /org/freedesktop/systemd1 \ --method org.freedesktop.systemd1.Manager.GetUnitFileState \ sshd.service ('enabled',)
Controlling service acquisition with SELinux
The D-Bus application, like systemd, will query the SELinux policy to verify whether to allow an operation. Again, it is the D-Bus application itself that enforces the policy and not a Linux kernel subsystem.
The first control that administrators can enable within D-Bus is to ensure that only well-established domains can acquire a specified object within D-Bus. Without this control, malicious code could register itself as
org.freedesktop.login1, for instance, and act as a system daemon on the bus. Other applications might mistakenly send out sensitive information to the application.
Applications store this policy information in files hosted in
/usr/share/dbus-1/system.d. The login service, for instance (stored as
org.freedesktop.login1.conf) has the following policy snippet installed:
<busconfig> <policy user="root"> <allow own="org.freedesktop.login1"/> <allow send_destination="org.freedesktop.login1"/> <allow receive_sender="org.freedesktop.login1"/> </policy> <policy context="default"> <deny send_destination="org.freedesktop.login1"/> <allow send_destination="org.freedesktop.login1" send_interface="org.freedesktop.DBus.Introspectable"/> ... </policy> </busconfig>
As the login daemon runs in the
systemd_logind_t domain, we could enhance this configuration as follows:
<busconfig> <selinux> <associate own="org.freedesktop.login1" context="system_u:system_r:systemd_logind_t:s0" /> </selinux> ... </busconfig>
With this enhancement in place, D-Bus will check whether the application (which we presume is running in the
systemd_logind_t context) has the
acquire_svc permission (of the
dbus class) against the
systemd_logind_t context. By default, the SELinux policy does not have this permission, and as such, the registration fails:
# systemctl restart dbus-org.freedesktop.login1 Job for systemd-logind.service failed because a timeout was exceeded. See "systemctl status systemd-logind.service" and "journalctl -xe" for details. # ausearch -m user_avc -ts recent
When we add the following SELinux policy rule, the registration of
systemd-logind will succeed, as expected:
(allow systemd_logind_t systemd_logind_t (dbus (acquire_svc)))
Load this policy (say
test.cil) and try the
restart operation again:
# semodule -i test.cil # systemctl restart dbus-org.freedesktop.login1
By limiting which domains can obtain a given service, we ensure that only trusted applications are used. Non-trusted applications will generally not run within the domain of that application (end users, for instance, cannot trigger a transition to such a domain) even if they receive root privileges (which is another check that D-Bus does for the login service, as shown in the first
Administrators can enhance this D-Bus configuration without having to alter the existing configuration files. For instance, the previously mentioned SELinux-governing
busconfig snippet could very well be saved as a different file.
Governing message flows
Whenever a source application is calling a method of a target application, D-Bus validates the
send_msg permission between the two domains associated with the source and target applications.
For instance, communication over D-Bus between a user domain (
sysadm_t) and service domain (
systemd_logind_t) will check the following permissions:
allow sysadm_t systemd_logind_t : dbus send_msg; allow systemd_logind_t sysadm_t : dbus send_msg;
If these permissions are not granted, then D-Bus will not allow the communication to happen. If at any point, the application context cannot be obtained, then the bus daemon context will be used.
Failures will be logged as
USER_AVC entries in the audit log. If the communication should be allowed, we can create a simple SELinux policy file to address this like so:
(allow sysadm_t systemd_logind_t (dbus (send_msg))) (allow systemd_logind_t sysadm_t (dbus (send_msg)))
Store these rules in a file with the suffix
local_logind_systemd.cil), and load it with
# semodule -i local_logind_systemd.cil
Let’s consider a few other applications that have SELinux support, not necessarily built-in, but through the SELinux policy and PAM integration within the system.