Subject: Re: getpeereid() or equivalent
To: Jason Thorpe <thorpej@shagadelic.org>
From: Arne H. Juul <arnej@pvv.ntnu.no>
List: tech-net
Date: 08/05/2007 19:32:38
First, thanks for answers and pointers - I've found the code in postgresql 
that uses LOCAL_CREDS, and there the other end of the connection needs to 
actively send a control message, so I don't think that's a workable 
solution for the code I'm working on.

On Wed, 1 Aug 2007, Jason Thorpe wrote:
> Right, we don't implement that socket option yet.  We'd need to do that 
> first.

After looking at SO_PEERCRED on linux I would guess implementing that 
exactly is a bit bothersome - it's a very linuxish API (also known as
a hack).
But the functionality is easy to replicate in NetBSD, especially after 
looking at how FreeBSD and OpenBSD does this.

Here's a patch implementing a LOCAL_PEEREID option for getsockopt().

   -  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	5 Aug 2007 17:06:50 -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_PEERCRED
+ */
+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	5 Aug 2007 16:51:44 -0000
@@ -78,11 +78,26 @@
  	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;	/* id 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, but does *not* contain the effective ids of the connected peer
+ * (there may not even be a peer).  This is set in unp_listen() 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: