D-Bus policies. system bus and bus names

Simon McVittie smcv at collabora.com
Fri Jun 30 17:18:28 UTC 2017


On Fri, 30 Jun 2017 at 01:09:15 +0200, David Sommerseth wrote:
> On 31/05/17 13:04, Simon McVittie wrote:
> > * In an appropriate <policy> to describe legitimate OpenVPN backends
> >   (probably <policy user="root">), have
> >   <allow own="net.openvpn.v3.any_backend"/> in addition to the own_prefix
> >   rule you already have
> 
> When you say "any_backend", do you mean that literary ... or is that as
> an example for "some reasonable value"?

Any appropriate string would do, but my (poorly-made) point was that it is
a shared token to represent "I am some OpenVPN v3 backend", rather than
pointing to any specific backend process.

> > * In each backend, request the name net.openvpn.v3.any_backend without
> >   using the DBUS_NAME_FLAG_DO_NOT_QUEUE flag (you may specify
> >   ALLOW_REPLACEMENT and/or REPLACE_EXISTING, or not, whichever you prefer).
> >   Do not consider DBUS_REQUEST_NAME_REPLY_IN_QUEUE to be an error.
> 
> Hmm ... and multiple backends can run in parallel?

Yes. One of them will be the "primary owner" (the one you normally see
in D-Bus debugging tools) and the others will be in the queue waiting
to own that name in future.

If you use both ALLOW_REPLACEMENT and REPLACE_EXISTING, which is what
I'd suggest, then the primary owner will be whichever one happens to have
run most recently:

* UDP backend runs. There is no other backend. UDP backend gets the
  net.openvpn.v3.any_backend name.
  Queue = [UDP (primary owner)]
* TCP backend runs. The TCP backend gains the net.openvpn.v3.any_backend
  name, and simultaneously the UDP backend loses primary ownership of
  that name but remains in the queue for it.
  Queue = [TCP (primary owner), UDP]
* TCP-on-port-443 backend runs. The TCP-on-443  backend gains the
  net.openvpn.v3.any_backend name, and simultaneously the TCP backend
  loses that name.
  Queue = [TCP-on-443 (primary owner), TCP, UDP]

> But does this mean I need to own both the proper backend name in
> addition to the any_backend variant, and that in each backend process?

Yes.

>     gchar be_name[256];
>     snprintf(be_name, 255, "net.openvpn.v3.backends.be%ld", getpid());

(If you are already using GLib, I would strongly recommend using GLib
coding style - g_strdup_printf() a newly-allocated string of exactly
the right length on the heap. Doubly so for security-sensitive software.)

>     guint dedicated = g_bus_own_name_on_connection(conn,
> 				be_name,
> 			   	G_BUS_NAME_OWNER_FLAGS_NONE,
> 				callback_acq, callback_lost,
> 				user_data, NULL);
> 
>     guint any = g_bus_own_name_on_connection(conn,
> 				"net.openvpn.v3.backends.any_backend",
> 			   	G_BUS_NAME_OWNER_FLAGS_NONE,
> 				callback_acq, callback_lost,
> 				user_data, NULL);

For flags, see below.

You'd probably want to use a different callback for owning be_name
(losing it or failing to get it is a fatal error) and the special
any_backend name (losing it or failing to get it is tolerable).

> What I do not quite understand is in which header file the
> DBUS_NAME_FLAG_DO_NOT_QUEUE and DBUS_REQUEST_NAME_REPLY_IN_QUEUE are
> defined.  I don't see them in any of them defined in the publicly
> available header files; but I might have overlooked something.  And
> which of these g_bus_*() functions need the DO_NOT_QUEUE flag.

Those flags are part of the D-Bus message-passing "wire protocol",
and the C API of the reference implementation libdbus. If you are using
a reimplementation like GDBus, you should use the reimplementation's
appropriate flags instead.

In the case of GDBus, G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT and
G_BUS_NAME_OWNER_FLAGS_REPLACE are part of the same set of flags as
G_BUS_NAME_OWNER_FLAGS_NONE, there is currently[1] no API to specify
DO_NOT_QUEUE (GDBus clients are currently[1] always willing to queue),
and DBUS_REQUEST_NAME_REPLY_IN_QUEUE is mapped to the "name lost"
callback.

[1] https://bugzilla.gnome.org/show_bug.cgi?id=784392

> So that means the policy can be:
> 
>    <allow send_destination="net.openvpn.v3.backends.any_backend"
>           send_interface="net.openvpn.v3.backends"
> 	  send_type="method_call"/>
> 
> While the proxy client does something like:
> [call a method, specifying the net.openvpn.v3.backends.be%i well-known name]

Yes.

> What is unclear to me, is if this it is needed to call
> g_dbus_proxy_get_connection() and call g_bus_own_name_on_connection()
> against "net.openvpn.v3.backends.any_backend" in addition.

All the backends need to (join the queue to) own that name. None of the
clients need to do so.

The clients must all be matched by the <policy> that surrounds the
<allow> that you quoted above, typically <policy user="root"> for
services that only make sense to have uid 0 clients, or
<policy context="default"> for services that all users are expected to
be able to access.

> > Using fork() without exec(), other than during very early process startup
> > as part of BSD-style daemonization, is usually a bad idea: it has a
> > tendency to leave global state in a weird mixture of what was correct
> > for the parent, and what is correct for the child, unless every library
> > in the process is extremely careful to use non-portable facilities
> > like pthread_atfork().
> 
> Okay, I'll be wary of that.  The backend processes is an independent
> binary which is started via fork() + execve().  But I have three other
> processes (configuration manager, session manager and log service) which
> are fork()ed out before it should receive any D-Bus calls at all.  But
> this can be changed.

I do not know of any D-Bus library that can be validly used across
a fork(). I would advise doing a fork() + execve() for each separate
process that you want.

If you want to combine several jobs into one executable, a robust
event loop like the one in GLib should be able to do lots of things
asynchronously in the same process, normally with one shared main loop
and some "hidden" worker threads encapsulated behind library code.
<https://developer.gnome.org/programming-guidelines/stable/main-contexts.html.en>
is very relevant.

> Speaking of Windows ... D-Bus on Windows, is that a viable approach?

D-Bus on Windows exists, but is not really security-supported, and its
implementation quality is not up to the same standard as on Unix.
If you are using it a lot, you will have to be prepared to fix its bugs
and contribute tested patches.

There is no system bus on Windows, partly because D-Bus-on-Windows
doesn't really have properly-thought-out multi-user security, and partly
because the main purpose of the system bus is to access OS infrastructure
like PackageKit and NetworkManager. On "freedesktop" platforms like
Linux and *BSD, the canonical implementation of several such services
is on the system bus; but on Windows, the canonical implementations
of all OS services are written by Microsoft and communicate in some
other way.

> Another related topic ... I'm pondering on better ways to kick-off the
> backend "client session" processes.  Would it be better to let D-Bus
> start these processes, through D-Bus activation?  Or should I aim for
> policy kit with some pkexec approach?

polkit (formerly PolicyKit) does not do what you think it does. At a
very high level, here is a sequence diagram of how polkit works:

    unprivileged client       system service                     polkit
    |                              |                                  |
    |---- please do something ---->|                                  |
    |                              |---- uid 1000 wants me to do ---->|
    |                              | something, is that OK?           |
    |                              |                                  |
    |                              |<---- yes that's fine ------------|
    |                              |                                  |
    |<---- OK I have done that ----|                                  |

polkit does not let your system service escalate its privileges.
polkit only lets your more-privileged system service query whether it
should act on instructions received from a less-privileged client.

If you want the D-Bus infrastructure to start a more-privileged D-Bus
service on demand, the jargon term you are looking for is /activation/
(.service files). dbus-daemon can start arbitrarily high-privilege
services, either by running them via a setuid-root helper or by
asking systemd to start them. (This is how polkit itself works -
it is only started when required.)

    S


More information about the dbus mailing list