dbus-daemon, passing fd and SELinux

Simon McVittie smcv at collabora.com
Wed Nov 7 15:49:17 UTC 2018


On Wed, 07 Nov 2018 at 15:22:26 +0100, David Sommerseth wrote:
> So this is a _denial_ where SELinux denied the dbus-daemon to do read/write
> operations on /dev/net/tun.

This seems bizarre. The dbus-daemon reads and writes its own AF_UNIX
sockets (plus a few ancillary fds like inotify and epoll); it doesn't
read or write fds that are merely passed through it.

Is SELinux maybe somehow representing "receive fd X attached to a message
passing through AF_UNIX socket Y" as a read or write operation on X as
well as a read operation on Y, on the basis that the dbus-daemon could
read or write fd X later on if it wanted to (even though in fact it
doesn't want to), instead of applying the access control at the point
of actually reading or writing X?

You can see the fd-passing code in _dbus_read_socket_with_unix_fds() and
_dbus_write_socket_with_unix_fds_two(), both in dbus/dbus-sysdeps-unix.c.
The read side uses recvmsg() and iterates through control messages
looking for a (SOL_SOCKET, SCM_RIGHTS). The write side sends a suitable
control message.

If SELinux doesn't allow what you are trying to do, and SELinux
systems are in-scope for your project, then you might need to find a
different protocol for passing this fd around (for instance using D-Bus
to negotiate the filename of a listening AF_UNIX socket owned by your
client or server, and having your server or client connect to that and
transfer the /dev/net/tun fd out-of-band, which would presumably mean
that your client and server need to be allowed to access /dev/net/tun
but the dbus-daemon does not).

Another (even more horrible) possible way to change your API to be robust
against this sort of thing would be to use a separate GDBusConnection
to send the /dev/net/tun fd, so that if it gets disconnected, it's only
that one connection that is affected.

> - When this error occurs (dbus-daemon being denied the recvmsg() call), the
>   netcfg service hits the GBusNameLostCallback function and the service is
>   being shutdown.  Why does this happen?  Is this expected?

Presumably the dbus-daemon interprets the recvmsg() failure as fatal for
the connection, because after failing to receive from a socket for a reason
not known to the dbus code, it can't know what state the socket is in:
repeated attempts to read might all return the same error (leading to
busy-looping), or the dbus-daemon might have lost an arbitrary number of
bytes from the stream (leading to the next read producing a corrupt
message).

EBADRQC isn't currently documented in Linux recvmsg(2), so I don't know
when it can be emitted, what the dbus-daemon can expect to happen when
it is, or what the dbus-daemon should do about it.

dbus-daemon has no way to deal with a connection going into an undefined
state, other than disconnecting it.

If there is a way to recover gracefully (without deadlocking, busy-looping
or corrupting the message stream), then you could contribute code to
dbus-daemon to make it special-case EBADRQC and do that instead.

> - Is there a way to grab more info anywhere /why/ the GBusNameLostCallback
>   event occurred?

No, there is currently no protocol by which the dbus-daemon can tell
a client *why* that client is getting disconnected: fatal errors just
disconnect the socket. Implementing this would not necessarily be
straightforward, because the dbus-daemon would need to be able to stop
reading from the client, but keep the connection alive long enough to
do all the asynchronous send operations for at least the rest of the
current partially-sent message if any (to avoid corrupting the stream by
inserting the error into the middle of an unrelated message), followed
by the error, followed by disconnection.

Messages can be large enough that they need to be sent in multiple
sendmsg() transactions (or even large enough that they exceed the socket
buffer), so we have to get the framing right, and we have to do it all
asynchronously.

I dimly remember once trying to prototype a feature where dbus-daemon
would try to add a signal or an unsolicited error message into the
sending socket before disconnecting clients, but it ended up sufficiently
complicated that I wasn't confident that it was right. Client libraries
would also have to be modified to look for those messages and use them
to populate a more specific error indicator.

> - What would be the best approach to recover from such a situation?  Would
>   it be feasible to try to call g_bus_own_name_on_connection() again to
>   re-establish the service on the D-Bus?

No, your connection has been disconnected and you can't reuse it. You
would need to make a new GDBusConnection and own the name on that.

>   What will happen to all the objects
>   which was already registered on the D-Bus for this service?

They will have become unregistered, because you register objects on
connections, and your connection has been disconnected.

    smcv


More information about the dbus mailing list