Subject: getpeereid() or equivalent
To: None <tech-net@netbsd.org>
From: Arne H. Juul <arnej@pvv.ntnu.no>
List: tech-net
Date: 07/30/2007 14:50:09
I'm trying to port some code that on FreeBSD uses the getpeereid()
system call, to get the user and group IDs of a peer connected on a
UNIX-domain socket.

Question 1:  has anybody considered porting this functionality
from FreeBSD to NetBSD?  it seems like a simple but useful system
call to have. (snippet from the FreeBSD man page at the bottom).

Question 2: it looks like it's possible to do the same using
setsockopt with LOCAL_CREDS, but I'm not at all sure if I have
understood the documentation correctly.  My first try that
actually compiles looks like this:

+#elif defined(__NetBSD__)
+    uid_t puid = 0xffffffff;
+    gid_t pgid = 0xffffffff;
+
+    int opt = 1;
+    if (setsockopt(s, 0, LOCAL_CREDS, &opt, sizeof(opt)) < 0) {
+      ::close(s);
+      continue;
+    }
+    char ctl[128];
+    struct msghdr msg;
+
+    msg.msg_name = NULL;
+    msg.msg_namelen = 0;
+    msg.msg_iov = NULL;
+    msg.msg_iovlen = 0;
+    msg.msg_control = ctl;
+    msg.msg_controllen = sizeof(ctl);
+    msg.msg_flags = 0;
+ 
+    len = recvmsg(s, &msg, 0);
+    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
+    if (cmsg != NULL
+        && cmsg->cmsg_level == SOL_SOCKET
+        && cmsg->cmsg_type == SCM_CREDS
+        && cmsg->cmsg_len >= CMSG_LEN(SOCKCREDSIZE(0)))
+    {
+      struct sockcred *creds = (struct sockcred *)CMSG_DATA(cmsg);
+      puid = creds->sc_uid;
+      pgid = creds->sc_gid;
+    } else {
+      ::close(s);
+      continue;
+    }

so does anybody see some obvious problems with this code?
And does anybody have some example code or small test programs that
does this the correct way that I could borrow code from, that would be
even better...

Here's the (much simpler to use) FreBSD call:

SYNOPSIS
   #include <sys/types.h>
   #include <unistd.h>

   int getpeereid(int s, uid_t *euid, gid_t *egid);

DESCRIPTION
   The getpeereid() function returns the effective user and group IDs of the
   peer connected to a UNIX-domain socket.  The argument s must be a
   UNIX-domain socket (unix(4)) of type SOCK_STREAM on which either
   connect(2) or listen(2) have been called.  The effective used ID is
   placed in euid, and the effective group ID in egid.

   The credentials returned to the listen(2) caller are those of its peer at
   the time it called connect(2); the credentials returned to the connect(2)
   caller are those of its peer at the time it called listen(2).  This mech-
   anism is reliable; there is no way for either side to influence the cre-
   dentials returned to its peer except by calling the appropriate system
   call (i.e., either connect(2) or listen(2)) under different effective
   credentials.


Thanks in advance,

    -  Arne H. J.