]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
privsep: Change IPC to use SOCK_STREAM (#604)
authorRoy Marples <roy@marples.name>
Sat, 9 May 2026 12:13:56 +0000 (13:13 +0100)
committerGitHub <noreply@github.com>
Sat, 9 May 2026 12:13:56 +0000 (13:13 +0100)
macOS does not support SOCK_SEQPACKET.

All our messages use a fixed header which includes the
lengths of all parts sent.
We can use these limits with MSG_WAITALL on blocking sockets
in place of MSG_EOR to get the same effect.

Start of the work for #524.

src/dhcpcd.c
src/logerr.c
src/logerr.h
src/privsep-bpf.c
src/privsep-control.c
src/privsep-inet.c
src/privsep-root.c
src/privsep.c

index 3f5feddaad281bfc301aa89e9833e29c0eb23fe7..b5c5ae2854501b759b0b77318038c34b2ac98880 100644 (file)
@@ -384,7 +384,7 @@ dhcpcd_daemonise(struct dhcpcd_ctx *ctx)
 
        eloop_event_delete(ctx->eloop, ctx->fork_fd);
        exit_code = EXIT_SUCCESS;
-       if (send(ctx->fork_fd, &exit_code, sizeof(exit_code), MSG_EOR) == -1)
+       if (send(ctx->fork_fd, &exit_code, sizeof(exit_code), 0) == -1)
                logerr(__func__);
        close(ctx->fork_fd);
        ctx->fork_fd = -1;
@@ -1498,7 +1498,7 @@ dhcpcd_signal_cb(int sig, void *arg)
 
        if (sig != SIGCHLD && ctx->options & DHCPCD_FORKED) {
                if (ctx->fork_fd != -1 && sig != SIGHUP &&
-                   send(ctx->fork_fd, &sig, sizeof(sig), MSG_EOR) == -1)
+                   send(ctx->fork_fd, &sig, sizeof(sig), 0) == -1)
                        logerr("%s: send", __func__);
                return;
        }
@@ -1905,7 +1905,7 @@ dhcpcd_fork_cb(void *arg, unsigned short events)
        if (!(events & ELE_READ))
                logerrx("%s: unexpected event 0x%04x", __func__, events);
 
-       len = read(ctx->fork_fd, &exit_code, sizeof(exit_code));
+       len = recv(ctx->fork_fd, &exit_code, sizeof(exit_code), MSG_WAITALL);
        if (len == -1) {
                logerr(__func__);
                eloop_exit(ctx->eloop, EXIT_FAILURE);
@@ -2469,7 +2469,7 @@ main(int argc, char **argv, char **envp)
        if (!(ctx.options & DHCPCD_DAEMONISE))
                goto start_manager;
 
-       if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, fork_fd) ==
+       if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fork_fd) ==
            -1) {
                logerr("socketpair");
                goto exit_failure;
@@ -2785,7 +2785,7 @@ exit1:
 #ifdef USE_SIGNALS
        /* If still attached, detach from the launcher */
        if (ctx.options & DHCPCD_STARTED && ctx.fork_fd != -1) {
-               if (send(ctx.fork_fd, &i, sizeof(i), MSG_EOR) == -1)
+               if (send(ctx.fork_fd, &i, sizeof(i), 0) == -1)
                        logerr("%s: send", __func__);
        }
 #endif
index af13b2498a3942831cb80ca45c9e9d4e4a446b4a..c2d318f9ad5c87c50b4c06d028acbba77de7727e 100644 (file)
@@ -218,22 +218,25 @@ __printflike(2, 0) static int vlogmessage(int pri, const char *fmt,
        if (ctx->log_fd != -1) {
                pid_t pid = getpid();
                char buf[LOGERR_SYSLOGBUF];
+               size_t mlen = 0;
                struct iovec iov[] = {
                        { .iov_base = &pri, .iov_len = sizeof(pri) },
                        { .iov_base = &pid, .iov_len = sizeof(pid) },
+                       { .iov_base = &mlen, .iov_len = sizeof(mlen) },
                        { .iov_base = buf },
                };
 
                len = vsnprintf(buf, sizeof(buf), fmt, args);
                if (len != -1) {
-                       if ((size_t)len >= sizeof(buf))
-                               len = (int)sizeof(buf) - 1;
-                       iov[2].iov_len = (size_t)(len + 1);
+                       mlen = (size_t)len + 1;
+                       if (mlen > sizeof(buf))
+                               mlen = sizeof(buf);
+                       iov[3].iov_len = mlen;
                        struct msghdr msg = {
                                .msg_iov = iov,
                                .msg_iovlen = sizeof(iov) / sizeof(iov[0]),
                        };
-                       len = (int)sendmsg(ctx->log_fd, &msg, MSG_EOR);
+                       len = (int)sendmsg(ctx->log_fd, &msg, 0);
                }
                return len;
        }
@@ -399,10 +402,11 @@ logreadfd(int fd)
        int pri;
        pid_t pid;
        char buf[LOGERR_SYSLOGBUF] = { '\0' };
+       size_t mlen;
        struct iovec iov[] = {
                { .iov_base = &pri, .iov_len = sizeof(pri) },
                { .iov_base = &pid, .iov_len = sizeof(pid) },
-               { .iov_base = buf, .iov_len = sizeof(buf) },
+               { .iov_base = &mlen, .iov_len = sizeof(mlen) },
        };
        struct msghdr msg = { .msg_iov = iov,
                .msg_iovlen = sizeof(iov) / sizeof(iov[0]) };
@@ -411,14 +415,25 @@ logreadfd(int fd)
        len = recvmsg(fd, &msg, MSG_WAITALL);
        if (len == -1 || len == 0)
                return len;
-       /* Ensure we received the minimum and at least one character to log */
-       if ((size_t)len < sizeof(pri) + sizeof(pid) + 1 ||
-           msg.msg_flags & MSG_TRUNC) {
+       if ((size_t)len != sizeof(pri) + sizeof(pid) + sizeof(mlen)) {
                errno = EMSGSIZE;
                return -1;
        }
+       if (mlen > sizeof(buf)) {
+               errno = ENOBUFS;
+               return -1;
+       }
+
+       len = recv(fd, buf, mlen, MSG_WAITALL);
+       if (len == -1)
+               return -1;
+       if ((size_t)len != mlen) {
+               errno = EINVAL;
+               return -1;
+       }
+
        /* Ensure what we receive is NUL terminated */
-       buf[(size_t)len - (sizeof(pri) + sizeof(pid)) - 1] = '\0';
+       buf[mlen == sizeof(buf) ? mlen - 1 : mlen] = '\0';
 
        ctx->log_pid = pid;
        logmessage(pri, "%s", buf);
index 5d3a01496bdb8aba87d2c3bdf785404a7ac1b57b..f5196a62d7273f7317ddb506991c6da40d4c866e 100644 (file)
@@ -76,7 +76,7 @@ __printflike(2, 3) void logerrmessage(int pri, const char *fmt, ...);
 #define logerr(...)    log_err(__VA_ARGS__)
 #define logerrx(...)   log_errx(__VA_ARGS__)
 
-/* For logging in a chroot using SOCK_SEQPACKET */
+/* For logging in a chroot using SOCK_STREAM */
 int loggetfd(void);
 void logsetfd(int);
 ssize_t logreadfd(int);
index 4277d5746d4f0150721ac3e99ad20c39510ec8ae..dab7b80261cec921cfaa3179cabde0ba4d6f23d5 100644 (file)
@@ -53,7 +53,7 @@
 #include "logerr.h"
 #include "privsep.h"
 
-/* We expect to have open 3 SEQPACKET and one RAW fd */
+/* We expect to have open 3 SOCK_STREAM and one RAW fd */
 
 static void
 ps_bpf_recvbpf(void *arg, unsigned short events)
index 245913249fbe818a5f80bfb9b8c2b6553abe21b4..76f6df79f8846953b443ff5ee040f7048496c2bc 100644 (file)
@@ -36,7 +36,7 @@
 #include "logerr.h"
 #include "privsep.h"
 
-/* We expect to have open 2 SEQPACKET, 2 STREAM and 2 file STREAM fds */
+/* We expect to have open 2 privsep STREAM, 2 STREAM and 2 file STREAM fds */
 
 static int
 ps_ctl_startcb(struct ps_process *psp)
index 590e770b3be6fae10dfa571e176b7584045cc68c..4ff48218faf2c9b1f919b638ce16441c60adf637 100644 (file)
@@ -47,7 +47,7 @@
 #include "logerr.h"
 #include "privsep.h"
 
-/* We expect to have open 2 SEQPACKET, 1 udp, 1 udp6 and 1 raw6 fds */
+/* We expect to have open 2 SOCK_STREAM, 1 udp, 1 udp6 and 1 raw6 fds */
 
 #ifdef INET
 static void
index 0b1bdd4b100fc8a05bfd279a87bf835b368a35cb..8ac2f4ed6e71592d0328419959ed698e997153b4 100644 (file)
@@ -36,6 +36,7 @@
 #include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <poll.h>
 #include <pwd.h>
 #include <signal.h>
 #include <stddef.h>
@@ -79,11 +80,6 @@ ps_root_readerrorcb(struct psr_ctx *pc)
        struct dhcpcd_ctx *ctx = pc->psr_ctx;
        int fd = PS_ROOT_FD(ctx);
        struct psr_error *psr_error = &pc->psr_error;
-       struct iovec iov[] = {
-               { .iov_base = psr_error, .iov_len = sizeof(*psr_error) },
-               { .iov_base = pc->psr_data, .iov_len = pc->psr_datalen },
-       };
-       struct msghdr msg = { .msg_iov = iov, .msg_iovlen = __arraycount(iov) };
        ssize_t len;
 
 #define PSR_ERROR(e)                        \
@@ -95,45 +91,27 @@ ps_root_readerrorcb(struct psr_ctx *pc)
        if (eloop_waitfd(fd) == -1)
                PSR_ERROR(errno);
 
-       if (!pc->psr_mallocdata)
-               goto recv;
-
-       /* We peek at the psr_error structure to tell us how much of a buffer
-        * we need to read the whole packet. */
-       msg.msg_iovlen--;
-       len = recvmsg(fd, &msg, MSG_PEEK | MSG_WAITALL);
+       len = recv(fd, psr_error, sizeof(*psr_error), MSG_WAITALL);
        if (len == -1)
                PSR_ERROR(errno);
 
-       /* After this point, we MUST do another recvmsg even on a failure
-        * to remove the message after peeking. */
-       if ((size_t)len < sizeof(*psr_error)) {
-               /* We can't use the header to work out buffers, so
-                * remove the message and bail. */
-               (void)recvmsg(fd, &msg, MSG_WAITALL);
+       if ((size_t)len < sizeof(*psr_error))
                PSR_ERROR(EINVAL);
-       }
 
-       /* No data to read? Unlikely but ... */
        if (psr_error->psr_datalen == 0)
-               goto recv;
+               return len;
 
-       pc->psr_data = malloc(psr_error->psr_datalen);
-       if (pc->psr_data != NULL) {
-               iov[1].iov_base = pc->psr_data;
-               iov[1].iov_len = psr_error->psr_datalen;
-               msg.msg_iovlen++;
-       }
+       if (pc->psr_mallocdata) {
+               pc->psr_data = malloc(psr_error->psr_datalen);
+               if (pc->psr_data == NULL)
+                       PSR_ERROR(errno);
+       } else if (psr_error->psr_datalen > pc->psr_datalen)
+               PSR_ERROR(EMSGSIZE);
 
-recv:
-       len = recvmsg(fd, &msg, MSG_WAITALL);
+       len = recv(fd, pc->psr_data, psr_error->psr_datalen, MSG_WAITALL);
        if (len == -1)
                PSR_ERROR(errno);
-       else if ((size_t)len < sizeof(*psr_error))
-               PSR_ERROR(EINVAL);
-       else if (msg.msg_flags & MSG_TRUNC)
-               PSR_ERROR(ENOBUFS);
-       else if ((size_t)len != sizeof(*psr_error) + psr_error->psr_datalen) {
+       else if ((size_t)len != psr_error->psr_datalen) {
 #ifdef PRIVSEP_DEBUG
                logerrx("%s: recvmsg returned %zd, expecting %zu", __func__,
                    len, sizeof(*psr_error) + psr_error->psr_datalen);
@@ -204,7 +182,7 @@ ps_root_writeerror(struct dhcpcd_ctx *ctx, ssize_t result, void *data,
 
        if (len == 0)
                msg.msg_iovlen = 1;
-       err = sendmsg(fd, &msg, MSG_EOR);
+       err = sendmsg(fd, &msg, 0);
 
        /* Error sending the message? Try sending the error of sending. */
        if (err == -1 && errno != EPIPE) {
@@ -214,7 +192,7 @@ ps_root_writeerror(struct dhcpcd_ctx *ctx, ssize_t result, void *data,
                psr.psr_errno = errno;
                psr.psr_datalen = 0;
                msg.msg_iovlen = 1;
-               err = sendmsg(fd, &msg, MSG_EOR);
+               err = sendmsg(fd, &msg, 0);
        }
 
        return err;
@@ -857,14 +835,14 @@ ps_root_start(struct dhcpcd_ctx *ctx)
        int logfd[2] = { -1, -1 }, datafd[2] = { -1, -1 };
        pid_t pid;
 
-       if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, logfd) == -1)
+       if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, logfd) == -1)
                return -1;
 #ifdef PRIVSEP_RIGHTS
        if (ps_rights_limit_fdpair(logfd) == -1)
                return -1;
 #endif
 
-       if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, datafd) == -1)
+       if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, datafd) == -1)
                return -1;
 
        if (ps_setbuf_fdpair(datafd) == -1)
@@ -950,20 +928,30 @@ ps_root_stop(struct dhcpcd_ctx *ctx)
                /* drain the log */
                if (ctx->ps_log_root_fd != -1) {
                        ssize_t loglen;
-
-#ifdef __linux__
-                       /* Seems to help to get the last parts,
-                        * sched_yield(2) does not. */
-                       sleep(0);
-#endif
-                       do {
+                       struct pollfd pfd = {
+                               .fd = ctx->ps_log_root_fd,
+                               .events = POLLIN
+                       };
+                       int n;
+
+                       /* the socket is blocking and we may not be able to
+                        * change it to non blocking, so poll for data */
+                       for (;;) {
+                               n = poll(&pfd, 1, 1);
+                               if (n == -1 || n == 0)
+                                       break;
                                loglen = logreadfd(ctx->ps_log_root_fd);
-                       } while (loglen != 0 && loglen != -1);
-                       close(ctx->ps_log_root_fd);
-                       ctx->ps_log_root_fd = -1;
+                               if (loglen == -1 || loglen == 0)
+                                       break;
+                       }
                }
        }
 
+       if (ctx->ps_log_root_fd != -1) {
+               close(ctx->ps_log_root_fd);
+               ctx->ps_log_root_fd = -1;
+       }
+
        if (ctx->ps_data_fd != -1) {
                eloop_event_delete(ctx->eloop, ctx->ps_data_fd);
                close(ctx->ps_data_fd);
@@ -971,8 +959,10 @@ ps_root_stop(struct dhcpcd_ctx *ctx)
        }
 
        /* Only the manager process gets past this point. */
-       if (ctx->options & DHCPCD_FORKED)
-               return 0;
+       if (ctx->options & DHCPCD_FORKED) {
+               err = 0;
+               goto out;
+       }
 
        /* We cannot log the root process exited before we
         * log dhcpcd exits because the latter requires the former.
@@ -988,6 +978,7 @@ ps_root_stop(struct dhcpcd_ctx *ctx)
        } /* else the root process has already exited :( */
 
        err = ps_stopwait(ctx);
+out:
        if (ctx->ps_root != NULL)
                ps_freeprocess(ctx->ps_root);
        return err;
index d59902fd2fae372e51911433594361543acf3441..e5fb9c6206bc522d45c2365a3bba63ebed92219a 100644 (file)
@@ -350,7 +350,7 @@ ps_startprocess(struct ps_process *psp,
        int fd[2];
        pid_t pid;
 
-       if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, fd) == -1) {
+       if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fd) == -1) {
                logerr("%s: socketpair", __func__);
                return -1;
        }
@@ -881,6 +881,7 @@ ps_sendpsmmsg(struct dhcpcd_ctx *ctx, int fd, struct ps_msghdr *psm,
 
                psm->ps_namelen = msg->msg_namelen;
                psm->ps_controllen = (socklen_t)msg->msg_controllen;
+               psm->ps_datalen = 0;
 
                iovp->iov_base = msg->msg_name;
                iovp->iov_len = msg->msg_namelen;
@@ -901,17 +902,19 @@ ps_sendpsmmsg(struct dhcpcd_ctx *ctx, int fd, struct ps_msghdr *psm,
                m.msg_iovlen++;
 
                for (i = 0; i < (int)msg->msg_iovlen; i++) {
-                       if ((size_t)(m.msg_iovlen++) > __arraycount(iov)) {
+                       if ((size_t)m.msg_iovlen >= __arraycount(iov)) {
                                errno = ENOBUFS;
                                return -1;
                        }
+                       m.msg_iovlen++;
                        iovp->iov_base = msg->msg_iov[i].iov_base;
                        iovp->iov_len = msg->msg_iov[i].iov_len;
                        iovp++;
+                       psm->ps_datalen += msg->msg_iov[i].iov_len;
                }
        }
 
-       len = sendmsg(fd, &m, MSG_EOR);
+       len = sendmsg(fd, &m, 0);
 
        if (len == -1 && ctx != NULL) {
                if (ctx->options & DHCPCD_FORKED &&
@@ -1064,12 +1067,14 @@ ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd, unsigned short events,
     ssize_t (*callback)(void *, struct ps_msghdr *, struct msghdr *),
     void *cbctx)
 {
-       struct ps_msg psm;
+       struct ps_msghdr psm;
+       uint8_t ps_data[PS_BUFLEN];
        ssize_t len;
        size_t dlen;
        struct iovec iov[1];
        struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1 };
        bool stop = false;
+       socklen_t cmsg_padlen;
 
        if (events & ELE_HANGUP) {
                len = 0;
@@ -1078,24 +1083,24 @@ ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd, unsigned short events,
        if (!(events & ELE_READ))
                logerrx("%s: unexpected event 0x%04x", __func__, events);
 
-       len = read(fd, &psm, sizeof(psm));
+       len = recv(fd, &psm, sizeof(psm), MSG_WAITALL);
 #ifdef PRIVSEP_DEBUG
-       logdebugx("%s: fd=%d %zd", __func__, fd, len);
+       logdebugx("%s: pid=%d fd=%d len=%zd", __func__, getpid(), fd, len);
 #endif
 
        if (len == -1 || len == 0)
                stop = true;
        else {
                dlen = (size_t)len;
-               if (dlen < sizeof(psm.psm_hdr)) {
+               if (dlen < sizeof(psm)) {
                        errno = EINVAL;
-                       return -1;
+                       goto stop;
                }
 
-               if (psm.psm_hdr.ps_cmd == PS_STOP) {
+               if (psm.ps_cmd == PS_STOP) {
                        stop = true;
                        len = 0;
-               } else if (psm.psm_hdr.ps_cmd == PS_DAEMONISED) {
+               } else if (psm.ps_cmd == PS_DAEMONISED) {
                        ps_daemonised(ctx);
                        return 0;
                }
@@ -1111,16 +1116,30 @@ ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd, unsigned short events,
                eloop_exit(ctx->eloop, len != -1 ? EXIT_SUCCESS : EXIT_FAILURE);
                return len;
        }
-       dlen -= sizeof(psm.psm_hdr);
 
-       if (ps_unrollmsg(&msg, &psm.psm_hdr, psm.psm_data, dlen) == -1)
+       cmsg_padlen = CALC_CMSG_PADLEN(psm.ps_controllen,
+           psm.ps_namelen);
+       dlen = psm.ps_namelen + psm.ps_controllen + cmsg_padlen + psm.ps_datalen;
+       if (dlen != 0) {
+               if (dlen > sizeof(ps_data)) {
+                       errno = EMSGSIZE;
+                       goto stop;
+               }
+               len = recv(fd, ps_data, dlen, MSG_WAITALL);
+               if ((size_t)len != dlen) {
+                       errno = EINVAL;
+                       goto stop;
+               }
+       }
+
+       if (ps_unrollmsg(&msg, &psm, ps_data, dlen) == -1)
                return -1;
 
        if (callback == NULL)
                return 0;
 
        errno = 0;
-       return callback(cbctx, &psm.psm_hdr, &msg);
+       return callback(cbctx, &psm, &msg);
 }
 
 struct ps_process *