[systemd-devel] euid 0 not honored authenticating control socket?

Lennart Poettering mzerqung at 0pointer.de
Mon Jan 25 06:44:02 PST 2016


On Thu, 21.01.16 18:12, Charles Duffy (charles at dyfis.net) wrote:

> Howdy --
> 
> I have a setuid-root executable from which I'm trying to start or stop a
> service. However, when invoking systemctl from this executable -- for which
> geteuid() returns 0 -- the control socket is rejected when the ruid does
> not match (such that the executable was invoked from a less-privileged
> user).
> 
> 2042 17:02:00.508893788 0 systemd (1) > recvmsg
> fd=14(<u>ffff883f648b4280->ffff883f648b4980 /run/systemd/private)
> 2043 17:02:00.508897318 0 systemd (1) < recvmsg res=45 size=45 data=.AUTH
> EXTERNAL 30..NEGOTIATE_UNIX_FD..BEGIN.. tuple=NULL
> 
> 2068 17:02:00.510329428 0 systemd (1) > sendmsg
> fd=14(<u>ffff883f648b4280->ffff883f648b4980 /run/systemd/private) size=24
> tuple=NULL
> 2069 17:02:00.510344760 0 systemd (1) < sendmsg res=24
> data=REJECTED..ERROR..ERROR..
> 
> What's going on, and why?

D-Bus authentication includes the UID in text form in the protocol,
and compares that with the kernel supplied connection metadata
included in the socket ucred stuff. The former currently passes along
the UID, the latter the EUID. If you invoke systemctl with uid != euid
then we'll consider the two bits of data to be out of sync, and not
allow the connection.

Of course, one could argue that if euid is 0, we should accept any
value for the uid included in the auth protocol.  However, the current
logic instead suggests: if you have the rights to fake creds data then
please also fake the data in the ucred structure... Which however
won't take place in systemctl however, since it doesn't expect to be
used from suid binaries...

Now, I'd be willing to change the current logic: 

a) we could either pass the UID in both ucred and the auth
   protocol. In this case, the connection would be conisdered coming
   from the UID.

b) or we could pass the EUID in both. In this case the connection
   would be considered coming from the EUID.

c) or we could continue to pass the UID in the auth proto, but allow
   it to be any desired value if the ucred-indicated EUID is 0. The
   auth proto would count, and hence the connection would be
   considered coming from the the UID.

d) or we could continue to pass the UID in the auth proto, but ignore
   it completely if the ucred-indicated EUID is 0. The ucred supplied
   euid would count in this case, and the connection would be coming
   from the EUID.

>From all these options, I figure b) or d) would be the behaviour you
would prefer, as only then the connection would be owned by the EUID
and thus your euid of root would be considered for method calls.

However I am pretty sure a) or c) would be more in-line with UNIX
logic: i.e. the euid data would be used for permissions but the
ownership of objects such as the connection would still be bound to
the uid.

So not sure what I could recommend you, as I don't think we should
change the sd-bus behaviour to anything you'd like...

One option of course would be to do the equivalent of
setresuid(geteuid(), geteuid(), geteuid()); in your app after forking
but before you exec systemctl. Or, even better, just do the bus call
directly from your app, and rely on PK permitting access for
unprivileged clients.

Lennart

-- 
Lennart Poettering, Red Hat


More information about the systemd-devel mailing list