]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
control: remove unprivileged socket master
authorRoy Marples <roy@marples.name>
Mon, 3 Aug 2026 12:38:54 +0000 (13:38 +0100)
committerGitHub <noreply@github.com>
Mon, 3 Aug 2026 12:38:54 +0000 (13:38 +0100)
Instead workout credentials from the connected socket
by getpeereid(2).
Remove the limit of only one connected control client.
Add readgroup, to so we can deny reading of dhcpcd interface
data to everyone on the system.

Always send back a result code to socket callers. This is a breaking ABI change, but allows dhcpcd-online, dhcpcd-gtk etc to display EPERM for example.

20 files changed:
compat/getpeereid.c [new file with mode: 0644]
compat/getpeereid.h [new file with mode: 0644]
configure
src/control.c
src/control.h
src/defs.h
src/dhcpcd.8.in
src/dhcpcd.c
src/dhcpcd.conf.5.in
src/dhcpcd.h
src/eloop.c
src/if-options.c
src/if-options.h
src/privsep-control.c
src/privsep-control.h
src/privsep-root.c
src/privsep-root.h
src/privsep.c
src/privsep.h
src/script.c

diff --git a/compat/getpeereid.c b/compat/getpeereid.c
new file mode 100644 (file)
index 0000000..404f8ab
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * compat: getpeereid
+ * SPDX-License-Identifier: BSD-2-Clause
+ * Copyright (c) 2006-2026 Roy Marples <roy@marples.name>
+ * 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 <sys/types.h>
+#include <sys/socket.h>
+
+#ifdef __sun
+#include <ucred.h>
+#endif
+
+#include "getpeereid.h"
+
+int
+getpeereid(int fd, uid_t *uid, gid_t *gid)
+{
+#if defined(SO_PEERCRED)
+       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;
+#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 (file)
index 0000000..c8949dc
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * compat: getpeereid
+ * SPDX-License-Identifier: BSD-2-Clause
+ * Copyright (c) 2006-2026 Roy Marples <roy@marples.name>
+ * 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 <sys/types.h>
+
+int getpeereid(int, uid_t *, gid_t *);
+
+#endif
index b246fd3ea9c6a359743c43a8988d1eabe1c8d8a5..32bf96ef9b39651fd5eeaa824b624b30776ccd44 100755 (executable)
--- a/configure
+++ b/configure
@@ -938,6 +938,29 @@ if [ "$ARC4RANDOM_UNIFORM" = no ]; then
        echo "#include                  \"compat/arc4random_uniform.h\"" >>$CONFIG_H
 fi
 
+if [ -z "$GETPEEREID" ]; then
+       printf "Testing for getpeereid ... "
+       cat <<EOF >_getpeereid.c
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+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 <<EOF >_memset_explicit.c
index ad006c203bf2ada0a2353f67a5291eaa5c749e19..6ed7dd16557465ee7355fa40beecc0d865846842 100644 (file)
@@ -33,6 +33,8 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
        (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
@@ -80,11 +86,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 +96,77 @@ 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
+static ssize_t
 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, in_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;
+               return bytes;
+
+       if (getpeereid(fd->fd, &uid, &gid) == -1) {
+               logerr("%s: getpeereid", __func__);
+               return -1;
+       }
 
 #ifdef PRIVSEP
        if (IN_PRIVSEP(fd->ctx)) {
-               ssize_t err;
-
-               fd->flags |= FD_SENDLEN;
-               err = ps_ctl_handleargs(fd, buffer, (size_t)bytes);
-               fd->flags &= ~FD_SENDLEN;
+               in_gid = fd->ctx->control_group;
+               if (uid == 0)
+                       err = 1;
+               else
+                       err = ps_root_user_ingroup(fd->ctx, uid, gid, in_gid);
+               if (err == -1) {
+                       logerr(__func__);
+                       return -1;
+               }
+               if (err == 0) {
+                       fd->flags &= ~FD_CONTROL;
+                       in_gid = fd->ctx->read_group;
+                       err = ps_root_user_ingroup(fd->ctx, uid, gid, in_gid);
+                       if (err == -1) {
+                               logerr(__func__);
+                               return -1;
+                       }
+                       if (err == 0)
+                               fd->flags &= ~FD_READ;
+                       else
+                               fd->flags |= FD_READ;
+               } else
+                       fd->flags |= FD_CONTROL | FD_READ;
+
+               err = ps_ctl_handleargs(fd, buf, (size_t)bytes);
                if (err == -1) {
                        logerr(__func__);
                        return 0;
                }
-               if (err == 1 &&
-                   ps_ctl_sendargs(fd, buffer, (size_t)bytes) == -1) {
+
+               if (fd->flags & FD_LISTEN)
+                       fd->flags &= ~FD_COMMAND;
+               else
+                       fd->flags |= FD_COMMAND;
+
+               iov[0].iov_len = (size_t)bytes;
+               /* If ps_ctl_handleargs returns 0 that means it didn't
+                * do anything with the command, so pass it to the manager. */
+               if (err == 0 && ps_ctl_sendmsg(fd, &msg) == -1) {
                        logerr(__func__);
                        return -1;
                }
@@ -136,31 +174,71 @@ control_handle_read(struct fd_list *fd)
        }
 #endif
 
-       return control_recvdata(fd, buffer, (size_t)bytes);
+       in_gid = fd->ctx->control_group;
+       err = control_user_ingroup(uid, gid, in_gid);
+       if (err == -1) {
+               logerr(__func__);
+               return -1;
+       }
+       if (err == 0) {
+               fd->flags &= ~FD_CONTROL;
+               in_gid = fd->ctx->read_group;
+               err = control_user_ingroup(uid, gid, in_gid);
+               if (err == -1) {
+                       logerr(__func__);
+                       return -1;
+               }
+               if (err == 0)
+                       fd->flags &= ~FD_READ;
+               else
+                       fd->flags |= FD_READ;
+       } else
+               fd->flags |= FD_CONTROL | FD_READ;
+       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 iovec iov[4];
+       struct msghdr msg = { .msg_iov = iov };
        struct fd_data *data;
+       ssize_t len;
+#ifdef PRIVSEP
+       size_t peer_len;
+#endif
 
        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[1].iov_base = data->data;
-               iov[1].iov_len = data->data_len;
-               iov_len = 2;
-       } else {
-               iov[0].iov_base = data->data;
-               iov[0].iov_len = data->data_len;
-               iov_len = 1;
+#ifdef PRIVSEP
+       if (data->data_peer_id != 0) {
+               /* Control messages for a peer are prefixed with it's fd
+                * fd and message length. */
+               iov[msg.msg_iovlen].iov_base = &data->data_peer_id;
+               iov[msg.msg_iovlen].iov_len = sizeof(data->data_peer_id);
+               msg.msg_iovlen++;
+
+               peer_len = data->data_len;
+               if (data->data_flags & FD_DATA_SENDLEN)
+                       peer_len += sizeof(data->data_len);
+               iov[msg.msg_iovlen].iov_base = &peer_len;
+               iov[msg.msg_iovlen].iov_len = sizeof(peer_len);
+               msg.msg_iovlen++;
+       }
+#endif
+
+       if (data->data_flags & FD_DATA_SENDLEN) {
+               iov[msg.msg_iovlen].iov_base = &data->data_len;
+               iov[msg.msg_iovlen].iov_len = sizeof(data->data_len);
+               msg.msg_iovlen++;
        }
 
-       if (writev(fd->fd, iov, iov_len) == -1) {
+       iov[msg.msg_iovlen].iov_base = data->data;
+       iov[msg.msg_iovlen].iov_len = data->data_len;
+       msg.msg_iovlen++;
+
+       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__);
@@ -179,27 +257,23 @@ control_handle_write(struct fd_list *fd)
 #endif
 
        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
+               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);
@@ -211,11 +285,9 @@ 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)
-               goto hangup;
 
        return;
 
@@ -224,12 +296,21 @@ hangup:
 }
 
 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) {
@@ -270,19 +351,64 @@ 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)
+                       logerr("%s: dhcpcd_handleargs", __func__);
+                       if (errno != EINTR && errno != EAGAIN && errno != EPERM)
                                return -1;
                }
+               if (!(fd->flags & FD_LISTEN))
+                       fd->flags |= FD_COMMAND;
        }
 
        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;
+       size_t cnt;
+#ifdef PRIVSEP
+       struct fd_list *n;
+       unsigned int id;
+#endif
+
+       l = control_find(ctx, fd);
+       if (l != NULL) {
+               l->flags = flags;
+               return l;
+       }
+
+#ifdef PRIVSEP
+again:
+       id = ++ctx->ps_control_id;
+       if (id == 0) /* wrapped */
+               id = ++ctx->ps_control_id;
+#endif
+       cnt = 0;
+       TAILQ_FOREACH(n, &ctx->control_fds, next) {
+               if (++cnt >= CONTROL_PEER_MAX) {
+                       errno = ENOBUFS;
+                       return NULL;
+               }
+#ifdef PRIVSEP
+               if (n->id == id)
+                       goto again;
+#endif
+       }
 
        l = malloc(sizeof(*l));
        if (l == NULL)
@@ -294,6 +420,10 @@ control_new(struct dhcpcd_ctx *ctx, int fd, unsigned int flags)
        TAILQ_INIT(&l->queue);
 #ifdef CTL_FREE_LIST
        TAILQ_INIT(&l->free_queue);
+#endif
+#ifdef PRIVSEP
+       l->peer_id = 0;
+       l->id = id;
 #endif
        TAILQ_INSERT_TAIL(&ctx->control_fds, l, next);
        return l;
@@ -306,7 +436,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);
@@ -321,13 +451,6 @@ control_handle1(struct dhcpcd_ctx *ctx, int lfd, unsigned int fd_flags,
            fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
                goto error;
 
-#ifdef PRIVSEP
-       if (IN_PRIVSEP(ctx) && !IN_PRIVSEP_SE(ctx))
-               ;
-       else
-#endif
-               fd_flags |= FD_SENDLEN;
-
        l = control_new(ctx, fd, fd_flags);
        if (l == NULL)
                goto error;
@@ -335,6 +458,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 +475,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 +491,53 @@ 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)
-
 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 ||
-           (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;
-       }
+           chmod(sa.sun_path, SUN_MODE) == -1 ||
+           listen(fd, LISTEN_BACKLOG) == -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 +548,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 +561,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 +597,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 +610,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);
@@ -577,8 +655,9 @@ control_send(struct dhcpcd_ctx *ctx, int argc, char *const *argv)
        return write(ctx->control_fd, buffer, len);
 }
 
-int
-control_queue(struct fd_list *fd, void *data, size_t data_len)
+ssize_t
+control_queuef(struct fd_list *fd, const void *data, size_t data_len,
+    unsigned int flags)
 {
        struct fd_data *d;
        unsigned short events;
@@ -623,12 +702,83 @@ control_queue(struct fd_list *fd, void *data, size_t data_len)
        }
        memcpy(d->data, data, data_len);
        d->data_len = data_len;
-       d->data_flags = fd->flags & FD_SENDLEN;
+       d->data_flags = flags;
+#ifdef PRIVSEP
+       d->data_peer_id = fd->peer_id;
+#endif
 
        TAILQ_INSERT_TAIL(&fd->queue, d, next);
        events = ELE_WRITE;
        if (fd->flags & FD_LISTEN)
                events |= ELE_READ;
-       return eloop_event_add(fd->ctx->eloop, fd->fd, events,
-           control_handle_data, fd);
+       if (eloop_event_add(fd->ctx->eloop, fd->fd, events, control_handle_data,
+               fd) == -1)
+               return -1;
+       return (ssize_t)d->data_len;
+}
+
+ssize_t
+control_queue(struct fd_list *fd, const void *data, size_t data_len)
+{
+       return control_queuef(fd, data, data_len, FD_DATA_SENDLEN);
+}
+
+int
+control_user_ingroup(uid_t uid, gid_t gid, gid_t grpid)
+{
+#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;
+
+       pw = getpwuid(uid);
+       if (pw == NULL)
+               return 0; /* unknown user is not privileged */
+
+       groups = reallocarray(groups, (size_t)ngroups, sizeof(*groups));
+       if (groups == NULL)
+               return -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 gid, groups, &ngroups) == -1)
+                       goto out;
+       }
+
+       for (gp = groups; ngroups != 0; ngroups--, gp++) {
+               if (*gp == GID grpid) {
+                       err = 1;
+                       goto out;
+               }
+       }
+       err = 0;
+
+out:
+       free(groups);
+       return err;
+}
+
+ssize_t
+control_handle_listen(struct fd_list *fd)
+{
+       int err;
+
+       if (fd->flags & FD_READ) {
+               err = 0;
+               if (!(fd->flags & FD_COMMAND))
+                       fd->flags |= FD_LISTEN;
+       } else {
+               err = errno = EPERM;
+               logwarn(__func__);
+       }
+       return control_queuef(fd, &err, sizeof(err), 0);
 }
index 9e966f7766a105c57d362b709028fc5432ca1e05..0ffdc9a0a47ede4634704ed1735cb48b630f14a7 100644 (file)
@@ -41,6 +41,9 @@
 #undef CTL_FREE_LIST
 #endif
 
+/* Maximum number of control socket users */
+#define CONTROL_PEER_MAX 10
+
 /* Limit queue size per fd */
 #define CONTROL_QUEUE_MAX 100
 
@@ -50,6 +53,10 @@ struct fd_data {
        size_t data_size;
        size_t data_len;
        unsigned int data_flags;
+#define FD_DATA_SENDLEN 0x01
+#ifdef PRIVSEP
+       unsigned int data_peer_id;
+#endif
 };
 TAILQ_HEAD(fd_data_head, fd_data);
 
@@ -62,20 +69,29 @@ struct fd_list {
 #ifdef CTL_FREE_LIST
        struct fd_data_head free_queue;
 #endif
+#ifdef PRIVSEP
+       unsigned int id;
+       unsigned int peer_id;
+#endif
 };
 TAILQ_HEAD(fd_list_head, fd_list);
 
-#define FD_LISTEN  0x01U
-#define FD_UNPRIV  0x02U
-#define FD_SENDLEN 0x04U
+#define FD_READ           0x01U
+#define FD_CONTROL 0x02U
+#define FD_COMMAND 0x04U
+#define FD_LISTEN  0x08U
 
 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);
+ssize_t control_queuef(struct fd_list *, const void *, size_t, unsigned int);
+ssize_t control_queue(struct fd_list *, const void *, size_t);
+int control_recvmsg(struct fd_list *, struct msghdr *, size_t);
+int control_user_ingroup(uid_t, gid_t, gid_t);
+ssize_t control_handle_listen(struct fd_list *);
 #endif
index d21a8faeb5b0ec09d57040b2d95a810f054b6810..61f8057986dc36c1375c4b557b39d34a0f4061b0 100644 (file)
@@ -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
index 86b69132c4f07d953d4c2f2a9bfcecccba5d34fa..26d37772da0dc9a2eb6077490ec4219956c8d3c6 100644 (file)
@@ -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 ,
index 36ee0e3bdd6fd5181d271e622bd485747c341349..2e8119cb18bc3cc8819321c7a54d7ba4e9b9b5a8 100644 (file)
@@ -1575,34 +1575,40 @@ dhcpcd_signal_cb(int sig, void *arg)
 }
 #endif
 
-int
+ssize_t
 dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd, int argc,
     char **argv)
 {
        struct interface *ifp;
        struct if_options *ifo;
-       unsigned long long opts, orig_opts;
-       int opt, oi, oifind, af = AF_UNSPEC;
-       bool do_reboot, do_renew;
+       unsigned long long opts = 0, orig_opts;
+       int opt, oi, oifind, af = AF_UNSPEC, error;
+       bool do_getifs = false, do_reboot = false, do_renew = false;
        size_t len, l, nifaces;
        char *tmp, *p;
 
+       optind = 0;
+
        /* 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);
+               return control_queue(fd, VERSION, strlen(VERSION) + 1);
        } else if (strcmp(*argv, "--getconfigfile") == 0) {
-               return control_queue(fd, UNCONST(fd->ctx->cffile),
+               return control_queue(fd, fd->ctx->cffile,
                    strlen(fd->ctx->cffile) + 1);
        } else if (strcmp(*argv, "--getinterfaces") == 0) {
                oifind = argc = 0;
-               goto dumplease;
+               opts |= DHCPCD_DUMPLEASE;
+               do_getifs = true;
+               goto doauth;
+       } else if (strcmp(*argv, "--isprivileged") == 0) {
+               const char *ret = fd->flags & FD_CONTROL ? "true" : "false";
+               return control_queue(fd, ret, strlen(ret) + 1);
        } else if (strcmp(*argv, "--listen") == 0) {
-               fd->flags |= FD_LISTEN;
-               return 0;
+               return control_handle_listen(fd);
        }
 
        /* Log the command */
@@ -1624,9 +1630,7 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd, int argc,
        loginfox("control command: %s", tmp);
        free(tmp);
 
-       optind = 0;
        oi = 0;
-       opts = 0;
        do_reboot = do_renew = false;
        while (
            (opt = getopt_long(argc, argv, IF_OPTS, cf_options, &oi)) != -1) {
@@ -1661,12 +1665,36 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd, int argc,
                }
        }
 
-       /* store the index; the optind will change when a getopt get called */
+       /* If we are not dumping the lease we must be able to control
+        * dhcpcd */
+doauth:
+       if (opts & DHCPCD_DUMPLEASE) {
+               if (fd->flags & FD_READ)
+                       error = 0;
+               else
+                       error = EPERM;
+       } else {
+               if (fd->flags & FD_CONTROL)
+                       error = 0;
+               else
+                       error = EPERM;
+       }
+
+       /* store the index; the optind will change when a getopt get
+        * called */
        oifind = optind;
 
        if (opts & DHCPCD_DUMPLEASE) {
-               ctx->options |= DHCPCD_DUMPLEASE;
-       dumplease:
+               if (control_queuef(fd, &error, sizeof(error), 0) == -1) {
+                       logerr("%s: control_queuef", __func__);
+                       return -1;
+               }
+               if (error != 0) {
+                       errno = error;
+                       return -1;
+               }
+               if (!do_getifs)
+                       ctx->options |= DHCPCD_DUMPLEASE;
                nifaces = 0;
                TAILQ_FOREACH(ifp, ctx->ifaces, next) {
                        if (!ifp->active)
@@ -1677,13 +1705,21 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd, int argc,
                        }
                        if (oifind == argc || oi < argc) {
                                opt = send_interface(NULL, ifp, af);
-                               if (opt == -1)
-                                       goto dumperr;
+                               if (opt == -1) {
+                                       nifaces = 0;
+                                       error = errno;
+                                       goto send_nifs;
+                               }
                                nifaces += (size_t)opt;
                        }
                }
-               if (write(fd->fd, &nifaces, sizeof(nifaces)) != sizeof(nifaces))
+       send_nifs:
+               if (control_queuef(fd, &nifaces, sizeof(nifaces), 0) == -1)
+                       goto dumperr;
+               if (error != 0) {
+                       errno = error;
                        goto dumperr;
+               }
                TAILQ_FOREACH(ifp, ctx->ifaces, next) {
                        if (!ifp->active)
                                continue;
@@ -1700,23 +1736,21 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd, int argc,
                return 0;
        dumperr:
                ctx->options &= ~DHCPCD_DUMPLEASE;
-               return -1;
+               error = errno;
+               goto out;
        }
 
-       /* Only privileged users can control dhcpcd via the socket. */
-       if (fd->flags & FD_UNPRIV) {
-               errno = EPERM;
-               return -1;
-       }
+       if (error != 0)
+               goto out;
 
        if (opts & (DHCPCD_EXITING | DHCPCD_RELEASE)) {
                if (oifind == argc && af == AF_UNSPEC) {
                        ctx->options |= DHCPCD_EXITING;
                        if (stop_all_interfaces(ctx, opts) == false)
                                eloop_exit(ctx->eloop, EXIT_SUCCESS);
-                       /* We did stop an interface, it will notify us once
-                        * dropped so we can exit. */
-                       return 0;
+                       /* We did stop an interface, it will notify us
+                        * once dropped so we can exit. */
+                       goto out;
                }
 
                TAILQ_FOREACH(ifp, ctx->ifaces, next) {
@@ -1748,24 +1782,34 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd, int argc,
                                stop_interface(ifp);
                        ifo->options = orig_opts;
                }
-               return 0;
+               goto out;
        }
 
        if (do_renew) {
                if (oifind == argc) {
                        dhcpcd_renew(ctx);
-                       return 0;
+                       goto out;
                }
                for (oi = oifind; oi < argc; oi++) {
                        if ((ifp = if_find(ctx->ifaces, argv[oi])) == NULL)
                                continue;
                        dhcpcd_ifrenew(ifp);
                }
-               return 0;
+               goto out;
        }
 
        reload_config(ctx);
        reconf_reboot(ctx, do_reboot, argc, argv, oifind);
+
+out:
+       if (control_queuef(fd, &error, sizeof(error), 0) == -1) {
+               logerr("%s: control_queuef", __func__);
+               error = errno;
+       }
+       if (error != 0) {
+               errno = error;
+               return -1;
+       }
        return 0;
 }
 
@@ -1847,6 +1891,23 @@ err:
        eloop_exit(ctx->eloop, EXIT_FAILURE);
 }
 
+static int
+dhcpcd_readerror(struct dhcpcd_ctx *ctx)
+{
+       int error = 0;
+       ssize_t len;
+
+       len = read(ctx->control_fd, &error, sizeof(error));
+       if (len == -1)
+               return -1;
+       if (len != sizeof(error)) {
+               errno = EINVAL;
+               return -1;
+       }
+       errno = error;
+       return error;
+}
+
 static void
 dhcpcd_readdump0(void *arg, unsigned short events)
 {
@@ -2078,7 +2139,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;
@@ -2266,8 +2327,8 @@ main(int argc, char **argv, char **envp)
 
        if (!(ctx.options & (DHCPCD_TEST | DHCPCD_DUMPLEASE))) {
        printpidfile:
-               /* If we have any other args, we should run as a single dhcpcd
-                *  instance for that interface. */
+               /* If we have any other args, we should run as a single
+                * dhcpcd instance for that interface. */
                if (optind == argc - 1 && !(ctx.options & DHCPCD_MANAGER)) {
                        const char *per;
                        const char *ifname;
@@ -2302,8 +2363,8 @@ main(int argc, char **argv, char **envp)
                        ctx.options |= DHCPCD_MANAGER;
 
                        /*
-                        * If we are given any interfaces or a family, we
-                        * cannot send a signal as that would impact
+                        * If we are given any interfaces or a family,
+                        * we cannot send a signal as that would impact
                         * other interfaces.
                         */
                        if (optind != argc || family != AF_UNSPEC)
@@ -2421,20 +2482,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);
@@ -2444,6 +2497,10 @@ main(int argc, char **argv, char **envp)
                                logerr("%s: control_send", __func__);
                                goto exit_failure;
                        }
+                       if (dhcpcd_readerror(&ctx) != 0) {
+                               logerr("dhcpcd_control_read");
+                               goto exit_failure;
+                       }
                        if (ctx.options & DHCPCD_DUMPLEASE) {
                                if (dhcpcd_readdump(&ctx) == -1) {
                                        logerr("%s: dhcpcd_readdump", __func__);
@@ -2490,7 +2547,7 @@ main(int argc, char **argv, char **envp)
 
        loginfox(PACKAGE "-" VERSION " starting");
 
-       // We don't need stdin past this point
+       /* We don't need stdin past this point */
        dup_null(STDIN_FILENO);
 
 #if defined(USE_SIGNALS) && !defined(THERE_IS_NO_FORK)
@@ -2580,6 +2637,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);
@@ -2672,8 +2731,8 @@ start_manager:
                goto exit_failure;
 #endif
 
-       /* When running dhcpcd against a single interface, we need to retain
-        * the old behaviour of waiting for an IP address */
+       /* When running dhcpcd against a single interface, we need to
+        * retain the old behaviour of waiting for an IP address */
        if (ctx.ifc == 1 && !(ctx.options & DHCPCD_BACKGROUND))
                ctx.options |= DHCPCD_WAITIP;
 
@@ -2751,7 +2810,8 @@ start_manager:
                        logmessage(loglevel, "no interfaces have a carrier");
                        dhcpcd_daemonise(&ctx);
                } else if (t > 0 &&
-                   /* Test mode removes the daemonise bit, so check for both */
+                   /* Test mode removes the daemonise bit, so check for
+                      both */
                    ctx.options & (DHCPCD_DAEMONISE | DHCPCD_TEST)) {
                        eloop_timeout_add_sec(ctx.eloop, t, handle_exit_timeout,
                            &ctx);
index 82425b1c720878d96bba331f9517dc623ab31573..b7d596e901ff34d60cea9e83ef510472e6bd756c 100644 (file)
@@ -23,7 +23,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd June 29, 2026
+.Dd July 29, 2026
 .Dt DHCPCD.CONF 5
 .Os
 .Sh NAME
@@ -162,10 +162,10 @@ In most cases,
 .Nm dhcpcd
 will set this automatically.
 .It Ic controlgroup Ar group
-Sets the group ownership of
-.Pa @RUNDIR@/sock
-so that users other than root can connect to
-.Nm dhcpcd .
+Members of this user group can control dhcpcd, including but not limited to
+stopping and starting interfaces.
+.It Ic readgroup Ar group
+Members of this user group can read dhcpcd interface configuration. 
 .It Ic debug
 Echo debug messages to the stderr and syslog.
 .It Ic dev Ar value
index cb2a93030f59506c6b98d37df1ebb6fc13f22baf..f097c95f3b83f391e74b51a1363240326d20304a 100644 (file)
@@ -174,10 +174,9 @@ 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 read_group;
        gid_t control_group;
 
        /* DHCP Enterprise options, RFC3925 */
@@ -199,13 +198,13 @@ 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 */
+       unsigned int ps_control_id; /* Unique monotonic control ID */
 #endif
 
 #ifdef INET
@@ -267,7 +266,7 @@ void dhcpcd_daemonised(struct dhcpcd_ctx *);
 void dhcpcd_daemonise(struct dhcpcd_ctx *);
 
 void dhcpcd_linkoverflow(struct dhcpcd_ctx *);
-int dhcpcd_handleargs(struct dhcpcd_ctx *, struct fd_list *, int, char **);
+ssize_t dhcpcd_handleargs(struct dhcpcd_ctx *, struct fd_list *, int, char **);
 void dhcpcd_handlecarrier(struct interface *, int, unsigned int);
 int dhcpcd_handleinterface(void *, int, const char *);
 void dhcpcd_handlehwaddr(struct interface *, uint16_t, const void *, uint8_t);
index 912742a5f988b13213014f8b32448380c3120025..700905519d9494b1e5d3bffa3e5598d4751f1f60 100644 (file)
@@ -236,7 +236,7 @@ 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;
+       unsigned short cmd = nsignals == 0 ? EV_DELETE : EV_ADD;
        struct kevent *ke, *kep;
        size_t i;
        int err;
index e584a2d562e3aecec3ffd77f31c60a05030b0a5f..df8e34861ffc2dcf8ec0e891bd56228e9b99203a 100644 (file)
@@ -158,6 +158,7 @@ const struct option cf_options[] = { { "background", no_argument, NULL, 'b' },
        { "dhcp6", no_argument, NULL, O_DHCP6 },
        { "nodhcp6", no_argument, NULL, O_NODHCP6 },
        { "controlgroup", required_argument, NULL, O_CONTROLGRP },
+       { "readgroup", required_argument, NULL, O_READGRP },
        { "slaac", required_argument, NULL, O_SLAAC },
        { "gateway", no_argument, NULL, O_GATEWAY },
        { "reject", required_argument, NULL, O_REJECT },
@@ -2431,6 +2432,7 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
                ifo->options &= ~DHCPCD_DHCP6;
                break;
        case O_CONTROLGRP:
+       case O_READGRP:
                ARG_REQUIRED;
 #ifdef PRIVSEP
                /* Control group is already set by this point.
@@ -2472,21 +2474,33 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
                        return -1;
                }
                if (grp == NULL) {
-                       if (!ctx->control_group)
-                               logerrx("controlgroup: %s: not found", arg);
+                       logerrx("group: %s: not found", arg);
                        free(p);
                        return -1;
                }
-               ctx->control_group = grp->gr_gid;
+               switch (opt) {
+               case O_CONTROLGRP:
+                       ctx->control_group = grp->gr_gid;
+                       break;
+               case O_READGRP:
+                       ctx->read_group = grp->gr_gid;
+                       break;
+               }
                free(p);
 #else
                grp = getgrnam(arg);
                if (grp == NULL) {
-                       if (!ctx->control_group)
-                               logerrx("controlgroup: %s: not found", arg);
+                       logerrx("group: %s: not found", arg);
                        return -1;
                }
-               ctx->control_group = grp->gr_gid;
+               switch (opt) {
+               case O_CONTROLGRP:
+                       ctx->control_group = grp->gr_gid;
+                       break;
+               case O_READGRP:
+                       ctx->read_group = grp->gr_gid;
+                       break;
+               }
 #endif
                break;
        case O_GATEWAY:
index 288426cea6b3e138f6a386deef22ee119806d26f..0158b9ae9f26af1e9fd3d944199eb7f4093e29fa 100644 (file)
 #define O_BACKOFF_CUTOFF     O_BASE + 61
 #define O_BACKOFF_JITTER     O_BASE + 62
 #define O_ALLOW                     O_BASE + 63
+#define O_READGRP           O_BASE + 64
 
 extern const struct option cf_options[];
 
index 3737d07dbd3f3eab42fd36afd00467eadfd217fb..f94d16614ce906f5af830abd69ec111fc037a32e 100644 (file)
@@ -37,6 +37,8 @@
 #include "logerr.h"
 #include "privsep.h"
 
+#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 */
 
 static int
@@ -79,68 +81,54 @@ ps_ctl_recvmsg(void *arg, unsigned short events)
 }
 
 ssize_t
-ps_ctl_handleargs(struct fd_list *fd, char *data, size_t len)
+ps_ctl_handleargs(struct fd_list *fd, const char *data, size_t len)
 {
-       /* 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);
-       } else if (strncmp(data, "--getconfigfile",
-                      MIN(strlen("--getconfigfile"), len)) == 0) {
-               return control_queue(fd, UNCONST(fd->ctx->cffile),
+#define strclcmp(d, l, c) \
+       ((l) == (__arraycount((c))) ? strncmp((d), (c), (l)) : -1)
+
+       /* Make any change here in dhcpcd.c as well.
+        * --version is NOT terminated with \n. */
+       if (strclcmp(data, len, "--version") == 0)
+               return control_queue(fd, VERSION, strlen(VERSION) + 1);
+       else if (strclcmp(data, len, "--getconfigfile\n") == 0)
+               return control_queue(fd, fd->ctx->cffile,
                    strlen(fd->ctx->cffile) + 1);
-       } else if (strncmp(data, "--listen", MIN(strlen("--listen"), len)) ==
-           0) {
-               fd->flags |= FD_LISTEN;
-               return 0;
-       }
+       else if (strclcmp(data, len, "--isprivileged\n") == 0) {
+               const char *ret = fd->flags & FD_CONTROL ? "true" : "false";
+               return control_queue(fd, ret, strlen(ret) + 1);
+       } else if (strclcmp(data, len, "--listen\n") == 0)
+               return control_handle_listen(fd);
 
-       if (fd->ctx->ps_control_client != NULL &&
-           fd->ctx->ps_control_client != fd) {
-               logerrx("%s: cannot handle another client", __func__);
-               return 0;
-       }
-       return 1;
+       fd->flags |= FD_COMMAND;
+       return 0;
 }
 
 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 ps_process *psp = arg;
+       struct dhcpcd_ctx *ctx = psp->psp_ctx;
        struct fd_list *fd;
-       unsigned int fd_flags = FD_SENDLEN;
+       unsigned int fd_flags = 0;
        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_CONTROL:
+               fd_flags |= FD_CONTROL; /* FALLTHROUGH */
+       case PS_CTL_READ:
+               fd_flags |= FD_READ; /* 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) {
+               fd->peer_id = (unsigned int)psm->ps_flags;
+               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;
@@ -155,7 +143,7 @@ ps_ctl_dodispatch(void *arg, unsigned short events)
        struct ps_process *psp = arg;
 
        if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, ps_ctl_dispatch,
-               psp->psp_ctx) == -1)
+               psp) == -1)
                logerr(__func__);
 }
 
@@ -163,28 +151,91 @@ static void
 ps_ctl_recv(void *arg, unsigned short events)
 {
        struct dhcpcd_ctx *ctx = arg;
-       char buf[BUFSIZ];
-       ssize_t len;
+       int fd;
+       unsigned int peer_id;
+       size_t msglen;
+       /* Control messages for a peer are prefixed with fd and message len */
+       struct iovec iov[] = {
+               {
+                   .iov_base = &peer_id,
+                   .iov_len = sizeof(peer_id),
+               },
+               {
+                   .iov_base = &msglen,
+                   .iov_len = sizeof(msglen),
+               },
+       };
+       struct msghdr msg = {
+               .msg_iov = iov,
+               .msg_iovlen = __arraycount(iov),
+       };
+       ssize_t rlen;
+       struct fd_list *fdl;
+
+       if (events & ELE_HANGUP) {
+       hangup:
+               eloop_exit(ctx->eloop, EXIT_SUCCESS);
+               return;
+       }
 
-       if (!(events & (ELE_READ | ELE_HANGUP)))
+       if (!(events & ELE_READ))
                logerrx("%s: unexpected event 0x%04x", __func__, events);
 
-       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__);
+       fd = ctx->ps_ctl->psp_work_fd;
+       rlen = recvmsg(fd, &msg, MSG_WAITALL);
+       if (rlen == 0)
+               goto hangup;
+       if (rlen == -1) {
+               logerr("%s: recvmsg hdr", __func__);
+               eloop_exit(ctx->eloop, EXIT_FAILURE);
+               return;
+       }
+       if (rlen != sizeof(peer_id) + sizeof(msglen)) {
+               errno = EINVAL;
+               logerr("%s: recvmsg hdr", __func__);
+               eloop_exit(ctx->eloop, EXIT_FAILURE);
+               return;
+       }
+
+       if (msglen == 0) /* ulikely */
+               return;
+
+       if (ctx->io_buflen < msglen) {
+               void *n = realloc(ctx->io_buf, msglen);
+               if (n == NULL) {
+                       logerr(__func__);
+                       eloop_exit(ctx->eloop, EXIT_FAILURE);
+                       return;
                }
+               ctx->io_buf = n;
+               ctx->io_buflen = msglen;
+       }
+
+       iov[0].iov_base = ctx->io_buf;
+       iov[0].iov_len = msglen;
+       msg.msg_iovlen = 1;
+       rlen = recvmsg(fd, &msg, MSG_WAITALL);
+       if (rlen == 0)
+               goto hangup;
+       if (rlen == -1) {
+               logerr("%s: recvmsg msg", __func__);
+               eloop_exit(ctx->eloop, EXIT_FAILURE);
+               return;
+       }
+       if ((size_t)rlen != msglen) {
+               errno = EINVAL;
+               logerr("%s: recvmsg msg", __func__);
+               eloop_exit(ctx->eloop, EXIT_FAILURE);
+               return;
+       }
+
+       /* Send to our peer */
+       TAILQ_FOREACH(fdl, &ctx->control_fds, next) {
+               if (fdl->id != peer_id)
+                       continue;
+               if (control_queuef(fdl, ctx->io_buf, (size_t)msglen, 0) == -1)
+                       logerr("%s: control_queue", __func__);
+               break;
        }
 }
 
@@ -192,27 +243,64 @@ 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;
+       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;
+
+       if (events & ELE_HANGUP) {
+       hangup:
+               eloop_exit(ctx->eloop, EXIT_SUCCESS);
+               return;
+       }
 
        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 = recvmsg(fd, &msg, MSG_WAITALL);
+       if (len == 0)
+               goto hangup;
+       if (len != sizeof(msglen)) {
+               logerr("%s: recvmsg len %zd", __func__, len);
+               goto err;
+       }
+
+       if (ps_bufalloc(ctx, msglen) == -1) {
+               logerr("%s: realloc", __func__);
+               goto err;
+       }
+
+       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: recvmsg", __func__);
+               goto err;
        }
 
        /* Send to our listeners */
-       TAILQ_FOREACH(fd, &ctx->control_fds, next) {
-               if (!(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
@@ -228,8 +316,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 ||
@@ -248,8 +338,7 @@ ps_ctl_start(struct dhcpcd_ctx *ctx)
                psp->psp_work_fd = work_fd[0];
                close(work_fd[1]);
                close(listen_fd[1]);
-               ctx->ps_control = control_new(ctx, listen_fd[0],
-                   FD_SENDLEN | FD_LISTEN);
+               ctx->ps_control = control_new(ctx, listen_fd[0], FD_LISTEN);
                if (ctx->ps_control == NULL)
                        return -1;
                return pid;
@@ -281,21 +370,17 @@ ps_ctl_stop(struct dhcpcd_ctx *ctx)
 }
 
 ssize_t
-ps_ctl_sendargs(struct fd_list *fd, void *data, size_t len)
+ps_ctl_sendmsg(struct fd_list *fd, const struct msghdr *msg)
 {
        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)
-{
-       struct dhcpcd_ctx *ctx = fd->ctx;
-
-       return ps_sendcmd(ctx, ctx->ps_ctl->psp_fd, PS_CTL_EOF, 0, NULL, 0);
+       uint16_t cmd;
+       unsigned long flags = (unsigned long)fd->id;
+
+       if (fd->flags & FD_CONTROL)
+               cmd = PS_CTL_CONTROL;
+       else if (fd->flags & FD_READ)
+               cmd = PS_CTL_READ;
+       else
+               cmd = PS_CTL;
+       return ps_sendmsg(ctx, PS_CTL_FD(ctx), cmd, flags, msg);
 }
index ef2a76d6fb0a4c21d5dc70c479583c0a07bfb099..480fdf20fb0fe92318b0166053cbc29d33b2bcf8 100644 (file)
@@ -34,8 +34,8 @@
 
 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_handleargs(struct fd_list *, const char *, size_t);
+ssize_t ps_ctl_sendmsg(struct fd_list *, const struct msghdr *);
+ssize_t ps_ctl_sendeof(struct dhcpcd_ctx *);
 
 #endif
index 497ca8931b59184ce2f73c8eaf96e277587b66c4..47170c8e52317598095d7c65f8659628039dce22 100644 (file)
@@ -320,6 +320,28 @@ 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_ingroup(void *data, size_t len)
+{
+       uid_t uid;
+       gid_t gid, grpid;
+       uint8_t *p;
+
+       if (len != sizeof(uid) + sizeof(gid) + sizeof(grpid)) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       p = data;
+       memcpy(&uid, p, sizeof(uid));
+       p += sizeof(uid);
+       memcpy(&gid, p, sizeof(gid));
+       p += sizeof(gid);
+       memcpy(&grpid, p, sizeof(grpid));
+
+       return control_user_ingroup(uid, gid, grpid);
+}
+
 #ifdef AUTH
 static ssize_t
 ps_root_monordm(uint64_t *rdm, size_t len)
@@ -563,6 +585,9 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
        case PS_LOGREOPEN:
                err = logopen(ctx->logfile);
                break;
+       case PS_USER_INGROUP:
+               err = ps_root_douser_ingroup(data, len);
+               break;
 #ifdef AUTH
        case PS_AUTH_MONORDM:
                err = ps_root_monordm(data, len);
@@ -1119,6 +1144,36 @@ ps_root_logreopen(struct dhcpcd_ctx *ctx)
        return ps_root_readerror(ctx, NULL, 0);
 }
 
+ssize_t
+ps_root_user_ingroup(struct dhcpcd_ctx *ctx, uid_t uid, gid_t gid, gid_t grpid)
+{
+       struct iovec iov[] = {
+               {
+                   .iov_base = &uid,
+                   .iov_len = sizeof(uid),
+               },
+               {
+                   .iov_base = &gid,
+                   .iov_len = sizeof(gid),
+               },
+               {
+                   .iov_base = &grpid,
+                   .iov_len = sizeof(grpid),
+               },
+
+       };
+       struct msghdr msg = {
+               .msg_iov = iov,
+               .msg_iovlen = __arraycount(iov),
+       };
+
+       if (ps_sendmsg(ctx, PS_ROOT_FD(ctx), PS_USER_INGROUP, 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)
index 58fe8da890eca64108fe50020fb64dfbedb1ec48..737f52ab5d837100721f81321c65423eb7833bbf 100644 (file)
@@ -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_ingroup(struct dhcpcd_ctx *, uid_t, gid_t, gid_t);
 #ifdef PRIVSEP_GETHOSTNAME
 int ps_root_gethostname(struct dhcpcd_ctx *, char *, size_t);
 #endif
index a0806994b7c8465b3bcbc324e175faacb7583d02..65955007ac0a01e94bc437475b167773e86f4063 100644 (file)
@@ -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);
        }
 }
index 8c6ec05463a98496ee2a63a57b39f89e2da382fd..3d1fdea3dafb8fefd857aab7a5e15c6b068bbd38 100644 (file)
 #define PS_FILEMTIME   0x0016
 #define PS_AUTH_MONORDM 0x0017
 #define PS_CTL         0x0018
-#define PS_CTL_EOF     0x0019
-#define PS_LOGREOPEN   0x0020
-#define PS_STOPPROCS   0x0021
-#define PS_DAEMONISED  0x0022
+#define PS_CTL_CONTROL 0x0019
+#define PS_CTL_READ    0x0020
+#define PS_LOGREOPEN   0x0021
+#define PS_STOPPROCS   0x0022
+#define PS_DAEMONISED  0x0023
+#define PS_USER_INGROUP 0x0024
 
 /* Domains */
 #define PS_ROOT           0x0101
 #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
index ca273e2be71d83c33b75b20baace5fcaa72269a5..210fb66ac8ce438165760874113675a55e3a99a1 100644 (file)
@@ -593,7 +593,7 @@ eexit:
        return -1;
 }
 
-static int
+static ssize_t
 send_interface1(struct fd_list *fd, const struct interface *ifp,
     const char *reason)
 {