Post by Konstantin BelousovPost by Gleb PopovPost by Gleb PopovPost by Gleb PopovThis makes sense, thanks.
However, this code works on Linux and seems to return credentials of the
user that started the process. I actually stumbled upon this when porting
https://github.com/CollaboraOnline/online/blob/master/net/Socket.cpp#L805
Post by Gleb PopovWould it make sense if FreeBSD followed Linux semantics in this case? If
not, what are my options for porting the software?
From what I can tell . . .
FreeBSD defines LOCAL_PEERCRED and what goes with its use, not linux.
Linux defines SO_PEERCRED and what goes with its use, not FreeBSD.
If I understand right, your code is incompatible with the referenced
CollaboraOnline code from just after the #else (so __FreeBSD__ case,
getsockopt(getFD(), 0, LOCAL_PEERCRED, &creds, &credSize)
getsockopt(s, SOL_LOCAL, LOCAL_PEERCRED, &creds, &credSize)
Note the 0 vs. the SOL_LOCAL. Your code is a mix of Linux
and FreeBSD code when it should not be.
SOL_LOCAL is defined to 0, so this is fine.
Post by Gleb PopovSee also the following that involved replacing a SOL_LOCAL
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234722
Yes, I'm aware that Linux SO_PEERCRED operates on socket level, while ours
operates on level 0. This is taken in account
in the code I posted.
As I said, the error stems from the fact that Linux allows getting creds
from the listening socket.
There is no peer for listening socket.
Well, I ran into the below while looking around as far as what
getsockopt gets access to for peercred on a listening socket:
net/unix/af_unix.c has unix_listen that it uses and that code
does "set credentials so connect can copy them" but the
getsockopt code has access to the copy that listen
established for making the copy. I initially show the
init_peercred(sk) side of things below.
static int unix_listen(struct socket *sock, int backlog)
{
int err;
struct sock *sk = sock->sk;
struct unix_sock *u = unix_sk(sk);
err = -EOPNOTSUPP;
if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET)
goto out; /* Only stream/seqpacket sockets accept */
err = -EINVAL;
if (!u->addr)
goto out; /* No listens on an unbound socket */
unix_state_lock(sk);
if (sk->sk_state != TCP_CLOSE && sk->sk_state != TCP_LISTEN)
goto out_unlock;
if (backlog > sk->sk_max_ack_backlog)
wake_up_interruptible_all(&u->peer_wait);
sk->sk_max_ack_backlog = backlog;
sk->sk_state = TCP_LISTEN;
/* set credentials so connect can copy them */
init_peercred(sk);
err = 0;
out_unlock:
unix_state_unlock(sk);
out:
return err;
}
where:
static void init_peercred(struct sock *sk)
{
put_pid(sk->sk_peer_pid);
if (sk->sk_peer_cred)
put_cred(sk->sk_peer_cred);
sk->sk_peer_pid = get_pid(task_tgid(current));
sk->sk_peer_cred = get_current_cred();
}
and unix_listen is used via:
static const struct proto_ops unix_stream_ops = {
.family = PF_UNIX,
. . .
.listen = unix_listen,
. . .
static const struct proto_ops unix_seqpacket_ops = {
.family = PF_UNIX,
. . .
.listen = unix_listen,
. . .
On the other side is the only use of SO_PEERCRED:
int sock_getsockopt(struct socket *sock, int level, int optname,
char __user *optval, int __user *optlen)
{
struct sock *sk = sock->sk;
. . .
case SO_PEERCRED:
{
struct ucred peercred;
if (len > sizeof(peercred))
len = sizeof(peercred);
cred_to_ucred(sk->sk_peer_pid, sk->sk_peer_cred, &peercred);
if (copy_to_user(optval, &peercred, len))
return -EFAULT;
goto lenout;
}
. . .
used via (only place):
if (level == SOL_SOCKET)
err = sock_getsockopt(sock, level, optname, optval, optlen);
else if (unlikely(!sock->ops->getsockopt))
err = -EOPNOTSUPP;
else
err = sock->ops->getsockopt(sock, level, optname, optval,
optlen);
This code appears to return the copied peercred
information for SOL_SOCKET and SO_PEERCRED used
together.
I did not find any documentation that sk->sk_peer_cred
recorded by listen should be externally accessible via
getsockopt on the listen socket but it is from what I
can tell.
I'm only noting that having such a request seems to be
valid in the Linux implementation and is not rejected,
I'm not claiming details of which "peer" is involved in
the returned information or the like.
Post by Konstantin BelousovShow minimal code that works for you on Linux.
===
Mark Millard
marklmi at yahoo.com
( dsl-only.net went
away in early 2018-Mar)