From 2716d832cc4fdca101282ee846e1ff522c591e53 Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Sun, 5 Jul 2026 15:15:57 +0100 Subject: [PATCH 01/14] control: remove unprivileged socket Instead workout credentials from the connected socket by getpeereid(2). Remove the limit of only one connected control client. This needs some testing and is only expected to compile on the BSDs right now. --- src/control.c | 238 +++++++++++++++++++++++++----------------- src/control.h | 8 +- src/dhcpcd.8.in | 6 +- src/dhcpcd.c | 20 ++-- src/dhcpcd.h | 15 ++- src/privsep-control.c | 105 ++++++++----------- src/privsep-control.h | 4 +- src/privsep-root.c | 46 ++++++++ src/privsep-root.h | 1 + src/privsep.c | 10 +- src/privsep.h | 7 +- 11 files changed, 261 insertions(+), 199 deletions(-) diff --git a/src/control.c b/src/control.c index ad006c20..09aebc04 100644 --- a/src/control.c +++ b/src/control.c @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -80,11 +81,6 @@ control_queue_free(struct fd_list *fd) void control_free(struct fd_list *fd) { -#ifdef PRIVSEP - if (fd->ctx->ps_control_client == fd) - fd->ctx->ps_control_client = NULL; -#endif - eloop_event_delete(fd->ctx->eloop, fd->fd); close(fd->fd); TAILQ_REMOVE(&fd->ctx->control_fds, fd, next); @@ -95,40 +91,59 @@ control_free(struct fd_list *fd) static void control_hangup(struct fd_list *fd) { -#ifdef PRIVSEP - if (IN_PRIVSEP(fd->ctx)) { - if (ps_ctl_sendeof(fd) == -1) - logerr(__func__); - } -#endif control_free(fd); } static int control_handle_read(struct fd_list *fd) { - char buffer[1024]; - ssize_t bytes; - - bytes = read(fd->fd, buffer, sizeof(buffer) - 1); + uid_t uid; + gid_t gid; + char buf[BUFSIZ]; + struct iovec iov[] = { { + .iov_base = buf, + .iov_len = sizeof(buf), + } }; + struct msghdr msg = { + .msg_iov = iov, + .msg_iovlen = __arraycount(iov), + }; + ssize_t bytes, err; + + bytes = recvmsg(fd->fd, &msg, 0); if (bytes == -1) logerr(__func__); if (bytes == -1 || bytes == 0) return (int)bytes; + if (getpeereid(fd->fd, &uid, &gid) == -1) { + logerr("%s: getpeereid", __func__); + return -1; + } + #ifdef PRIVSEP if (IN_PRIVSEP(fd->ctx)) { - ssize_t err; + ssize_t perr; + + perr = ps_root_user_ispriv(fd->ctx, uid, gid); + if (perr == -1) { + logerr(__func__); + return -1; + } + if (perr == 0) + fd->flags &= ~FD_PRIV; + else + fd->flags |= FD_PRIV; fd->flags |= FD_SENDLEN; - err = ps_ctl_handleargs(fd, buffer, (size_t)bytes); + perr = ps_ctl_handleargs(fd, buf, (size_t)bytes); fd->flags &= ~FD_SENDLEN; - if (err == -1) { + if (perr == -1) { logerr(__func__); return 0; } - if (err == 1 && - ps_ctl_sendargs(fd, buffer, (size_t)bytes) == -1) { + iov[0].iov_len = (size_t)bytes; + if (perr == 1 && ps_ctl_sendmsg(fd, &msg) == -1) { logerr(__func__); return -1; } @@ -136,7 +151,16 @@ control_handle_read(struct fd_list *fd) } #endif - return control_recvdata(fd, buffer, (size_t)bytes); + err = control_user_ispriv(fd->ctx, uid, gid); + if (err == -1) { + logerr(__func__); + return -1; + } + if (err == 0) + fd->flags &= ~FD_PRIV; + else + fd->flags |= FD_PRIV; + return control_recvmsg(fd, &msg, (size_t)bytes); } static int @@ -181,13 +205,6 @@ control_handle_write(struct fd_list *fd) if (TAILQ_FIRST(&fd->queue) != NULL) return 0; -#ifdef PRIVSEP - if (IN_PRIVSEP_SE(fd->ctx) && !(fd->flags & FD_LISTEN)) { - if (ps_ctl_sendeof(fd) == -1) - logerr(__func__); - } -#endif - /* Done sending data, stop watching write to fd */ if (eloop_event_add(fd->ctx->eloop, fd->fd, ELE_READ, control_handle_data, fd) == -1) @@ -211,7 +228,7 @@ control_handle_data(void *arg, unsigned short events) } if (events & ELE_READ) { err = control_handle_read(fd); - if (err == -1 || err == 0) + if ((err == -1 && errno != EPERM) || err == 0) goto hangup; } if (events & ELE_HANGUP) @@ -224,12 +241,21 @@ control_handle_data(void *arg, unsigned short events) } int -control_recvdata(struct fd_list *fd, char *data, size_t len) +control_recvmsg(struct fd_list *fd, struct msghdr *msg, size_t len) { - char *p = data, *e; + struct iovec *iov; + char *p, *e; char *argvp[255], **ap; int argc; + if (msg->msg_iovlen == 0) { + errno = EINVAL; + return -1; + } + + iov = msg->msg_iov; + p = (char *)iov->iov_base; + /* Each command is \n terminated * Each argument is NULL separated */ while (len != 0) { @@ -271,7 +297,7 @@ control_recvdata(struct fd_list *fd, char *data, size_t len) *ap = NULL; if (dhcpcd_handleargs(fd->ctx, fd, argc, argvp) == -1) { logerr(__func__); - if (errno != EINTR && errno != EAGAIN) + if (errno != EINTR && errno != EAGAIN && errno != EPERM) return -1; } } @@ -279,11 +305,31 @@ control_recvdata(struct fd_list *fd, char *data, size_t len) return 1; } +struct fd_list * +control_find(struct dhcpcd_ctx *ctx, int fd) +{ + struct fd_list *l; + + TAILQ_FOREACH(l, &ctx->control_fds, next) { + if (l->fd == fd) + return l; + } + + errno = ESRCH; + return NULL; +} + struct fd_list * control_new(struct dhcpcd_ctx *ctx, int fd, unsigned int flags) { struct fd_list *l; + l = control_find(ctx, fd); + if (l != NULL) { + l->flags = flags; + return l; + } + l = malloc(sizeof(*l)); if (l == NULL) return NULL; @@ -306,7 +352,7 @@ control_handle1(struct dhcpcd_ctx *ctx, int lfd, unsigned int fd_flags, struct sockaddr_un run; socklen_t len; struct fd_list *l; - int fd, flags; + int fd, flags = 1; if (events != ELE_READ) logerrx("%s: unexpected event 0x%04x", __func__, events); @@ -335,6 +381,7 @@ control_handle1(struct dhcpcd_ctx *ctx, int lfd, unsigned int fd_flags, if (eloop_event_add(ctx->eloop, l->fd, ELE_READ, control_handle_data, l) == -1) logerr("%s: eloop_event_add", __func__); + return; error: @@ -351,20 +398,10 @@ control_handle(void *arg, unsigned short events) control_handle1(ctx, ctx->control_fd, 0, events); } -static void -control_handle_unpriv(void *arg, unsigned short events) -{ - struct dhcpcd_ctx *ctx = arg; - - control_handle1(ctx, ctx->control_unpriv_fd, FD_UNPRIV, events); -} - static int -make_path(char *path, size_t len, const char *ifname, sa_family_t family, - bool unpriv) +make_path(char *path, size_t len, const char *ifname, sa_family_t family) { const char *per; - const char *sunpriv; switch (family) { case AF_INET: @@ -377,70 +414,57 @@ make_path(char *path, size_t len, const char *ifname, sa_family_t family, per = ""; break; } - if (unpriv) - sunpriv = ifname ? ".unpriv" : "unpriv."; - else - sunpriv = ""; return snprintf(path, len, CONTROLSOCKET, ifname ? ifname : "", - ifname ? per : "", sunpriv, ifname ? "." : ""); + ifname ? per : "", "", ifname ? "." : ""); } static int -make_sock(struct sockaddr_un *sa, const char *ifname, sa_family_t family, - bool unpriv) +make_sock(struct sockaddr_un *sa, const char *ifname, sa_family_t family) { int fd; - if ((fd = xsocket(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0)) == -1) + if ((fd = xsocket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1) return -1; memset(sa, 0, sizeof(*sa)); sa->sun_family = AF_UNIX; - make_path(sa->sun_path, sizeof(sa->sun_path), ifname, family, unpriv); + make_path(sa->sun_path, sizeof(sa->sun_path), ifname, family); return fd; } -#define S_PRIV (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) -#define S_UNPRIV (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) +#define SOCK_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) static int -control_start1(struct dhcpcd_ctx *ctx, const char *ifname, sa_family_t family, - mode_t fmode) +control_start1(struct dhcpcd_ctx *ctx, const char *ifname, sa_family_t family) { struct sockaddr_un sa; int fd; socklen_t len; - fd = make_sock(&sa, ifname, family, (fmode & S_UNPRIV) == S_UNPRIV); + fd = make_sock(&sa, ifname, family); if (fd == -1) return -1; len = (socklen_t)SUN_LEN(&sa); unlink(sa.sun_path); if (bind(fd, (struct sockaddr *)&sa, len) == -1 || - chmod(sa.sun_path, fmode) == -1 || + chmod(sa.sun_path, 0666) == -1 || (ctx->control_group && chown(sa.sun_path, geteuid(), ctx->control_group) == -1) || - listen(fd, sizeof(ctx->control_fds)) == -1) { - close(fd); - unlink(sa.sun_path); - return -1; - } + listen(fd, sizeof(ctx->control_fds)) == -1) + goto err; #ifdef PRIVSEP_RIGHTS - if (IN_PRIVSEP(ctx) && ps_rights_limit_fd_fctnl(fd) == -1) { - close(fd); - unlink(sa.sun_path); - return -1; - } + if (IN_PRIVSEP(ctx) && ps_rights_limit_fd_fctnl(fd) == -1) + goto err; #endif - if ((fmode & S_UNPRIV) == S_UNPRIV) - strlcpy(ctx->control_sock_unpriv, sa.sun_path, - sizeof(ctx->control_sock_unpriv)); - else - strlcpy(ctx->control_sock, sa.sun_path, - sizeof(ctx->control_sock)); + strlcpy(ctx->control_sock, sa.sun_path, sizeof(ctx->control_sock)); return fd; + +err: + close(fd); + unlink(sa.sun_path); + return -1; } int @@ -451,14 +475,12 @@ control_start(struct dhcpcd_ctx *ctx, const char *ifname, sa_family_t family) #ifdef PRIVSEP if (IN_PRIVSEP_SE(ctx)) { make_path(ctx->control_sock, sizeof(ctx->control_sock), ifname, - family, false); - make_path(ctx->control_sock_unpriv, - sizeof(ctx->control_sock_unpriv), ifname, family, true); + family); return 0; } #endif - if ((fd = control_start1(ctx, ifname, family, S_PRIV)) == -1) + if ((fd = control_start1(ctx, ifname, family)) == -1) return -1; ctx->control_fd = fd; @@ -466,12 +488,6 @@ control_start(struct dhcpcd_ctx *ctx, const char *ifname, sa_family_t family) -1) logerr("%s: eloop_event_add", __func__); - if ((fd = control_start1(ctx, ifname, family, S_UNPRIV)) != -1) { - ctx->control_unpriv_fd = fd; - if (eloop_event_add(ctx->eloop, fd, ELE_READ, - control_handle_unpriv, ctx) == -1) - logerr("%s: eloop_event_add", __func__); - } return ctx->control_fd; } @@ -508,9 +524,6 @@ control_stop(struct dhcpcd_ctx *ctx) if (ctx->control_sock[0] != '\0' && ps_root_unlink(ctx, ctx->control_sock) == -1) retval = -1; - if (ctx->control_sock_unpriv[0] != '\0' && - ps_root_unlink(ctx, ctx->control_sock_unpriv) == -1) - retval = -1; return retval; } else if (ctx->options & DHCPCD_FORKED) return retval; @@ -524,24 +537,16 @@ control_stop(struct dhcpcd_ctx *ctx) retval = -1; } - if (ctx->control_unpriv_fd != -1) { - eloop_event_delete(ctx->eloop, ctx->control_unpriv_fd); - close(ctx->control_unpriv_fd); - ctx->control_unpriv_fd = -1; - if (control_unlink(ctx, ctx->control_sock_unpriv) == -1) - retval = -1; - } - return retval; } int -control_open(const char *ifname, sa_family_t family, bool unpriv) +control_open(const char *ifname, sa_family_t family) { struct sockaddr_un sa; int fd; - if ((fd = make_sock(&sa, ifname, family, unpriv)) != -1) { + if ((fd = make_sock(&sa, ifname, family)) != -1) { socklen_t len; len = (socklen_t)SUN_LEN(&sa); @@ -632,3 +637,40 @@ control_queue(struct fd_list *fd, void *data, size_t data_len) return eloop_event_add(fd->ctx->eloop, fd->fd, events, control_handle_data, fd); } + +int +control_user_ispriv(struct dhcpcd_ctx *ctx, uid_t uid, gid_t gid) +{ + gid_t *groups = NULL, *gp; + struct passwd *pw; + int ngroups = 10, err = -1; + + pw = getpwuid(uid); + if (pw == NULL) + return -1; + + groups = reallocarray(groups, (size_t)ngroups, sizeof(*groups)); + if (groups == NULL) + return -1; + + if (getgrouplist(pw->pw_name, gid, groups, &ngroups) == -1) { + gp = reallocarray(groups, (size_t)ngroups, sizeof(*groups)); + if (gp == NULL) + goto out; + groups = gp; + if (getgrouplist(pw->pw_name, gid, groups, &ngroups) == -1) + goto out; + } + + for (gp = groups; ngroups != 0; ngroups--, gp++) { + if (*gp == ctx->control_group) { + err = 1; + goto out; + } + } + err = 0; + +out: + free(groups); + return err; +} diff --git a/src/control.h b/src/control.h index 9e966f77..85086025 100644 --- a/src/control.h +++ b/src/control.h @@ -66,16 +66,18 @@ struct fd_list { TAILQ_HEAD(fd_list_head, fd_list); #define FD_LISTEN 0x01U -#define FD_UNPRIV 0x02U +#define FD_PRIV 0x02U #define FD_SENDLEN 0x04U int control_start(struct dhcpcd_ctx *, const char *, sa_family_t); int control_stop(struct dhcpcd_ctx *); -int control_open(const char *, sa_family_t, bool); +int control_open(const char *, sa_family_t); ssize_t control_send(struct dhcpcd_ctx *, int, char *const *); +struct fd_list *control_find(struct dhcpcd_ctx *, int); struct fd_list *control_new(struct dhcpcd_ctx *, int, unsigned int); void control_free(struct fd_list *); void control_delete(struct fd_list *); int control_queue(struct fd_list *, void *, size_t); -int control_recvdata(struct fd_list *fd, char *, size_t); +int control_recvmsg(struct fd_list *, struct msghdr *, size_t); +int control_user_ispriv(struct dhcpcd_ctx *ctx, uid_t uid, gid_t gid); #endif diff --git a/src/dhcpcd.8.in b/src/dhcpcd.8.in index 86b69132..26d37772 100644 --- a/src/dhcpcd.8.in +++ b/src/dhcpcd.8.in @@ -23,7 +23,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 8, 2025 +.Dd July 5, 2026 .Dt DHCPCD 8 .Os .Sh NAME @@ -861,12 +861,8 @@ running on the .Ar interface . .It Pa @RUNDIR@/sock Control socket to the manager daemon. -.It Pa @RUNDIR@/unpriv.sock -Unprivileged socket to the manager daemon, only allows state retrieval. .It Pa @RUNDIR@/ Ns Ar interface Ns .sock Control socket to per interface daemon. -.It Pa @RUNDIR@/ Ns Ar interface Ns .unpriv.sock -Unprivileged socket to per interface daemon, only allows state retrieval. .El .Sh SEE ALSO .Xr fnmatch 3 , diff --git a/src/dhcpcd.c b/src/dhcpcd.c index 36ee0e3b..f45c4f38 100644 --- a/src/dhcpcd.c +++ b/src/dhcpcd.c @@ -1704,7 +1704,7 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd, int argc, } /* Only privileged users can control dhcpcd via the socket. */ - if (fd->flags & FD_UNPRIV) { + if (!(fd->flags & FD_PRIV)) { errno = EPERM; return -1; } @@ -2078,7 +2078,7 @@ main(int argc, char **argv, char **envp) ifo = NULL; ctx.cffile = CONFIG; ctx.script = UNCONST(dhcpcd_default_script); - ctx.control_fd = ctx.control_unpriv_fd = ctx.link_fd = -1; + ctx.control_fd = ctx.link_fd = -1; ctx.pf_inet_fd = -1; #ifdef PF_LINK ctx.pf_link_fd = -1; @@ -2421,20 +2421,12 @@ main(int argc, char **argv, char **envp) if (!(ctx.options & DHCPCD_TEST)) { ctx.options |= DHCPCD_FORKED; /* avoid socket unlink */ if (!(ctx.options & DHCPCD_MANAGER)) - ctx.control_fd = control_open(argv[optind], family, - ctx.options & DHCPCD_DUMPLEASE); + ctx.control_fd = control_open(argv[optind], family); if (!(ctx.options & DHCPCD_MANAGER) && ctx.control_fd == -1) - ctx.control_fd = control_open(argv[optind], AF_UNSPEC, - ctx.options & DHCPCD_DUMPLEASE); + ctx.control_fd = control_open(argv[optind], AF_UNSPEC); if (ctx.control_fd == -1) - ctx.control_fd = control_open(NULL, AF_UNSPEC, - ctx.options & DHCPCD_DUMPLEASE); + ctx.control_fd = control_open(NULL, AF_UNSPEC); if (ctx.control_fd != -1) { -#ifdef PRIVSEP - if (IN_PRIVSEP(&ctx) && - ps_managersandbox(&ctx, NULL) == -1) - goto exit_failure; -#endif if (!(ctx.options & DHCPCD_DUMPLEASE)) loginfox("sending commands to dhcpcd process"); len = control_send(&ctx, argc, argv); @@ -2580,6 +2572,8 @@ main(int argc, char **argv, char **envp) logdebugx("spawned manager process on PID %d", (int)getpid()); start_manager: + + logdebugx("spawned manager process on PID %d", (int)getpid()); ctx.options |= DHCPCD_STARTED; if ((pid = pidfile_lock(ctx.pidfile)) != 0) { logerr("%s: pidfile_lock %d", __func__, (int)pid); diff --git a/src/dhcpcd.h b/src/dhcpcd.h index cb2a9303..e1865cb5 100644 --- a/src/dhcpcd.h +++ b/src/dhcpcd.h @@ -174,10 +174,8 @@ struct dhcpcd_ctx { size_t io_buflen; int control_fd; - int control_unpriv_fd; struct fd_list_head control_fds; char control_sock[sizeof(CONTROLSOCKET) + IF_NAMESIZE]; - char control_sock_unpriv[sizeof(CONTROLSOCKET) + IF_NAMESIZE + 7]; gid_t control_group; /* DHCP Enterprise options, RFC3925 */ @@ -199,13 +197,12 @@ struct dhcpcd_ctx { struct ps_process *ps_root; struct ps_process *ps_inet; struct ps_process *ps_ctl; - int ps_data_fd; /* data returned from processes */ - int ps_log_fd; /* chroot logging */ - int ps_log_root_fd; /* outside chroot log reader */ - struct fd_list *ps_control; /* Queue for the above */ - struct fd_list *ps_control_client; /* Queue for the above */ - void *ps_buf; /* IPC buffer */ - size_t ps_buflen; /* IPC buffer length */ + int ps_data_fd; /* data returned from processes */ + int ps_log_fd; /* chroot logging */ + int ps_log_root_fd; /* outside chroot log reader */ + struct fd_list *ps_control; /* Queue for the above */ + void *ps_buf; /* IPC buffer */ + size_t ps_buflen; /* IPC buffer length */ #endif #ifdef INET diff --git a/src/privsep-control.c b/src/privsep-control.c index 3737d07d..8c2fa514 100644 --- a/src/privsep-control.c +++ b/src/privsep-control.c @@ -37,6 +37,9 @@ #include "logerr.h" #include "privsep.h" +#define PS_CTL_FD(ctx) (ctx)->ps_ctl->psp_fd +#define PS_CTL_FLAGS_PRIV ~(~0UL >> 1) + /* We expect to have open 2 privsep STREAM, 2 STREAM and 2 file STREAM fds */ static int @@ -93,12 +96,6 @@ ps_ctl_handleargs(struct fd_list *fd, char *data, size_t len) fd->flags |= FD_LISTEN; return 0; } - - if (fd->ctx->ps_control_client != NULL && - fd->ctx->ps_control_client != fd) { - logerrx("%s: cannot handle another client", __func__); - return 0; - } return 1; } @@ -106,41 +103,24 @@ static ssize_t ps_ctl_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg) { struct dhcpcd_ctx *ctx = arg; - struct iovec *iov = msg->msg_iov; struct fd_list *fd; unsigned int fd_flags = FD_SENDLEN; int err; - switch (psm->ps_flags) { - case PS_CTL_PRIV: - break; - case PS_CTL_UNPRIV: - fd_flags |= FD_UNPRIV; - break; - } - switch (psm->ps_cmd) { + case PS_CTL_PRIV: + fd_flags |= FD_PRIV; /* FALLTHROUGH */ case PS_CTL: if (msg->msg_iovlen != 1) { errno = EINVAL; return -1; } - if (ctx->ps_control_client != NULL) { - logerrx("%s: cannot handle another client", __func__); - return 0; - } fd = control_new(ctx, ctx->ps_ctl->psp_work_fd, fd_flags); if (fd == NULL) return -1; - ctx->ps_control_client = fd; - err = control_recvdata(fd, iov->iov_base, iov->iov_len); - if (err == -1 || err == 0) { + err = control_recvmsg(fd, msg, psm->ps_datalen); + if (err == -1 || err == 0) control_free(fd); - ctx->ps_control_client = NULL; - } - break; - case PS_CTL_EOF: - ctx->ps_control_client = NULL; break; default: errno = ENOTSUP; @@ -162,29 +142,34 @@ ps_ctl_dodispatch(void *arg, unsigned short events) static void ps_ctl_recv(void *arg, unsigned short events) { - struct dhcpcd_ctx *ctx = arg; char buf[BUFSIZ]; + struct dhcpcd_ctx *ctx = arg; ssize_t len; + struct fd_list *fd; - if (!(events & (ELE_READ | ELE_HANGUP))) + if (events & ELE_HANGUP) { + hangup: + eloop_exit(ctx->eloop, EXIT_SUCCESS); + return; + } + + if (!(events & ELE_READ)) { logerrx("%s: unexpected event 0x%04x", __func__, events); + return; + } - if (events & ELE_READ) { - len = read(ctx->ps_ctl->psp_work_fd, buf, sizeof(buf)); - if (len == -1) - logerr("%s: read", __func__); - else if (len == 0) - // FIXME: Why does this happen? - ; - else if (ctx->ps_control_client == NULL) - logerrx("%s: clientfd #%d disconnected (len=%zd)", - __func__, ctx->ps_ctl->psp_work_fd, len); - else { - errno = 0; - if (control_queue(ctx->ps_control_client, buf, - (size_t)len) == -1) - logerr("%s: control_queue", __func__); - } + len = read(ctx->ps_ctl->psp_work_fd, buf, sizeof(buf)); + if (len == -1) { + logerr("%s: read", __func__); + return; + } else if (len == 0) + goto hangup; + + TAILQ_FOREACH(fd, &ctx->control_fds, next) { + if (fd->flags & FD_LISTEN) + continue; + if (control_queue(fd, buf, (size_t)len) == -1) + logerr("%s: control_queue", __func__); } } @@ -196,6 +181,12 @@ ps_ctl_listen(void *arg, unsigned short events) ssize_t len; struct fd_list *fd; + if (events & ELE_HANGUP) { + hangup: + eloop_exit(ctx->eloop, EXIT_SUCCESS); + return; + } + if (!(events & ELE_READ)) logerrx("%s: unexpected event 0x%04x", __func__, events); @@ -205,10 +196,12 @@ ps_ctl_listen(void *arg, unsigned short events) eloop_exit(ctx->eloop, EXIT_FAILURE); return; } + if (len == 0) + goto hangup; /* Send to our listeners */ TAILQ_FOREACH(fd, &ctx->control_fds, next) { - if (!(fd->flags & FD_LISTEN)) + if (fd == ctx->ps_control || !(fd->flags & FD_LISTEN)) continue; if (control_queue(fd, buf, (size_t)len) == -1) logerr("%s: control_queue", __func__); @@ -263,7 +256,7 @@ ps_ctl_start(struct dhcpcd_ctx *ctx) ctx) == -1) return -1; - ctx->ps_control = control_new(ctx, listen_fd[1], 0); + ctx->ps_control = control_new(ctx, listen_fd[1], FD_LISTEN); if (ctx->ps_control == NULL) return -1; if (eloop_event_add(ctx->eloop, ctx->ps_control->fd, ELE_READ, @@ -281,21 +274,11 @@ ps_ctl_stop(struct dhcpcd_ctx *ctx) } ssize_t -ps_ctl_sendargs(struct fd_list *fd, void *data, size_t len) -{ - struct dhcpcd_ctx *ctx = fd->ctx; - - if (ctx->ps_control_client != NULL && ctx->ps_control_client != fd) - logerrx("%s: cannot deal with another client", __func__); - ctx->ps_control_client = fd; - return ps_sendcmd(ctx, ctx->ps_ctl->psp_fd, PS_CTL, - fd->flags & FD_UNPRIV ? PS_CTL_UNPRIV : PS_CTL_PRIV, data, len); -} - -ssize_t -ps_ctl_sendeof(struct fd_list *fd) +ps_ctl_sendmsg(struct fd_list *fd, const struct msghdr *msg) { struct dhcpcd_ctx *ctx = fd->ctx; + uint16_t cmd = fd->flags & FD_PRIV ? PS_CTL_PRIV : PS_CTL; + unsigned long flags = (unsigned long)fd->fd; - return ps_sendcmd(ctx, ctx->ps_ctl->psp_fd, PS_CTL_EOF, 0, NULL, 0); + return ps_sendmsg(ctx, PS_CTL_FD(ctx), cmd, flags, msg); } diff --git a/src/privsep-control.h b/src/privsep-control.h index ef2a76d6..08b75862 100644 --- a/src/privsep-control.h +++ b/src/privsep-control.h @@ -35,7 +35,7 @@ pid_t ps_ctl_start(struct dhcpcd_ctx *); int ps_ctl_stop(struct dhcpcd_ctx *); ssize_t ps_ctl_handleargs(struct fd_list *, char *, size_t); -ssize_t ps_ctl_sendargs(struct fd_list *, void *, size_t); -ssize_t ps_ctl_sendeof(struct fd_list *fd); +ssize_t ps_ctl_sendmsg(struct fd_list *, const struct msghdr *); +ssize_t ps_ctl_sendeof(struct dhcpcd_ctx *); #endif diff --git a/src/privsep-root.c b/src/privsep-root.c index 497ca893..755b3b43 100644 --- a/src/privsep-root.c +++ b/src/privsep-root.c @@ -320,6 +320,26 @@ ps_root_dowritefile(const struct dhcpcd_ctx *ctx, mode_t mode, void *data, return writefile(file, mode, nc, len - flen); } +static ssize_t +ps_root_douser_ispriv(struct dhcpcd_ctx *ctx, void *data, size_t len) +{ + uid_t uid; + gid_t gid; + uint8_t *p = data; + + if (len != sizeof(uid) + sizeof(gid)) { + errno = EINVAL; + return -1; + } + + p = data; + memcpy(&uid, p, sizeof(uid)); + p += sizeof(uid); + memcpy(&gid, p, sizeof(gid)); + + return control_user_ispriv(ctx, uid, gid); +} + #ifdef AUTH static ssize_t ps_root_monordm(uint64_t *rdm, size_t len) @@ -563,6 +583,9 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg) case PS_LOGREOPEN: err = logopen(ctx->logfile); break; + case PS_USER_ISPRIV: + err = ps_root_douser_ispriv(ctx, data, len); + break; #ifdef AUTH case PS_AUTH_MONORDM: err = ps_root_monordm(data, len); @@ -1119,6 +1142,29 @@ ps_root_logreopen(struct dhcpcd_ctx *ctx) return ps_root_readerror(ctx, NULL, 0); } +ssize_t +ps_root_user_ispriv(struct dhcpcd_ctx *ctx, uid_t uid, gid_t gid) +{ + struct iovec iov[] = { { + .iov_base = &uid, + .iov_len = sizeof(uid), + }, + { + .iov_base = &gid, + .iov_len = sizeof(gid), + } }; + struct msghdr msg = { + .msg_iov = iov, + .msg_iovlen = __arraycount(iov), + }; + + if (ps_sendmsg(ctx, PS_ROOT_FD(ctx), PS_USER_ISPRIV, 0, &msg) == -1) { + logerr(__func__); + return -1; + } + return ps_root_readerror(ctx, NULL, 0); +} + #ifdef PRIVSEP_GETHOSTNAME int ps_root_gethostname(struct dhcpcd_ctx *ctx, char *hname, size_t hnamelen) diff --git a/src/privsep-root.h b/src/privsep-root.h index 58fe8da8..af5fad95 100644 --- a/src/privsep-root.h +++ b/src/privsep-root.h @@ -53,6 +53,7 @@ ssize_t ps_root_logreopen(struct dhcpcd_ctx *); ssize_t ps_root_script(struct dhcpcd_ctx *, const void *, size_t); ssize_t ps_root_stopprocesses(struct dhcpcd_ctx *); int ps_root_getauthrdm(struct dhcpcd_ctx *, uint64_t *); +ssize_t ps_root_user_ispriv(struct dhcpcd_ctx *, uid_t, gid_t); #ifdef PRIVSEP_GETHOSTNAME int ps_root_gethostname(struct dhcpcd_ctx *, char *, size_t); #endif diff --git a/src/privsep.c b/src/privsep.c index a0806994..65955007 100644 --- a/src/privsep.c +++ b/src/privsep.c @@ -358,11 +358,13 @@ ps_startprocess(struct ps_process *psp, /* Close things we no longer need */ pidfile_unlock(); - eloop_closefdwaiter(ctx->eloop); + if (ctx->ps_ctl != psp) + eloop_closefdwaiter(ctx->eloop); /* Close more if we are not root */ if (ctx->ps_root != psp) { - ps_root_close(ctx); + if (ctx->ps_ctl != psp) + ps_root_close(ctx); #ifdef PLUGIN_DEV dev_stop(ctx); #endif @@ -391,7 +393,6 @@ ps_startprocess(struct ps_process *psp, if (ctx->ps_root != psp) { ctx->options &= ~DHCPCD_PRIVSEPROOT; - ctx->ps_root = NULL; if (ctx->ps_log_root_fd != -1) { /* Already removed from eloop thanks to above clear. */ close(ctx->ps_log_root_fd); @@ -1185,6 +1186,9 @@ ps_freeprocesses(struct dhcpcd_ctx *ctx, struct ps_process *notthis) TAILQ_FOREACH_SAFE(psp, &ctx->ps_processes, next, psn) { if (psp == notthis) continue; + /* control needs root access to work out user group */ + if (ctx->ps_ctl == notthis && psp == ctx->ps_root) + continue; ps_freeprocess(psp); } } diff --git a/src/privsep.h b/src/privsep.h index 8c6ec054..20e59170 100644 --- a/src/privsep.h +++ b/src/privsep.h @@ -52,10 +52,11 @@ #define PS_FILEMTIME 0x0016 #define PS_AUTH_MONORDM 0x0017 #define PS_CTL 0x0018 -#define PS_CTL_EOF 0x0019 +#define PS_CTL_PRIV 0x0019 #define PS_LOGREOPEN 0x0020 #define PS_STOPPROCS 0x0021 #define PS_DAEMONISED 0x0022 +#define PS_USER_ISPRIV 0x0023 /* Domains */ #define PS_ROOT 0x0101 @@ -82,10 +83,6 @@ #define PS_DEV_IFREMOVED 0x0002 #define PS_DEV_IFUPDATED 0x0003 -/* Control Type (via flags) */ -#define PS_CTL_PRIV 0x0004 -#define PS_CTL_UNPRIV 0x0005 - /* Sysctl Needs (via flags) */ #define PS_SYSCTL_OLEN 0x0001 #define PS_SYSCTL_ODATA 0x0002 From 3554f56523dff642a90bb4cda21c18cbee7a519e Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Sun, 5 Jul 2026 16:01:13 +0100 Subject: [PATCH 02/14] Fix compile on Linux --- src/control.c | 31 ++++++++++++++++++++++++------- src/privsep-control.c | 1 - src/privsep-root.c | 2 +- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/control.c b/src/control.c index 09aebc04..960ac4c3 100644 --- a/src/control.c +++ b/src/control.c @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -54,6 +55,10 @@ (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) #endif +#define SUN_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) + +#define LISTEN_BACKLOG 5 + static void control_handle_data(void *, unsigned short); static void @@ -94,6 +99,22 @@ control_hangup(struct fd_list *fd) control_free(fd); } +#ifdef SO_PEERCRED +static int +getpeereid(int fd, uid_t *uid, gid_t *gid) +{ + struct ucred creds; + socklen_t creds_len = sizeof(creds); + + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &creds_len) == -1) + return -1; + + *uid = creds.uid; + *gid = creds.gid; + return 0; +} +#endif + static int control_handle_read(struct fd_list *fd) { @@ -431,8 +452,6 @@ make_sock(struct sockaddr_un *sa, const char *ifname, sa_family_t family) return fd; } -#define SOCK_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) - static int control_start1(struct dhcpcd_ctx *ctx, const char *ifname, sa_family_t family) { @@ -447,10 +466,8 @@ control_start1(struct dhcpcd_ctx *ctx, const char *ifname, sa_family_t family) len = (socklen_t)SUN_LEN(&sa); unlink(sa.sun_path); if (bind(fd, (struct sockaddr *)&sa, len) == -1 || - chmod(sa.sun_path, 0666) == -1 || - (ctx->control_group && - chown(sa.sun_path, geteuid(), ctx->control_group) == -1) || - listen(fd, sizeof(ctx->control_fds)) == -1) + chmod(sa.sun_path, SUN_MODE) == -1 || + listen(fd, LISTEN_BACKLOG) == -1) goto err; #ifdef PRIVSEP_RIGHTS @@ -647,7 +664,7 @@ control_user_ispriv(struct dhcpcd_ctx *ctx, uid_t uid, gid_t gid) pw = getpwuid(uid); if (pw == NULL) - return -1; + return 0; /* unknown user is not privileged */ groups = reallocarray(groups, (size_t)ngroups, sizeof(*groups)); if (groups == NULL) diff --git a/src/privsep-control.c b/src/privsep-control.c index 8c2fa514..3ace3ad3 100644 --- a/src/privsep-control.c +++ b/src/privsep-control.c @@ -38,7 +38,6 @@ #include "privsep.h" #define PS_CTL_FD(ctx) (ctx)->ps_ctl->psp_fd -#define PS_CTL_FLAGS_PRIV ~(~0UL >> 1) /* We expect to have open 2 privsep STREAM, 2 STREAM and 2 file STREAM fds */ diff --git a/src/privsep-root.c b/src/privsep-root.c index 755b3b43..dec46b0d 100644 --- a/src/privsep-root.c +++ b/src/privsep-root.c @@ -325,7 +325,7 @@ ps_root_douser_ispriv(struct dhcpcd_ctx *ctx, void *data, size_t len) { uid_t uid; gid_t gid; - uint8_t *p = data; + uint8_t *p; if (len != sizeof(uid) + sizeof(gid)) { errno = EINVAL; From 86997131e5b596a0e73d5cee09881f8536751d38 Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Sun, 5 Jul 2026 16:05:25 +0100 Subject: [PATCH 03/14] Add compat for Ilumos --- src/control.c | 20 +++++++++++++++++++- src/privsep-control.c | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/control.c b/src/control.c index 960ac4c3..8afc4f62 100644 --- a/src/control.c +++ b/src/control.c @@ -41,6 +41,10 @@ #include #include +#if defined(__sun) +#include +#endif + #include "common.h" #include "config.h" #include "control.h" @@ -55,7 +59,7 @@ (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) #endif -#define SUN_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) +#define SUN_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) #define LISTEN_BACKLOG 5 @@ -113,6 +117,20 @@ getpeereid(int fd, uid_t *uid, gid_t *gid) *gid = creds.gid; return 0; } +#elif defined(__sun) +static int +getpeereid(int fd, uid_t *uid, gid_t *gid) +{ + ucred_t *ucred = NULL; + + if (getpeerucred(fd, &ucred) == -1) + return -1; + + *uid = ucred_geteuid(ucred); + *gid_t gid = ucred_getegid(ucred); + ucred_free(ucred); + return 0; +} #endif static int diff --git a/src/privsep-control.c b/src/privsep-control.c index 3ace3ad3..cb27de1b 100644 --- a/src/privsep-control.c +++ b/src/privsep-control.c @@ -37,7 +37,7 @@ #include "logerr.h" #include "privsep.h" -#define PS_CTL_FD(ctx) (ctx)->ps_ctl->psp_fd +#define PS_CTL_FD(ctx) (ctx)->ps_ctl->psp_fd /* We expect to have open 2 privsep STREAM, 2 STREAM and 2 file STREAM fds */ From 90b239e1e847b71e5fca84d805edbcebcfa4a3ac Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Tue, 14 Jul 2026 15:01:47 +0100 Subject: [PATCH 04/14] Fix the control socket for dhcpcd-ui Add --isprivileged to return either true or false so the caller can work this out as we now just have the one socket. Bump version for this. --- src/control.c | 38 +++++++++------- src/control.h | 7 +-- src/defs.h | 2 +- src/dhcpcd.c | 43 ++++++++++-------- src/privsep-control.c | 100 +++++++++++++++++++++++++++++------------- 5 files changed, 120 insertions(+), 70 deletions(-) diff --git a/src/control.c b/src/control.c index 8afc4f62..473d6f37 100644 --- a/src/control.c +++ b/src/control.c @@ -133,7 +133,7 @@ getpeereid(int fd, uid_t *uid, gid_t *gid) } #endif -static int +static ssize_t control_handle_read(struct fd_list *fd) { uid_t uid; @@ -153,7 +153,7 @@ control_handle_read(struct fd_list *fd) if (bytes == -1) logerr(__func__); if (bytes == -1 || bytes == 0) - return (int)bytes; + return bytes; if (getpeereid(fd->fd, &uid, &gid) == -1) { logerr("%s: getpeereid", __func__); @@ -177,12 +177,14 @@ control_handle_read(struct fd_list *fd) fd->flags |= FD_SENDLEN; perr = ps_ctl_handleargs(fd, buf, (size_t)bytes); fd->flags &= ~FD_SENDLEN; + if (!(fd->flags & FD_LISTEN)) + fd->flags |= FD_COMMAND; if (perr == -1) { logerr(__func__); return 0; } iov[0].iov_len = (size_t)bytes; - if (perr == 1 && ps_ctl_sendmsg(fd, &msg) == -1) { + if (perr == 0 && ps_ctl_sendmsg(fd, &msg) == -1) { logerr(__func__); return -1; } @@ -202,28 +204,30 @@ control_handle_read(struct fd_list *fd) return control_recvmsg(fd, &msg, (size_t)bytes); } -static int +static ssize_t control_handle_write(struct fd_list *fd) { struct iovec iov[2]; - int iov_len; + struct msghdr msg = { .msg_iov = iov }; struct fd_data *data; + ssize_t len; data = TAILQ_FIRST(&fd->queue); if (data->data_flags & FD_SENDLEN) { iov[0].iov_base = &data->data_len; - iov[0].iov_len = sizeof(size_t); + iov[0].iov_len = sizeof(data->data_len); iov[1].iov_base = data->data; iov[1].iov_len = data->data_len; - iov_len = 2; + msg.msg_iovlen = 2; } else { iov[0].iov_base = data->data; iov[0].iov_len = data->data_len; - iov_len = 1; + msg.msg_iovlen = 1; } - if (writev(fd->fd, iov, iov_len) == -1) { + len = sendmsg(fd->fd, &msg, 0); + if (len == -1) { if (errno != EPIPE && errno != ENOTCONN) { // We don't get ELE_HANGUP for some reason logerr("%s: write", __func__); @@ -242,20 +246,23 @@ control_handle_write(struct fd_list *fd) #endif if (TAILQ_FIRST(&fd->queue) != NULL) - return 0; + return len; /* Done sending data, stop watching write to fd */ if (eloop_event_add(fd->ctx->eloop, fd->fd, ELE_READ, control_handle_data, fd) == -1) logerr("%s: eloop_event_add", __func__); - return 0; + return len; } static void control_handle_data(void *arg, unsigned short events) { struct fd_list *fd = arg; - int err; + ssize_t err; + + if (events & ELE_HANGUP) + goto hangup; if (!(events & (ELE_READ | ELE_WRITE | ELE_HANGUP))) logerrx("%s: unexpected event 0x%04x", __func__, events); @@ -270,8 +277,6 @@ control_handle_data(void *arg, unsigned short events) if ((err == -1 && errno != EPERM) || err == 0) goto hangup; } - if (events & ELE_HANGUP) - goto hangup; return; @@ -339,6 +344,8 @@ control_recvmsg(struct fd_list *fd, struct msghdr *msg, size_t len) if (errno != EINTR && errno != EAGAIN && errno != EPERM) return -1; } + if (!(fd->flags & FD_LISTEN)) + fd->flags |= FD_COMMAND; } return 1; @@ -412,7 +419,6 @@ control_handle1(struct dhcpcd_ctx *ctx, int lfd, unsigned int fd_flags, else #endif fd_flags |= FD_SENDLEN; - l = control_new(ctx, fd, fd_flags); if (l == NULL) goto error; @@ -618,7 +624,7 @@ control_send(struct dhcpcd_ctx *ctx, int argc, char *const *argv) } int -control_queue(struct fd_list *fd, void *data, size_t data_len) +control_queue(struct fd_list *fd, const void *data, size_t data_len) { struct fd_data *d; unsigned short events; diff --git a/src/control.h b/src/control.h index 85086025..fd8d64b7 100644 --- a/src/control.h +++ b/src/control.h @@ -66,8 +66,9 @@ struct fd_list { TAILQ_HEAD(fd_list_head, fd_list); #define FD_LISTEN 0x01U -#define FD_PRIV 0x02U -#define FD_SENDLEN 0x04U +#define FD_COMMAND 0x02U +#define FD_PRIV 0x04U +#define FD_SENDLEN 0x08U int control_start(struct dhcpcd_ctx *, const char *, sa_family_t); int control_stop(struct dhcpcd_ctx *); @@ -77,7 +78,7 @@ struct fd_list *control_find(struct dhcpcd_ctx *, int); struct fd_list *control_new(struct dhcpcd_ctx *, int, unsigned int); void control_free(struct fd_list *); void control_delete(struct fd_list *); -int control_queue(struct fd_list *, void *, size_t); +int control_queue(struct fd_list *, const void *, size_t); int control_recvmsg(struct fd_list *, struct msghdr *, size_t); int control_user_ispriv(struct dhcpcd_ctx *ctx, uid_t uid, gid_t gid); #endif diff --git a/src/defs.h b/src/defs.h index d21a8fae..61f80579 100644 --- a/src/defs.h +++ b/src/defs.h @@ -29,7 +29,7 @@ #define DEFS_H #define PACKAGE "dhcpcd" -#define VERSION "10.3.2" +#define VERSION "10.5.0" #ifndef PRIVSEP_USER #define PRIVSEP_USER "_" PACKAGE diff --git a/src/dhcpcd.c b/src/dhcpcd.c index f45c4f38..d3b2bbea 100644 --- a/src/dhcpcd.c +++ b/src/dhcpcd.c @@ -1587,24 +1587,6 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd, int argc, size_t len, l, nifaces; char *tmp, *p; - /* Special commands for our control socket - * as the other end should be blocking until it gets the - * expected reply we should be safely able just to change the - * write callback on the fd */ - /* Make any change here in privsep-control.c as well. */ - if (strcmp(*argv, "--version") == 0) { - return control_queue(fd, UNCONST(VERSION), strlen(VERSION) + 1); - } else if (strcmp(*argv, "--getconfigfile") == 0) { - return control_queue(fd, UNCONST(fd->ctx->cffile), - strlen(fd->ctx->cffile) + 1); - } else if (strcmp(*argv, "--getinterfaces") == 0) { - oifind = argc = 0; - goto dumplease; - } else if (strcmp(*argv, "--listen") == 0) { - fd->flags |= FD_LISTEN; - return 0; - } - /* Log the command */ len = 1; for (opt = 0; opt < argc; opt++) @@ -1624,6 +1606,27 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd, int argc, loginfox("control command: %s", tmp); free(tmp); + /* Special commands for our control socket + * as the other end should be blocking until it gets the + * expected reply we should be safely able just to change the + * write callback on the fd */ + /* Make any change here in privsep-control.c as well. */ + if (strcmp(*argv, "--version") == 0) { + return control_queue(fd, VERSION, strlen(VERSION) + 1); + } else if (strcmp(*argv, "--getconfigfile") == 0) { + return control_queue(fd, fd->ctx->cffile, + strlen(fd->ctx->cffile) + 1); + } else if (strcmp(*argv, "--getinterfaces") == 0) { + oifind = argc = 0; + goto dumplease; + } else if (strcmp(*argv, "--isprivileged") == 0) { + const char *ret = fd->flags & FD_PRIV ? "true" : "false"; + return control_queue(fd, ret, strlen(ret) + 1); + } else if (strcmp(*argv, "--listen") == 0) { + fd->flags |= FD_LISTEN; + return 0; + } + optind = 0; oi = 0; opts = 0; @@ -1682,8 +1685,10 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd, int argc, nifaces += (size_t)opt; } } - if (write(fd->fd, &nifaces, sizeof(nifaces)) != sizeof(nifaces)) + fd->flags &= ~FD_SENDLEN; + if (control_queue(fd, &nifaces, sizeof(nifaces)) == -1) goto dumperr; + fd->flags |= FD_SENDLEN; TAILQ_FOREACH(ifp, ctx->ifaces, next) { if (!ifp->active) continue; diff --git a/src/privsep-control.c b/src/privsep-control.c index cb27de1b..468270b8 100644 --- a/src/privsep-control.c +++ b/src/privsep-control.c @@ -83,19 +83,26 @@ ps_ctl_recvmsg(void *arg, unsigned short events) ssize_t ps_ctl_handleargs(struct fd_list *fd, char *data, size_t len) { + int err; + /* Make any change here in dhcpcd.c as well. */ if (strncmp(data, "--version", MIN(strlen("--version"), len)) == 0) { - return control_queue(fd, UNCONST(VERSION), strlen(VERSION) + 1); + err = control_queue(fd, UNCONST(VERSION), strlen(VERSION) + 1); + return err == -1 ? -1 : 1; } else if (strncmp(data, "--getconfigfile", MIN(strlen("--getconfigfile"), len)) == 0) { - return control_queue(fd, UNCONST(fd->ctx->cffile), + err = control_queue(fd, UNCONST(fd->ctx->cffile), strlen(fd->ctx->cffile) + 1); + return err == -1 ? -1 : 1; + } else if (strcmp(data, "--isprivileged") == 0) { + const char *ret = fd->flags & FD_PRIV ? "true" : "false"; + return control_queue(fd, ret, strlen(ret) + 1); } else if (strncmp(data, "--listen", MIN(strlen("--listen"), len)) == 0) { fd->flags |= FD_LISTEN; - return 0; + return 1; } - return 1; + return 0; } static ssize_t @@ -141,10 +148,19 @@ ps_ctl_dodispatch(void *arg, unsigned short events) static void ps_ctl_recv(void *arg, unsigned short events) { - char buf[BUFSIZ]; struct dhcpcd_ctx *ctx = arg; - ssize_t len; - struct fd_list *fd; + char buf[BUFSIZ]; + struct iovec iov[] = { { + .iov_base = buf, + .iov_len = sizeof(buf), + } }; + struct msghdr msg = { + .msg_iov = iov, + .msg_iovlen = __arraycount(iov), + }; + ssize_t msglen; + int fd; + struct fd_list *fdl; if (events & ELE_HANGUP) { hangup: @@ -152,22 +168,23 @@ ps_ctl_recv(void *arg, unsigned short events) return; } - if (!(events & ELE_READ)) { + if (!(events & ELE_READ)) logerrx("%s: unexpected event 0x%04x", __func__, events); - return; - } - len = read(ctx->ps_ctl->psp_work_fd, buf, sizeof(buf)); - if (len == -1) { - logerr("%s: read", __func__); - return; - } else if (len == 0) + fd = ctx->ps_ctl->psp_work_fd; + msglen = recvmsg(fd, &msg, 0); + if (msglen == 0) goto hangup; + if (msglen == -1) { + logerr("%s: read", __func__); + eloop_exit(ctx->eloop, EXIT_FAILURE); + } - TAILQ_FOREACH(fd, &ctx->control_fds, next) { - if (fd->flags & FD_LISTEN) + /* Send to our command controls */ + TAILQ_FOREACH(fdl, &ctx->control_fds, next) { + if (!(fdl->flags & FD_COMMAND)) continue; - if (control_queue(fd, buf, (size_t)len) == -1) + if (control_queue(fdl, buf, (size_t)msglen) == -1) logerr("%s: control_queue", __func__); } } @@ -176,9 +193,10 @@ static void ps_ctl_listen(void *arg, unsigned short events) { struct dhcpcd_ctx *ctx = arg; - char buf[BUFSIZ]; ssize_t len; - struct fd_list *fd; + size_t msglen; + int fd; + struct fd_list *fdl; if (events & ELE_HANGUP) { hangup: @@ -189,22 +207,40 @@ ps_ctl_listen(void *arg, unsigned short events) if (!(events & ELE_READ)) logerrx("%s: unexpected event 0x%04x", __func__, events); - len = read(ctx->ps_control->fd, buf, sizeof(buf)); - if (len == -1) { - logerr("%s: read", __func__); - eloop_exit(ctx->eloop, EXIT_FAILURE); - return; + fd = ctx->ps_control->fd; + len = read(fd, &msglen, sizeof(msglen)); + if (len == 0) + goto hangup; + if (len != sizeof(msglen)) { + logerr("%s: read len", __func__); + goto err; } + + if (ps_bufalloc(ctx, msglen) == -1) { + logerr("%s: realloc", __func__); + goto err; + } + + len = read(fd, ctx->ps_buf, msglen); if (len == 0) goto hangup; + if ((size_t)len != msglen) { + logerr("%s: read", __func__); + goto err; + } /* Send to our listeners */ - TAILQ_FOREACH(fd, &ctx->control_fds, next) { - if (fd == ctx->ps_control || !(fd->flags & FD_LISTEN)) + TAILQ_FOREACH(fdl, &ctx->control_fds, next) { + if (!(fdl->flags & FD_LISTEN)) continue; - if (control_queue(fd, buf, (size_t)len) == -1) + if (control_queue(fdl, ctx->ps_buf, msglen) == -1) logerr("%s: control_queue", __func__); } + + return; + +err: + eloop_exit(ctx->eloop, EXIT_FAILURE); } pid_t @@ -220,8 +256,10 @@ ps_ctl_start(struct dhcpcd_ctx *ctx) if_closesockets(ctx); - if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, work_fd) == -1 || - xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, listen_fd) == -1) + if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, work_fd) == + -1 || + xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, listen_fd) == + -1) return -1; #ifdef PRIVSEP_RIGHTS if (ps_rights_limit_fdpair(work_fd) == -1 || @@ -255,7 +293,7 @@ ps_ctl_start(struct dhcpcd_ctx *ctx) ctx) == -1) return -1; - ctx->ps_control = control_new(ctx, listen_fd[1], FD_LISTEN); + ctx->ps_control = control_new(ctx, listen_fd[1], 0); if (ctx->ps_control == NULL) return -1; if (eloop_event_add(ctx->eloop, ctx->ps_control->fd, ELE_READ, From 56e36fb5107f8972296dba6261a1a4de4924194b Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Tue, 14 Jul 2026 15:25:08 +0100 Subject: [PATCH 05/14] Bound the data length --- src/privsep-control.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/privsep-control.c b/src/privsep-control.c index 468270b8..4df347e8 100644 --- a/src/privsep-control.c +++ b/src/privsep-control.c @@ -94,7 +94,8 @@ ps_ctl_handleargs(struct fd_list *fd, char *data, size_t len) err = control_queue(fd, UNCONST(fd->ctx->cffile), strlen(fd->ctx->cffile) + 1); return err == -1 ? -1 : 1; - } else if (strcmp(data, "--isprivileged") == 0) { + } else if (strncmp(data, "--isprivileged", + MIN(strlen("--isprivileged"), len)) == 0) { const char *ret = fd->flags & FD_PRIV ? "true" : "false"; return control_queue(fd, ret, strlen(ret) + 1); } else if (strncmp(data, "--listen", MIN(strlen("--listen"), len)) == From e9496930e404eecd2e30adada9c59770d1ef9c4f Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Tue, 14 Jul 2026 15:35:33 +0100 Subject: [PATCH 06/14] recvmsg WAITALL --- src/dhcpcd.c | 38 +++++++++++++++++++------------------- src/privsep-control.c | 18 +++++++++++++----- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/dhcpcd.c b/src/dhcpcd.c index d3b2bbea..fb9a45c8 100644 --- a/src/dhcpcd.c +++ b/src/dhcpcd.c @@ -1587,25 +1587,6 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd, int argc, size_t len, l, nifaces; char *tmp, *p; - /* Log the command */ - len = 1; - for (opt = 0; opt < argc; opt++) - len += strlen(argv[opt]) + 1; - tmp = malloc(len); - if (tmp == NULL) - return -1; - p = tmp; - for (opt = 0; opt < argc; opt++) { - l = strlen(argv[opt]); - strlcpy(p, argv[opt], len); - len -= l + 1; - p += l; - *p++ = ' '; - } - *--p = '\0'; - loginfox("control command: %s", tmp); - free(tmp); - /* Special commands for our control socket * as the other end should be blocking until it gets the * expected reply we should be safely able just to change the @@ -1627,6 +1608,25 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd, int argc, return 0; } + /* Log the command */ + len = 1; + for (opt = 0; opt < argc; opt++) + len += strlen(argv[opt]) + 1; + tmp = malloc(len); + if (tmp == NULL) + return -1; + p = tmp; + for (opt = 0; opt < argc; opt++) { + l = strlen(argv[opt]); + strlcpy(p, argv[opt], len); + len -= l + 1; + p += l; + *p++ = ' '; + } + *--p = '\0'; + loginfox("control command: %s", tmp); + free(tmp); + optind = 0; oi = 0; opts = 0; diff --git a/src/privsep-control.c b/src/privsep-control.c index 4df347e8..d143bdbf 100644 --- a/src/privsep-control.c +++ b/src/privsep-control.c @@ -177,7 +177,7 @@ ps_ctl_recv(void *arg, unsigned short events) if (msglen == 0) goto hangup; if (msglen == -1) { - logerr("%s: read", __func__); + logerr("%s: recvmsg", __func__); eloop_exit(ctx->eloop, EXIT_FAILURE); } @@ -196,6 +196,12 @@ ps_ctl_listen(void *arg, unsigned short events) struct dhcpcd_ctx *ctx = arg; ssize_t len; size_t msglen; + struct iovec iov[] = { + { .iov_base = &msglen, .iov_len = sizeof(msglen), } + }; + struct msghdr msg = { + .msg_iov = iov, .msg_iovlen = __arraycount(iov), + }; int fd; struct fd_list *fdl; @@ -209,11 +215,11 @@ ps_ctl_listen(void *arg, unsigned short events) logerrx("%s: unexpected event 0x%04x", __func__, events); fd = ctx->ps_control->fd; - len = read(fd, &msglen, sizeof(msglen)); + len = recvmsg(fd, &msg, MSG_WAITALL); if (len == 0) goto hangup; if (len != sizeof(msglen)) { - logerr("%s: read len", __func__); + logerr("%s: recvmsg len %zd", __func__, len); goto err; } @@ -222,11 +228,13 @@ ps_ctl_listen(void *arg, unsigned short events) goto err; } - len = read(fd, ctx->ps_buf, msglen); + iov->iov_base = ctx->ps_buf; + iov->iov_len = msglen; + len = recvmsg(fd, &msg, MSG_WAITALL); if (len == 0) goto hangup; if ((size_t)len != msglen) { - logerr("%s: read", __func__); + logerr("%s: recvmsg", __func__); goto err; } From f8366075687be847d36749dfac27e0447c66ebd7 Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Tue, 14 Jul 2026 15:37:19 +0100 Subject: [PATCH 07/14] Format --- src/privsep-control.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/privsep-control.c b/src/privsep-control.c index d143bdbf..68c55c87 100644 --- a/src/privsep-control.c +++ b/src/privsep-control.c @@ -196,11 +196,13 @@ ps_ctl_listen(void *arg, unsigned short events) struct dhcpcd_ctx *ctx = arg; ssize_t len; size_t msglen; - struct iovec iov[] = { - { .iov_base = &msglen, .iov_len = sizeof(msglen), } - }; + struct iovec iov[] = { { + .iov_base = &msglen, + .iov_len = sizeof(msglen), + } }; struct msghdr msg = { - .msg_iov = iov, .msg_iovlen = __arraycount(iov), + .msg_iov = iov, + .msg_iovlen = __arraycount(iov), }; int fd; struct fd_list *fdl; From ec2065bc86a1b0aa3c976d3288caa6f5f8461c83 Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Wed, 15 Jul 2026 00:02:51 +0100 Subject: [PATCH 08/14] Fix compile on IllumOS --- src/control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/control.c b/src/control.c index 473d6f37..547b4bef 100644 --- a/src/control.c +++ b/src/control.c @@ -127,7 +127,7 @@ getpeereid(int fd, uid_t *uid, gid_t *gid) return -1; *uid = ucred_geteuid(ucred); - *gid_t gid = ucred_getegid(ucred); + *gid = ucred_getegid(ucred); ucred_free(ucred); return 0; } From 77331decbef31b4147da667965d429132569a64f Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Wed, 15 Jul 2026 14:47:08 +0100 Subject: [PATCH 09/14] compat: add getpeereid.c and move compat out of control.c --- compat/getpeereid.c | 66 +++++++++++++++++++++++++++++++++++++++++++++ compat/getpeereid.h | 36 +++++++++++++++++++++++++ configure | 22 +++++++++++++++ src/control.c | 34 ----------------------- 4 files changed, 124 insertions(+), 34 deletions(-) create mode 100644 compat/getpeereid.c create mode 100644 compat/getpeereid.h diff --git a/compat/getpeereid.c b/compat/getpeereid.c new file mode 100644 index 00000000..8f38b7b2 --- /dev/null +++ b/compat/getpeereid.c @@ -0,0 +1,66 @@ +/* + * compat: getpeereid + * SPDX-License-Identifier: BSD-2-Clause + * Copyright (c) 2006-2026 Roy Marples + * All rights reserved + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#ifdef __sun +#include +#endif + +int +getpeereid(int fd, uid_t *uid, gid_t *gid) +{ +#if defined(SO_PEERCRED) +#if defined(__OpenBSD__) + struct sockpeercred creds; +#else + struct ucred creds; +#endif + socklen_t creds_len = sizeof(creds); + + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &creds_len) == -1) + return -1; + + *uid = creds.uid; + *gid = creds.gid; +#elif defined(__sun) + ucred_t *ucred = NULL; + + if (getpeerucred(fd, &ucred) == -1) + return -1; + + *uid = ucred_geteuid(ucred); + *gid = ucred_getegid(ucred); + ucred_free(ucred); +#else +#error OS has no getpeereid support! +#endif + + return 0; +} diff --git a/compat/getpeereid.h b/compat/getpeereid.h new file mode 100644 index 00000000..c8949dc6 --- /dev/null +++ b/compat/getpeereid.h @@ -0,0 +1,36 @@ +/* + * compat: getpeereid + * SPDX-License-Identifier: BSD-2-Clause + * Copyright (c) 2006-2026 Roy Marples + * All rights reserved + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef GETPEEREID_H +#define GETPEEREID_H + +#include + +int getpeereid(int, uid_t *, gid_t *); + +#endif diff --git a/configure b/configure index b246fd3e..3c56eda7 100755 --- a/configure +++ b/configure @@ -938,6 +938,28 @@ if [ "$ARC4RANDOM_UNIFORM" = no ]; then echo "#include \"compat/arc4random_uniform.h\"" >>$CONFIG_H fi +if [ -z "$GETPEEREID" ]; then + printf "Testing for getpeereid ... " + cat <_getpeereid.c +#include +#include +int main(void) { + return getpeereid(0, NULL, NULL); +} +EOF + if $XCC _getpeereid.c -o _getpeereid 2>&3; then + GETPEEREID=yes + else + GETPEEREID=no + fi + echo "$GETPEEREID" + rm -rf _getpeereid.* _getpeereid +fi +if [ "$GETPEEREID" = no ]; then + echo "COMPAT_SRCS+= compat/getpeereid.c" >>$CONFIG_MK + echo "#include \"compat/getpeereid.h\"" >>$CONFIG_H +fi + if [ -z "$MEMSET_EXPLICIT" ]; then printf "Testing for memset_explicit ... " cat <_memset_explicit.c diff --git a/src/control.c b/src/control.c index 547b4bef..d4ff3a71 100644 --- a/src/control.c +++ b/src/control.c @@ -41,10 +41,6 @@ #include #include -#if defined(__sun) -#include -#endif - #include "common.h" #include "config.h" #include "control.h" @@ -103,36 +99,6 @@ control_hangup(struct fd_list *fd) control_free(fd); } -#ifdef SO_PEERCRED -static int -getpeereid(int fd, uid_t *uid, gid_t *gid) -{ - struct ucred creds; - socklen_t creds_len = sizeof(creds); - - if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &creds_len) == -1) - return -1; - - *uid = creds.uid; - *gid = creds.gid; - return 0; -} -#elif defined(__sun) -static int -getpeereid(int fd, uid_t *uid, gid_t *gid) -{ - ucred_t *ucred = NULL; - - if (getpeerucred(fd, &ucred) == -1) - return -1; - - *uid = ucred_geteuid(ucred); - *gid = ucred_getegid(ucred); - ucred_free(ucred); - return 0; -} -#endif - static ssize_t control_handle_read(struct fd_list *fd) { From e57871189e1804d4e0fec362f3a54c31455e91e1 Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Wed, 15 Jul 2026 14:56:32 +0100 Subject: [PATCH 10/14] Fix compile --- compat/getpeereid.c | 4 ---- configure | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/compat/getpeereid.c b/compat/getpeereid.c index 8f38b7b2..dc2bfa02 100644 --- a/compat/getpeereid.c +++ b/compat/getpeereid.c @@ -37,11 +37,7 @@ int getpeereid(int fd, uid_t *uid, gid_t *gid) { #if defined(SO_PEERCRED) -#if defined(__OpenBSD__) - struct sockpeercred creds; -#else struct ucred creds; -#endif socklen_t creds_len = sizeof(creds); if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &creds_len) == -1) diff --git a/configure b/configure index 3c56eda7..32bf96ef 100755 --- a/configure +++ b/configure @@ -943,6 +943,7 @@ if [ -z "$GETPEEREID" ]; then cat <_getpeereid.c #include #include +#include int main(void) { return getpeereid(0, NULL, NULL); } From 71f802862b840cae708361b3088ede171c3ad7f2 Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Wed, 15 Jul 2026 15:00:01 +0100 Subject: [PATCH 11/14] Fix compat --- compat/getpeereid.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compat/getpeereid.c b/compat/getpeereid.c index dc2bfa02..404f8ab9 100644 --- a/compat/getpeereid.c +++ b/compat/getpeereid.c @@ -33,6 +33,8 @@ #include #endif +#include "getpeereid.h" + int getpeereid(int fd, uid_t *uid, gid_t *gid) { From 5f6020c53f9fd7eadbf48cba9af59a366f280504 Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Wed, 15 Jul 2026 16:06:48 +0100 Subject: [PATCH 12/14] eloop: fix a compile issue on Dragonfly while here --- src/eloop.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/eloop.c b/src/eloop.c index 912742a5..3880388b 100644 --- a/src/eloop.c +++ b/src/eloop.c @@ -236,7 +236,6 @@ eloop_event_count(const struct eloop *eloop) static int eloop_signal_kqueue(struct eloop *eloop, const int *signals, size_t nsignals) { - unsigned int cmd = nsignals == 0 ? EV_DELETE : EV_ADD; struct kevent *ke, *kep; size_t i; int err; @@ -253,8 +252,8 @@ eloop_signal_kqueue(struct eloop *eloop, const int *signals, size_t nsignals) return -1; for (i = 0; i < nsignals; i++) - EV_SET(kep++, (uintptr_t)signals[i], EVFILT_SIGNAL, cmd, 0, 0, - NULL); + EV_SET(kep++, (uintptr_t)signals[i], EVFILT_SIGNAL, + nsignals == 0 ? EV_DELETE : EV_ADD, 0, 0, NULL); err = kevent(eloop->fd, ke, (KEVENT_N)nsignals, NULL, 0, NULL); free(ke); From 422109a43d00bfe1a513822f18a448e5c2961d14 Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Wed, 15 Jul 2026 16:25:13 +0100 Subject: [PATCH 13/14] Apple: remove compile warnings --- src/control.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/control.c b/src/control.c index d4ff3a71..233a7446 100644 --- a/src/control.c +++ b/src/control.c @@ -648,7 +648,14 @@ control_queue(struct fd_list *fd, const void *data, size_t data_len) int control_user_ispriv(struct dhcpcd_ctx *ctx, uid_t uid, gid_t gid) { +#ifdef __APPLE__ + /* why is getgrouplist API is different ..... */ + int *groups = NULL, *gp; +#define GID (int) +#else gid_t *groups = NULL, *gp; +#define GID +#endif struct passwd *pw; int ngroups = 10, err = -1; @@ -660,17 +667,17 @@ control_user_ispriv(struct dhcpcd_ctx *ctx, uid_t uid, gid_t gid) if (groups == NULL) return -1; - if (getgrouplist(pw->pw_name, gid, groups, &ngroups) == -1) { + if (getgrouplist(pw->pw_name, GID gid, groups, &ngroups) == -1) { gp = reallocarray(groups, (size_t)ngroups, sizeof(*groups)); if (gp == NULL) goto out; groups = gp; - if (getgrouplist(pw->pw_name, gid, groups, &ngroups) == -1) + if (getgrouplist(pw->pw_name, GID gid, groups, &ngroups) == -1) goto out; } for (gp = groups; ngroups != 0; ngroups--, gp++) { - if (*gp == ctx->control_group) { + if (*gp == GID ctx->control_group) { err = 1; goto out; } From aa61e1b58d9699e238e5094888f7160ee00e7cd9 Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Wed, 15 Jul 2026 16:59:24 +0100 Subject: [PATCH 14/14] Fix eloop better --- src/eloop.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/eloop.c b/src/eloop.c index 3880388b..70090551 100644 --- a/src/eloop.c +++ b/src/eloop.c @@ -236,6 +236,7 @@ eloop_event_count(const struct eloop *eloop) static int eloop_signal_kqueue(struct eloop *eloop, const int *signals, size_t nsignals) { + unsigned short cmd = nsignals == 0 ? EV_DELETE : EV_ADD; struct kevent *ke, *kep; size_t i; int err; @@ -252,8 +253,8 @@ eloop_signal_kqueue(struct eloop *eloop, const int *signals, size_t nsignals) return -1; for (i = 0; i < nsignals; i++) - EV_SET(kep++, (uintptr_t)signals[i], EVFILT_SIGNAL, - nsignals == 0 ? EV_DELETE : EV_ADD, 0, 0, NULL); + EV_SET(kep++, (uintptr_t)signals[i], EVFILT_SIGNAL, cmd, 0, 0, + NULL); err = kevent(eloop->fd, ke, (KEVENT_N)nsignals, NULL, 0, NULL); free(ke);