Search code examples
dbus

Permission denied for a shutdown over DBus from a non-root user


I'm trying to execute a PowerOff dbus command from an application I'm running on our Yocto Linux system. I use org.freedesktop.login1, the D-Bus interface of systemd-logind for this. I can see the command being executed but I get a permission denied error.

Then I tried to alter /etc/dbus-1/system.conf to add an allow rule to let user "user1" execute the poweroff function. I tried several things here but somehow these allow rules still don't work for me. When there's an error I do notice dbus won't be able to restart. But it seems the policies are ignored.

<busconfig>

<!-- Allow anyone to call methods on the org.freedesktop.login1 interface -->
 <policy user="root">
   <allow own="org.freedesktop.login1"/>
 </policy>

<policy user="user1">
    <allow send_destination="org.freedesktop.login1" send_interface="org.freedesktop.DBus.Properties"/>
    <allow send_destination="org.freedesktop.login1" send_interface="org.freedesktop.login1.Manager"/>
  </policy>

<policy user="user1">
  <allow
     send_destination="org.freedesktop.login1"
     send_interface="org.freedesktop.login1.Manager"
     send_member="PowerOff"/>
  <allow send_interface="org.freedesktop.DBus.ObjectManager"/>
  <allow send_interface="org.freedesktop.DBus.Properties"/>
</policy>
</busconfig>

I've also tried adding this to /etc/dbus-1/system.d/logind.conf and /etc/dbus-1/system-local.conf with no success.

We do not have Polkit or Consolekit running on this system.

Any feedback on either the allow rules or debug suggestions?


Solution

  • D-Bus policies are not where the main access check happens. They're more like a firewall for D-Bus calls; but just as with any other kind of firewall, the service is free to implement its own access checks after it successfully receives the call.

    Importantly, on systems that do have Polkit installed, the Polkit access checks are not done by D-Bus as you probably think – instead, Polkit checks are done by logind itself, whereas the D-Bus policy for systemd-logind is already wide open by default. You can see this in /usr/share/dbus-1/system.d – by default all users are allowed to send a 'PowerOff' method call, because logind itself has code for permission checking.

    In fact, the service doesn't have visibility into the D-Bus policies, much like a web server doesn't know what firewall rules are present. The requests either reach it or not.

    Together, this means that if Polkit is not installed, the service's own access checks must fail closed – i.e. it must work under the assumption that the same "wide open" D-Bus firewall policy is still installed, and must continue to reject unprivileged calls even if the D-Bus policy allows them through. Having the service fail open (as you're expecting) would likely lead to security issues as soon as the Polkit access-checking service failed to start for any reason, as the system would be running with an open D-Bus policy and no Polkit.

    This means that you cannot achieve what you want purely with D-Bus policy rules. If you're required to talk to logind via D-Bus (as opposed to 'sudo systemctl') then you have to use Polkit and write rules in /etc/polkit to allow the operations for specific users.

    (You don't need ConsoleKit – that's literally what systemd-logind replaces.)

    Alternatively, you need to write your own "proxy" service that runs as root, accepts requests via your preferred protocol (D-Bus or other) and calls logind.