Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 31 Jul 2001 04:40:45 -0700
From:      Dima Dorfman <dima@unixfreak.org>
To:        audit@freebsd.org
Cc:        dwmalone@freebsd.org
Subject:   Peer credentials on a Unix domain socket
Message-ID:  <20010731114050.B730E3E31@bazooka.unixfreak.org>

next in thread | raw e-mail | index | archive | help
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 <sys/queue.h>
+#include <sys/ucred.h>
 
 /*
  * 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 <sys/types.h>
X.Fd #include <unistd.h>
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 <sys/param.h>
X#include <sys/socket.h>
X#include <sys/ucred.h>
X#include <sys/un.h>
X
X#include <unistd.h>
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




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20010731114050.B730E3E31>