$NetBSD: patch-ae,v 1.1 2005/08/30 23:24:33 jlam Exp $

--- libgamin/gam_api.c.orig	2005-08-05 18:31:46.000000000 -0400
+++ libgamin/gam_api.c
@@ -7,6 +7,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <unistd.h>
+#include <sys/param.h>
 #include <sys/types.h>
 #include <fcntl.h>
 #include <errno.h>
@@ -51,6 +52,20 @@ const char *FamErrlist[] = {
     NULL
 };
 
+#if 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
+
 #ifdef GAMIN_DEBUG_API
 int FAMDebug(FAMConnection *fc, const char *filename, FAMRequest * fr,
              void *userData);
@@ -307,12 +322,6 @@ gamin_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
      */
@@ -372,6 +381,18 @@ gamin_connect_unix_socket(const char *pa
     }
     strncpy(&addr.sun_path[0], path, (sizeof(addr) - 4) - 1);
 #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 (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
         if (retries == 0) {
@@ -419,37 +440,35 @@ gamin_connect_unix_socket(const char *pa
 static int
 gamin_write_credential_byte(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;
 
-    iov.iov_base = &data[0];
-    iov.iov_len = 1;
+#if defined(BSDCRED) && !defined(LOCAL_CREDS)
+    struct cmsghdr *cmsg;
+    char cmsgbuf[CMSG_SPACE(CRED_DATASIZE)];
+#endif
 
-    memset (&msg, 0, sizeof (msg));
+    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, sizeof(cmsgbuf));
+    msg.msg_control = (void *)cmsgbuf;
+    msg.msg_controllen = sizeof(cmsgbuf);
+    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;
@@ -457,7 +476,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);
@@ -641,43 +660,26 @@ gamin_check_cred(GAMDataPtr conn, int fd
 {
     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(-1);
-        }
-    }
+#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:
@@ -685,26 +687,33 @@ retry:
         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;
+    }
+    if ((msg.msg_flags & MSG_CTRUNC) != 0) {
+        GAM_DEBUG(DEBUG_INFO,
+                  "Lost control message data over recvmsg()\n");
         goto failed;
     }
-#ifdef HAVE_CMSGCRED
-    if (cmsg.hdr.cmsg_len < sizeof(cmsg) || cmsg.hdr.cmsg_type != SCM_CREDS) {
+    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);
 
@@ -719,23 +728,31 @@ retry:
                       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(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);