2014-10-15 13:42:48

by David Madore

[permalink] [raw]
Subject: feature suggestion: implement SO_PEERCRED on local AF_INET/AF_INET6 sockets (allow uid-based identification on localhost)

Given an AF_UNIX socket, the getsockopt(, SOL_SOCKET, SO_PEERCRED,,)
call allows one endpoint to authenticate the other endpoint's pid, uid
and gid.

The call is valid on AF_INET and AF_INET6 sockets but returns no data
(pid=0, uid=-1, gid=-1). Obviously it is meaningless to try to get
such credentials from a INET/INET6 socket in general, but there is one
case where it would make sense: namely, when the endpoint is local
(i.e., when the socket is a connection to the same machine, e.g., when
connecting to 127.0.0.0/8 or ::1/32).

Being able to authenticate local INET/INET6 sockets would be immensely
valuable for a number of programs, to provide some kind of access
control to local sockets. For example, ssh allows port forwarding
using the -L and -D options: by default or by option (cf. the
GatewayPorts option of ssh), these port-forwarding sockets can be
restricted to localhost, but of course they cannot be restricted to
the user running ssh, which makes them a huge security problem. Many
programs suffer from the same problem (they restrict some kind of
connection to localhost, but they of course cannot make a restriction
on which user will be able to connect).

One cannot simply retort "these programs should be using Unix-domain
sockets instead": I don't think many browsers support using a SOCKS
proxy or an HTTP proxy over a Unix-domain socket, and in the latter
case I'm not even sure it would make sense (protocol-wise).

If I believe <URL:
http://www.lehman.cuny.edu/cgi-bin/man-cgi?getpeerucred+3
> ("The system currently supports both sides of connection end-points
for local AF_UNIX, AF_INET, and AF_INET6 sockets"), Solaris, or at
least some version thereof, support authentication of local AF_INET
and AF_INET6 sockets.

I think it would be wonderful if Linux had this. I'm willing to work
on the implementation if it is considered *a priori* acceptable for
inclusion.

The data seems to be available, since it is exposed in /proc/net/tcp
and /proc/net/tcp6 and whatnots (implementation details left aside, it
is merely a question of matching a line with opposite endpoints to the
current socket and returning it).

[In principle, a userland program can parse /proc/net/tcp so it does
not need the feature I am suggesting, but in practice parsing a text
file to communicate with the kernel is yucky at best, and probably not
very robust (e.g., /proc might not be mounted), and it would be very
difficult to convince, say, the OpenSSH authors to include code that
parses the Linux /proc/net/tcp format (or even link with a library
which does this) in order to add access-control on ssh port-forwards:
having this under a more standard getsockpot() interface is cleaner
and opens at least some kind of hope that programs would agree to use
it.]

Question number 1: If this feature were implemented, would it be
considered acceptable for inclusion in the kernel? (If there is some
reason why it can't be accepted, I'd like to know in advance, to avoid
working in vain.)

Question number 2: A priori, how difficult would it be to implement
this? (As mentioned above, it seems trivial in principle to merely go
through the local endpoints to find a matching connection, but maybe
there are locking issues that I don't understand that make it much
more difficult than it would seem.) Any guidelines on implementation?
(I imagine one should try to fill sk->sk_peer_cred at connect time,
but I don't really know how difficult this might turn out.)

Any comments on the matter are welcome.

Happy hacking,

--
David A. Madore
( http://www.madore.org/~david/ )


2014-10-15 14:41:55

by Andy Lutomirski

[permalink] [raw]
Subject: Re: feature suggestion: implement SO_PEERCRED on local AF_INET/AF_INET6 sockets (allow uid-based identification on localhost)

On 10/15/2014 06:35 AM, David Madore wrote:
> Given an AF_UNIX socket, the getsockopt(, SOL_SOCKET, SO_PEERCRED,,)
> call allows one endpoint to authenticate the other endpoint's pid, uid
> and gid.
>
> The call is valid on AF_INET and AF_INET6 sockets but returns no data
> (pid=0, uid=-1, gid=-1). Obviously it is meaningless to try to get
> such credentials from a INET/INET6 socket in general, but there is one
> case where it would make sense: namely, when the endpoint is local
> (i.e., when the socket is a connection to the same machine, e.g., when
> connecting to 127.0.0.0/8 or ::1/32).
>
> Being able to authenticate local INET/INET6 sockets would be immensely
> valuable for a number of programs, to provide some kind of access
> control to local sockets. For example, ssh allows port forwarding
> using the -L and -D options: by default or by option (cf. the
> GatewayPorts option of ssh), these port-forwarding sockets can be
> restricted to localhost, but of course they cannot be restricted to
> the user running ssh, which makes them a huge security problem. Many
> programs suffer from the same problem (they restrict some kind of
> connection to localhost, but they of course cannot make a restriction
> on which user will be able to connect).
>
> One cannot simply retort "these programs should be using Unix-domain
> sockets instead": I don't think many browsers support using a SOCKS
> proxy or an HTTP proxy over a Unix-domain socket, and in the latter
> case I'm not even sure it would make sense (protocol-wise).
>
> If I believe <URL:
> http://www.lehman.cuny.edu/cgi-bin/man-cgi?getpeerucred+3
> > ("The system currently supports both sides of connection end-points
> for local AF_UNIX, AF_INET, and AF_INET6 sockets"), Solaris, or at
> least some version thereof, support authentication of local AF_INET
> and AF_INET6 sockets.
>
> I think it would be wonderful if Linux had this. I'm willing to work
> on the implementation if it is considered *a priori* acceptable for
> inclusion.
>
> The data seems to be available, since it is exposed in /proc/net/tcp
> and /proc/net/tcp6 and whatnots (implementation details left aside, it
> is merely a question of matching a line with opposite endpoints to the
> current socket and returning it).
>
> [In principle, a userland program can parse /proc/net/tcp so it does
> not need the feature I am suggesting, but in practice parsing a text
> file to communicate with the kernel is yucky at best, and probably not
> very robust (e.g., /proc might not be mounted), and it would be very
> difficult to convince, say, the OpenSSH authors to include code that
> parses the Linux /proc/net/tcp format (or even link with a library
> which does this) in order to add access-control on ssh port-forwards:
> having this under a more standard getsockpot() interface is cleaner
> and opens at least some kind of hope that programs would agree to use
> it.]
>
> Question number 1: If this feature were implemented, would it be
> considered acceptable for inclusion in the kernel? (If there is some
> reason why it can't be accepted, I'd like to know in advance, to avoid
> working in vain.)

I will object to adding it as described, for the same reason that I
object to anything that extends the current model of socket-based
credential passing. Ideally, credentials would *never* be implicitly
captured by socket syscalls. We live in the real world, and SO_****CRED
exists, so I think the best we can do is to try to minimize its use.

I can elaborate further, or you can IIRC search the archives for
SCM_IDENTITY, and you can also look at CVE-2013-1979 for a nasty example
of why this model is broken.

That being said, if anyone ever finished the SCM_IDENTITY work, I think
I'd be okay with allowing it over TCP. I can't speak for the network
people, though, and you should ask on netdev (cc'd).

>
> Question number 2: A priori, how difficult would it be to implement
> this? (As mentioned above, it seems trivial in principle to merely go
> through the local endpoints to find a matching connection, but maybe
> there are locking issues that I don't understand that make it much
> more difficult than it would seem.) Any guidelines on implementation?
> (I imagine one should try to fill sk->sk_peer_cred at connect time,
> but I don't really know how difficult this might turn out.)

Dunno.

--Andy

2014-10-15 22:30:38

by David Madore

[permalink] [raw]
Subject: Re: feature suggestion: implement SO_PEERCRED on local AF_INET/AF_INET6 sockets (allow uid-based identification on localhost)

On Wed, Oct 15, 2014 at 07:41:48AM -0700, Andy Lutomirski wrote:
> On 10/15/2014 06:35 AM, David Madore wrote:
> > Given an AF_UNIX socket, the getsockopt(, SOL_SOCKET, SO_PEERCRED,,)
> > call allows one endpoint to authenticate the other endpoint's pid, uid
> > and gid.
> >
> > The call is valid on AF_INET and AF_INET6 sockets but returns no data
> > (pid=0, uid=-1, gid=-1). Obviously it is meaningless to try to get
> > such credentials from a INET/INET6 socket in general, but there is one
> > case where it would make sense: namely, when the endpoint is local
> > (i.e., when the socket is a connection to the same machine, e.g., when
> > connecting to 127.0.0.0/8 or ::1/32).
>
> I will object to adding it as described, for the same reason that I
> object to anything that extends the current model of socket-based
> credential passing. Ideally, credentials would *never* be implicitly
> captured by socket syscalls. We live in the real world, and SO_****CRED
> exists, so I think the best we can do is to try to minimize its use.
>
> I can elaborate further, or you can IIRC search the archives for
> SCM_IDENTITY, and you can also look at CVE-2013-1979 for a nasty example
> of why this model is broken.

>From what I understand, what was broken is mainly that the credentials
were evaluated when the write() system call took place rather than
when socket() or bind(): this violates the Unix security model
(privilege control occurs when the file descriptor is created, not
when it is used). On the contrary, it is conform to Unix security
principles that credentials are checked implicitly when binding a
socket (this happens when permissions are being checked on the path
when binding or connecting on a Unix domain socket; and to allow
binding to secure ports in the INET domain; and so on). It seems to
me that a suid program that is willing to create or bind a socket on
behalf of its caller without knowing exactly what it will be
connecting to, it should intrinsically be treated as a security
vulnerability, even when it is not obviously exploitable.

Also, to go along the real world examples, identd exists and is used
for identification on local networks (e.g. localhost), so the capture
of credentials already takes place. Unix programmers are aware of
this, and know that a privileged program should not bind a socket if
they don't want to leak privileges. (Another example is the use of -m
owner in iptables.)

And, of course, if Solaris already has this feature, there is some
experience for it. Has there been any documented vulnerability
relating to the fact that Solaris allows getpeerucred() to
authenticate locally connected AF_INET sockets?

Note that since the possibility of using SO_PEERCRED on AF_INET
sockets does not hitherto exist on Linux, we can be sure that nobody
uses it, so it's not like it might open vulnerabilities in existing
code. If you think it's insecure, it can be documented as such (by
comparing it with identd): I still think it's better than having no
control at all when binding to localhost, which is the present
situation (causing, e.g., CVE-2014-2914).

Because SO_PEERCRED currently returns {pid=0,uid=-1,gid=-1} on
AF_INET, we might still return this value if there is any risk that
the endpoint would be unwilling to share its credentials: for example,
this value might be returned if the other endpoint is not ptraceable
by the caller - this would still cover the essential use case, which
is for unprivileged users to authenticate the connections from their
own processes. Would this limitation assuage your worries about the
proposed feature?

The thing is, I don't see any other way the ssh port forwarding mess
can ever be improved. Do you have another solution in mind that?

Any attempt to have some kind of authentication of local sockets that
required participation on the client (authenticatee)'s part is doomed:
if modifying the protocol and/or client code is an option, we might as
well use some form of crypto / TLS. Or Unix-domain sockets. But what
are we supposed to do when modifying the client (to make it send
credentials, use crypto or connect on AF_UNIX) is not an option?

--
David A. Madore
( http://www.madore.org/~david/ )

2014-10-15 22:54:32

by Andy Lutomirski

[permalink] [raw]
Subject: Re: feature suggestion: implement SO_PEERCRED on local AF_INET/AF_INET6 sockets (allow uid-based identification on localhost)

On Wed, Oct 15, 2014 at 3:30 PM, David Madore <[email protected]> wrote:
> On Wed, Oct 15, 2014 at 07:41:48AM -0700, Andy Lutomirski wrote:
>> On 10/15/2014 06:35 AM, David Madore wrote:
>> > Given an AF_UNIX socket, the getsockopt(, SOL_SOCKET, SO_PEERCRED,,)
>> > call allows one endpoint to authenticate the other endpoint's pid, uid
>> > and gid.
>> >
>> > The call is valid on AF_INET and AF_INET6 sockets but returns no data
>> > (pid=0, uid=-1, gid=-1). Obviously it is meaningless to try to get
>> > such credentials from a INET/INET6 socket in general, but there is one
>> > case where it would make sense: namely, when the endpoint is local
>> > (i.e., when the socket is a connection to the same machine, e.g., when
>> > connecting to 127.0.0.0/8 or ::1/32).
>>
>> I will object to adding it as described, for the same reason that I
>> object to anything that extends the current model of socket-based
>> credential passing. Ideally, credentials would *never* be implicitly
>> captured by socket syscalls. We live in the real world, and SO_****CRED
>> exists, so I think the best we can do is to try to minimize its use.
>>
>> I can elaborate further, or you can IIRC search the archives for
>> SCM_IDENTITY, and you can also look at CVE-2013-1979 for a nasty example
>> of why this model is broken.
>
> From what I understand, what was broken is mainly that the credentials
> were evaluated when the write() system call took place rather than
> when socket() or bind(): this violates the Unix security model
> (privilege control occurs when the file descriptor is created, not
> when it is used). On the contrary, it is conform to Unix security
> principles that credentials are checked implicitly when binding a
> socket (this happens when permissions are being checked on the path
> when binding or connecting on a Unix domain socket; and to allow
> binding to secure ports in the INET domain; and so on). It seems to
> me that a suid program that is willing to create or bind a socket on
> behalf of its caller without knowing exactly what it will be
> connecting to, it should intrinsically be treated as a security
> vulnerability, even when it is not obviously exploitable.

socket has little precedent for checking credentials and none in
POSIX. And you're talking about connect, not bind.

>
> Also, to go along the real world examples, identd exists and is used
> for identification on local networks (e.g. localhost), so the capture
> of credentials already takes place. Unix programmers are aware of
> this, and know that a privileged program should not bind a socket if
> they don't want to leak privileges. (Another example is the use of -m
> owner in iptables.)

Ugh.

Identd is completely insecure. Quite a few years ago personally broke
Stanford's entire single sign-on mechanism by exploiting it, against a
hardened, kerberized version, so less. And iptables -m owner is all
about what you can connect to, not what is assumed by the recipient.
(And it's probably insecure, too, in many cases.)

>
> And, of course, if Solaris already has this feature, there is some
> experience for it. Has there been any documented vulnerability
> relating to the fact that Solaris allows getpeerucred() to
> authenticate locally connected AF_INET sockets?

Does it matter? CVE-2013-1979 existed for many years before anyone noticed.

>
> Note that since the possibility of using SO_PEERCRED on AF_INET
> sockets does not hitherto exist on Linux, we can be sure that nobody
> uses it, so it's not like it might open vulnerabilities in existing
> code. If you think it's insecure, it can be documented as such (by
> comparing it with identd): I still think it's better than having no
> control at all when binding to localhost, which is the present
> situation (causing, e.g., CVE-2014-2914).

This doesn't follow. *Everybody* uses connect on AF_INET.

IMO anything that sends a caller's credentials needs to be explicit and opt-in.

>
> Because SO_PEERCRED currently returns {pid=0,uid=-1,gid=-1} on
> AF_INET, we might still return this value if there is any risk that
> the endpoint would be unwilling to share its credentials: for example,
> this value might be returned if the other endpoint is not ptraceable
> by the caller - this would still cover the essential use case, which
> is for unprivileged users to authenticate the connections from their
> own processes. Would this limitation assuage your worries about the
> proposed feature?
>
> The thing is, I don't see any other way the ssh port forwarding mess
> can ever be improved. Do you have another solution in mind that?

UNIX sockets. Firewall rules. An opt-in mechanism like SCM_IDENTITY
that has explicit support in OpenSSH and doesn't happen unless the
administrator actually requests it in sshd_config.

>
> Any attempt to have some kind of authentication of local sockets that
> required participation on the client (authenticatee)'s part is doomed:
> if modifying the protocol and/or client code is an option, we might as
> well use some form of crypto / TLS. Or Unix-domain sockets. But what
> are we supposed to do when modifying the client (to make it send
> credentials, use crypto or connect on AF_UNIX) is not an option?

Exactly.

I believe that there is no secure way to authenticate clients that
currently don't authenticate themselves without changing the clients.
That's the whole point: currently-secure are written under the
assumption that they are not exercising their credentials. You can't
safely change that without making it opt-in.

--Andy

2014-10-16 00:07:10

by David Madore

[permalink] [raw]
Subject: Re: feature suggestion: implement SO_PEERCRED on local AF_INET/AF_INET6 sockets (allow uid-based identification on localhost)

On Wed, Oct 15, 2014 at 03:54:08PM -0700, Andy Lutomirski wrote:
> On Wed, Oct 15, 2014 at 3:30 PM, David Madore <[email protected]> wrote:
> > Note that since the possibility of using SO_PEERCRED on AF_INET
> > sockets does not hitherto exist on Linux, we can be sure that nobody
> > uses it, so it's not like it might open vulnerabilities in existing
> > code. If you think it's insecure, it can be documented as such (by
> > comparing it with identd): I still think it's better than having no
> > control at all when binding to localhost, which is the present
> > situation (causing, e.g., CVE-2014-2914).
>
> This doesn't follow. *Everybody* uses connect on AF_INET.
>
> IMO anything that sends a caller's credentials needs to be explicit and opt-in.

I'm confused as to whether you mean "opt-in" on the side of the caller
(=process requesting the endpoint's credentials), or on that of the
endpoint (=authenticated process). On the one hand I don't understand
what it could mean on the caller side, on the other hand you mention
explicit support in OpenSSH, which would be the caller in my scenario.

So, in case I haven't been clear enough, the situation I have in mind
is: on "thishost", I run "ssh -L 14321:remotehost:4321 somehost" to
forward connexions on from the local port 14321 of thishost (where ssh
listens on the loopback) to the port 4321 of remotehost.
Unfortunately, now everyone with an acccount on thishost can connect
to port 14321 and effectively emit a connection from somehost to
remotehost on my behalf. I think everyone agrees that this is a huge
problem. But I don't understand how you propose to remedy this.

Patching ssh is an option, but I don't see how to do it (ssh needs to
make sure that the connections it receives on 14321 are from the same
uid, and this seems impossible without the feature I'm discussing).
Patching the kernel is an option. Patching clients that connect to
14321, on the other hand, is not, because there are many different
ones, and their protocol is defined by immutable Internet standards,
so we have no latitude there (for example, we can't ask a Web browser
to connect to Unix domain sockets: there simply isn't a URL scheme to
refer to them). Adding iptables rules is not an option if I'm not the
system administrator on thishost.

So, how can we solve this problem securely?

> I believe that there is no secure way to authenticate clients that
> currently don't authenticate themselves without changing the clients.
> That's the whole point: currently-secure are written under the
> assumption that they are not exercising their credentials. You can't
> safely change that without making it opt-in.

Then what are we to do, given that modifying the clients is
impossible?

What about my proposal that user credentials would be returned only if
they refer to the same user as the caller user and that the caller is
permitted to ptrace the endpoint? This answers your objection of
leaking credentials: the caller could do anything at all with the
other side since it could ptrace it - we're just permitting a user to
authenticate their own sockets. A further sysctl could enable the use
of the call in more general cases, for those administrators who think
it should be allowed.

--
David A. Madore
( http://www.madore.org/~david/ )

2014-10-16 00:12:16

by Andy Lutomirski

[permalink] [raw]
Subject: Re: feature suggestion: implement SO_PEERCRED on local AF_INET/AF_INET6 sockets (allow uid-based identification on localhost)

On Wed, Oct 15, 2014 at 5:07 PM, David Madore <[email protected]> wrote:
> On Wed, Oct 15, 2014 at 03:54:08PM -0700, Andy Lutomirski wrote:
>> On Wed, Oct 15, 2014 at 3:30 PM, David Madore <[email protected]> wrote:
>> > Note that since the possibility of using SO_PEERCRED on AF_INET
>> > sockets does not hitherto exist on Linux, we can be sure that nobody
>> > uses it, so it's not like it might open vulnerabilities in existing
>> > code. If you think it's insecure, it can be documented as such (by
>> > comparing it with identd): I still think it's better than having no
>> > control at all when binding to localhost, which is the present
>> > situation (causing, e.g., CVE-2014-2914).
>>
>> This doesn't follow. *Everybody* uses connect on AF_INET.
>>
>> IMO anything that sends a caller's credentials needs to be explicit and opt-in.
>
> I'm confused as to whether you mean "opt-in" on the side of the caller
> (=process requesting the endpoint's credentials), or on that of the
> endpoint (=authenticated process). On the one hand I don't understand
> what it could mean on the caller side, on the other hand you mention
> explicit support in OpenSSH, which would be the caller in my scenario.

I mean the authenticated process, not the process doing the authentication.

>
> So, in case I haven't been clear enough, the situation I have in mind
> is: on "thishost", I run "ssh -L 14321:remotehost:4321 somehost" to
> forward connexions on from the local port 14321 of thishost (where ssh
> listens on the loopback) to the port 4321 of remotehost.
> Unfortunately, now everyone with an acccount on thishost can connect
> to port 14321 and effectively emit a connection from somehost to
> remotehost on my behalf. I think everyone agrees that this is a huge
> problem. But I don't understand how you propose to remedy this.

Unfortunately, I think that you need client changes. These could be
semi-transparent (using LD_PRELOAD) or almost completely transparent
(using network namespaces).

Actually, a network namespace-based proxying tool could be very useful.

>
> Patching ssh is an option, but I don't see how to do it (ssh needs to
> make sure that the connections it receives on 14321 are from the same
> uid, and this seems impossible without the feature I'm discussing).
> Patching the kernel is an option. Patching clients that connect to
> 14321, on the other hand, is not, because there are many different
> ones, and their protocol is defined by immutable Internet standards,
> so we have no latitude there (for example, we can't ask a Web browser
> to connect to Unix domain sockets: there simply isn't a URL scheme to
> refer to them). Adding iptables rules is not an option if I'm not the
> system administrator on thishost.

I misunderstood. I though that you wanted a server-side solution.

>
> So, how can we solve this problem securely?
>
>> I believe that there is no secure way to authenticate clients that
>> currently don't authenticate themselves without changing the clients.
>> That's the whole point: currently-secure are written under the
>> assumption that they are not exercising their credentials. You can't
>> safely change that without making it opt-in.
>
> Then what are we to do, given that modifying the clients is
> impossible?
>
> What about my proposal that user credentials would be returned only if
> they refer to the same user as the caller user and that the caller is
> permitted to ptrace the endpoint? This answers your objection of
> leaking credentials: the caller could do anything at all with the
> other side since it could ptrace it - we're just permitting a user to
> authenticate their own sockets. A further sysctl could enable the use
> of the call in more general cases, for those administrators who think
> it should be allowed.
>

Ugh.

That's probably safe, but it's quite disgusting IMO.

--Andy