[systemd-devel] SD_BUS_VTABLE_CAPABILITY

Andy Lutomirski luto at amacapital.net
Thu Apr 16 07:52:37 PDT 2015


On Thu, Apr 16, 2015 at 3:23 AM, Tom Gundersen <teg at jklm.no> wrote:
> Hi Andy,
>
> On Thu, Apr 16, 2015 at 2:55 AM, Andy Lutomirski <luto at amacapital.net> wrote:
>> Yesterday, I discovered SD_BUS_VTABLE_CAPABILITY.  Are there any
>> examples in which it does anything?
>
> Please note that you need to be using kdbus to get any capabilities
> transported, so in dbus1 this does nothing (as on dbus1 using
> capabilities would be racy). Are you testing this on kdbus?

No.

I'm looking at sd_bus_query_sender_privilege, which does:

r = sd_bus_query_sender_creds(call,
SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS,
&creds);

That, in turn, does:

        if (!c || !(c->mask & SD_BUS_CREDS_PID)) {
                /* We couldn't read anything from the call, let's try
                 * to get it from the sender or peer */

                if (call->sender)
                        return sd_bus_get_name_creds(call->bus,
call->sender, mask, creds);
                else
                        return sd_bus_get_owner_creds(call->bus, mask, creds);
        }

        return bus_creds_extend_by_pid(c, mask, creds);

The sd_bus_get_name_creds call seems questionable to me -- isn't that
trying to augment missing information from the time of call with
metadata from the sender's connection?  (I really think this dichotomy
needs to be removed, *especially* since it looks like code already
exists to try to use both metadata sources.  This seems like it's just
asking for security screw-ups.)

The sd_bus_get_owner_creds call looks totally bogus.  I don't know
when it would be called, but I hope the answer is "provably never in
response to a message from an unprivileged user", in which case I'd
wonder why it's there.

The bus_creds_extend_by_pid is what I'm asking about.  When is it used?

In any event, under certain (strewn-across-multiple-files with
duplicated code in various places) conditions, all three paths above
seem to land in bus_creds_add_more.

It looks like some of this is gated on whether the sending PID is
known (IMO this is odd -- it should be gated by whether you think it's
a good idea, not whether a piece of information is known), but maybe
mac_selinux_generic_access_check can circumvent that.

So why exactly are capabilities only used with kdbus?  I don't see it,
and I do see code that tries to extract them from /proc.

>
>> If so, I don't suppose any of you
>> could give me an example of:
>>
>> $ cp `which dbus-send` .
>> $ sudo setcap all=eip dbus-send
>> $ dbus-send [not sure what goes here]
>>
>> that passes an authentication test that would have failed without the setcap?
>
> Note that dbus-send is the old dbus1 command, and will always go via
> the bus proxy that connects dbus1 clients to kdbus. As such, it will
> only carry the credentials that are available safely on dbus1, and
> hence not capabilities. So even if you do use kdbus on your machine,
> this example would not work.
>
> To be clear, the reason we do not use the capability logic on dbus1,
> but only on kdbus is as follows: On kdbus, the capabilities are
> attached to the method call itself, hence we safely know that at the
> time the method call was issued by the client side, that client
> actually really had those capabilities. On dbus1 however, which uses
> AF_UNIX/SOCK_STREAM, this is not possible: we only get the
> UID/GID/PID, hence we could only derive the caps from
> /proc/$PID/status. But this opens this up for a vulnerability: if a
> client quickly issues a method call, and then exec()s a suid binary,
> it could happen that the service side would read the capabilities from
> that SUID process, and not the original process, and the exploit was
> successful. We cannot allow that, hence no capabilities on dbus1,
> instead we open up things to a much, much broader uid == 0. kdbus
> allows us to be much stricter there, by allowing us to check only one
> specific capability.

Agreed, and that's not the only vulnerability.  But code to do it
certainly exists in systemd.

>
> An equivalent example to the one you gave using native kdbus would be:
>
> $ cp `which busctl` .
> $ sudo setcap all=eip busctl
> $ ./busctl call org.freedesktop.timedate1 /org/freedesktop/timedate1
> org.freedesktop.timedate1 SetTimezone "sb" Europe/London 0
>

I still don't see why, even if this were bug-free, it's even remotely
useful.  Keep in mind that basically no one has a capability if
they're not root, and dbus users should really use *dbus* mechanisms
to retain selected privileges when they drop other privileges.

--Andy

>> In the interest of full disclosure, I'm asking because I think that
>> one of two things is true:
>>
>> 1. The SD_BUS_VTABLE_CAPABILITY code is useless and should therefore be deleted.
>
> This is not the case.
>
>> 2. The SD_BUS_VTABLE_CAPABILITY code is exploitably buggy and should
>> therefore be deleted.
>
> I have heard this claim from you many times, but so far I haven't been
> able to figure out what you have in mind. Could you either give an
> example of an exploit or at least the general scheme you have in mind?
>

Sure.  Unshare your user namespace, set things up right, and systemd
or any other server will see you as having all capabilities.  You've
fixed that in kdbus, but you haven't (and probably can't!) fix it in
the legacy code, and that legacy code is still there (!).  (Actually,
that code seems to have been actively developed as recently as
November 2014.)

The ratio of complexity of capability code the kdbus folks have
already written (hundreds of lines across multiple files) to its
utility (very near zero AFAICT) is, in my book, not a good sign at
all.

--Andy


More information about the systemd-devel mailing list