$NetBSD$

--- server/gam_channel.c.orig	2007-07-04 09:36:49.000000000 -0400
+++ server/gam_channel.c
@@ -1,8 +1,10 @@
 #include "server_config.h"
+#include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <errno.h>
 #include <glib.h>
+#include <sys/param.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/un.h>
@@ -12,6 +14,37 @@
 #include "gam_channel.h"
 #include "gam_protocol.h"
 
+#if defined(LOCAL_PEEREID)
+static gboolean
+gam_nb_getpeereid(int fd, pid_t *pid, uid_t *uid, gid_t *gid)
+{
+    struct unpcbid cred;
+    socklen_t len = sizeof(cred);
+
+    if (getsockopt(fd, 0, LOCAL_PEEREID, &cred, &len) < 0)
+	return FALSE;
+    if (pid)
+	*pid = cred.unp_pid;
+    if (uid)
+	*uid = cred.unp_euid;
+    if (gid)
+	*gid = cred.unp_egid;
+    return TRUE;
+}
+#elif defined(SOCKCREDSIZE)
+#define BSDCRED	struct sockcred
+#define CRED_DATASIZE	(SOCKCREDSIZE(NGROUPS))
+#define credpid(c,p)	(p)
+#define creduid(c)	(c->sc_euid)
+#define credgid(c)	(c->sc_egid)
+#elif defined(HAVE_CMSGCRED)
+#define BSDCRED	struct cmsgcred
+#define CRED_DATASIZE	(sizeof(struct cmsgcred))
+#define credpid(c,p)	(c->cmcred_pid)
+#define creduid(c)	(c->cmcred_euid)
+#define credgid(c)	(c->cmcred_groups[0])
+#endif
+
 /* #define CHANNEL_VERBOSE_DEBUGGING */
 /************************************************************************
  *									*
@@ -28,37 +61,35 @@
 static gboolean
 gam_client_conn_send_cred(int fd)
 {
-    char data[2] = { 0, 0 };
-    int written;
-#if defined(HAVE_CMSGCRED) && !defined(LOCAL_CREDS)
-    struct {
-	    struct cmsghdr hdr;
-	    struct cmsgcred cred;
-    } cmsg;
-    struct iovec iov;
     struct msghdr msg;
+    struct iovec iov;
+    pid_t pid = getpid();
+    int written;
+
+#if defined(BSDCRED) && !defined(LOCAL_CREDS)
+    struct cmsghdr *cmsg;
+    char cmsgbuf[CMSG_SPACE(CRED_DATASIZE)];
+#endif
 
-    iov.iov_base = &data[0];
-    iov.iov_len = 1;
+    iov.iov_base = &pid;
+    iov.iov_len = sizeof(pid_t);
 
     memset (&msg, 0, sizeof (msg));
     msg.msg_iov = &iov;
     msg.msg_iovlen = 1;
 
-    msg.msg_control = &cmsg;
-    msg.msg_controllen = sizeof (cmsg);
-    memset (&cmsg, 0, sizeof (cmsg));
-    cmsg.hdr.cmsg_len = sizeof (cmsg);
-    cmsg.hdr.cmsg_level = SOL_SOCKET;
-    cmsg.hdr.cmsg_type = SCM_CREDS;
+#if defined(BSDCRED) && !defined(LOCAL_CREDS)
+    memset(cmsgbuf, 0, CMSG_SPACE(CRED_DATASIZE));
+    msg.msg_control = (void *)cmsgbuf;
+    msg.msg_controllen = CMSG_LEN(CRED_DATASIZE);
+    cmsg = CMSG_FIRSTHDR(&msg);
+    cmsg->cmsg_len = CMSG_LEN(CRED_DATASIZE);
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type = SCM_CREDS;
 #endif
 
 retry:
-#if defined(HAVE_CMSGCRED) && !defined(LOCAL_CREDS)
     written = sendmsg(fd, &msg, 0);
-#else
-    written = write(fd, &data[0], 1);
-#endif
     if (written < 0) {
         if (errno == EINTR)
             goto retry;
@@ -66,7 +97,7 @@ retry:
 		  "Failed to write credential bytes to socket %d\n", fd);
 	return (-1);
     }
-    if (written != 1) {
+    if (written != iov.iov_len) {
 	gam_error(DEBUG_INFO, "Wrote %d credential bytes to socket %d\n",
 		  written, fd);
 	return (-1);
@@ -89,43 +120,26 @@ gam_client_conn_check_cred(GIOChannel * 
 {
     struct msghdr msg;
     struct iovec iov;
-    char buf;
-    pid_t c_pid;
+    pid_t c_pid, pid;
     uid_t c_uid, s_uid;
     gid_t c_gid;
 
-#ifdef HAVE_CMSGCRED
-    struct {
-	    struct cmsghdr hdr;
-	    struct cmsgcred cred;
-    } cmsg;
-#endif
-
-    s_uid = getuid();
-
-#if defined(LOCAL_CREDS) && defined(HAVE_CMSGCRED)
-    /* Set the socket to receive credentials on the next message */
-    {
-        int on = 1;
-
-        if (setsockopt(fd, 0, LOCAL_CREDS, &on, sizeof(on)) < 0) {
-            gam_error(DEBUG_INFO, "Unable to set LOCAL_CREDS socket option\n");
-            return FALSE;
-        }
-    }
+#if defined(BSDCRED)
+    struct cmsghdr *cmsg;
+    char cmsgbuf[CMSG_SPACE(CRED_DATASIZE)];
 #endif
 
-    iov.iov_base = &buf;
-    iov.iov_len = 1;
+    iov.iov_base = &pid;
+    iov.iov_len = sizeof(pid_t);
 
     memset(&msg, 0, sizeof(msg));
     msg.msg_iov = &iov;
     msg.msg_iovlen = 1;
 
-#ifdef HAVE_CMSGCRED
-    memset(&cmsg, 0, sizeof(cmsg));
-    msg.msg_control = &cmsg;
-    msg.msg_controllen = sizeof(cmsg);
+#if defined(BSDCRED)
+    memset(cmsgbuf, 0, sizeof(cmsgbuf));
+    msg.msg_control = (void *)cmsgbuf;
+    msg.msg_controllen = sizeof(cmsgbuf);
 #endif
 
   retry:
@@ -133,26 +147,33 @@ gam_client_conn_check_cred(GIOChannel * 
         if (errno == EINTR)
             goto retry;
 
-        GAM_DEBUG(DEBUG_INFO, "Failed to read credentials byte on %d\n", fd);
+        GAM_DEBUG(DEBUG_INFO, "Failed to read credential bytes on %d\n", fd);
         goto failed;
     }
-
-    if (buf != '\0') {
-        GAM_DEBUG(DEBUG_INFO, "Credentials byte was not nul on %d\n", fd);
+    GAM_DEBUG(DEBUG_INFO, "Read pid %d on %d\n", pid, fd);
+#if defined(BSDCRED)
+    if (msg.msg_controllen == 0) {
+        GAM_DEBUG(DEBUG_INFO,
+                  "No control message received over recvmsg()\n");
         goto failed;
     }
-#ifdef HAVE_CMSGCRED
-    if (cmsg.hdr.cmsg_len < sizeof(cmsg) || cmsg.hdr.cmsg_type != SCM_CREDS) {
+    if ((msg.msg_flags & MSG_CTRUNC) != 0) {
+        GAM_DEBUG(DEBUG_INFO,
+                  "Lost control message data over recvmsg()\n");
+        goto failed;
+    }
+    cmsg = CMSG_FIRSTHDR(&msg);
+    if (cmsg->cmsg_type != SCM_CREDS) {
         GAM_DEBUG(DEBUG_INFO,
                   "Message from recvmsg() was not SCM_CREDS\n");
         goto failed;
     }
 #endif
 
-    GAM_DEBUG(DEBUG_INFO, "read credentials byte\n");
+    GAM_DEBUG(DEBUG_INFO, "read credential bytes\n");
 
     {
-#ifdef SO_PEERCRED
+#if defined(SO_PEERCRED)
         struct ucred cr;
         socklen_t cr_len = sizeof(cr);
 
@@ -167,23 +188,37 @@ gam_client_conn_check_cred(GIOChannel * 
                       fd, cr_len, (int) sizeof(cr));
             goto failed;
         }
-#elif defined(HAVE_CMSGCRED)
-	c_pid = cmsg.cred.cmcred_pid;
-	c_uid = cmsg.cred.cmcred_euid;
-	c_gid = cmsg.cred.cmcred_groups[0];
-#else /* !SO_PEERCRED && !HAVE_CMSGCRED */
+#elif defined(LOCAL_PEEREID)
+        if (gam_nb_getpeereid(fd, &c_pid, &c_uid, &c_gid) == FALSE) {
+	    GAM_DEBUG(DEBUG_INFO,
+		      "Failed to gam_nb_getpeereid() credentials on %d\n", fd);
+            goto failed;
+	}
+#elif defined(BSDCRED)
+        BSDCRED *cr = (BSDCRED *)CMSG_DATA(cmsg);
+        c_pid = credpid(cr, pid);
+        c_uid = creduid(cr);
+        c_gid = credgid(cr);
+#else
         GAM_DEBUG(DEBUG_INFO,
                   "Socket credentials not supported on this OS\n");
         goto failed;
 #endif
     }
 
+    s_uid = getuid();
     if (s_uid != c_uid) {
         GAM_DEBUG(DEBUG_INFO,
                   "Credentials check failed: s_uid %d, c_uid %d\n",
                   (int) s_uid, (int) c_uid);
         goto failed;
     }
+    if (pid != c_pid) {
+        GAM_DEBUG(DEBUG_INFO,
+                  "read credentials do not match: pid %d, c_pid %d\n",
+                  (int) pid, (int) c_pid);
+        goto failed;
+    }
     GAM_DEBUG(DEBUG_INFO,
               "Credentials: s_uid %d, c_uid %d, c_gid %d, c_pid %d\n",
               (int) s_uid, (int) c_uid, (int) c_gid, (int) c_pid);
@@ -194,7 +229,7 @@ gam_client_conn_check_cred(GIOChannel * 
     }
 
     if (!gam_client_conn_send_cred(fd)) {
-        GAM_DEBUG(DEBUG_INFO, "Failed to send credential byte to client\n");
+        GAM_DEBUG(DEBUG_INFO, "Failed to send credential bytes to client\n");
         goto failed;
     }
 
@@ -551,12 +586,6 @@ gam_check_secure_path(const char *path)
 	goto cleanup;
     }
 #endif
-    if (st.st_mode & (S_IRWXG|S_IRWXO)) {
-	gam_error(DEBUG_INFO,
-		  "Socket %s has wrong permissions\n",
-		  path);
-	goto cleanup;
-    }
     /*
      * Looks good though binding may fail due to an existing server
      */
@@ -620,6 +649,7 @@ gam_listen_unix_socket(const char *path)
 {
     int fd;
     struct sockaddr_un addr;
+    struct stat st;
 
     fd = socket(PF_UNIX, SOCK_STREAM, 0);
     if (fd < 0) {
@@ -646,6 +676,18 @@ gam_listen_unix_socket(const char *path)
     strncpy(&addr.sun_path[0], path, (sizeof(addr) - 4) - 1);
     umask(0077);
 #endif
+#if defined(BSDCRED) && defined(LOCAL_CREDS)
+    /* Set the socket to receive credentials. */
+    {
+        int on = 1;
+
+        if (setsockopt(fd, 0, LOCAL_CREDS, &on, sizeof(on)) < 0) {
+            gam_error(DEBUG_INFO,
+                      "Unable to setsockopt() LOCAL_CREDS on %d\n", fd);
+            return(-1);
+        }
+    }
+#endif
 
     if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
         GAM_DEBUG(DEBUG_INFO, "Failed to bind to socket %s\n", path);
