From nobody@FreeBSD.org  Sun Feb 17 03:41:28 2013
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1])
	by hub.freebsd.org (Postfix) with ESMTP id BC322D19
	for <freebsd-gnats-submit@FreeBSD.org>; Sun, 17 Feb 2013 03:41:28 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from red.freebsd.org (red.freebsd.org [IPv6:2001:4f8:fff6::22])
	by mx1.freebsd.org (Postfix) with ESMTP id 9A189850
	for <freebsd-gnats-submit@FreeBSD.org>; Sun, 17 Feb 2013 03:41:28 +0000 (UTC)
Received: from red.freebsd.org (localhost [127.0.0.1])
	by red.freebsd.org (8.14.5/8.14.5) with ESMTP id r1H3fSSc034768
	for <freebsd-gnats-submit@FreeBSD.org>; Sun, 17 Feb 2013 03:41:28 GMT
	(envelope-from nobody@red.freebsd.org)
Received: (from nobody@localhost)
	by red.freebsd.org (8.14.5/8.14.5/Submit) id r1H3fSB4034750;
	Sun, 17 Feb 2013 03:41:28 GMT
	(envelope-from nobody)
Message-Id: <201302170341.r1H3fSB4034750@red.freebsd.org>
Date: Sun, 17 Feb 2013 03:41:28 GMT
From: Danny Warren <danny@dannywarren.com>
To: freebsd-gnats-submit@FreeBSD.org
Subject: [patch] devel/gamin: Drop privileges to effective user and group
X-Send-Pr-Version: www-3.1
X-GNATS-Notify:

>Number:         176203
>Category:       ports
>Synopsis:       [patch] devel/gamin: Drop privileges to effective user and group
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    gnome
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Sun Feb 17 03:50:00 UTC 2013
>Closed-Date:    Fri Mar 22 16:31:19 UTC 2013
>Last-Modified:  Fri Mar 22 16:40:00 UTC 2013
>Originator:     Danny Warren
>Release:        9.1-RELEASE
>Organization:
>Environment:
9.1-RELEASE FreeBSD 9.1-RELEASE #0 r243825: Tue Dec 4 09:23:10 UTC 2012 root@farrell.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC amd64  
>Description:
The base release of gamin uses the current real uid to determine the location of the socket and during the security routines.

It also forks off a copy of "gam_server" as the current real uid as well.

This breaks gamin functionality with any application that uses effective an uid/gid for privilege separation, such as samba.

Samba runs as root, but always sets the effective uid/gid to that of the connecting user.  Since gamin is only aware of the real uid, it always creates a root-owned gamin socket that the effective user can't touch (gamin's own security checks refuse it).

For a description of the problem with samba, see:
https://bugzilla.samba.org/show_bug.cgi?id=9558

A similar bug has already been filed against gamin, but has gone unanswered by gamin's maintainer for years:
https://bugzilla.gnome.org/show_bug.cgi?id=345244

This patch adds a "RUN_AS_EUID" definition which will enable the following behavior at compile time:

* use the effective uid to determine the name of user socket to create
* use the effective uid when verifying ownership for security
* runs setuid/setgid to change to the effective uid/gid after forking but before starting a copy of gam_server when run via libgamin
* refuses to escalate privileges to root during setuid/setgid, only dropping privileges (though it would be easy to change that if someone needed it, but I'm erring on the side of caution here since setuid is involved)

I'm not sure which effect this will have on other applications that use gamin, so I leave it up to you to determine the right behavior for the Makefile.  Right now it is provided as a "Drop privileges to effective user" but it may make sense to have it always on by default for security reasons.

It works great on my system so far, though.  This is the first time I have seen samba's file alteration monitoring work on freebsd.

(NOTE: My patch to gam_api.c includes the existing patches, I wasn't sure whether to give you something you can just slap in place that would match svn or provide a patch-of-a-patch)
>How-To-Repeat:

>Fix:


Patch attached with submission follows:

--- Makefile.orig	2013-02-16 19:09:52.507178348 -0800
+++ Makefile	2013-02-16 16:18:30.230098850 -0800
@@ -27,9 +27,10 @@
 GNU_CONFIGURE=	yes
 
 .if !defined(GAMIN_SLAVE)
-OPTIONS_DEFINE=	GAM_POLLER LIBINOTIFY 
+OPTIONS_DEFINE=	GAM_POLLER LIBINOTIFY RUN_AS_EUID
 GAM_POLLER_DESC=Use gamin's poller instead of kqueue's
 LIBINOTIFY_DESC=Use libinotify as the FAM backend
+RUN_AS_EUID_DESC=Drop privileges to effective user
 .endif
 
 .include <bsd.port.options.mk>
@@ -48,6 +49,10 @@
 .endif
 .endif
 
+.if ${PORT_OPTIONS:MRUN_AS_EUID}
+CPPFLAGS+=	-DRUN_AS_EUID=1
+.endif
+
 post-patch:
 	@${REINPLACE_CMD} "s|/etc|${PREFIX}/etc|g" ${WRKSRC}/server/gam_conf.c
 
--- libgamin/gam_api.c.orig	2007-08-27 03:21:03.000000000 -0700
+++ libgamin/gam_api.c	2013-02-16 15:51:11.927100135 -0800
@@ -14,6 +14,7 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <sys/uio.h>
+#include <string.h>
 #include "fam.h"
 #include "gam_protocol.h"
 #include "gam_data.h"
@@ -117,7 +118,11 @@
     if (user_name[0] != 0)
         return (user_name);
 
+#ifdef RUN_AS_EUID
+    pw = getpwuid(geteuid());
+#else
     pw = getpwuid(getuid());
+#endif
 
     if (pw != NULL) {
 	strncpy(user_name, pw->pw_name, 99);
@@ -224,7 +229,11 @@
 	free(dir);
 	return(0);
     }
+#ifdef RUN_AS_EUID
+    if (st.st_uid != geteuid()) {
+#else
     if (st.st_uid != getuid()) {
+#endif
 	gam_error(DEBUG_INFO,
 		  "Socket directory %s has different owner\n",
 		  dir);
@@ -301,7 +310,11 @@
     if (ret < 0)
 	return(0);
     
+#ifdef RUN_AS_EUID
+    if (st.st_uid != geteuid()) {
+#else
     if (st.st_uid != getuid()) {
+#endif
 	gam_error(DEBUG_INFO,
 		  "Socket %s has different owner\n",
 		  path);
@@ -428,10 +441,10 @@
 {
     char data[2] = { 0, 0 };
     int written;
-#if defined(HAVE_CMSGCRED) && !defined(LOCAL_CREDS)
-    struct {
+#if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__))
+    union {
 	    struct cmsghdr hdr;
-	    struct cmsgcred cred;
+	    char cred[CMSG_SPACE (sizeof (struct cmsgcred))];
     } cmsg;
     struct iovec iov;
     struct msghdr msg;
@@ -443,16 +456,16 @@
     msg.msg_iov = &iov;
     msg.msg_iovlen = 1;
 
-    msg.msg_control = &cmsg;
-    msg.msg_controllen = sizeof (cmsg);
+    msg.msg_control = (caddr_t) &cmsg;
+    msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred));
     memset (&cmsg, 0, sizeof (cmsg));
-    cmsg.hdr.cmsg_len = sizeof (cmsg);
+    cmsg.hdr.cmsg_len = CMSG_LEN (sizeof (struct cmsgcred));
     cmsg.hdr.cmsg_level = SOL_SOCKET;
     cmsg.hdr.cmsg_type = SCM_CREDS;
 #endif
 
 retry:
-#if defined(HAVE_CMSGCRED) && !defined(LOCAL_CREDS)
+#if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__))
     written = sendmsg(fd, &msg, 0);
 #else
     written = write(fd, &data[0], 1);
@@ -654,15 +667,20 @@
     gid_t c_gid;
 
 #ifdef HAVE_CMSGCRED
-    struct {
+    struct cmsgcred *cred;
+    union {
 	    struct cmsghdr hdr;
-	    struct cmsgcred cred;
+	    char cred[CMSG_SPACE (sizeof (struct cmsgcred))];
     } cmsg;
 #endif
 
+#ifdef RUN_AS_EUID
+    s_uid = geteuid();
+#else
     s_uid = getuid();
+#endif
 
-#if defined(LOCAL_CREDS) && defined(HAVE_CMSGCRED)
+#if defined(LOCAL_CREDS) && defined(HAVE_CMSGCRED) && !defined(__FreeBSD__)
     /* Set the socket to receive credentials on the next message */
     {
         int on = 1;
@@ -683,8 +701,8 @@
 
 #ifdef HAVE_CMSGCRED
     memset(&cmsg, 0, sizeof(cmsg));
-    msg.msg_control = &cmsg;
-    msg.msg_controllen = sizeof(cmsg);
+    msg.msg_control = (caddr_t) &cmsg;
+    msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred));
 #endif
 
 retry:
@@ -701,7 +719,7 @@
         goto failed;
     }
 #ifdef HAVE_CMSGCRED
-    if (cmsg.hdr.cmsg_len < sizeof(cmsg) || cmsg.hdr.cmsg_type != SCM_CREDS) {
+    if (cmsg.hdr.cmsg_len < CMSG_LEN (sizeof (struct cmsgcred)) || cmsg.hdr.cmsg_type != SCM_CREDS) {
         GAM_DEBUG(DEBUG_INFO,
                   "Message from recvmsg() was not SCM_CREDS\n");
         goto failed;
@@ -727,9 +745,10 @@
             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];
+        cred = (struct cmsgcred *) CMSG_DATA (&cmsg);
+        c_pid = cred->cmcred_pid;
+        c_uid = cred->cmcred_euid;
+        c_gid = cred->cmcred_groups[0];
 #else /* !SO_PEERCRED && !HAVE_CMSGCRED */
         GAM_DEBUG(DEBUG_INFO,
                   "Socket credentials not supported on this OS\n");
@@ -1288,14 +1307,17 @@
 
     // FIXME: drop and reacquire lock while blocked?
     gamin_data_lock(conn);
-    if (!gamin_data_event_ready(conn)) {
+    while ((ret = gamin_data_event_ready(conn)) == 0) {
         if (gamin_read_data(conn, fc->fd, 1) < 0) {
 	    gamin_try_reconnect(conn, fc->fd);
 	    FAMErrno = FAM_CONNECT;
 	    return (-1);
 	}
     }
-    ret = gamin_data_read_event(conn, fe);
+
+    if (ret > 0)
+        ret = gamin_data_read_event(conn, fe);
+
     gamin_data_unlock(conn);
 
     if (ret < 0) {
--- libgamin/gam_fork.c.orig	2007-07-04 06:36:48.000000000 -0700
+++ libgamin/gam_fork.c	2013-02-16 19:02:46.360093751 -0800
@@ -42,6 +42,82 @@
     return NULL;
 }
 
+#ifdef RUN_AS_EUID
+/**
+ * gamin_drop_privileges
+ *
+ * Attempt to drop privileges to the effective uid and gid before
+ * forking a copy of the gamin server
+ * 
+ * Return 0 in case of success or -1 in case of detected error.
+ */
+int
+gamin_drop_privileges(void)
+{
+    GAM_DEBUG(DEBUG_INFO, "Dropping privileges to effective user and group\n");
+
+    /* get the current real user and group */
+    int from_uid = getuid();
+    int from_gid = getgid();
+
+    /* get the effective user and group */
+    int to_uid = geteuid();
+    int to_gid = getegid();
+
+    /* make sure we were able to get the user and group values */
+    if ( from_uid == -1 || to_uid == -1 || from_gid == -1 || to_gid == -1 ) {
+        gam_error(DEBUG_INFO, "failed to get user or group info, unable to drop privileges\n");
+        return(-1);
+    }
+
+    /* refuse to run setuid if it would escalate privileges */
+    if ( from_uid != 0 && to_uid == 0 )
+    {
+        gam_error(DEBUG_INFO, "refusing to escalate user privileges from=%d to=%d\n", from_uid, to_uid);
+        return(-1);
+    }
+
+    /* refuse to run setgid if it would escalate privileges */
+    if ( from_gid != 0 && to_gid == 0 )
+    {
+        gam_error(DEBUG_INFO, "refusing to escalate group privileges from=%d to=%d\n", from_gid, to_gid);
+        return(-1);
+    }
+
+    /* run setuid to drop privileges to the effective user */
+    if ( from_uid != to_uid ) {
+        GAM_DEBUG(DEBUG_INFO, "Attempting setuid from=%d to=%d\n", from_uid, to_uid);
+
+        /* run setuid and check for errors */
+        if (setuid(to_uid) == -1) {
+            gam_error(DEBUG_INFO, "failed to run setuid from=%d to=%d\n", from_uid, to_uid);
+            return(-1);
+        }
+    }
+    else {
+        GAM_DEBUG(DEBUG_INFO, "Already running as effective user, skipping setuid\n");
+    }
+
+    /* run setgid to drop privileges to the effective group */
+    if ( from_gid != to_gid ) {
+        GAM_DEBUG(DEBUG_INFO, "Attempting setgid from=%d to=%d\n", from_gid, to_gid);
+
+        /* run setuid and check for errors */
+        if (setgid(to_gid) == -1) {
+            gam_error(DEBUG_INFO, "failed to run setgid from=%d to=%d\n", from_uid, to_uid);
+            return(-1);
+        }
+    }
+    else {
+        GAM_DEBUG(DEBUG_INFO, "Already running as effective group, skipping setgid\n");
+    }
+
+    GAM_DEBUG(DEBUG_INFO, "Succeeded in dropping privileges from %d:%d to %d:%d\n", from_uid, from_gid, to_uid, to_gid);
+
+    return(0);
+}
+#endif
+
 /**
  * gamin_fork_server:
  * @fam_client_id: the client ID string to use
@@ -71,6 +147,14 @@
         long open_max;
 	long i;
 
+#ifdef RUN_AS_EUID
+        /* Drop privileges to the current uid/gid and return on failure */
+        if(gamin_drop_privileges() == -1)
+        {
+            return(-1);
+        }
+#endif
+
         /* don't hold open fd opened from the client of the library */
 	open_max = sysconf (_SC_OPEN_MAX);
 	for (i = 0; i < open_max; i++)
--- libgamin/gam_api.c.orig	2007-08-27 03:21:03.000000000 -0700
+++ libgamin/gam_api.c	2013-02-16 15:51:11.927100135 -0800
@@ -14,6 +14,7 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <sys/uio.h>
+#include <string.h>
 #include "fam.h"
 #include "gam_protocol.h"
 #include "gam_data.h"
@@ -117,7 +118,11 @@
     if (user_name[0] != 0)
         return (user_name);
 
+#ifdef RUN_AS_EUID
+    pw = getpwuid(geteuid());
+#else
     pw = getpwuid(getuid());
+#endif
 
     if (pw != NULL) {
 	strncpy(user_name, pw->pw_name, 99);
@@ -224,7 +229,11 @@
 	free(dir);
 	return(0);
     }
+#ifdef RUN_AS_EUID
+    if (st.st_uid != geteuid()) {
+#else
     if (st.st_uid != getuid()) {
+#endif
 	gam_error(DEBUG_INFO,
 		  "Socket directory %s has different owner\n",
 		  dir);
@@ -301,7 +310,11 @@
     if (ret < 0)
 	return(0);
     
+#ifdef RUN_AS_EUID
+    if (st.st_uid != geteuid()) {
+#else
     if (st.st_uid != getuid()) {
+#endif
 	gam_error(DEBUG_INFO,
 		  "Socket %s has different owner\n",
 		  path);
@@ -428,10 +441,10 @@
 {
     char data[2] = { 0, 0 };
     int written;
-#if defined(HAVE_CMSGCRED) && !defined(LOCAL_CREDS)
-    struct {
+#if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__))
+    union {
 	    struct cmsghdr hdr;
-	    struct cmsgcred cred;
+	    char cred[CMSG_SPACE (sizeof (struct cmsgcred))];
     } cmsg;
     struct iovec iov;
     struct msghdr msg;
@@ -443,16 +456,16 @@
     msg.msg_iov = &iov;
     msg.msg_iovlen = 1;
 
-    msg.msg_control = &cmsg;
-    msg.msg_controllen = sizeof (cmsg);
+    msg.msg_control = (caddr_t) &cmsg;
+    msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred));
     memset (&cmsg, 0, sizeof (cmsg));
-    cmsg.hdr.cmsg_len = sizeof (cmsg);
+    cmsg.hdr.cmsg_len = CMSG_LEN (sizeof (struct cmsgcred));
     cmsg.hdr.cmsg_level = SOL_SOCKET;
     cmsg.hdr.cmsg_type = SCM_CREDS;
 #endif
 
 retry:
-#if defined(HAVE_CMSGCRED) && !defined(LOCAL_CREDS)
+#if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__))
     written = sendmsg(fd, &msg, 0);
 #else
     written = write(fd, &data[0], 1);
@@ -654,15 +667,20 @@
     gid_t c_gid;
 
 #ifdef HAVE_CMSGCRED
-    struct {
+    struct cmsgcred *cred;
+    union {
 	    struct cmsghdr hdr;
-	    struct cmsgcred cred;
+	    char cred[CMSG_SPACE (sizeof (struct cmsgcred))];
     } cmsg;
 #endif
 
+#ifdef RUN_AS_EUID
+    s_uid = geteuid();
+#else
     s_uid = getuid();
+#endif
 
-#if defined(LOCAL_CREDS) && defined(HAVE_CMSGCRED)
+#if defined(LOCAL_CREDS) && defined(HAVE_CMSGCRED) && !defined(__FreeBSD__)
     /* Set the socket to receive credentials on the next message */
     {
         int on = 1;
@@ -683,8 +701,8 @@
 
 #ifdef HAVE_CMSGCRED
     memset(&cmsg, 0, sizeof(cmsg));
-    msg.msg_control = &cmsg;
-    msg.msg_controllen = sizeof(cmsg);
+    msg.msg_control = (caddr_t) &cmsg;
+    msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred));
 #endif
 
 retry:
@@ -701,7 +719,7 @@
         goto failed;
     }
 #ifdef HAVE_CMSGCRED
-    if (cmsg.hdr.cmsg_len < sizeof(cmsg) || cmsg.hdr.cmsg_type != SCM_CREDS) {
+    if (cmsg.hdr.cmsg_len < CMSG_LEN (sizeof (struct cmsgcred)) || cmsg.hdr.cmsg_type != SCM_CREDS) {
         GAM_DEBUG(DEBUG_INFO,
                   "Message from recvmsg() was not SCM_CREDS\n");
         goto failed;
@@ -727,9 +745,10 @@
             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];
+        cred = (struct cmsgcred *) CMSG_DATA (&cmsg);
+        c_pid = cred->cmcred_pid;
+        c_uid = cred->cmcred_euid;
+        c_gid = cred->cmcred_groups[0];
 #else /* !SO_PEERCRED && !HAVE_CMSGCRED */
         GAM_DEBUG(DEBUG_INFO,
                   "Socket credentials not supported on this OS\n");
@@ -1288,14 +1307,17 @@
 
     // FIXME: drop and reacquire lock while blocked?
     gamin_data_lock(conn);
-    if (!gamin_data_event_ready(conn)) {
+    while ((ret = gamin_data_event_ready(conn)) == 0) {
         if (gamin_read_data(conn, fc->fd, 1) < 0) {
 	    gamin_try_reconnect(conn, fc->fd);
 	    FAMErrno = FAM_CONNECT;
 	    return (-1);
 	}
     }
-    ret = gamin_data_read_event(conn, fe);
+
+    if (ret > 0)
+        ret = gamin_data_read_event(conn, fe);
+
     gamin_data_unlock(conn);
 
     if (ret < 0) {


>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-ports-bugs->gnome 
Responsible-Changed-By: edwin 
Responsible-Changed-When: Sun Feb 17 03:50:10 UTC 2013 
Responsible-Changed-Why:  
Over to maintainer (via the GNATS Auto Assign Tool) 

http://www.freebsd.org/cgi/query-pr.cgi?pr=176203 

From: Danny Warren <danny@dannywarren.com>
To: bug-followup@FreeBSD.org, danny@dannywarren.com
Cc:  
Subject: Re: ports/176203: [patch] devel/gamin: Drop privileges to effective
 user and group
Date: Sat, 16 Feb 2013 20:17:55 -0800

 This is a multi-part message in MIME format.
 --------------090305060503020705000402
 Content-Type: text/plain; charset=ISO-8859-1; format=flowed
 Content-Transfer-Encoding: 7bit
 
 Apologies, I forgot to include one of the files in the patch (and 
 instead included one of them twice).
 
 Here is an updated patch.
 
 --------------090305060503020705000402
 Content-Type: text/plain; charset=windows-1252;
  name="gamin-drop_privileges.patch.txt"
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
  filename="gamin-drop_privileges.patch.txt"
 
 --- Makefile.orig	2013-02-16 19:09:52.507178348 -0800
 +++ Makefile	2013-02-16 16:18:30.230098850 -0800
 @@ -27,9 +27,10 @@
  GNU_CONFIGURE=	yes
  
  .if !defined(GAMIN_SLAVE)
 -OPTIONS_DEFINE=	GAM_POLLER LIBINOTIFY 
 +OPTIONS_DEFINE=	GAM_POLLER LIBINOTIFY RUN_AS_EUID
  GAM_POLLER_DESC=Use gamin's poller instead of kqueue's
  LIBINOTIFY_DESC=Use libinotify as the FAM backend
 +RUN_AS_EUID_DESC=Drop privileges to effective user
  .endif
  
  .include <bsd.port.options.mk>
 @@ -48,6 +49,10 @@
  .endif
  .endif
  
 +.if ${PORT_OPTIONS:MRUN_AS_EUID}
 +CPPFLAGS+=	-DRUN_AS_EUID=1
 +.endif
 +
  post-patch:
  	@${REINPLACE_CMD} "s|/etc|${PREFIX}/etc|g" ${WRKSRC}/server/gam_conf.c
  
 --- libgamin/gam_api.c.orig	2007-08-27 03:21:03.000000000 -0700
 +++ libgamin/gam_api.c	2013-02-16 15:51:11.927100135 -0800
 @@ -14,6 +14,7 @@
  #include <sys/socket.h>
  #include <sys/un.h>
  #include <sys/uio.h>
 +#include <string.h>
  #include "fam.h"
  #include "gam_protocol.h"
  #include "gam_data.h"
 @@ -117,7 +118,11 @@
      if (user_name[0] != 0)
          return (user_name);
  
 +#ifdef RUN_AS_EUID
 +    pw = getpwuid(geteuid());
 +#else
      pw = getpwuid(getuid());
 +#endif
  
      if (pw != NULL) {
  	strncpy(user_name, pw->pw_name, 99);
 @@ -224,7 +229,11 @@
  	free(dir);
  	return(0);
      }
 +#ifdef RUN_AS_EUID
 +    if (st.st_uid != geteuid()) {
 +#else
      if (st.st_uid != getuid()) {
 +#endif
  	gam_error(DEBUG_INFO,
  		  "Socket directory %s has different owner\n",
  		  dir);
 @@ -301,7 +310,11 @@
      if (ret < 0)
  	return(0);
      
 +#ifdef RUN_AS_EUID
 +    if (st.st_uid != geteuid()) {
 +#else
      if (st.st_uid != getuid()) {
 +#endif
  	gam_error(DEBUG_INFO,
  		  "Socket %s has different owner\n",
  		  path);
 @@ -428,10 +441,10 @@
  {
      char data[2] = { 0, 0 };
      int written;
 -#if defined(HAVE_CMSGCRED) && !defined(LOCAL_CREDS)
 -    struct {
 +#if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__))
 +    union {
  	    struct cmsghdr hdr;
 -	    struct cmsgcred cred;
 +	    char cred[CMSG_SPACE (sizeof (struct cmsgcred))];
      } cmsg;
      struct iovec iov;
      struct msghdr msg;
 @@ -443,16 +456,16 @@
      msg.msg_iov = &iov;
      msg.msg_iovlen = 1;
  
 -    msg.msg_control = &cmsg;
 -    msg.msg_controllen = sizeof (cmsg);
 +    msg.msg_control = (caddr_t) &cmsg;
 +    msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred));
      memset (&cmsg, 0, sizeof (cmsg));
 -    cmsg.hdr.cmsg_len = sizeof (cmsg);
 +    cmsg.hdr.cmsg_len = CMSG_LEN (sizeof (struct cmsgcred));
      cmsg.hdr.cmsg_level = SOL_SOCKET;
      cmsg.hdr.cmsg_type = SCM_CREDS;
  #endif
  
  retry:
 -#if defined(HAVE_CMSGCRED) && !defined(LOCAL_CREDS)
 +#if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__))
      written = sendmsg(fd, &msg, 0);
  #else
      written = write(fd, &data[0], 1);
 @@ -654,15 +667,20 @@
      gid_t c_gid;
  
  #ifdef HAVE_CMSGCRED
 -    struct {
 +    struct cmsgcred *cred;
 +    union {
  	    struct cmsghdr hdr;
 -	    struct cmsgcred cred;
 +	    char cred[CMSG_SPACE (sizeof (struct cmsgcred))];
      } cmsg;
  #endif
  
 +#ifdef RUN_AS_EUID
 +    s_uid = geteuid();
 +#else
      s_uid = getuid();
 +#endif
  
 -#if defined(LOCAL_CREDS) && defined(HAVE_CMSGCRED)
 +#if defined(LOCAL_CREDS) && defined(HAVE_CMSGCRED) && !defined(__FreeBSD__)
      /* Set the socket to receive credentials on the next message */
      {
          int on = 1;
 @@ -683,8 +701,8 @@
  
  #ifdef HAVE_CMSGCRED
      memset(&cmsg, 0, sizeof(cmsg));
 -    msg.msg_control = &cmsg;
 -    msg.msg_controllen = sizeof(cmsg);
 +    msg.msg_control = (caddr_t) &cmsg;
 +    msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred));
  #endif
  
  retry:
 @@ -701,7 +719,7 @@
          goto failed;
      }
  #ifdef HAVE_CMSGCRED
 -    if (cmsg.hdr.cmsg_len < sizeof(cmsg) || cmsg.hdr.cmsg_type != SCM_CREDS) {
 +    if (cmsg.hdr.cmsg_len < CMSG_LEN (sizeof (struct cmsgcred)) || cmsg.hdr.cmsg_type != SCM_CREDS) {
          GAM_DEBUG(DEBUG_INFO,
                    "Message from recvmsg() was not SCM_CREDS\n");
          goto failed;
 @@ -727,9 +745,10 @@
              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];
 +        cred = (struct cmsgcred *) CMSG_DATA (&cmsg);
 +        c_pid = cred->cmcred_pid;
 +        c_uid = cred->cmcred_euid;
 +        c_gid = cred->cmcred_groups[0];
  #else /* !SO_PEERCRED && !HAVE_CMSGCRED */
          GAM_DEBUG(DEBUG_INFO,
                    "Socket credentials not supported on this OS\n");
 @@ -1288,14 +1307,17 @@
  
      // FIXME: drop and reacquire lock while blocked?
      gamin_data_lock(conn);
 -    if (!gamin_data_event_ready(conn)) {
 +    while ((ret = gamin_data_event_ready(conn)) == 0) {
          if (gamin_read_data(conn, fc->fd, 1) < 0) {
  	    gamin_try_reconnect(conn, fc->fd);
  	    FAMErrno = FAM_CONNECT;
  	    return (-1);
  	}
      }
 -    ret = gamin_data_read_event(conn, fe);
 +
 +    if (ret > 0)
 +        ret = gamin_data_read_event(conn, fe);
 +
      gamin_data_unlock(conn);
  
      if (ret < 0) {
 --- libgamin/gam_fork.c.orig	2007-07-04 06:36:48.000000000 -0700
 +++ libgamin/gam_fork.c	2013-02-16 19:02:46.360093751 -0800
 @@ -42,6 +42,82 @@
      return NULL;
  }
  
 +#ifdef RUN_AS_EUID
 +/**
 + * gamin_drop_privileges
 + *
 + * Attempt to drop privileges to the effective uid and gid before
 + * forking a copy of the gamin server
 + * 
 + * Return 0 in case of success or -1 in case of detected error.
 + */
 +int
 +gamin_drop_privileges(void)
 +{
 +    GAM_DEBUG(DEBUG_INFO, "Dropping privileges to effective user and group\n");
 +
 +    /* get the current real user and group */
 +    int from_uid = getuid();
 +    int from_gid = getgid();
 +
 +    /* get the effective user and group */
 +    int to_uid = geteuid();
 +    int to_gid = getegid();
 +
 +    /* make sure we were able to get the user and group values */
 +    if ( from_uid == -1 || to_uid == -1 || from_gid == -1 || to_gid == -1 ) {
 +        gam_error(DEBUG_INFO, "failed to get user or group info, unable to drop privileges\n");
 +        return(-1);
 +    }
 +
 +    /* refuse to run setuid if it would escalate privileges */
 +    if ( from_uid != 0 && to_uid == 0 )
 +    {
 +        gam_error(DEBUG_INFO, "refusing to escalate user privileges from=%d to=%d\n", from_uid, to_uid);
 +        return(-1);
 +    }
 +
 +    /* refuse to run setgid if it would escalate privileges */
 +    if ( from_gid != 0 && to_gid == 0 )
 +    {
 +        gam_error(DEBUG_INFO, "refusing to escalate group privileges from=%d to=%d\n", from_gid, to_gid);
 +        return(-1);
 +    }
 +
 +    /* run setuid to drop privileges to the effective user */
 +    if ( from_uid != to_uid ) {
 +        GAM_DEBUG(DEBUG_INFO, "Attempting setuid from=%d to=%d\n", from_uid, to_uid);
 +
 +        /* run setuid and check for errors */
 +        if (setuid(to_uid) == -1) {
 +            gam_error(DEBUG_INFO, "failed to run setuid from=%d to=%d\n", from_uid, to_uid);
 +            return(-1);
 +        }
 +    }
 +    else {
 +        GAM_DEBUG(DEBUG_INFO, "Already running as effective user, skipping setuid\n");
 +    }
 +
 +    /* run setgid to drop privileges to the effective group */
 +    if ( from_gid != to_gid ) {
 +        GAM_DEBUG(DEBUG_INFO, "Attempting setgid from=%d to=%d\n", from_gid, to_gid);
 +
 +        /* run setuid and check for errors */
 +        if (setgid(to_gid) == -1) {
 +            gam_error(DEBUG_INFO, "failed to run setgid from=%d to=%d\n", from_uid, to_uid);
 +            return(-1);
 +        }
 +    }
 +    else {
 +        GAM_DEBUG(DEBUG_INFO, "Already running as effective group, skipping setgid\n");
 +    }
 +
 +    GAM_DEBUG(DEBUG_INFO, "Succeeded in dropping privileges from %d:%d to %d:%d\n", from_uid, from_gid, to_uid, to_gid);
 +
 +    return(0);
 +}
 +#endif
 +
  /**
   * gamin_fork_server:
   * @fam_client_id: the client ID string to use
 @@ -71,6 +147,14 @@
          long open_max;
  	long i;
  
 +#ifdef RUN_AS_EUID
 +        /* Drop privileges to the current uid/gid and return on failure */
 +        if(gamin_drop_privileges() == -1)
 +        {
 +            return(-1);
 +        }
 +#endif
 +
          /* don't hold open fd opened from the client of the library */
  	open_max = sysconf (_SC_OPEN_MAX);
  	for (i = 0; i < open_max; i++)
 --- libgamin/gam_fork.h.orig	2007-07-04 06:36:48.000000000 -0700
 +++ libgamin/gam_fork.h	2013-02-16 18:55:08.220102407 -0800
 @@ -32,6 +32,9 @@
  #endif
  
  int		gamin_fork_server	(const char *fam_client_id);
 +#ifdef RUN_AS_EUID
 +int		gamin_drop_privileges	(void);
 +#endif
  
  #ifdef __cplusplus
  }
 
 --------------090305060503020705000402--

From: Danny Warren <danny@dannywarren.com>
To: bug-followup@FreeBSD.org, danny@dannywarren.com
Cc:  
Subject: Re: ports/176203: [patch] devel/gamin: Drop privileges to effective
 user and group
Date: Sat, 16 Feb 2013 20:47:01 -0800

 This is a multi-part message in MIME format.
 --------------020205000508090601030105
 Content-Type: text/plain; charset=ISO-8859-1; format=flowed
 Content-Transfer-Encoding: 7bit
 
 Alright, this should hopefully be the last change.
 
 * fixed typo in setgid debug message
 * reworked "gamin_drop_privileges" method to accept a uid and gid value 
 instead of assuming you always want the effective user, so that it can 
 be used in other parts of the code if needed
 * changed call to "gamin_drop_privileges" to pass the effective uid/gid 
 after fork
 
 --------------020205000508090601030105
 Content-Type: text/plain; charset=windows-1252;
  name="gamin-drop_privileges.patch.txt"
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
  filename="gamin-drop_privileges.patch.txt"
 
 --- Makefile.orig	2013-02-16 19:09:52.507178348 -0800
 +++ Makefile	2013-02-16 16:18:30.230098850 -0800
 @@ -27,9 +27,10 @@
  GNU_CONFIGURE=	yes
  
  .if !defined(GAMIN_SLAVE)
 -OPTIONS_DEFINE=	GAM_POLLER LIBINOTIFY 
 +OPTIONS_DEFINE=	GAM_POLLER LIBINOTIFY RUN_AS_EUID
  GAM_POLLER_DESC=Use gamin's poller instead of kqueue's
  LIBINOTIFY_DESC=Use libinotify as the FAM backend
 +RUN_AS_EUID_DESC=Drop privileges to effective user
  .endif
  
  .include <bsd.port.options.mk>
 @@ -48,6 +49,10 @@
  .endif
  .endif
  
 +.if ${PORT_OPTIONS:MRUN_AS_EUID}
 +CPPFLAGS+=	-DRUN_AS_EUID=1
 +.endif
 +
  post-patch:
  	@${REINPLACE_CMD} "s|/etc|${PREFIX}/etc|g" ${WRKSRC}/server/gam_conf.c
  
 --- libgamin/gam_api.c.orig	2007-08-27 03:21:03.000000000 -0700
 +++ libgamin/gam_api.c	2013-02-16 15:51:11.927100135 -0800
 @@ -14,6 +14,7 @@
  #include <sys/socket.h>
  #include <sys/un.h>
  #include <sys/uio.h>
 +#include <string.h>
  #include "fam.h"
  #include "gam_protocol.h"
  #include "gam_data.h"
 @@ -117,7 +118,11 @@
      if (user_name[0] != 0)
          return (user_name);
  
 +#ifdef RUN_AS_EUID
 +    pw = getpwuid(geteuid());
 +#else
      pw = getpwuid(getuid());
 +#endif
  
      if (pw != NULL) {
  	strncpy(user_name, pw->pw_name, 99);
 @@ -224,7 +229,11 @@
  	free(dir);
  	return(0);
      }
 +#ifdef RUN_AS_EUID
 +    if (st.st_uid != geteuid()) {
 +#else
      if (st.st_uid != getuid()) {
 +#endif
  	gam_error(DEBUG_INFO,
  		  "Socket directory %s has different owner\n",
  		  dir);
 @@ -301,7 +310,11 @@
      if (ret < 0)
  	return(0);
      
 +#ifdef RUN_AS_EUID
 +    if (st.st_uid != geteuid()) {
 +#else
      if (st.st_uid != getuid()) {
 +#endif
  	gam_error(DEBUG_INFO,
  		  "Socket %s has different owner\n",
  		  path);
 @@ -428,10 +441,10 @@
  {
      char data[2] = { 0, 0 };
      int written;
 -#if defined(HAVE_CMSGCRED) && !defined(LOCAL_CREDS)
 -    struct {
 +#if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__))
 +    union {
  	    struct cmsghdr hdr;
 -	    struct cmsgcred cred;
 +	    char cred[CMSG_SPACE (sizeof (struct cmsgcred))];
      } cmsg;
      struct iovec iov;
      struct msghdr msg;
 @@ -443,16 +456,16 @@
      msg.msg_iov = &iov;
      msg.msg_iovlen = 1;
  
 -    msg.msg_control = &cmsg;
 -    msg.msg_controllen = sizeof (cmsg);
 +    msg.msg_control = (caddr_t) &cmsg;
 +    msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred));
      memset (&cmsg, 0, sizeof (cmsg));
 -    cmsg.hdr.cmsg_len = sizeof (cmsg);
 +    cmsg.hdr.cmsg_len = CMSG_LEN (sizeof (struct cmsgcred));
      cmsg.hdr.cmsg_level = SOL_SOCKET;
      cmsg.hdr.cmsg_type = SCM_CREDS;
  #endif
  
  retry:
 -#if defined(HAVE_CMSGCRED) && !defined(LOCAL_CREDS)
 +#if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__))
      written = sendmsg(fd, &msg, 0);
  #else
      written = write(fd, &data[0], 1);
 @@ -654,15 +667,20 @@
      gid_t c_gid;
  
  #ifdef HAVE_CMSGCRED
 -    struct {
 +    struct cmsgcred *cred;
 +    union {
  	    struct cmsghdr hdr;
 -	    struct cmsgcred cred;
 +	    char cred[CMSG_SPACE (sizeof (struct cmsgcred))];
      } cmsg;
  #endif
  
 +#ifdef RUN_AS_EUID
 +    s_uid = geteuid();
 +#else
      s_uid = getuid();
 +#endif
  
 -#if defined(LOCAL_CREDS) && defined(HAVE_CMSGCRED)
 +#if defined(LOCAL_CREDS) && defined(HAVE_CMSGCRED) && !defined(__FreeBSD__)
      /* Set the socket to receive credentials on the next message */
      {
          int on = 1;
 @@ -683,8 +701,8 @@
  
  #ifdef HAVE_CMSGCRED
      memset(&cmsg, 0, sizeof(cmsg));
 -    msg.msg_control = &cmsg;
 -    msg.msg_controllen = sizeof(cmsg);
 +    msg.msg_control = (caddr_t) &cmsg;
 +    msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred));
  #endif
  
  retry:
 @@ -701,7 +719,7 @@
          goto failed;
      }
  #ifdef HAVE_CMSGCRED
 -    if (cmsg.hdr.cmsg_len < sizeof(cmsg) || cmsg.hdr.cmsg_type != SCM_CREDS) {
 +    if (cmsg.hdr.cmsg_len < CMSG_LEN (sizeof (struct cmsgcred)) || cmsg.hdr.cmsg_type != SCM_CREDS) {
          GAM_DEBUG(DEBUG_INFO,
                    "Message from recvmsg() was not SCM_CREDS\n");
          goto failed;
 @@ -727,9 +745,10 @@
              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];
 +        cred = (struct cmsgcred *) CMSG_DATA (&cmsg);
 +        c_pid = cred->cmcred_pid;
 +        c_uid = cred->cmcred_euid;
 +        c_gid = cred->cmcred_groups[0];
  #else /* !SO_PEERCRED && !HAVE_CMSGCRED */
          GAM_DEBUG(DEBUG_INFO,
                    "Socket credentials not supported on this OS\n");
 @@ -1288,14 +1307,17 @@
  
      // FIXME: drop and reacquire lock while blocked?
      gamin_data_lock(conn);
 -    if (!gamin_data_event_ready(conn)) {
 +    while ((ret = gamin_data_event_ready(conn)) == 0) {
          if (gamin_read_data(conn, fc->fd, 1) < 0) {
  	    gamin_try_reconnect(conn, fc->fd);
  	    FAMErrno = FAM_CONNECT;
  	    return (-1);
  	}
      }
 -    ret = gamin_data_read_event(conn, fe);
 +
 +    if (ret > 0)
 +        ret = gamin_data_read_event(conn, fe);
 +
      gamin_data_unlock(conn);
  
      if (ret < 0) {
 --- libgamin/gam_fork.c.orig	2007-07-04 06:36:48.000000000 -0700
 +++ libgamin/gam_fork.c	2013-02-16 20:37:31.298176973 -0800
 @@ -42,6 +42,78 @@
      return NULL;
  }
  
 +#ifdef RUN_AS_EUID
 +/**
 + * gamin_drop_privileges
 + *
 + * Attempt to drop privileges to another user and group before forking
 + * a copy of the gam server
 + * 
 + * Return 0 in case of success or -1 in case of detected error.
 + */
 +int
 +gamin_drop_privileges(int to_uid, int to_gid)
 +{
 +    GAM_DEBUG(DEBUG_INFO, "Dropping privileges to %d:%d before forking server\n", to_uid, to_gid);
 +
 +    /* Get the current real user and group */
 +    int from_uid = getuid();
 +    int from_gid = getgid();
 +
 +    /* Make sure we were able to get the user and group values */
 +    if ( from_uid == -1 || to_uid == -1 || from_gid == -1 || to_gid == -1 ) {
 +        gam_error(DEBUG_INFO, "failed to get user or group info, unable to drop privileges\n");
 +        return(-1);
 +    }
 +
 +    /* Refuse to run setuid if it would escalate privileges */
 +    if ( from_uid != 0 && to_uid == 0 )
 +    {
 +        gam_error(DEBUG_INFO, "refusing to escalate user privileges from=%d to=%d\n", from_uid, to_uid);
 +        return(-1);
 +    }
 +
 +    /* Refuse to run setgid if it would escalate privileges */
 +    if ( from_gid != 0 && to_gid == 0 )
 +    {
 +        gam_error(DEBUG_INFO, "refusing to escalate group privileges from=%d to=%d\n", from_gid, to_gid);
 +        return(-1);
 +    }
 +
 +    /* Run setuid to drop privileges to the effective user */
 +    if ( from_uid != to_uid ) {
 +        GAM_DEBUG(DEBUG_INFO, "Attempting setuid from=%d to=%d\n", from_uid, to_uid);
 +
 +        /* run setuid and check for errors */
 +        if (setuid(to_uid) == -1) {
 +            gam_error(DEBUG_INFO, "failed to run setuid from=%d to=%d\n", from_uid, to_uid);
 +            return(-1);
 +        }
 +    }
 +    else {
 +        GAM_DEBUG(DEBUG_INFO, "Already running as effective user, skipping setuid\n");
 +    }
 +
 +    /* Run setgid to drop privileges to the effective group */
 +    if ( from_gid != to_gid ) {
 +        GAM_DEBUG(DEBUG_INFO, "Attempting setgid from=%d to=%d\n", from_gid, to_gid);
 +
 +        /* run setuid and check for errors */
 +        if (setgid(to_gid) == -1) {
 +            gam_error(DEBUG_INFO, "failed to run setgid from=%d to=%d\n", from_gid, to_gid);
 +            return(-1);
 +        }
 +    }
 +    else {
 +        GAM_DEBUG(DEBUG_INFO, "Already running as effective group, skipping setgid\n");
 +    }
 +
 +    GAM_DEBUG(DEBUG_INFO, "Succeeded in dropping privileges from %d:%d to %d:%d\n", from_uid, from_gid, to_uid, to_gid);
 +
 +    return(0);
 +}
 +#endif
 +
  /**
   * gamin_fork_server:
   * @fam_client_id: the client ID string to use
 @@ -71,6 +143,13 @@
          long open_max;
  	long i;
  
 +#ifdef RUN_AS_EUID
 +        /* Drop privileges to the current effective uid/gid and return on failure */
 +        if(gamin_drop_privileges( geteuid(), getegid() ) == -1) {
 +            return(-1);
 +        }
 +#endif
 +
          /* don't hold open fd opened from the client of the library */
  	open_max = sysconf (_SC_OPEN_MAX);
  	for (i = 0; i < open_max; i++)
 --- libgamin/gam_fork.h.orig	2007-07-04 06:36:48.000000000 -0700
 +++ libgamin/gam_fork.h	2013-02-16 20:38:00.328594608 -0800
 @@ -32,6 +32,9 @@
  #endif
  
  int		gamin_fork_server	(const char *fam_client_id);
 +#ifdef RUN_AS_EUID
 +int		gamin_drop_privileges	(int to_uid, int to_gid);
 +#endif
  
  #ifdef __cplusplus
  }
 
 --------------020205000508090601030105--

From: Danny Warren <danny@dannywarren.com>
To: bug-followup@FreeBSD.org
Cc: freebsd-gnome@freebsd.org
Subject: Re: ports/176203: [patch] devel/gamin: Drop privileges to effective
 user and group
Date: Sat, 16 Mar 2013 16:02:42 -0700

 Has anyone had a chance to look in to this?  Thanks!
State-Changed-From-To: open->closed 
State-Changed-By: kwm 
State-Changed-When: Fri Mar 22 16:31:07 UTC 2013 
State-Changed-Why:  
Committed, thanks! 

http://www.freebsd.org/cgi/query-pr.cgi?pr=176203 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: ports/176203: commit references a PR
Date: Fri, 22 Mar 2013 16:30:50 +0000 (UTC)

 Author: kwm
 Date: Fri Mar 22 16:30:36 2013
 New Revision: 314945
 URL: http://svnweb.freebsd.org/changeset/ports/314945
 
 Log:
   Add patches to gamin to enable support for effective uid/gid, add a option for
   this and turn it on by default.
   
   Current gamin uses the current real uid to determine the location of the
   socket and during the security routines. It also forks off a copy of
   "gam_server" as the current real uid as well.
   
   This breaks gamin functionality with any application that uses effective an
   uid/gid for privilege separation, such as samba. Samba runs as root, but
   always sets the effective uid/gid to that of the connecting user. Since
   gamin is only aware of the real uid, it always creates a root-owned gamin
   socket that the effective user can't touch (gamin's own security checks
   refuse it).
   
   PR:		ports/176203
   Submitted by:	Danny Warren <danny@dannywarren.com>
   Reviewed by:	marcus@
 
 Added:
   head/devel/gamin/files/patch-libgamin_gam_fork.c   (contents, props changed)
   head/devel/gamin/files/patch-libgamin_gam_fork.h   (contents, props changed)
 Modified:
   head/devel/gamin/Makefile
   head/devel/gamin/files/patch-libgamin_gam_api.c   (contents, props changed)
 
 Modified: head/devel/gamin/Makefile
 ==============================================================================
 --- head/devel/gamin/Makefile	Fri Mar 22 16:24:53 2013	(r314944)
 +++ head/devel/gamin/Makefile	Fri Mar 22 16:30:36 2013	(r314945)
 @@ -4,7 +4,7 @@
  
  PORTNAME=	gamin
  PORTVERSION=	0.1.10
 -PORTREVISION?=	4
 +PORTREVISION?=	5
  CATEGORIES?=	devel
  MASTER_SITES=	http://people.gnome.org/~veillard/gamin/sources/
  
 @@ -27,9 +27,11 @@ CONFLICTS=	fam-[0-9]*
  GNU_CONFIGURE=	yes
  
  .if !defined(GAMIN_SLAVE)
 -OPTIONS_DEFINE=	GAM_POLLER LIBINOTIFY
 +OPTIONS_DEFINE=	GAM_POLLER LIBINOTIFY RUN_AS_EUID
 +OPTIONS_DEFAULT=RUN_AS_EUID
  GAM_POLLER_DESC=Use gamin's poller instead of kqueue's
  LIBINOTIFY_DESC=Use libinotify as the FAM backend
 +RUN_AS_EUID_DESC=Drop privileges to effective user
  .endif
  
  .include <bsd.port.options.mk>
 @@ -48,6 +50,10 @@ CONFIGURE_ARGS+=--disable-inotify
  .endif
  .endif
  
 +.if ${PORT_OPTIONS:MRUN_AS_EUID}
 +CPPFLAGS+=	-DRUN_AS_EUID=1
 +.endif
 +
  post-patch:
  	@${REINPLACE_CMD} "s|/etc|${PREFIX}/etc|g" ${WRKSRC}/server/gam_conf.c
  
 
 Modified: head/devel/gamin/files/patch-libgamin_gam_api.c
 ==============================================================================
 --- head/devel/gamin/files/patch-libgamin_gam_api.c	Fri Mar 22 16:24:53 2013	(r314944)
 +++ head/devel/gamin/files/patch-libgamin_gam_api.c	Fri Mar 22 16:30:36 2013	(r314945)
 @@ -1,5 +1,5 @@
 ---- libgamin/gam_api.c.orig	Tue Feb  7 17:49:07 2006
 -+++ libgamin/gam_api.c	Tue Feb  7 17:49:13 2006
 +--- libgamin/gam_api.c.orig	2007-08-27 03:21:03.000000000 -0700
 ++++ libgamin/gam_api.c	2013-02-16 15:51:11.927100135 -0800
  @@ -14,6 +14,7 @@
   #include <sys/socket.h>
   #include <sys/un.h>
 @@ -8,7 +8,43 @@
   #include "fam.h"
   #include "gam_protocol.h"
   #include "gam_data.h"
 -@@ -421,10 +422,10 @@
 +@@ -117,7 +118,11 @@
 +     if (user_name[0] != 0)
 +         return (user_name);
 + 
 ++#ifdef RUN_AS_EUID
 ++    pw = getpwuid(geteuid());
 ++#else
 +     pw = getpwuid(getuid());
 ++#endif
 + 
 +     if (pw != NULL) {
 + 	strncpy(user_name, pw->pw_name, 99);
 +@@ -224,7 +229,11 @@
 + 	free(dir);
 + 	return(0);
 +     }
 ++#ifdef RUN_AS_EUID
 ++    if (st.st_uid != geteuid()) {
 ++#else
 +     if (st.st_uid != getuid()) {
 ++#endif
 + 	gam_error(DEBUG_INFO,
 + 		  "Socket directory %s has different owner\n",
 + 		  dir);
 +@@ -301,7 +310,11 @@
 +     if (ret < 0)
 + 	return(0);
 +     
 ++#ifdef RUN_AS_EUID
 ++    if (st.st_uid != geteuid()) {
 ++#else
 +     if (st.st_uid != getuid()) {
 ++#endif
 + 	gam_error(DEBUG_INFO,
 + 		  "Socket %s has different owner\n",
 + 		  path);
 +@@ -428,10 +441,10 @@
   {
       char data[2] = { 0, 0 };
       int written;
 @@ -22,7 +58,7 @@
       } cmsg;
       struct iovec iov;
       struct msghdr msg;
 -@@ -436,16 +437,16 @@
 +@@ -443,16 +456,16 @@
       msg.msg_iov = &iov;
       msg.msg_iovlen = 1;
   
 @@ -43,7 +79,7 @@
       written = sendmsg(fd, &msg, 0);
   #else
       written = write(fd, &data[0], 1);
 -@@ -647,15 +648,16 @@
 +@@ -654,15 +667,20 @@
       gid_t c_gid;
   
   #ifdef HAVE_CMSGCRED
 @@ -56,14 +92,18 @@
       } cmsg;
   #endif
   
 ++#ifdef RUN_AS_EUID
 ++    s_uid = geteuid();
 ++#else
       s_uid = getuid();
 ++#endif
   
  -#if defined(LOCAL_CREDS) && defined(HAVE_CMSGCRED)
  +#if defined(LOCAL_CREDS) && defined(HAVE_CMSGCRED) && !defined(__FreeBSD__)
       /* Set the socket to receive credentials on the next message */
       {
           int on = 1;
 -@@ -676,8 +678,8 @@
 +@@ -683,8 +701,8 @@
   
   #ifdef HAVE_CMSGCRED
       memset(&cmsg, 0, sizeof(cmsg));
 @@ -74,7 +114,7 @@
   #endif
   
   retry:
 -@@ -694,7 +696,7 @@
 +@@ -701,7 +719,7 @@
           goto failed;
       }
   #ifdef HAVE_CMSGCRED
 @@ -83,7 +123,7 @@
           GAM_DEBUG(DEBUG_INFO,
                     "Message from recvmsg() was not SCM_CREDS\n");
           goto failed;
 -@@ -720,9 +722,10 @@
 +@@ -727,9 +745,10 @@
               goto failed;
           }
   #elif defined(HAVE_CMSGCRED)
 @@ -97,7 +137,7 @@
   #else /* !SO_PEERCRED && !HAVE_CMSGCRED */
           GAM_DEBUG(DEBUG_INFO,
                     "Socket credentials not supported on this OS\n");
 -@@ -1283,14 +1286,17 @@
 +@@ -1288,14 +1307,17 @@
   
       // FIXME: drop and reacquire lock while blocked?
       gamin_data_lock(conn);
 
 Added: head/devel/gamin/files/patch-libgamin_gam_fork.c
 ==============================================================================
 --- /dev/null	00:00:00 1970	(empty, because file is newly added)
 +++ head/devel/gamin/files/patch-libgamin_gam_fork.c	Fri Mar 22 16:30:36 2013	(r314945)
 @@ -0,0 +1,95 @@
 +--- libgamin/gam_fork.c.orig	2007-07-04 06:36:48.000000000 -0700
 ++++ libgamin/gam_fork.c	2013-02-16 20:37:31.298176973 -0800
 +@@ -42,6 +42,78 @@
 +     return NULL;
 + }
 + 
 ++#ifdef RUN_AS_EUID
 ++/**
 ++ * gamin_drop_privileges
 ++ *
 ++ * Attempt to drop privileges to another user and group before forking
 ++ * a copy of the gam server
 ++ * 
 ++ * Return 0 in case of success or -1 in case of detected error.
 ++ */
 ++int
 ++gamin_drop_privileges(int to_uid, int to_gid)
 ++{
 ++    GAM_DEBUG(DEBUG_INFO, "Dropping privileges to %d:%d before forking server\n", to_uid, to_gid);
 ++
 ++    /* Get the current real user and group */
 ++    int from_uid = getuid();
 ++    int from_gid = getgid();
 ++
 ++    /* Make sure we were able to get the user and group values */
 ++    if ( from_uid == -1 || to_uid == -1 || from_gid == -1 || to_gid == -1 ) {
 ++        gam_error(DEBUG_INFO, "failed to get user or group info, unable to drop privileges\n");
 ++        return(-1);
 ++    }
 ++
 ++    /* Refuse to run setuid if it would escalate privileges */
 ++    if ( from_uid != 0 && to_uid == 0 )
 ++    {
 ++        gam_error(DEBUG_INFO, "refusing to escalate user privileges from=%d to=%d\n", from_uid, to_uid);
 ++        return(-1);
 ++    }
 ++
 ++    /* Refuse to run setgid if it would escalate privileges */
 ++    if ( from_gid != 0 && to_gid == 0 )
 ++    {
 ++        gam_error(DEBUG_INFO, "refusing to escalate group privileges from=%d to=%d\n", from_gid, to_gid);
 ++        return(-1);
 ++    }
 ++
 ++    /* Run setuid to drop privileges to the effective user */
 ++    if ( from_uid != to_uid ) {
 ++        GAM_DEBUG(DEBUG_INFO, "Attempting setuid from=%d to=%d\n", from_uid, to_uid);
 ++
 ++        /* run setuid and check for errors */
 ++        if (setuid(to_uid) == -1) {
 ++            gam_error(DEBUG_INFO, "failed to run setuid from=%d to=%d\n", from_uid, to_uid);
 ++            return(-1);
 ++        }
 ++    }
 ++    else {
 ++        GAM_DEBUG(DEBUG_INFO, "Already running as effective user, skipping setuid\n");
 ++    }
 ++
 ++    /* Run setgid to drop privileges to the effective group */
 ++    if ( from_gid != to_gid ) {
 ++        GAM_DEBUG(DEBUG_INFO, "Attempting setgid from=%d to=%d\n", from_gid, to_gid);
 ++
 ++        /* run setuid and check for errors */
 ++        if (setgid(to_gid) == -1) {
 ++            gam_error(DEBUG_INFO, "failed to run setgid from=%d to=%d\n", from_gid, to_gid);
 ++            return(-1);
 ++        }
 ++    }
 ++    else {
 ++        GAM_DEBUG(DEBUG_INFO, "Already running as effective group, skipping setgid\n");
 ++    }
 ++
 ++    GAM_DEBUG(DEBUG_INFO, "Succeeded in dropping privileges from %d:%d to %d:%d\n", from_uid, from_gid, to_uid, to_gid);
 ++
 ++    return(0);
 ++}
 ++#endif
 ++
 + /**
 +  * gamin_fork_server:
 +  * @fam_client_id: the client ID string to use
 +@@ -71,6 +143,13 @@
 +         long open_max;
 + 	long i;
 + 
 ++#ifdef RUN_AS_EUID
 ++        /* Drop privileges to the current effective uid/gid and return on failure */
 ++        if(gamin_drop_privileges( geteuid(), getegid() ) == -1) {
 ++            return(-1);
 ++        }
 ++#endif
 ++
 +         /* don't hold open fd opened from the client of the library */
 + 	open_max = sysconf (_SC_OPEN_MAX);
 + 	for (i = 0; i < open_max; i++)
 
 Added: head/devel/gamin/files/patch-libgamin_gam_fork.h
 ==============================================================================
 --- /dev/null	00:00:00 1970	(empty, because file is newly added)
 +++ head/devel/gamin/files/patch-libgamin_gam_fork.h	Fri Mar 22 16:30:36 2013	(r314945)
 @@ -0,0 +1,12 @@
 +--- libgamin/gam_fork.h.orig	2007-07-04 06:36:48.000000000 -0700
 ++++ libgamin/gam_fork.h	2013-02-16 20:38:00.328594608 -0800
 +@@ -32,6 +32,9 @@
 + #endif
 + 
 + int		gamin_fork_server	(const char *fam_client_id);
 ++#ifdef RUN_AS_EUID
 ++int		gamin_drop_privileges	(int to_uid, int to_gid);
 ++#endif
 + 
 + #ifdef __cplusplus
 + }
 _______________________________________________
 svn-ports-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-ports-all
 To unsubscribe, send any mail to "svn-ports-all-unsubscribe@freebsd.org"
 
>Unformatted:
