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?
Thanks,
Simon Ser
[1]: https://gitlab.freedesktop.org/wayland/weston/issues/206
[2]: https://github.com/flatpak/flatpak/issues/2995
On Tue, Mar 17, 2020 at 05:54:47PM +0000, Simon Ser wrote:
> 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?
Puh, I knew this would happen. I've been asked to add this feature by
the systemd people as well and also at a conference last year. And
honestly, I don't know yet. pidfds right now are mostly about
guaranteeing (stable) identity and they come with the necessary
restrictions in place to prevent shenanigans (such as signaling across
pid namespaces a restriction I'd like to lift at _some_ point). But I
have been thinking about attaching some capability like features to
pidfds soon as that has been an even more frequent request. At that
point having them receivable this way might be problematic unless we put
restrictions in place.
I would like to go through codepaths for SO_PEERCRED as I don't have
them in my head and so can't really say something definitely about this
just now.
(From the top of my head it seems that if we were to do this it might
need to be a separate SO_* flag? Mainly so people don't suddenly receive
fds they didn't expect.)
Christian
Simon Ser <[email protected]> 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.
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.
I have to dash but I will think about this and see if I can give a
concrete example of using a capability model. Other than the current
one that works (handing out trusted sockets at the logical beginning of
time). Though frankly I am not certain there is anything much better
than that.
Eric
> Thanks,
>
> Simon Ser
>
> [1]: https://gitlab.freedesktop.org/wayland/weston/issues/206
> [2]: https://github.com/flatpak/flatpak/issues/2995
On Tuesday, March 17, 2020 7:18 PM, Christian Brauner <[email protected]> wrote:
> On Tue, Mar 17, 2020 at 05:54:47PM +0000, Simon Ser wrote:
>
> > 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?
>
> Puh, I knew this would happen. I've been asked to add this feature by
> the systemd people as well and also at a conference last year. And
> honestly, I don't know yet. pidfds right now are mostly about
> guaranteeing (stable) identity and they come with the necessary
> restrictions in place to prevent shenanigans (such as signaling across
> pid namespaces a restriction I'd like to lift at some point). But I
> have been thinking about attaching some capability like features to
> pidfds soon as that has been an even more frequent request. At that
> point having them receivable this way might be problematic unless we put
> restrictions in place.
Wouldn't this new mechanism just be an atomic getsockopt+pidfd_open?
(It would make sure the process is still alive of course.)
Can you elaborate wrt. capabilities? I'm not sure I understand what
that means.
> I would like to go through codepaths for SO_PEERCRED as I don't have
> them in my head and so can't really say something definitely about this
> just now.
> (From the top of my head it seems that if we were to do this it might
> need to be a separate SO_* flag? Mainly so people don't suddenly receive
> fds they didn't expect.)
Yeah, this would need to be either a separate SO_* flag or a completely
different thing to prevent surprises.
On Tuesday, March 17, 2020 7:58 PM, <[email protected]> wrote:
> Simon Ser [email protected] 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?
> 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.
> I have to dash but I will think about this and see if I can give a
> concrete example of using a capability model. Other than the current
> one that works (handing out trusted sockets at the logical beginning of
> time). Though frankly I am not certain there is anything much better
> than that.
On Wed, Mar 18, 2020 at 10:31:00AM +0000, Simon Ser wrote:
> On Tuesday, March 17, 2020 7:58 PM, <[email protected]> wrote:
>
> > Simon Ser [email protected] 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.
On Wed, Mar 18, 2020 at 10:16:07AM +0000, Simon Ser wrote:
> On Tuesday, March 17, 2020 7:18 PM, Christian Brauner <[email protected]> wrote:
>
> > On Tue, Mar 17, 2020 at 05:54:47PM +0000, Simon Ser wrote:
> >
> > > 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?
> >
> > Puh, I knew this would happen. I've been asked to add this feature by
> > the systemd people as well and also at a conference last year. And
> > honestly, I don't know yet. pidfds right now are mostly about
> > guaranteeing (stable) identity and they come with the necessary
> > restrictions in place to prevent shenanigans (such as signaling across
> > pid namespaces a restriction I'd like to lift at some point). But I
> > have been thinking about attaching some capability like features to
> > pidfds soon as that has been an even more frequent request. At that
> > point having them receivable this way might be problematic unless we put
> > restrictions in place.
>
> Wouldn't this new mechanism just be an atomic getsockopt+pidfd_open?
> (It would make sure the process is still alive of course.)
Yes, it would. My point was rather if pidfds mean something more than
identity at some point then we just need to make sure that you can only
get a getsockopt+pidfd_open(pid, 0) equivalent pidfd from it, i.e. one
without additional abilities (see below).
>
> Can you elaborate wrt. capabilities? I'm not sure I understand what
> that means.
As a concrete example, think of it as root calling clone3() with e.g. an
additional flag PIDFD_GETFD which would stash the creds of the calling
process (in this example root) and when pidfd_getfd() is passed such a
pidfd it would use the creds of root instead of the calling threads
cred. This would e.g. allow a process to delegate the ability to
retrieve file descriptors from another task but nothing else.
>
> > I would like to go through codepaths for SO_PEERCRED as I don't have
> > them in my head and so can't really say something definitely about this
> > just now.
> > (From the top of my head it seems that if we were to do this it might
> > need to be a separate SO_* flag? Mainly so people don't suddenly receive
> > fds they didn't expect.)
>
> Yeah, this would need to be either a separate SO_* flag or a completely
> different thing to prevent surprises.
Simon Ser <[email protected]> writes:
> On Tuesday, March 17, 2020 7:58 PM, <[email protected]> wrote:
>
>> Simon Ser [email protected] 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?
prctl(PR_SET_MM_MAP, ...);
It is locked down a bit but not enough to trust it in general.
Further there are games I can play with ptrace where I can start an
executable and control it, so that you think it is the expected
executable calling the shots, when in fact it is the process acting
as the debugger performing the work.
Plus there are the other million and ways known to hijack a setuid
executable which also apply to this executable you would trust
because of it's exe_link.
Even beyond that to have a trusted process it's entire life cycle needs
to be trusted, so that you don't have the danger of someone unscrupulous
hijacking the process with bad input.
>> 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.
Once we reach the point of having a special file, it is much better
to pass that special file. Or possibly something derived from the
special file in a zero knowledge proof sort of way, to prove you
are a trusted process.
Passing a file descriptor as a token the process is the trusted
process, is a perfectly fine way to provide proof and unix
domains sockets have supported that from day one.
I don't see how inspection of a process could make anything
better than having the process provide something, and I think it could
be even worse.
Eric
On Wed, Mar 18, 2020 at 08:07:29AM -0500, Eric W. Biederman wrote:
> Simon Ser <[email protected]> writes:
>
> > On Tuesday, March 17, 2020 7:58 PM, <[email protected]> wrote:
> >
> >> Simon Ser [email protected] 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?
>
> prctl(PR_SET_MM_MAP, ...);
> It is locked down a bit but not enough to trust it in general.
That at least requires CAP_SYS_ADMIN in the current user namespace. But
it seems potentially dangerous when you think about cross-user-namespace
interactions, e.g. execing a binary located on the host and doing
setns(mnt+user).
>
> Further there are games I can play with ptrace where I can start an
> executable and control it, so that you think it is the expected
> executable calling the shots, when in fact it is the process acting
> as the debugger performing the work.
>
> Plus there are the other million and ways known to hijack a setuid
> executable which also apply to this executable you would trust
> because of it's exe_link.
>
> Even beyond that to have a trusted process it's entire life cycle needs
> to be trusted, so that you don't have the danger of someone unscrupulous
> hijacking the process with bad input.
Relying on /proc/<pid>/exe for anything has been a constant source of
bugs and is something we have warned against. The rexec trick we do for
both runC and lxc nowadays when attaching/running containers are
basically ways to ensure that you're executing an in-memory program that
has no attachment to the actual on-disk binary and is a result of
problems like this.
>
> >> 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.
>
> Once we reach the point of having a special file, it is much better
> to pass that special file. Or possibly something derived from the
> special file in a zero knowledge proof sort of way, to prove you
> are a trusted process.
>
> Passing a file descriptor as a token the process is the trusted
> process, is a perfectly fine way to provide proof and unix
> domains sockets have supported that from day one.
The use-case that e.g. comes from system daemons is that they need to
talk to e.g. glibc's syslog() api. And daemon loggers need to know who
they are talking to and that needs to be race-free. And they don't get
sent fds. Assuming we can patch glibc and all loggers to send a
pidfd and put a contract in place saying any message without a pidfd
sent along or a pidfd that is not referring to the SO_PEERCREDs uid
sent along will be rejected still feels wrong. In such cases the daemon
wants to be in control and requested the credentials be given to him. So
I can see the use for another SO_*