Subject: Re: getpeereid() or equivalent
To: Jason Thorpe <thorpej@shagadelic.org>
From: Arne H. Juul <arnej@pvv.ntnu.no>
List: tech-net
Date: 08/06/2007 11:08:33
On Sun, 5 Aug 2007, Arne H. Juul wrote:
> Here's a patch implementing a LOCAL_PEEREID option for getsockopt().

and here's an updated version with some comments fixed, one
change to fstat (has to include sys/un.h before sys/unpcb.h)
and getpeereid() library function added.

   -  Arne H. J.


Index: sys/sys/un.h
===================================================================
RCS file: /usr/cvs/src/sys/sys/un.h,v
retrieving revision 1.39
diff -u -r1.39 un.h
--- sys/sys/un.h	23 Jul 2006 22:06:14 -0000	1.39
+++ sys/sys/un.h	6 Aug 2007 08:11:39 -0000
@@ -57,8 +57,18 @@
  #if defined(_NETBSD_SOURCE)
  #define	LOCAL_CREDS	0x0001		/* pass credentials to receiver */
  #define	LOCAL_CONNWAIT	0x0002		/* connects block until accepted */
+#define	LOCAL_PEEREID	0x0003		/* get peer identification */
  #endif

+/*
+ * data automatically stored inside connect() for use by LOCAL_PEEREID
+ */
+struct	unpcbid {
+	pid_t unp_pid;	/* process id */
+	uid_t unp_euid;	/* effective user id */
+	gid_t unp_egid;	/* effective group id */
+};
+
  #ifdef _KERNEL
  struct unpcb;
  struct socket;
Index: sys/sys/unpcb.h
===================================================================
RCS file: /usr/cvs/src/sys/sys/unpcb.h,v
retrieving revision 1.14
diff -u -r1.14 unpcb.h
--- sys/sys/unpcb.h	11 Dec 2005 12:25:21 -0000	1.14
+++ sys/sys/unpcb.h	6 Aug 2007 08:18:11 -0000
@@ -78,11 +78,25 @@
  	int	unp_mbcnt;		/* copy of rcv.sb_mbcnt */
  	struct	timespec unp_ctime;	/* holds creation time */
  	int	unp_flags;		/* misc flags; see below*/
+	struct	unpcbid unp_connid;	/* pid and eids of peer */
  };

-/* unp_flags */
+/*
+ * Flags in unp_flags.
+ *
+ * UNP_EIDSVALID - indicates that the unp_connid member is filled in
+ * and is really the effective ids of the connected peer.  This is used
+ * to determine whether the contents should be sent to the user or
+ * not.
+ * 
+ * UNP_EIDSBIND - indicates that the unp_connid member is filled 
+ * in with data for the listening process.  This is set in unp_bind() when
+ * it fills in unp_connid for later consumption by unp_connect().
+ */
  #define	UNP_WANTCRED	0x0001		/* credentials wanted */
  #define	UNP_CONNWAIT	0x0002		/* connect blocks until accepted */
+#define	UNP_EIDSVALID	0x0004		/* unp_connid contains valid data */
+#define	UNP_EIDSBIND	0x0008		/* unp_connid was set by a bind */

  #define	sotounpcb(so)	((struct unpcb *)((so)->so_pcb))

Index: sys/kern/uipc_usrreq.c
===================================================================
RCS file: /usr/cvs/src/sys/kern/uipc_usrreq.c,v
retrieving revision 1.98
diff -u -r1.98 uipc_usrreq.c
--- sys/kern/uipc_usrreq.c	3 Aug 2007 20:49:45 -0000	1.98
+++ sys/kern/uipc_usrreq.c	5 Aug 2007 17:07:33 -0000
@@ -113,8 +113,8 @@
  #include <sys/protosw.h>
  #include <sys/socket.h>
  #include <sys/socketvar.h>
-#include <sys/unpcb.h>
  #include <sys/un.h>
+#include <sys/unpcb.h>
  #include <sys/namei.h>
  #include <sys/vnode.h>
  #include <sys/file.h>
@@ -499,17 +499,22 @@

  	case PRCO_GETOPT:
  		switch (optname) {
+		case LOCAL_PEEREID:
+			if (unp->unp_flags & UNP_EIDSVALID) {
+				*mp = m = m_get(M_WAIT, MT_SOOPTS);
+				m->m_len = sizeof(struct unpcbid);
+				*mtod(m, struct unpcbid *) = unp->unp_connid;
+			} else {
+				error = EINVAL;
+			}
+			break;
  		case LOCAL_CREDS:
  			*mp = m = m_get(M_WAIT, MT_SOOPTS);
  			m->m_len = sizeof(int);
-			switch (optname) {

  #define	OPTBIT(bit)	(unp->unp_flags & (bit) ? 1 : 0)

-			case LOCAL_CREDS:
-				optval = OPTBIT(UNP_WANTCRED);
-				break;
-			}
+			optval = OPTBIT(UNP_WANTCRED);
  			*mtod(m, int *) = optval;
  			break;
  #undef OPTBIT
@@ -658,6 +663,10 @@
  	unp->unp_vnode = vp;
  	unp->unp_addrlen = addrlen;
  	unp->unp_addr = sun;
+	unp->unp_connid.unp_pid = p->p_pid;
+	unp->unp_connid.unp_euid = kauth_cred_geteuid(p->p_cred);
+	unp->unp_connid.unp_egid = kauth_cred_getegid(p->p_cred);
+	unp->unp_flags |= UNP_EIDSBIND;
  	VOP_UNLOCK(vp, 0);
  	return (0);

@@ -672,11 +681,13 @@
  	struct sockaddr_un *sun;
  	struct vnode *vp;
  	struct socket *so2, *so3;
-	struct unpcb *unp2, *unp3;
+	struct unpcb *unp, *unp2, *unp3;
  	size_t addrlen;
+	struct proc *p;
  	int error;
  	struct nameidata nd;

+	p = l->l_proc;
  	/*
  	 * Allocate a temporary sockaddr.  We have to allocate one extra
  	 * byte so that we can ensure that the pathname is nul-terminated.
@@ -714,6 +725,7 @@
  			error = ECONNREFUSED;
  			goto bad;
  		}
+		unp = sotounpcb(so);
  		unp2 = sotounpcb(so2);
  		unp3 = sotounpcb(so3);
  		if (unp2->unp_addr) {
@@ -724,7 +736,15 @@
  			unp3->unp_addrlen = unp2->unp_addrlen;
  		}
  		unp3->unp_flags = unp2->unp_flags;
+		unp3->unp_connid.unp_pid = p->p_pid;
+		unp3->unp_connid.unp_euid = kauth_cred_geteuid(p->p_cred);
+		unp3->unp_connid.unp_egid = kauth_cred_getegid(p->p_cred);
+		unp3->unp_flags |= UNP_EIDSVALID;
  		so2 = so3;
+		if (unp2->unp_flags & UNP_EIDSBIND) {
+			unp->unp_connid = unp2->unp_connid;
+			unp->unp_flags |= UNP_EIDSVALID;
+		}
  	}
  	error = unp_connect2(so, so2, PRU_CONNECT);
   bad:
Index: usr.bin/fstat/fstat.c
===================================================================
RCS file: /usr/cvs/src/usr.bin/fstat/fstat.c,v
retrieving revision 1.75
diff -u -r1.75 fstat.c
--- usr.bin/fstat/fstat.c	11 May 2006 11:56:38 -0000	1.75
+++ usr.bin/fstat/fstat.c	5 Aug 2007 20:26:53 -0000
@@ -52,6 +52,7 @@
  #include <sys/socketvar.h>
  #include <sys/domain.h>
  #include <sys/protosw.h>
+#include <sys/un.h>
  #include <sys/unpcb.h>
  #include <sys/sysctl.h>
  #include <sys/filedesc.h>
Index: include/unistd.h
===================================================================
RCS file: /usr/cvs/src/include/unistd.h,v
retrieving revision 1.112
diff -u -r1.112 unistd.h
--- include/unistd.h	2 Aug 2007 21:49:10 -0000	1.112
+++ include/unistd.h	5 Aug 2007 20:33:06 -0000
@@ -312,6 +312,7 @@
  int	 getgrouplist(const char *, gid_t, gid_t *, int *);
  int	 getgroupmembership(const char *, gid_t, gid_t *, int, int *);
  mode_t	 getmode(const void *, mode_t);
+int	 getpeereid(int, uid_t *, gid_t *);
  int	 getsubopt(char **, char * const *, char **);
  __aconst char *getusershell(void);
  int	 initgroups(const char *, gid_t);
Index: lib/libc/net/Makefile.inc
===================================================================
RCS file: /usr/cvs/src/lib/libc/net/Makefile.inc,v
retrieving revision 1.71
diff -u -r1.71 Makefile.inc
--- lib/libc/net/Makefile.inc	26 Aug 2006 16:11:05 -0000	1.71
+++ lib/libc/net/Makefile.inc	5 Aug 2007 20:40:48 -0000
@@ -5,7 +5,7 @@
  .PATH: ${ARCHDIR}/net ${.CURDIR}/net

  SRCS+=	__cmsg_alignbytes.c base64.c ethers.c gethnamaddr.c getifaddrs.c \
-	getnetnamadr.c getnetent.c \
+	getnetnamadr.c getnetent.c getpeereid.c \
  	getprotobyname.c getprotobynumber.c getprotoent.c \
  	getprotobyname_r.c getprotobynumber_r.c getprotoent_r.c \
  	getservbyname.c getservbyport.c getservent.c \
@@ -42,7 +42,7 @@
  .include "${ARCHDIR}/net/Makefile.inc"

  MAN+=	byteorder.3 ethers.3 gethostbyname.3 getifaddrs.3 \
-	getnetent.3 getprotoent.3 \
+	getnetent.3 getprotoent.3 getpeereid.3 \
  	getservent.3 inet.3 inet_net.3 iso_addr.3 linkaddr.3 \
  	nsdispatch.3 rcmd.3 resolver.3 sockatmark.3

Index: lib/libc/net/getpeereid.c
===================================================================
RCS file: lib/libc/net/getpeereid.c
diff -N lib/libc/net/getpeereid.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ lib/libc/net/getpeereid.c	5 Aug 2007 20:34:50 -0000
@@ -0,0 +1,18 @@
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+
+int
+getpeereid(int s, uid_t *euid, gid_t *egid)
+{
+	struct unpcbid cred;
+	socklen_t len = sizeof(cred);
+	if (getsockopt(s, 0, LOCAL_PEEREID, &cred, &len) < 0) {
+		return -1;
+	} else {
+		*euid = cred.unp_euid;
+		*egid = cred.unp_egid;
+		return 0;
+	}
+}
Index: lib/libc/net/getpeereid.3
===================================================================
RCS file: lib/libc/net/getpeereid.3
diff -N lib/libc/net/getpeereid.3
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ lib/libc/net/getpeereid.3	5 Aug 2007 20:43:08 -0000
@@ -0,0 +1,140 @@
+.\"
+.\" Copyright (c) 2001 Dima Dorfman.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD: src/lib/libc/gen/getpeereid.3,v 1.6 2002/12/18 10:13:54 ru Exp $
+.\"
+.Dd July 15, 2001
+.Dt GETPEEREID 3
+.Os
+.Sh NAME
+.Nm getpeereid
+.Nd get the effective credentials of a UNIX-domain peer
+.Sh LIBRARY
+.Lb libc
+.Sh SYNOPSIS
+.In sys/types.h
+.In unistd.h
+.Ft int
+.Fn getpeereid "int s" "uid_t *euid" "gid_t *egid"
+.Sh DESCRIPTION
+The
+.Fn getpeereid
+function returns the effective user and group IDs of the
+peer connected to a
+.Ux Ns -domain
+socket.
+The argument
+.Fa s
+must be a
+.Ux Ns -domain
+socket
+.Pq Xr unix 4
+of type
+.Dv SOCK_STREAM
+on which either
+.Xr connect 2
+has been called, or one returned from
+.Xr accept 2
+after
+.Xr bind 2
+and
+.Xr listen 2
+have been called.
+The effective used ID is placed in
+.Fa euid ,
+and the effective group ID in
+.Fa egid .
+.Pp
+The credentials returned to the
+.Xr accept 2
+caller are those of its peer at the time it called
+.Xr connect 2 ;
+the credentials returned to the
+.Xr connect 2
+caller are those of its peer at the time it called
+.Xr bind 2 .
+This mechanism is reliable; there is no way for either side to influence
+the credentials returned to its peer except by calling the appropriate
+system call (i.e., either
+.Xr connect 2
+or
+.Xr bind 2 )
+under different effective credentials.
+.Pp
+One common use of this routine is for a
+.Ux Ns -domain
+server
+to verify the credentials of its client.
+Likewise, the client can verify the credentials of the server.
+.Sh IMPLEMENTATION NOTES
+On
+.Nx ,
+.Fn getpeereid
+is implemented in terms of the
+.Dv LOCAL_PEEREID
+.Xr unix 4
+socket option.
+.Sh RETURN VALUES
+.Rv -std getpeereid
+.Sh ERRORS
+The
+.Fn getpeereid
+function
+fails if:
+.Bl -tag -width Er
+.It Bq Er EBADF
+The argument
+.Fa s
+is not a valid descriptor.
+.It Bq Er ENOTSOCK
+The argument
+.Fa s
+is a file, not a socket.
+.It Bq Er ENOTCONN
+The argument
+.Fa s
+does not refer to a socket on which
+.Xr connect 2
+have been called nor one returned from
+.Xr listen 2 .
+.It Bq Er EINVAL
+The argument
+.Fa s
+does not refer to a socket of type
+.Dv SOCK_STREAM ,
+or the kernel returned invalid data.
+.El
+.Sh SEE ALSO
+.Xr connect 2 ,
+.Xr getpeername 2 ,
+.Xr getsockname 2 ,
+.Xr getsockopt 2 ,
+.Xr listen 2 ,
+.Xr unix 4
+.Sh HISTORY
+The
+.Fn getpeereid
+function appeared in
+.Fx 4.6 .