Date: Sun, 17 Feb 2013 04:50:01 GMT From: Danny Warren <danny@dannywarren.com> To: gnome@FreeBSD.org Subject: Re: ports/176203: [patch] devel/gamin: Drop privileges to effective user and group Message-ID: <201302170450.r1H4o17g097546@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
The following reply was made to PR ports/176203; it has been noted by GNATS. 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--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201302170450.r1H4o17g097546>