Comment 5 for bug 1362469

Tyler Hicks (tyhicks) wrote :

I now have a good understanding of the problem as well as a standalone
reproducer. The issue stems from the NO_REPLY_EXPECTED flag in the
message header. When the sender of a message sets the NO_REPLY_EXPECTED
flag, it means tells the recipient that a reply, either in the form of a
method_return or an error message, is not needed even if the method
normally provides a reply message. The D-Bus spec
does not require that the receiver of the message honor the
NO_REPLY_EXPECTED flag. Here's the text:

  "... the reply can be omitted as an optimization. It is compliant with
   this specification to return the reply despite this flag, although
   doing so on a bus with a non-trivial security policy (such as the
   well-known system bus) may result in access denial messages being
   logged for the reply."

The dbus-daemon code has a mechanism for tracking whether or not a reply
message (method_return and error messages) is a reply that the sender of
the original message actually asked for. If the reply message was not
requested, the dbus-daemon code treats it as an "unrequested reply".
This is important to the AppArmor mediation code in dbus-daemon because
it lets reply messages that *were* requested through without requiring
any sort of AppArmor policy query. The thought is that the AppArmor
security policy allowed the original message to go through so surely the
security policy author expects any corresponding reply message to go
through. That all works great except for when the original message has
the NO_REPLY_EXPECTED flag. The dbus-daemon code treats any replies to
that message as an unrequested reply. That means that it is subject to
the AppArmor policy and may be rejected if the process sending the reply
does not have "send" permissions.

The bug description for this bug mentions an AppArmor denial of pasaffe
sending an UnknownMethod error message to an unconfined process:

apparmor="DENIED" operation="dbus_error" bus="session"
  error_name="org.freedesktop.DBus.Error.UnknownMethod" mask="send"
  name=":1.22" pid=4993 profile="/usr/bin/pasaffe" peer_pid=3624

What is happening there is that some unconfined UI component (global
menus backend?) is communicating with the UI components of the confined
pasaffe process over D-Bus. The confined UI components are tearing
themselves down after the user enters their master password into a
pasaffe modal dialog box and presses "Ok". This results in the confined
UI components unregistering the D-Bus object path corresponding to the
modal dialog box. After this happens, the unconfined UI components are
attempting to send one last message, with the NO_REPLY_EXPECTED flag
set, to the confined UI components (the "End" method call). This is an
error condition since the D-Bus object has already been unregistered.
The gdbus (glib) bindings notice that this is an error and send an
UnknownMethod error message reply, despite the fact that the
NO_REPLY_EXPECTED flag was set on the original message. This causes
dbus-daemon to treat the reply as an unrequested reply and the AppArmor
mediation code blocks the message and generates a denial message.

Outside of the noisy denial message, there is no harm in this. The
original sender of the message stated that a reply was not needed and
the gdbus bindings went ahead and sent an error reply message, which is
fine according to the D-Bus spec. The spec even warns that complex
security policy may result in a denial message for such a reply.

We could patch the gdbus bindings to not send an error reply message in
this situation but I'm not sure upstream would accept such a patch and
there may be other bindings or services that ignore the

In the lp1362469.tar.gz attachment, I have two example services (one
written against the python-dbus bindings and the other against the gdbus
bindgs) that demonstrate that these unrequested reply denials can come
from a service, too. If you gunzip it, untar it, and run 'make', it'll
test both services. It will result in two denials:

apparmor="DENIED" operation="dbus_method_return" bus="session"
  mask="send" name=":1.154" pid=21732 profile="service" peer_pid=21734
apparmor="DENIED" operation="dbus_error" bus="session"
  error_name="org.freedesktop.DBus.Error.UnknownMethod" mask="send"
  name=":1.154" pid=21732 profile="service" peer_pid=21734

The second denial is the same sort of denial as the pasaffe denial that
I described above. The first denial is an example of a service receiving
a message with the NO_REPLY_EXPECTED flag set and still returning a
reply message. This also results in an unrequested reply denial.

I think this shows that it is unreasonable to try to fix every D-Bus
binding, service, and client that ignores the NO_REPLY_EXPECTED flag in
order to allow or quiet these types of denials.

Our policy language doesn't allow us to easily allow or quiet the
denials, either, since we cannot specify message type (method_call,
signal, method_return, error) and the language has no notion of
unrequested replies. This leaves us with course-grained options such as
"dbus (send) peer=(label=unconfined)," to allow the two denials above. I
don't even know a good way to quiet those denials without disallowing
the ability to send any outgoing messages.

Adjusting the policy language to allow the author to specify
unrequested replies or method_return/error messages in the policy
doesn't feel like the right thing to do. That clutters the already
complex dbus syntax, requires changes from dbus-daemon to the kernel,
and doesn't really add much other than being able to quiet denials
caused by unrequested replies. Additionally, all policy authors are
going to want to do the same thing: deny, without auditing, any
unrequested replies.

I think the most reasonable solution is to simply not audit the denials
for unrequested replies. AppArmor will always block all unrequested
replies. There are potentially lots of innocent unrequested replies that
are allowed by the D-Bus spec they also have the potential to really
litter up the audit logs. The AppArmor mediation code already knows
if the message is a reply type and if the reply was requested. It is
trivial to deny but not audit those messages.