Hello,

Digging this old thread back, because I happened to think about the issue
for an unrelated microkernel-related security question.

Sergey Bugaev, le mer. 08 févr. 2023 16:15:23 +0300, a ecrit:
> Forgot to mention another interesting possibility:
> 
> clearly, the auth_{user,server}_authenticate () API has been designed
> with io_reauth in mind. The newport being passed from the server to
> the client fits the io_reauth model very well, the newport being a
> newly authenticated port to the same peropen. But this doesn't work as
> well for the SCM_CREDS case. But if we were to add, say,
> 
> simpleroutine auth_user_authenticate_inv (
>     handle: auth_t;
>     rendezvous: mach_port_send_t;
>     refport: mach_port_send_t);
> 
> routine auth_server_authenticate_inv (
>     handle: auth_t;
>     rendezvous: mach_port_send_t;
>     out refport: mach_port_t;
>     out euids: idarray_t;
>     out auids: idarray_t;
>     out egids: idarray_t;
>     out agids: idarray_t);
> 
> where 'refport' is passed from the user to the server and not the
> other way around -- then this would work out nicely in the SCM_CREDS
> case.
> 
> The user (the first peer) would pass refport = sockaddr, place the
> receive right to the rendezvous port into the socket packet, and be
> done with it. The server (the other peer) would read this out, get the
> user's IDs, compare the refport to the sockaddr, and proceed if they
> match. Neither peer would have to wait for the other one.
> 
> I'm not saying we _should_ do this, but it really looks like it would work.

I had kept this in my inbox without having the time to really look at
it. Now that I look at it, it looks completely reasonable: we have
auth_user/server_authenticate that allows to safely pass a port from
server to user. Having another one that allows to safely pass a port
from user to server just looks like the logical dual operation, so with
this actual user of it, we should just implement it :)

Samuel

Sergey Bugaev wrote:
> Hello!
> 
> I came across this glibc patch [0] from Svante Signell that is
> apparently not upstream, but shipped in Debian. I have also seen this
> wiki page [1]. So here is a braindump of how that patch is broken and
> why we cannot easily fix it.
> 
> [0]: 
> https://salsa.debian.org/glibc-team/glibc/-/blob/sid/debian/patches/hurd-i386/tg-sendmsg-SCM_CREDS.diff
> [1]: https://www.gnu.org/software/hurd/open_issues/sendmsg_scm_creds.html
> 
> I understand that implementing these APIs is important for D-Bus and
> Wayland (and X.Org, maybe? I don't know), but I can also see why the
> patch can not be upstreamed.
> 
> For one thing, the patch uses auth_server_authenticate (newport =
> MACH_PORT_NULL), and you know what that means, right?
> Man-in-the-middle attacks! Just get root to authenticate to you (could
> be from root sending these same SCM_CREDS to you, or from something
> else entirely like io_reauth), and forward its rendezvous port to your
> peer. Boom, the peer thinks you're root.
> 
> I'm posting this publicly because it doesn't look like there is
> anything sensitive on D-Bus right now. I've checked on my Debian
> GNU/Hurd box, and there's only org.freedesktop.DBus itself, while on
> darnassus there does not seem to be a D-Bus socket at all, despite the
> daemon running and the socket path being specified in its config as
> one would expect. But if we had systemd or Polkit or something
> listening on D-Bus and providing privileged services (such as: spawn
> this command as root), it'd be trivial to trick them into believing
> you're root and getting them to run a program of your choosing as
> root.
> 
> And I don't think it's possible to fix this, at least not easily.
> 
> Fixing this would require making use of the newport mechanism for
> sure. Ideally, that'd require us to somehow replace the socket port in
> the first peer with a new "reauthenticated" socket port (not actually
> reauthenticated as in io_reauth). This leaves the question of what to
> do with already queued data unclear -- we can neither trust it (since
> it was queued on the old socket port), nor ignore it (that would
> surely mess with the peer's expectations) The alternative to replacing
> the socket port is some awkward "confirm that you're actually trying
> to reauth this socket port" dance, a la what we did with
> proc_reauthenticate_complete (), but more complicated (you'd have to
> pass the sockaddr as a Mach port). But that would not work either,
> because...
> 
> it's important that both send() and recv() on the socket remain
> asynchronous, i.e. the two calls on the two sides must not need to
> actually rendezvous with each other. The first peer must be able to
> queue some data (and creds) with a non-blocking send() and go on its
> merry way, without waiting for the second peer to recv(). In
> particular this means that the first peer cannot synchronously wait
> for auth_user_authenticate () to return, since that waits until the
> second peer calls recv(). Similarly, once the second peer does get to
> recv() the data & creds, it must be able to so without blocking, so
> even if we do manage to send some newport to the first peer, we cannot
> e.g. wait until it calls some sort of "socket_scm_creds_ack ()" RPC on
> it.
> 
> -------
> 
> What D-Bus and Wayland would really prefer is getsockopt (SO_PEERCRED)
> rather than SCM_CREDS. The former gives you the peer's creds without
> the peer having to do anything special at all. Importantly, it should
> return peer's credentials *at the time of socket creation*, otherwise
> there's the possibility of a race-based attack: you create the socket
> and queue (send) your request, then you quickly exec ("su"). By the
> time dbus-daemon sees the request and checks your credentials, the
> process belongs to root.
> 
> Since [2], pflocal already caches creator's primary [UG]ID, so I guess
> that could be used to implement getsockopt (SO_PEERCRED) without any
> client involvement. (Except that it seems to set them to 0 if unknown,
> which is not going to work for obvious reasons...)
> 
> [2]: 
> https://git.savannah.gnu.org/cgit/hurd/hurd.git/commit/?id=594cfb7586089dfefab60574495baf6ed4048c1d
> 
> But the issue with *that* is also the usual one when transferring
> [UG]IDs as data (and not finding out the [UG]IDs using the auth
> protocol); namely the two peers can be in different "UID namespaces",
> that is, use different auth servers (think fakeauth). Specifically for
> SO_PEERCRED and fakeauth, imagine the two peers both run with the same
> fakeauth server, but pflocal does not. So the peer who calls
> getsockopt (SO_PEERCRED) will get UID=1000 from pflocal, even though
> its peer's UID is 0 as far as it's concerned.
> 
> But note that:
> * fakeauth [currently] doesn't support this -- it always forwards
> authenticate() RPCs to the upstream auth server, and only itself acts
> on auth_getids () & friends. So even if a client and a server are both
> using the same fakeauth, the server will still see the 'outer'
> credentials of the client (i.e. UID=1000). IMO this should be fixed
> independently.
> * Linux _does_ support using SO_PEERCRED across PID and UID
> namespaces, and converts the IDs as appropriate.
> 
> Sergey

Reply via email to