Docs/Security/AppArmor/D-Bus

From Apertis
Jump to: navigation, search

Contents

Integration

D-Bus mediation with AppArmor was introduced in 14.12. Three packages were updated to integrate the feature:

  • AppArmor (the userspace library) 2.8.95 from Ubuntu. That new version includes the D-Bus mediation feature.
  • The kernel with the patches needed for D-Bus mediation (not upstreamed yet)
    • x86: The 3.13 kernel from Ubuntu 2014.04 already has the patches
    • arm: The patches were ported to our 3.8 kernel
  • dbus-daemon with the AppArmor patches (upstreaming tracked on bfo#75113)

Policies

Applications can use the following abstractions:

  • <abstractions/dbus>: unrestricted access to the system bus
  • <abstractions/dbus-session>: unrestricted access to the session bus
  • <abstractions/dbus-strict>: connection to the system bus granted but additional rules needed for using it
  • <abstractions/dbus-session-strict>: connection to the session bus granted but additional rules needed for using it

Before Apertis 14.12, AppArmor was not restricting specific D-Bus traffic, as if <abstractions/dbus> and <abstractions/dbus-session> was included in all applications using D-Bus in Apertis 14.12. In general, we want to use the "-strict" version of the abstractions instead.

The current version of AppArmor and dbus-daemon allows to restrict the D-Bus traffic using both routing criteria and content criteria:

  • routing criteria: determines whether two process can communicate together. The relevant D-Bus fields are source and destination.
  • content criteria: determines whether a message is allowed. The relevant D-Bus fields are path, interface and member.

However, an hypothetical future integration of AppArmor and kdbus (as a replacement of dbus-daemon) would only be able to use routing criteria because of kdbus architecture. In order to keep future options open, we chose to restrict the D-Bus traffic only on routing criteria.

There are three main D-Bus capabilities in AppArmor D-Bus mediation:

  • bind: the profile is allowed to own a D-Bus well-known name
  • send: the profile is allowed to send D-Bus messages
  • receive: the profile is allowed to receive D-Bus messages

Examples:

dbus bind    bus=session  name=org.PulseAudio*,
dbus send    bus=system   peer=(name=org.freedesktop.RealtimeKit1),
dbus send    bus=session  peer=(label=unconfined),
dbus send    bus=session  peer=(label=/usr/lib/tracker/tracker-miner-fs),
dbus receive bus=session  peer=(label=/usr/lib/tracker/tracker-miner-fs),

Test cases

QA/Test Cases/apparmor-dbus

Debugging tips

Inspecting the complains

Complaints in AppArmor D-Bus mediation are logged in /var/log/audit/audit.log. D-Bus complaints can be seen with:

sudo apt-get install sac-apparmor-report
sudo cat /var/log/audit/audit.log | sudo aa_log_extract_tokens.pl REJECTING

Disabling AppArmor

AppArmor can be disabled with:

sudo systemctl stop apparmor
sudo systemctl disable apparmor
# replace <apparmor mode="enabled"/> by <apparmor mode="disabled"/>
sudo vim /etc/dbus-1/system.conf
sudo vim /etc/dbus-1/session.conf

Enabling eavesdropping

dbus-monitor on the system bus would need eavesdropping enabled in /etc/dbus-1/system.conf:

  • Use the following policy:
 <policy context="default">
   <allow send_destination="*" eavesdrop="true"/>
   <allow eavesdrop="true"/>
   <allow own="*"/>
   <allow user="*"/>
 </policy>
  • Delete the following include:
 <includedir>system.d</includedir>

Generic D-Bus service communicating with unknown clients

Description of the problem

connection_manager is a D-Bus service installed on the default image. It communicates with its clients on the session bus. The clients can be third-party applications. With AppArmor restricting D-Bus communications, the AppArmor profiles of connection_manager and its clients must be defined to allow that communication:

# /etc/apparmor.d/usr.local.bin.client_A
# part of clientA's package
/usr/local/bin/client_A {
  dbus send    bus=session peer=(label=/usr/local/bin/connection_manager),
  dbus receive bus=session peer=(label=/usr/local/bin/connection_manager),
}

# /etc/apparmor.d/usr.local.bin.client_B
# part of clientB's package
/usr/local/bin/client_B {
  dbus send    bus=session peer=(label=/usr/local/bin/connection_manager),
  dbus receive bus=session peer=(label=/usr/local/bin/connection_manager),
}

# /etc/apparmor.d/usr.local.bin.connection_manager
# part of connection_manager's package
/usr/local/bin/connection_manager {
  dbus send    bus=session peer=(label=/usr/local/bin/client_A),
  dbus receive bus=session peer=(label=/usr/local/bin/client_A),

  dbus send    bus=session peer=(label=/usr/local/bin/client_B),
  dbus receive bus=session peer=(label=/usr/local/bin/client_B),
  ...
  [list all clients here!]
}

The issue is when the clients are third-parties applications that are not pre-installed on the image: it's not possible to mention all possible clients in connection_manager's AppArmor profile because the list of clients is not known yet.

Solution 0: white list connection_manager

With the following configuration, connection_manager would have no restriction on the session bus.

# /etc/apparmor.d/usr.local.bin.connection_manager
# part of connection_manager's package
/usr/local/bin/connection_manager {
  dbus send    bus=session,
  dbus receive bus=session,
}

Solution 1: use AppArmor labels

The AppArmor profiles would be written this way:

# /etc/apparmor.d/usr.local.bin.client_A
# part of clientA's package
profile clientA_ConnectionManagerClient /usr/local/bin/client_A {
  dbus send    bus=session peer=(label=/usr/local/bin/connection_manager),
  dbus receive bus=session peer=(label=/usr/local/bin/connection_manager),
}

# /etc/apparmor.d/usr.local.bin.client_B
# part of clientB's package
profile clientB_ConnectionManagerClient /usr/local/bin/client_A {
  dbus send    bus=session peer=(label=/usr/local/bin/connection_manager),
  dbus receive bus=session peer=(label=/usr/local/bin/connection_manager),
}

# /etc/apparmor.d/usr.local.bin.connection_manager
# part of connection_manager's package
/usr/local/bin/connection_manager {
  dbus send    bus=session peer=(label=*ConnectionManagerClient*),
  dbus receive bus=session peer=(label=*ConnectionManagerClient*),
}

The changes are:

  • give an explicit label to clients instead of using the path as default label.
  • as convention, the client label MUST contains the substring "ConnectionManagerClient"
  • the connection_manager profile uses wildcards to match all labels containing the substring "ConnectionManagerClient"

In this way, the connection_manager package does not need to be modified when a new client application is added in the app store and installed.

It is still not an ideal solution in case the application uses several services with this system. For example, if client_A uses both connection_manager (mandating the substring "ConnectionManagerClient" in the label) and another_service (mandating the substring "AnotherServiceClient" in the label), the AppArmor profile would contain something like:

profile clientA_ConnectionManagerClient_AnotherServiceClient /usr/local/bin/client_A {
  ...
}

With more than two services, the application label become even more contrived... And labels with 252 characters (or more) don't seem to work.

Solution 2: use D-Bus well-known names

This solution does not require to change AppArmor labels.

# /etc/apparmor.d/usr.local.bin.client_A
# part of clientA's package
/usr/local/bin/client_A {
  dbus send
       bus=session
       path=/org/freedesktop/DBus
       interface=org.freedesktop.DBus
       member={RequestName,ReleaseName}
       peer=(name=org.freedesktop.DBus),
  dbus bind    bus=session name=org.sac.ConnectionManager.Client.ClientA,

  dbus send    bus=session peer=(label=/usr/local/bin/connection_manager),
  dbus receive bus=session peer=(label=/usr/local/bin/connection_manager),
}

# /etc/apparmor.d/usr.local.bin.client_B
# part of clientB's package
/usr/local/bin/client_B {
  dbus send
       bus=session
       path=/org/freedesktop/DBus
       interface=org.freedesktop.DBus
       member={RequestName,ReleaseName}
       peer=(name=org.freedesktop.DBus),
  dbus bind    bus=session name=org.sac.ConnectionManager.Client.ClientB,

  dbus send    bus=session peer=(label=/usr/local/bin/connection_manager),
  dbus receive bus=session peer=(label=/usr/local/bin/connection_manager),
}

# /etc/apparmor.d/usr.local.bin.connection_manager
# part of connection_manager's package
/usr/local/bin/connection_manager {
  dbus send    bus=session peer=(name=org.sac.ConnectionManager.Client.*),
  dbus receive bus=session peer=(name=org.sac.ConnectionManager.Client.*),
}

The clients are allowed to claim a D-Bus well-known name starting with "org.sac.ConnectionManager.Client." and connection_manager is allowed to communicate with whoever owns such a D-Bus well-known name.

But with this solution, the code of clients has to be modified to claim the specified D-Bus well-known name. With GLib/GDBus, it can be done by calling:

 own_name_id = g_bus_own_name (G_BUS_TYPE_SESSION,
     "org.sac.ConnectionManager.Client.ClientA",
     G_BUS_NAME_OWNER_FLAGS_NONE,
     NULL, NULL, NULL, NULL, NULL);
Personal tools
Namespaces

Variants
Actions
Navigation
Tools