From owner-freebsd-audit Tue Jul 31 4:41:10 2001 Delivered-To: freebsd-audit@freebsd.org Received: from bazooka.unixfreak.org (bazooka.unixfreak.org [63.198.170.138]) by hub.freebsd.org (Postfix) with ESMTP id 0ADEA37B403; Tue, 31 Jul 2001 04:40:51 -0700 (PDT) (envelope-from dima@unixfreak.org) Received: by bazooka.unixfreak.org (Postfix, from userid 1000) id B730E3E31; Tue, 31 Jul 2001 04:40:50 -0700 (PDT) Received: from bazooka.unixfreak.org (localhost [127.0.0.1]) by bazooka.unixfreak.org (Postfix) with ESMTP id A96D63C12C; Tue, 31 Jul 2001 04:40:50 -0700 (PDT) To: audit@freebsd.org Cc: dwmalone@freebsd.org Subject: Peer credentials on a Unix domain socket Date: Tue, 31 Jul 2001 04:40:45 -0700 From: Dima Dorfman Message-Id: <20010731114050.B730E3E31@bazooka.unixfreak.org> Sender: owner-freebsd-audit@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.ORG As discussed on -arch... Attached are two things: one is a patch that implements the socket option and documents it in unix(4). The second is a sharball of the getpeereid() implementation and man page. Here's an excerpt from unix(4) that pretty much sums up what's there: The effective credentials (i.e., the user ID and group list) the of a peer on a SOCK_STREAM socket may be obtained using the LOCAL_PEERCRED socket option. This may be used by a server to obtain and verify the credentials of its client, and vice versa by the client to verify the credentials of the server. These will arrive in the form of a filled in struct xucred (defined in sys/ucred.h). The credentials presented to the server (the listen(2) caller) are those of the client when it called connect(2); the credentials presented to the client (the connect(2) caller) are those of the server when it called listen(2). This mechanism is reliable; there is no way for either party to influence the creden- tials presented to its peer except by calling the appropriate system call (e.g., connect(2) or listen(2)) under different effective credentials. The above, with s/LOCAL_PEERCRED/SO_PEERCRED/ also describes that Linux does. They obviously don't use struct xucred, but the idea is the same. I.e., they also record the credentials at connect(2) and listen(2) time. Please review. Thanks in advance. Index: sys/sys/un.h =================================================================== RCS file: /stl/src/FreeBSD/src/sys/sys/un.h,v retrieving revision 1.17 diff -u -r1.17 un.h --- sys/sys/un.h 1999/12/29 04:24:49 1.17 +++ sys/sys/un.h 2001/07/15 11:21:14 @@ -46,12 +46,16 @@ char sun_path[104]; /* path name (gag) */ }; +/* Socket options. */ +#define LOCAL_PEERCRED 0x001 /* retrieve peer credentials */ + #ifdef _KERNEL struct mbuf; struct socket; int uipc_usrreq __P((struct socket *so, int req, struct mbuf *m, struct mbuf *nam, struct mbuf *control)); +int uipc_ctloutput __P((struct socket *so, struct sockopt *sopt)); int unp_connect2 __P((struct socket *so, struct socket *so2)); void unp_dispose __P((struct mbuf *m)); int unp_externalize __P((struct mbuf *rights)); Index: sys/sys/unpcb.h =================================================================== RCS file: /stl/src/FreeBSD/src/sys/sys/unpcb.h,v retrieving revision 1.11 diff -u -r1.11 unpcb.h --- sys/sys/unpcb.h 2000/05/26 02:06:59 1.11 +++ sys/sys/unpcb.h 2001/07/15 11:21:14 @@ -38,6 +38,7 @@ #define _SYS_UNPCB_H_ #include +#include /* * Protocol control block for an active @@ -80,7 +81,25 @@ int unp_cc; /* copy of rcv.sb_cc */ int unp_mbcnt; /* copy of rcv.sb_mbcnt */ unp_gen_t unp_gencnt; /* generation count of this instance */ + int unp_flags; /* flags */ + struct xucred unp_peercred; /* peer credentials, if applicable */ }; + +/* + * Flags in unp_flags. + * + * UNP_HAVEPC - indicates that the unp_peercred member is filled in + * and is really the credentials of the connected peer. This is used + * to determine whether the contents should be sent to the user or + * not. + * + * UNP_HAVEPCCACHED - indicates that the unp_peercred member is filled + * in, but does *not* contain the credentials of the connected peer + * (there may not even be a peer). This is set in unp_listen() when + * it fills in unp_peercred for later consumption by unp_connect(). + */ +#define UNP_HAVEPC 0x001 +#define UNP_HAVEPCCACHED 0x002 #define sotounpcb(so) ((struct unpcb *)((so)->so_pcb)) Index: sys/kern/uipc_proto.c =================================================================== RCS file: /stl/src/FreeBSD/src/sys/kern/uipc_proto.c,v retrieving revision 1.21 diff -u -r1.21 uipc_proto.c --- sys/kern/uipc_proto.c 1999/10/11 15:19:11 1.21 +++ sys/kern/uipc_proto.c 2001/07/15 11:21:14 @@ -51,7 +51,7 @@ static struct protosw localsw[] = { { SOCK_STREAM, &localdomain, 0, PR_CONNREQUIRED|PR_WANTRCVD|PR_RIGHTS, - 0, 0, 0, 0, + 0, 0, 0, &uipc_ctloutput, 0, 0, 0, 0, 0, &uipc_usrreqs Index: sys/kern/uipc_usrreq.c =================================================================== RCS file: /stl/src/FreeBSD/src/sys/kern/uipc_usrreq.c,v retrieving revision 1.66 diff -u -r1.66 uipc_usrreq.c --- sys/kern/uipc_usrreq.c 2001/05/25 16:59:07 1.66 +++ sys/kern/uipc_usrreq.c 2001/07/15 11:21:14 @@ -91,6 +91,7 @@ static void unp_mark __P((struct file *)); static void unp_discard __P((struct file *)); static int unp_internalize __P((struct mbuf *, struct proc *)); +static int unp_listen __P((struct unpcb *, struct proc *)); static int uipc_abort(struct socket *so) @@ -199,7 +200,7 @@ if (unp == 0 || unp->unp_vnode == 0) return EINVAL; - return 0; + return unp_listen(unp, p); } static int @@ -434,6 +435,41 @@ uipc_send, uipc_sense, uipc_shutdown, uipc_sockaddr, sosend, soreceive, sopoll }; + +int +uipc_ctloutput(so, sopt) + struct socket *so; + struct sockopt *sopt; +{ + struct unpcb *unp = sotounpcb(so); + int error; + + switch (sopt->sopt_dir) { + case SOPT_GET: + switch (sopt->sopt_name) { + case LOCAL_PEERCRED: + if (unp->unp_flags & UNP_HAVEPC) + error = sooptcopyout(sopt, &unp->unp_peercred, + sizeof(unp->unp_peercred)); + else { + if (so->so_type == SOCK_STREAM) + error = ENOTCONN; + else + error = EINVAL; + } + break; + default: + error = EOPNOTSUPP; + break; + } + break; + case SOPT_SET: + default: + error = EOPNOTSUPP; + break; + } + return (error); +} /* * Both send and receive buffers are allocated PIPSIZ bytes of buffering @@ -609,7 +645,7 @@ register struct sockaddr_un *soun = (struct sockaddr_un *)nam; register struct vnode *vp; register struct socket *so2, *so3; - struct unpcb *unp2, *unp3; + struct unpcb *unp, *unp2, *unp3; int error, len; struct nameidata nd; char buf[SOCK_MAXADDRLEN]; @@ -648,12 +684,40 @@ error = ECONNREFUSED; goto bad; } + unp = sotounpcb(so); unp2 = sotounpcb(so2); unp3 = sotounpcb(so3); if (unp2->unp_addr) unp3->unp_addr = (struct sockaddr_un *) dup_sockaddr((struct sockaddr *) unp2->unp_addr, 1); + + /* + * unp_peercred management: + * + * The connecter's (client's) credentials are copied + * from its process structure at the time of connect() + * (which is now). + */ + memset(&unp3->unp_peercred, '\0', sizeof(unp3->unp_peercred)); + unp3->unp_peercred.cr_uid = p->p_ucred->cr_uid; + unp3->unp_peercred.cr_ngroups = p->p_ucred->cr_ngroups; + memcpy(unp3->unp_peercred.cr_groups, p->p_ucred->cr_groups, + sizeof(unp3->unp_peercred.cr_groups)); + unp3->unp_flags |= UNP_HAVEPC; + /* + * The receiver's (server's) credentials are copied + * from the unp_peercred member of socket on which the + * former called listen(); unp_listen() cached that + * process's credentials at that time so we can use + * them now. + */ + KASSERT(unp2->unp_flags & UNP_HAVEPCCACHED, + ("unp_connect: listener without cached peercred")); + memcpy(&unp->unp_peercred, &unp2->unp_peercred, + sizeof(unp->unp_peercred)); + unp->unp_flags |= UNP_HAVEPC; + so2 = so3; } error = unp_connect2(so, so2); @@ -1242,6 +1306,21 @@ if (m) unp_scan(m, unp_discard); +} + +static int +unp_listen(unp, p) + struct unpcb *unp; + struct proc *p; +{ + + bzero(&unp->unp_peercred, sizeof(unp->unp_peercred)); + unp->unp_peercred.cr_uid = p->p_ucred->cr_uid; + unp->unp_peercred.cr_ngroups = p->p_ucred->cr_ngroups; + bcopy(p->p_ucred->cr_groups, unp->unp_peercred.cr_groups, + sizeof(unp->unp_peercred.cr_groups)); + unp->unp_flags |= UNP_HAVEPCCACHED; + return (0); } static void Index: share/man/man4/unix.4 =================================================================== RCS file: /stl/src/FreeBSD/src/share/man/man4/unix.4,v retrieving revision 1.4 diff -u -r1.4 unix.4 --- share/man/man4/unix.4 2001/07/14 19:40:48 1.4 +++ share/man/man4/unix.4 2001/07/15 11:21:14 @@ -32,7 +32,7 @@ .\" @(#)unix.4 8.1 (Berkeley) 6/9/93 .\" $FreeBSD: src/share/man/man4/unix.4,v 1.4 2001/07/14 19:40:48 schweikh Exp $ .\" -.Dd June 9, 1993 +.Dd July 15, 2001 .Dt UNIX 4 .Os .Sh NAME @@ -147,6 +147,35 @@ Descriptors that are awaiting delivery, or that are purposely not received, are automatically closed by the system when the destination socket is closed. +.Pp +The effective credentials (i.e., the user ID and group list) the of a +peer on a +.Dv SOCK_STREAM +socket may be obtained using the +.Dv LOCAL_PEERCRED +socket option. +This may be used by a server to obtain and verify the credentials of +its client, and vice versa by the client to verify the credentials +of the server. +These will arrive in the form of a filled in +.Ar struct xucred +(defined in +.Pa sys/ucred.h ) . +The credentials presented to the server (the +.Xr listen 2 +caller) are those of the client when it called +.Xr connect 2 ; +the credentials presented to the client (the +.Xr connect 2 +caller) are those of the server when it called +.Xr listen 2 . +This mechanism is reliable; there is no way for either party to influence +the credentials presented to its peer except by calling the appropriate +system call (e.g., +.Xr connect 2 +or +.Xr listen 2 ) +under different effective credentials. .Sh SEE ALSO .Xr socket 2 , .Xr intro 4 # This is a shell archive. Save it in a file, remove anything before # this line, and then unpack it by entering "sh file". Note, it may # create directories; files and directories will be owned by you and # have default permissions. # # This archive contains: # # getpeereid.3 # getpeereid.c # echo x - getpeereid.3 sed 's/^X//' >getpeereid.3 << 'END-of-getpeereid.3' X.\" X.\" Copyright (c) 2001 Dima Dorfman. X.\" All rights reserved. X.\" X.\" Redistribution and use in source and binary forms, with or without X.\" modification, are permitted provided that the following conditions X.\" are met: X.\" 1. Redistributions of source code must retain the above copyright X.\" notice, this list of conditions and the following disclaimer. X.\" 2. Redistributions in binary form must reproduce the above copyright X.\" notice, this list of conditions and the following disclaimer in the X.\" documentation and/or other materials provided with the distribution. X.\" X.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND X.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE X.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE X.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE X.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL X.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS X.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) X.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT X.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY X.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF X.\" SUCH DAMAGE. X.\" X.\" $FreeBSD$ X.\" X.Dd July 15, 2001 X.Dt GETPEEREID 3 X.Os X.Sh NAME X.Nm getpeereid , X.Nd get the effective credentials of a UNIX-domain peer X.Sh LIBRARY X.Lb libc X.Sh SYNOPSIS X.Fd #include X.Fd #include X.Ft int X.Fn getpeereid "int s" "uid_t *euid" "gid_t *egid" X.Sh DESCRIPTION XThe X.Fn getpeereid Xroutine returns the effective user and group IDs of the Xpeer connected to a UNIX-domain socket. XThe argument X.Fa s Xmust be a UNIX-domain socket X.Pq Xr unix 4 Xof type X.Dv SOCK_STREAM Xon which either X.Xr connect 2 Xor X.Xr listen 2 Xhave been called. XThe effective used ID is placed in X.Fa euid , Xand the effective group ID in X.Fa egid . X.Pp XThe credentials returned to the X.Xr listen 2 Xcaller are those of its peer at the time it called X.Xr connect 2 ; Xthe credentials returned to the X.Xr connect 2 Xcaller are those of its peer at the time it called X.Xr listen 2 . XThis mechanism is reliable; there is no way for either side to influence Xthe credentials returned to its peer except by calling the appropriate Xsystem call (i.e., either X.Xr connect 2 Xor X.Xr listen 2 ) Xunder different effective credentials. X.Pp XOne common use of this routine is for a UNIX-domain server Xto verify the credentials of its client. XLikewise, the client can verify the credentials of the server. X.Sh IMPLEMENTATION NOTES XOn X.Fx , X.Fn getpeereid Xis implemented in terms of the X.Dv LOCAL_PEERCRED X.Xr unix 4 Xsocket option. X.Sh RETURN VALUES XIf the call succeeds, a value of 0 is returned and X.Fa euid Xand X.Fa egid Xcontain the effective user and group IDs of the peer on X.Fa s , Xrespectively. XIf the call fails, a value of \-1 is returned and X.Va errno Xis set to indicate the error. X.Sh ERRORS XThe call succeeds unless: X.Bl -tag -width Er X.It Bq Er EBADF XThe argument X.Fa s Xis not a valid descriptor. X.It Bq Er ENOTSOCK XThe argument X.Fa s Xis a file, not a socket. X.It Bq Er ENOTCONN XThe argument X.Fa s Xdoes not refer to a socket on which X.Xr connect 2 Xor X.Xr listen 2 Xhave been called. X.It Bq Er EINVAL XThe argument X.Fa s Xdoes not refer to a socket of type X.Dv SOCK_STREAM . X.El X.Sh SEE ALSO X.Xr connect 2 , X.Xr getpeername 2 , X.Xr getsockname 2 , X.Xr getsockopt 2 , X.Xr listen 2 , X.Xr unix 4 X.Sh HISTORY XThe X.Fn getpeereid Xroutine appeared in X.Fx 5.0 . END-of-getpeereid.3 echo x - getpeereid.c sed 's/^X//' >getpeereid.c << 'END-of-getpeereid.c' X/* X * Copyright (c) 2001 Dima Dorfman. X * All rights reserved. X * X * Redistribution and use in source and binary forms, with or without X * modification, are permitted provided that the following conditions X * are met: X * 1. Redistributions of source code must retain the above copyright X * notice, this list of conditions and the following disclaimer. X * 2. Redistributions in binary form must reproduce the above copyright X * notice, this list of conditions and the following disclaimer in the X * documentation and/or other materials provided with the distribution. X * X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE X * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF X * SUCH DAMAGE. X */ X X#if defined(LIBC_RCS) && !defined(lint) Xstatic const char rcsid[] = X "$FreeBSD$"; X#endif /* LIBC_RCS and not lint */ X X#include X#include X#include X#include X X#include X Xint Xgetpeereid(int s, uid_t *euid, gid_t *egid) X{ X struct xucred xuc; X socklen_t xuclen; X int error; X X xuclen = sizeof(xuc); X error = getsockopt(s, LOCAL_PEERCRED, 1, &xuc, &xuclen); X if (error != 0) X return (error); X *euid = xuc.cr_uid; X *egid = xuc.cr_gid; X return (0); X} END-of-getpeereid.c exit To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-audit" in the body of the message