$NetBSD: patch-ae,v 1.3 2012/03/17 02:19:08 wiz Exp $ --- libgamin/gam_api.c.orig 2007-08-27 10:21:03.000000000 +0000 +++ libgamin/gam_api.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,37 @@ const char *FamErrlist[] = { NULL }; +#if defined(LOCAL_PEEREID) +static int +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 0; + if (pid) + *pid = cred.unp_pid; + if (uid) + *uid = cred.unp_euid; + if (gid) + *gid = cred.unp_egid; + return 1; +} +#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 + #ifdef GAMIN_DEBUG_API int FAMDebug(FAMConnection *fc, const char *filename, FAMRequest * fr, void *userData); @@ -314,12 +346,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 */ @@ -379,6 +405,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) { @@ -426,37 +464,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 + + iov.iov_base = &pid; + iov.iov_len = sizeof(pid_t); - memset (&msg, 0, sizeof (msg)); + 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; @@ -464,7 +500,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); @@ -648,43 +684,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; +#if defined(BSDCRED) + struct cmsghdr *cmsg; + char cmsgbuf[CMSG_SPACE(CRED_DATASIZE)]; #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); - } - } -#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: @@ -692,26 +711,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); @@ -726,23 +752,37 @@ 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(LOCAL_PEEREID) + if (gam_nb_getpeereid(fd, &c_pid, &c_uid, &c_gid) == 0) { + 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); @@ -1288,14 +1328,16 @@ FAMNextEvent(FAMConnection * fc, FAMEven // 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) {