[apparmor] [RFC] Controlling the ability to start D-Bus services

John Johansen john.johansen at canonical.com
Thu Oct 22 13:06:40 PDT 2015


On 10/21/2015 10:56 AM, Simon McVittie wrote:
> One of the features of D-Bus that are not typically present in other IPC
> mechanisms is the ability to start services that have a .service file
> on-demand (the jargon term is "service activation"). I'm currently
> looking into ways to limit who can start services, particularly for
> sandboxed applications.
> 
> There are two ways this can happen, either StartServiceByName:
> 
> * a client C sends a StartServiceByName message to the dbus-daemon,
>   naming a service S, which is not currently running
> * dbus-daemon checks in its own policies whether C is allowed to send
>   StartServiceByName to it; by default, all clients of the well-known
>   session and system buses are allowed to do this [*]
> * If enabled, dbus-daemon checks with LSMs (currently SELinux or
>   AppArmor) whether C is allowed to send StartServiceByName to it [*]
> * dbus-daemon starts S, either directly or by asking systemd [*]
> * S requests and receives its bus name [*]
> * dbus-daemon sends a successful reply back to C
> 
> or auto-starting, which is preferred because it is simpler for clients
> and avoids some race conditions:
> 
> * a client C sends any non-broadcast message M to the dbus-daemon,
>   with its destination field set to a service S, which is not currently
>   running
> * dbus-daemon does *not* check whether C is allowed to send messages to
>   S at this stage [1]
> * dbus-daemon starts S, either directly or by asking systemd [*]
> * S requests and receives its bus name [*]
> * dbus-daemon checks whether C is allowed to send M to S, via
>   its own policies (as above); by default, all clients of the
>   well-known session bus may send messages, and all clients of the
>   well-known system bus may send signals but not other messages
>   [*][2]
> * dbus-daemon checks whether S is allowed to receive M from C via
>   its own policies; by default, all clients of the well-known
>   session and system buses can receive any message [*]
> * if enabled, dbus-daemon checks with LSMs whether C is allowed to send
>   M to S, and whether S is allowed to receive M from C [*]
> * dbus-daemon delivers M to S
> 
> Any step marked [*] can stop the process, either by failing or by
> denying access.
> 
> If that happens during auto-starting, then S will still be started, even
> though the auto-starting message M is never actually delivered. That
> might be considered to be unexpected. The reason that there isn't a
> check at that stage is that dbus-daemon cannot evaluate all of its
> access-control rules at this stage, because it does not know most of the
> attributes of the process that will implement S (in particular the uid,
> the LSM context, and the complete set of bus names that it will have).
> 
> Our policy on this has traditionally been that this is not a bug:
> StartServiceByName is not normally restricted, so if you don't want your
> D-Bus service to be available for starting by anyone, don't install a
> .service file. That's fine for traditional D-Bus services on mainstream
> computers, but less good if you are using D-Bus activation to start
> arbitrary applications that might have side-effects on startup, or if
> your system is resource-constrained.
> 
> To reduce the impact on existing services and systems, it might be
> interesting to add a new RestrictActivation key to .service files, with
> new activation-time checks only performed for services that have it;
> services wishing to be available to all users on-demand would not have
> this key, but services with activation-time side-effects would have it.
> 
> For StartServiceByName, it is easy for an OS designer or a system
> administrator to lock down who can call that method. However, it is
> allowed by default in D-Bus, and is also allowed by default in
> AppArmor's <abstractions/dbus-strict> and
> <abstractions/dbus-session-strict> abstractions (which allow access to
> the system or session bus, but restrict interaction with the bus and
> services to a whitelist). One interesting limitation here is that the
> D-Bus policy language doesn't currently have a way to discriminate
> according to method arguments, and neither does AppArmor's D-Bus
> mediation - so the choice here would currently be between allowing all
> StartServiceByName calls from a particular client, or denying all of
> them. A client can typically replace StartServiceByName calls with a
> call to a harmless method (and get simpler code and reduced latency as a
> side-effect), so the conservative thing to do would be to deny all of them.
> 
> For auto-starting, there is currently no way to limit who can start
> services; the solution is to add a check at the step marked [1]. One
> possible check would be to evaluate D-Bus' built-in "may send" policies
> as if the message M was being delivered to a hypothetical service with
> the bus name S requested as destination, and no other bus names. This
> should not replace the check marked [2], because that check is carried
> out with more information available, and could conceivably produce a
> more negative result (it is possible to deny the ability to send to a
> process owning a particular name, although I don't know why anyone would
> want to do so).
> 
> D-Bus' "may receive" policies are not really suitable for step [1],
> because they require that we know the uid of the service, which we do
> not necessarily: a system service run with User=root might drop
> privileges to a different user before connecting to D-Bus. However,
> those policies are rarely (never?) used in practice, so that doesn't
> seem like a real problem. I would recommend just not checking the
> receive rules at [1]; they will be checked later anyway.
> 
> For systems using an LSM, we can potentially also do an LSM check at
> [1]. Again, one possibility is to behave as though M was being delivered
> to a service with the bus name S that was requested as destination, and
> no other bus names, and do checks on that basis. In an AppArmor
> environment, we would probably also want to check against an AppArmor
> label, but we can't necessarily know what AppArmor profile transitions
> the service S would go through during startup until we run it: its
> eventual AppArmor label might not actually match the executable in the
> Exec= line of its D-Bus .service file, if a profile-changing rule like
> "/usr/lib/mydaemon Cx -> profile-for-daemons" has been applied. As a
> result, what I have been prototyping is to have an optional

True, though we do have plans to extend the query interface so that this
question can be asked. It wouldn't take much to supply this info the
basic file query is already present, however currently it does not
return the target label for an executable.

The only problem with doing this query is it is possible that a policy
change would occur between the query and the actual activation. Its
not likely as only the admin is loading policy and it would only
result in the current situation of the service being unexpectedly
started

> "AssumeAppArmorProfile" key in .service files. If present, the check at
> [1] will assume that S will have that profile; if absent, no label is
> specified while querying the kernel, which I think means that any rule
> that specifies a peer label will not match.
> 
correct, its rule would have to say it accepts any peer label for this
to match

> Another possibility would be to introduce a new AppArmor action which
> would be evaluated for both StartServiceByName and auto-activation: in
> addition to
> 
>     dbus (send, receive) peer=(label=com.example.MyService),
> 
> we could perhaps have rules like
> 
>     dbus (activate) name=com.example.MyService,
> 
sure this is possible

> This could either be implied by the ability to send to
> peer=(name=com.example.MyService), or entirely independent. I'm not sure
> whether this would need kernel-side changes, or whether it could be done
> entirely in AppArmor user-space.
> 
it can be done entirely in the userspace code. It would require adding
a little bit of code to the policy compiler to support the additional
syntax and permission and the dbus code to make the query.

> Any opinions on this?
> 
I am inclined to agree that while unexpected, I wouldn't call it a bug.



More information about the dbus mailing list