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: