Date: Thu, 22 Apr 2021 09:11:23 -0700 From: Mark Millard <marklmi@yahoo.com> To: Konstantin Belousov <kostikbel@gmail.com> Cc: Gleb Popov <arrowd@freebsd.org>, freebsd-hackers <freebsd-hackers@freebsd.org> Subject: Re: A bug with getsockopt(SOL_LOCAL, LOCAL_PEERCRED) ? Message-ID: <71F2CA02-7FEB-42EE-B5F6-D0A864ADA651@yahoo.com> In-Reply-To: <YIFQ/U34i1Jbxjv7@kib.kiev.ua> References: <CALH631kLCApctk4iQJj6br0Pzeb6qsh9g3jz_SA8hH91ftQGDQ@mail.gmail.com> <YHiQ6qEjS2w8uYpS@kib.kiev.ua> <CALH631=3hqvfraume467OM%2BqGqp854sGJFfhO8b61mF%2BkbsJ2Q@mail.gmail.com> <E6626DFC-2014-412C-AEA2-ECE57835B058@yahoo.com> <CALH631mi0xOQGjd6F3Beu_BQdqAx8RYCrbkUi524F8S4rwo1aA@mail.gmail.com> <40116716-D8D9-438D-A168-B26A112D199E@yahoo.com> <CALH631nrhyY%2BK3tPSwt1wuYHoZkz371UtjK%2BdG844RybtA2yKw@mail.gmail.com> <YIFQ/U34i1Jbxjv7@kib.kiev.ua>
next in thread | previous in thread | raw e-mail | index | archive | help
On 2021-Apr-22, at 03:33, Konstantin Belousov <kostikbel at gmail.com> wrote: > > On Thu, Apr 22, 2021 at 07:54:26AM +0300, Gleb Popov wrote: >> On Thu, Apr 22, 2021 at 1:00 AM Mark Millard <marklmi@yahoo.com> wrote: >> >>> >>> On 2021-Apr-21, at 11:27, Gleb Popov <arrowd at freebsd.org> wrote: >>>> >>>> This 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 >>> this code: >>> https://github.com/CollaboraOnline/online/blob/master/net/Socket.cpp#L805 >>>> >>>> Would 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, >>> not the linux case): >>> >>> getsockopt(getFD(), 0, LOCAL_PEERCRED, &creds, &credSize) >>> vs. your: >>> 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. >> >> >>> See also the following that involved replacing a SOL_LOCAL >>> with a 0 for getsockopt used with LOCAL_PEERCRED: >>> >>> 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. > Show minimal code that works for you on Linux. > === Mark Millard marklmi at yahoo.com ( dsl-only.net went away in early 2018-Mar)
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?71F2CA02-7FEB-42EE-B5F6-D0A864ADA651>