Implementing a way to get UNIX socket peer credentials

644 views
Skip to first unread message

Alberto Bertogli

unread,
Sep 16, 2016, 8:15:20 AM9/16/16
to golan...@googlegroups.com

Hi!

I wanted to get the peer credentials on an UNIX socket (not send/receive
it, just get it passively) for a small thing that I'm doing, and
couldn't find a reasonably portable way to do that in Go.

In C, almost all UNIX-alikes have getpeereid() as a somewhat portable
abstraction.

I looked into it and most systems support this feature via slightly
different variants of getsockopt(). Linux' one is already present and
works fine, but the rest (*BSD and I guess Darwin) are missing.


So I wrote https://go-review.googlesource.com/#/c/29310/ as a proof of
concept, which seems to work fine on Linux (untested everywhere else).


I have a few questions:

1) Is this something worth doing?

https://github.com/golang/go/issues/1101 has been open about this (or at
least in part about this) for a while.

Linux support exists, and I think the functionality is useful, but would
this be something you consider too obscure/not worth doing?


2) What would the API look like?

In the proof of concept, I went with:

type UnixPeerCreds struct { Uid, Gid, Pid int }
func (c *UnixConn) GetPeerCredentials() (creds *UnixPeerCreds, err error)

so that it could be potentially extended in the future, does this seem
ok to you? If not, what would you suggest?


3) What's the best way to implement it?

In the proof of concept I extended the syscall package with different
GetsockoptXXX functions, according to the platform; and then used them
in os-specific files in the net package.

However, IIUC the syscall package is supposed to be frozen. I looked at
internal/syscall/unix/ but it doesn't have much, and in particular does
not seem to have any use of cgo to get structures from C, so I'm not
sure if that'd be allowed there.

Similarly, are OS-specific files in net the best way? Should I just
provide a getpeereid()-equivalent at the syscall level, and then avoid
OS-specific files in net entirely?

What would be the right approach here?


Thanks a lot!
Alberto


PS: In case it's useful for reference, here are some of the
implementations of getpeereid() for non-Linux. Go already supports it on
Linux via syscall.GetsockoptUcred.

Linux+others: https://cgit.freedesktop.org/libbsd/tree/src/getpeereid.c
FreeBSD: https://github.com/freebsd/freebsd/blob/master/lib/libc/gen/getpeereid.c
OpenBSD: https://github.com/openbsd/src/blob/master/lib/libc/net/getpeereid.c
DragonflyBSD: https://github.com/DragonFlyBSD/DragonFlyBSD/blob/master/lib/libc/gen/getpeereid.c
NetBSD: https://github.com/jsonn/src/blob/trunk/lib/libc/net/getpeereid.c
Darwin: http://opensource.apple.com/source/Libc/Libc-1081.1.3/gen/FreeBSD/getpeereid.c (?)

Ian Lance Taylor

unread,
Sep 16, 2016, 10:10:07 AM9/16/16
to Alberto Bertogli, golan...@googlegroups.com
On Thu, Sep 15, 2016 at 2:54 PM, Alberto Bertogli
<albe...@blitiri.com.ar> wrote:
>
> I wanted to get the peer credentials on an UNIX socket (not send/receive
> it, just get it passively) for a small thing that I'm doing, and
> couldn't find a reasonably portable way to do that in Go.
>
> In C, almost all UNIX-alikes have getpeereid() as a somewhat portable
> abstraction.
>
> I looked into it and most systems support this feature via slightly
> different variants of getsockopt(). Linux' one is already present and
> works fine, but the rest (*BSD and I guess Darwin) are missing.
>
>
> So I wrote https://go-review.googlesource.com/#/c/29310/ as a proof of
> concept, which seems to work fine on Linux (untested everywhere else).
>
>
> I have a few questions:
>
> 1) Is this something worth doing?
>
> https://github.com/golang/go/issues/1101 has been open about this (or at
> least in part about this) for a while.
>
> Linux support exists, and I think the functionality is useful, but would
> this be something you consider too obscure/not worth doing?

We would normally put this kind of thing into golang.org/x/net, if at
all possible. Perhaps we need golang.org/x/net/unix.


> 2) What would the API look like?
>
> In the proof of concept, I went with:
>
> type UnixPeerCreds struct { Uid, Gid, Pid int }
> func (c *UnixConn) GetPeerCredentials() (creds *UnixPeerCreds, err error)
>
> so that it could be potentially extended in the future, does this seem
> ok to you? If not, what would you suggest?

Sounds fine to me. I guess we could use -1 for "value unknown".


> 3) What's the best way to implement it?
>
> In the proof of concept I extended the syscall package with different
> GetsockoptXXX functions, according to the platform; and then used them
> in os-specific files in the net package.
>
> However, IIUC the syscall package is supposed to be frozen. I looked at
> internal/syscall/unix/ but it doesn't have much, and in particular does
> not seem to have any use of cgo to get structures from C, so I'm not
> sure if that'd be allowed there.
>
> Similarly, are OS-specific files in net the best way? Should I just
> provide a getpeereid()-equivalent at the syscall level, and then avoid
> OS-specific files in net entirely?
>
> What would be the right approach here?

Ideally, put it in golang.org/x/net and follow the pattern of the ipv4
and ipv6 directories there.

Ian

Alberto Bertogli

unread,
Sep 16, 2016, 5:08:31 PM9/16/16
to Ian Lance Taylor, golan...@googlegroups.com
Great, thanks for the detailed response!

Does https://go-review.googlesource.com/#/c/29292/ match what you had in
mind?

I included a move of the get/setsockopt on the same change because I
don't know how to make a change on top of another in gerrit, you can
find that in isolation at https://go-review.googlesource.com/#/c/29291/.


Assuming this is roughly ok, what's the best way to run "cgo -godefs"
for the different platforms? Do I need to get a hold of them myself, or
is there something common that can be used for this?

Thanks!
Alberto


PS: If you prefer a diff view of the individual patches:
https://blitiri.com.ar/git/r/go-net/c/d06f1c2971a83c319103b39e99b217c11d4280c4/
https://blitiri.com.ar/git/r/go-net/c/c98446e4549b60af3b281395356dbfd549ebe4be/

Giovanni Bajo

unread,
Sep 16, 2016, 8:42:31 PM9/16/16
to golang-dev, albe...@blitiri.com.ar
On Friday, September 16, 2016 at 4:10:07 PM UTC+2, Ian Lance Taylor wrote:

> 3) What's the best way to implement it?
>
> In the proof of concept I extended the syscall package with different
> GetsockoptXXX functions, according to the platform; and then used them
> in os-specific files in the net package.
>
> However, IIUC the syscall package is supposed to be frozen. I looked at
> internal/syscall/unix/ but it doesn't have much, and in particular does
> not seem to have any use of cgo to get structures from C, so I'm not
> sure if that'd be allowed there.
>
> Similarly, are OS-specific files in net the best way? Should I just
> provide a getpeereid()-equivalent at the syscall level, and then avoid
> OS-specific files in net entirely?
>
> What would be the right approach here?

Ideally, put it in golang.org/x/net and follow the pattern of the ipv4
and ipv6 directories there.


There's also https://github.com/golang/go/issues/16385 about using local sockets over HTTP, using getpeername() to authenticate the remote peer via the UID/GID. Currently, net.UnixListener works with http.Server, but it doesn't expose information about the peer; in fact, the http.Request.RemoteAddr is always "@" when using local sockets. Ideally, merging this functionality in the standard library somehow would allow to expose pid/uid/gid in RemoteAddr, which would be the easiest way for users. It wouldn't even require a new public API.

Giovanni

mikioh...@gmail.com

unread,
Sep 24, 2016, 9:06:33 PM9/24/16
to golang-dev

Does https://go-review.googlesource.com/#/c/29292/ match what you had in
mind?

please split it into 4 cls; "internal/netsyscall: new package", "unix: new package", "ipv4: use of internet/netsyscall package" and "ipv6: use of internal/netsyscall pacakge" on the same topic branch, and  run git mail for each change.

mikioh...@gmail.com

unread,
Sep 24, 2016, 9:10:25 PM9/24/16
to golang-dev
also, see http://golang.org/issues/1101 and don't forget to refer it.
Reply all
Reply to author
Forward
0 new messages