13

I'm trying to set up remote access to D-Bus, and I don't understand how authentication and authorization are (not) working.

I have a D-Bus server listening on an abstract socket.

$ echo $DBUS_SESSION_BUS_ADDRESS 
unix:abstract=/tmp/dbus-g5sxxvDlmz,guid=49bd93b893fe40d83604952155190c31

I run dbus-monitor to watch what's going on. My test case is notify-send hello, which works when executed from the local machine.

From another account on the same machine, I can't connect to that bus.

otheraccount$ DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-g5sxxvDlmz,guid=49bd93b893fe40d83604952155190c31 dbus-monitor
Failed to open connection to session bus: Did not receive a reply. Possible causes include: the remote application did not send a reply, the message bus security policy blocked the reply, the reply timeout expired, or the network connection was broken.
otheraccount$ DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-g5sxxvDlmz,guid=49bd93b893fe40d83604952155190c31 notify-send hello

After browsing the D-Bus specification, I copied ~/.dbus-keyrings/org_freedesktop_general to the other account, but it doesn't help.

I tried forwarding the D-Bus socket over TCP, inspired by schedar's Access D-Bus remotely using socat.

socat TCP-LISTEN:8004,reuseaddr,fork,range=127.0.0.1/32 ABSTRACT-CONNECT:/tmp/dbus-g5sxxvDlmz

I can connect to the TCP socket from my account.

DBUS_SESSION_BUS_ADDRESS=tcp:host=127.0.0.1,port=8004 notify-send hello

But not from the other account, neither with dbus-monitor nor with notify-send. Same error message for dbus-monitor as above with the abstract socket; notify-send now emits a trace:

otheraccount$ DBUS_SESSION_BUS_ADDRESS=tcp:host=127.0.0.1,port=8004 notify-send hello

** (notify-send:2952): WARNING **: The connection is closed

Stracing reveals that this version of notify-send doesn't try to read the cookie file, so I understand why it wouldn't be able to connect.

I also tried SSHing into another machine and forwarding the TCP connection.

ssh -R 8004:localhost:8004 remotehost

Surprisingly, dbus-monitor works without a cookie file! I can watch the D-Bus traffic from the remote host. I see a notice about eavesdropping in my local dbus-monitor instance.

remotehost$ DBUS_SESSION_BUS_ADDRESS=tcp:host=127.0.0.1,port=8004 dbus-monitor
signal sender=org.freedesktop.DBus -> dest=:1.58 serial=2 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired
   string ":1.58"
method call sender=:1.58 -> dest=org.freedesktop.DBus serial=2 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "eavesdrop=true"

If I run notify-send on the local machine, dbus-monitor on the remote host sees the notification. It's definitely reached a level of access that should require authentication.

notify-send complained about not finding a cookie. After copying the cookie file, notify-send works from the remote machine.

The local machine runs Debian wheezy. The remote machine runs FreeBSD 10.1.

I don't understand how D-Bus authentication and authorization work.

  1. Why can I eavesdrop, as far as I can tell, with no credentials from the remote machine? What am I exposing when I forward D-Bus to a TCP connection? Why are authorizations for dbus-monitor and notify-send different?
  2. Why can I not eavesdrop from another account on the same machine, whether over the abstract socket or over the TCP connection?
  3. I noticed that the cookie file changes every few minutes (I haven't figured out if it's at regular intervals or not). Why?

(I know I can launch a D-Bus daemon that listens on TCP. That's not the purpose of my question, I want to understand why what I did did and did not work.)

7

D-Bus isn't using the magic cookie file here; it's passing credentials over the UNIX domain socket (SCM_CREDENTIALS).

The magic cookie file is only one of several D-Bus authentication mechanisms. D-Bus implements a SASL-compliant interface (see RFC4422) to support a wide range of authentication mechanisms. One of these mechanisms is called "EXTERNAL" auth, and it means that the transport channel itself should be used to guarantee authentication. At least in the case of D-Bus over UNIX sockets, this appears to be the first authentication mechanism that is tried.

From the D-Bus spec:

Special credentials-passing nul byte

Immediately after connecting to the server, the client must send a single nul byte. This byte may be accompanied by credentials information on some operating systems that use sendmsg() with SCM_CREDS or SCM_CREDENTIALS to pass credentials over UNIX domain sockets. However, the nul byte must be sent even on other kinds of socket, and even on operating systems that do not require a byte to be sent in order to transmit credentials. The text protocol described in this document begins after the single nul byte. If the first byte received from the client is not a nul byte, the server may disconnect that client.

A nul byte in any context other than the initial byte is an error; the protocol is ASCII-only.

The credentials sent along with the nul byte may be used with the SASL mechanism EXTERNAL.

If you strace an instance of dbus-daemon, you can see that when you connect to it, it checks the credentials of the connecting user:

$ strace dbus-daemon --session --nofork
...
accept4(4, {sa_family=AF_LOCAL, NULL}, [2], SOCK_CLOEXEC) = 8
...
recvmsg(8, {msg_name(0)=NULL, msg_iov(1)=[{"\0", 1}], msg_controllen=0, msg_flags=0}, 0) = 1
getsockopt(8, SOL_SOCKET, SO_PEERCRED, {pid=6694, uid=1000, gid=1000}, [12]) = 0

So to answer your questions:

  1. The D-Bus daemon is using your kernel-verified user ID to verify your identity. By using socat to proxy connections, you are letting anybody connect to the D-Bus daemon using your UID.

  2. If you try to connect directly to the socket from another UID, the daemon recognizes that the connecting UID is not a UID that is supposed to be allowed to connect. I believe the default is that only the daemon's own UID is allowed, but haven't formally verified that. You can allow other users, though: see the configuration files in /etc/dbus-1/, and also man dbus-daemon.

  3. This is the D-Bus server replacing old/expired cookies with new ones. According to the DBUS_COOKIE_SHA1 section of the D-Bus spec, a cookie is stored along with its creation time, and the server is supposed to delete cookies that it decides are too old. Apparently the lifetime "can be fairly short".

  • The reference implementation of D-Bus does not use SCM_CREDENTIALS specifically. On Linux, it uses the SO_PEERCRED socket option instead. – Vasiliy Faronov Jun 7 '16 at 13:51
  • @VasiliyFaronov You're right -- how interesting! Further, it looks like using SCM_CREDENTIALS would have prevented such a simple proxy, since it requires the sender to actively present its credentials, whereas SO_PEERCRED merely checks who made the connection. I wonder why they made this choice. – Jander Jun 7 '16 at 14:07
  • Apparently because it “does not require the peer's cooperation,” so “this is much less fragile” (from comments in dbus-sysdeps-unix.c). – Vasiliy Faronov Jun 7 '16 at 14:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.