]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
privsep: Implement pdfork(2) for capsicum(4)
authorRoy Marples <roy@marples.name>
Wed, 17 Mar 2021 20:13:02 +0000 (20:13 +0000)
committerRoy Marples <roy@marples.name>
Wed, 17 Mar 2021 20:13:02 +0000 (20:13 +0000)
Capsicum does not allow waitpid(2).
As such we need to use pdfork(2) and watch for the descriptor to
hangup.

src/eloop.c
src/privsep.c
src/privsep.h

index 0e7aac8a3243bbeffaafb0ac3940d1caf43ec49d..bf85a5d567fc845f712b7e4894dc83797f37151a 100644 (file)
@@ -322,7 +322,7 @@ eloop_event_add(struct eloop *eloop, int fd, unsigned short events,
 
        assert(eloop != NULL);
        assert(cb != NULL && cb_arg != NULL);
-       if (fd == -1 || !(events & (ELE_READ | ELE_WRITE))) {
+       if (fd == -1 || !(events & (ELE_READ | ELE_WRITE | ELE_HANGUP))) {
                errno = EINVAL;
                return -1;
        }
@@ -367,6 +367,13 @@ eloop_event_add(struct eloop *eloop, int fd, unsigned short events,
                EV_SET(kep++, (uintptr_t)fd, EVFILT_WRITE, EV_DELETE, 0, 0, e);
        else
                n--;
+#ifdef EVFILT_PROCDESC
+       if (events & ELE_HANGUP)
+               EV_SET(kep++, (uintptr_t)fd, EVFILT_PROCDESC, EV_ADD,
+                   NOTE_EXIT, 0, e);
+       else
+               n--;
+#endif
        if (n != 0 && _kevent(eloop->fd, ke, n, NULL, 0, NULL) == -1) {
                if (added) {
                        TAILQ_REMOVE(&eloop->events, e, next);
index 1599507fddfb42b673f17006a4f7b88a9476333d..f09cbd8f4127a594473a0eec95ed9d2ea07fed42 100644 (file)
@@ -75,6 +75,7 @@
 
 #ifdef HAVE_CAPSICUM
 #include <sys/capsicum.h>
+#include <sys/procdesc.h>
 #include <capsicum_helpers.h>
 #endif
 #ifdef HAVE_UTIL_H
@@ -317,6 +318,29 @@ ps_rights_limit_stdio(struct dhcpcd_ctx *ctx)
 }
 #endif
 
+#ifdef HAVE_CAPSICUM
+static void
+ps_processhangup(void *arg, unsigned short events)
+{
+       struct ps_process *psp = arg;
+       struct dhcpcd_ctx *ctx = psp->psp_ctx;
+
+       if (!(events & ELE_HANGUP))
+               logerrx("%s: unexpected event 0x%04x", __func__, events);
+
+       logdebugx("%s%s%s exited from PID %d",
+           psp->psp_ifname, psp->psp_ifname[0] != '\0' ? ": " : "",
+           psp->psp_name, psp->psp_pid);
+
+       ps_freeprocess(psp);
+
+       if (!(ctx->options & DHCPCD_EXITING))
+               return;
+       if (!(PS_WAITING_FOR_PROCESSES(ctx)))
+               eloop_exit(ctx->ps_eloop, EXIT_SUCCESS);
+}
+#endif
+
 pid_t
 ps_startprocess(struct ps_process *psp,
     void (*recv_msg)(void *, unsigned short),
@@ -343,9 +367,18 @@ ps_startprocess(struct ps_process *psp,
        }
 #endif
 
-       switch (pid = fork()) {
+#ifdef HAVE_CAPSICUM
+       pid = pdfork(&psp->psp_pfd, PD_CLOEXEC);
+#else
+       pid = fork();
+#endif
+       switch (pid) {
        case -1:
+#ifdef HAVE_CAPSICUM
+               logerr("pdfork");
+#else
                logerr("fork");
+#endif
                return -1;
        case 0:
                psp->psp_pid = getpid();
@@ -365,6 +398,15 @@ ps_startprocess(struct ps_process *psp,
                            __func__, psp->psp_fd);
                        return -1;
                }
+#ifdef HAVE_CAPSICUM
+               if (eloop_event_add(ctx->eloop, psp->psp_pfd, ELE_HANGUP,
+                   ps_processhangup, psp) == -1)
+               {
+                       logerr("%s: eloop_event_add pfd %d",
+                           __func__, psp->psp_pfd);
+                       return -1;
+               }
+#endif
                return pid;
        }
 
@@ -681,6 +723,20 @@ ps_stopwait(struct dhcpcd_ctx *ctx)
                    ps_process_timeout, ctx) == -1)
                        logerr("%s: eloop_timeout_add_sec", __func__);
                eloop_enter(ctx->ps_eloop);
+
+#ifdef HAVE_CAPSICUM
+               struct ps_process *psp;
+
+               TAILQ_FOREACH(psp, &ctx->ps_processes, next) {
+                       if (psp->psp_pfd == -1)
+                               continue;
+                       if (eloop_event_add(ctx->ps_eloop, psp->psp_pfd,
+                           ELE_HANGUP, ps_processhangup, psp) == -1)
+                               logerr("%s: eloop_event_add pfd %d",
+                                   __func__, psp->psp_pfd);
+               }
+#endif
+
                waited = eloop_start(ctx->ps_eloop, &ctx->sigset);
                if (waited != EXIT_SUCCESS) {
                        logerr("%s: eloop_start", __func__);
@@ -713,6 +769,7 @@ ps_freeprocess(struct ps_process *psp)
        struct dhcpcd_ctx *ctx = psp->psp_ctx;
 
        TAILQ_REMOVE(&ctx->ps_processes, psp, next);
+
        if (psp->psp_fd != -1) {
                eloop_event_delete(ctx->eloop, psp->psp_fd);
                close(psp->psp_fd);
@@ -721,6 +778,14 @@ ps_freeprocess(struct ps_process *psp)
                eloop_event_delete(ctx->eloop, psp->psp_work_fd);
                close(psp->psp_work_fd);
        }
+#ifdef HAVE_CAPSICUM
+       if (psp->psp_pfd != -1) {
+               eloop_event_delete(ctx->eloop, psp->psp_pfd);
+               if (ctx->ps_eloop != NULL)
+                       eloop_event_delete(ctx->ps_eloop, psp->psp_pfd);
+               close(psp->psp_pfd);
+       }
+#endif
        if (ctx->ps_root == psp)
                ctx->ps_root = NULL;
        if (ctx->ps_inet == psp)
@@ -1108,6 +1173,10 @@ ps_newprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid)
        psp->psp_ctx = ctx;
        memcpy(&psp->psp_id, psid, sizeof(psp->psp_id));
        psp->psp_work_fd = -1;
+#ifdef HAVE_CAPSICUM
+       psp->psp_pfd = -1;
+#endif
+
        if (!(ctx->options & DHCPCD_MANAGER))
                strlcpy(psp->psp_ifname, ctx->ifv[0], sizeof(psp->psp_name));
        TAILQ_INSERT_TAIL(&ctx->ps_processes, psp, next);
index 57a562e49d4c6a24cae3829c94b6fdb447b2d387..deb24b9fe73ab1efeb8c2a1a6947e13a02c81ee2 100644 (file)
@@ -182,6 +182,10 @@ struct ps_process {
        struct interface psp_ifp; /* Move BPF gubbins elsewhere */
        struct bpf *psp_bpf;
 #endif
+
+#ifdef HAVE_CAPSICUM
+       int psp_pfd;
+#endif
 };
 TAILQ_HEAD(ps_process_head, ps_process);