Re: SO_PEERCRED and pidfd

From: Christian Brauner
Date: Wed Mar 18 2020 - 07:56:43 EST


On Wed, Mar 18, 2020 at 10:31:00AM +0000, Simon Ser wrote:
> On Tuesday, March 17, 2020 7:58 PM, <ebiederm@xxxxxxxxxxxx> wrote:
>
> > Simon Ser contact@xxxxxxxxxxx writes:
> >
> > > Hi all,
> > > I'm a Wayland developer and I've been working on protocol security,
> > > which involves identifying the process on the other end of a Unix
> > > socket 1. This is already done by e.g. D-Bus via the PID, however
> > > this is racy 2.
> > > Getting the PID is done via SO_PEERCRED. Would there be interest in
> > > adding a way to get a pidfd out of a Unix socket to fix the race?
> >
> > I think we are passing a struct pid through the socket metadata.
> > So it should be technically feasible.
> >
> > However it does come with some long term mainteance costs.
> >
> > The big question is what is a pid being used for when being passed.
> > Last I looked most of the justifications for using metadata like that
> > with unix domain sockets led to patterns of trust that were also
> > exploitable.
> >
> > Looking at the proposale in 1 even if you have race free access
> > to /proc/<pid>/exe using pidfds it is possible to change /proc/<pid>/exe
> > to be anything you can map so that seems to be an example of a problem.
>
> /proc/<pid>/exe is a symlink. It doesn't seem like it's possible to
> unlink it and re-link it to something else (fails with EPERM).
>
> Is there a way to do this?

Not while the process is running afaik. Given the right permission there
are some tricks you can do to overwrite the host binary and trick
someone into rexecuting itself and thus the ovewritten binary (There's
been an exploit in runC around this which Aleksa and I fixed a while
back.) but as long as the process is running you can't overwrite it
(unless there are bugs).

>
> > So it would be very nice to see a use case spelled out where
> > the pid reuse race mattered, and that trusting a pid makes sense.
>
> The use-case is identifying which process is at the other end of the
> socket. Once the process is identified, security rules can be applied.
> For instance a Wayland compositor might give access to a
> screen capture interface if the program is a trusted screen shooter.
>
> Some want to get the full path to the executable, and read the
> /proc/<pid>/exe symlink. Some want to read a special file created at
> the root of the process' file system namespace, and access
> /proc/<pid>/root.

You can translate solely from a pidfd to a pid and then open /proc/<pid>
and verify that the directory you just opened referes to the same
process as the pidfd. That's illustrated in a sample program I wrote.
It's located in the kernel sources at:

samples/pidfd/pidfd_metadata.c

specifically the pidfd_metadata_fd() helper.

If you only have the pidfd and want to know the pid of the process you
can translate from the fd's fdinfo file to the pid. That's e.g. how
systemd is doing it too. See
https://github.com/systemd/systemd/blob/06ae8800d0bd9f8b01df7443daec37f90708bd84/src/basic/process-util.c#L1499
If the process has already exited _and_ been reaped fdinfo will show -1
as pid. The format of the fdinfo file matches the output for the Pid:
and NSpid: fields in the /prop/<pid>/status file.