]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
privsep: Enable capsicum for network facing processes
authorRoy Marples <roy@marples.name>
Thu, 7 May 2020 19:57:22 +0000 (20:57 +0100)
committerRoy Marples <roy@marples.name>
Thu, 7 May 2020 19:57:22 +0000 (20:57 +0100)
All fd's in network facing processes are fully limited.
Capability mode is only enabled for BPF processes because
it's too restrictive otherwise - the reasons are noted
in the commit.

configure
src/dhcpcd.c
src/privsep-bpf.c
src/privsep-inet.c
src/privsep.c
src/privsep.h

index ebf104c48f2fe2b475849a9ec81f7f62aecb33a1..b38fe6c18760d6c1fdb564cae8bb4564e34c88d8 100755 (executable)
--- a/configure
+++ b/configure
@@ -648,6 +648,23 @@ else
        echo "EMBEDDEDINSTALL=  _embeddedinstall" >>$CONFIG_MK
 fi
 
+if [ "$PRIVSEP" = yes ]; then
+       printf "Testing for capsicum ... "
+       cat <<EOF >_capsicum.c
+#include <sys/capsicum.h>
+int main(void) {
+       return cap_enter();
+}
+EOF
+       if $XCC _capsicum.c -o _capsicum 2>&3; then
+               echo "yes"
+               echo "#define   HAVE_CAPSICUM" >>$CONFIG_H
+       else
+               echo "no"
+       fi
+       rm -f _capsicum.c _capsicum
+fi
+
 if [ "$OS" = linux ]; then
        printf "Testing for nl80211 ... "
        cat <<EOF >_nl80211.c
index 060f2e935d0df02f08e824d88e0697ea9c2397e2..4c61c6f0a54240c49b02381a5164e251b3de33ae 100644 (file)
@@ -2236,9 +2236,16 @@ printpidfile:
                dev_start(&ctx);
 
 #ifdef PRIVSEP
-       if (ctx.options & DHCPCD_PRIVSEP && ps_dropprivs(&ctx) == -1) {
-               logerr("ps_dropprivs");
-               goto exit_failure;
+       if (ctx.options & DHCPCD_PRIVSEP) {
+               /*
+                * PSF_CAP_ENTER is not set because the following functions
+                * won't work in it:
+                * getifaddrs(3), gethostname(3), uname(3).
+                */
+               if (ps_dropprivs(&ctx, 0) == -1) {
+                       logerr("ps_dropprivs");
+                       goto exit_failure;
+               }
        }
 #endif
 
index 28394ea974e0984965f73f539076de9e83c8317a..b1ed821d2923c84bb2e22404c0e12580987d0d20 100644 (file)
 #include "logerr.h"
 #include "privsep.h"
 
+#ifdef HAVE_CAPSICUM
+#include <sys/capsicum.h>
+#endif
+
 static void
 ps_bpf_recvbpf(void *arg)
 {
@@ -152,6 +156,13 @@ ps_bpf_start_bpf(void *arg)
 {
        struct ps_process *psp = arg;
        struct dhcpcd_ctx *ctx = psp->psp_ctx;
+#ifdef HAVE_CAPSICUM
+       cap_rights_t rights;
+
+       /* We need CAP_IOCTL so we can change the BPF filter when we
+        * need to. */
+       cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_IOCTL);
+#endif
 
        setproctitle("[BPF %s] %s", psp->psp_protostr, psp->psp_ifname);
 
@@ -160,6 +171,11 @@ ps_bpf_start_bpf(void *arg)
        psp->psp_work_fd = bpf_open(&psp->psp_ifp, psp->psp_filter);
        if (psp->psp_work_fd == -1)
                logerr("%s: bpf_open",__func__);
+#ifdef HAVE_CAPSICUM
+       else if (cap_rights_limit(psp->psp_work_fd, &rights) == -1 &&
+           errno != ENOSYS)
+               logerr("%s: cap_rights_limit", __func__);
+#endif
        else if (eloop_event_add(ctx->eloop,
            psp->psp_work_fd, ps_bpf_recvbpf, psp) == -1)
                logerr("%s: eloop_event_add", __func__);
@@ -252,7 +268,8 @@ ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
        start = ps_dostart(ctx,
            &psp->psp_pid, &psp->psp_fd,
            ps_bpf_recvmsg, NULL, psp,
-           ps_bpf_start_bpf, ps_bpf_signal_bpfcb, PSF_DROPPRIVS);
+           ps_bpf_start_bpf, ps_bpf_signal_bpfcb,
+           PSF_DROPPRIVS | PSF_CAP_ENTER);
        switch (start) {
        case -1:
                ps_freeprocess(psp);
index 0191ccd7d84189995fe5dcdcbaef3f865497a997..f0dc79a205529638abac9b7ee1d0f1312ea70de8 100644 (file)
 #include "logerr.h"
 #include "privsep.h"
 
+#ifdef HAVE_CAPSICUM
+/* We never call ps_dostart with PSF_CAP_ENTER because
+ * our sockets require the use of CAP_CONNECT which does not
+ * work in capabilities mode according to rights(4). */
+#include <sys/capsicum.h>
+#endif
+
 #ifdef INET
 static void
 ps_inet_recvbootp(void *arg)
@@ -92,6 +99,11 @@ ps_inet_startcb(void *arg)
 {
        struct dhcpcd_ctx *ctx = arg;
        int ret = 0;
+#ifdef HAVE_CAPSICUM
+       cap_rights_t rights;
+
+       cap_rights_init(&rights, CAP_RECV, CAP_CONNECT, CAP_SEND, CAP_EVENT);
+#endif
 
        if (ctx->options & DHCPCD_MASTER)
                setproctitle("[network proxy]");
@@ -114,6 +126,15 @@ ps_inet_startcb(void *arg)
                ctx->udp_fd = dhcp_openudp(NULL);
                if (ctx->udp_fd == -1)
                        logerr("%s: dhcp_open", __func__);
+#ifdef HAVE_CAPSICUM
+               else if (cap_rights_limit(ctx->udp_fd, &rights) == -1
+                   && errno != ENOSYS)
+               {
+                       logerr("%s: cap_rights_limit", __func__);
+                       close(ctx->udp_fd);
+                       ctx->udp_fd = -1;
+               }
+#endif
                else if (eloop_event_add(ctx->eloop, ctx->udp_fd,
                    ps_inet_recvbootp, ctx) == -1)
                {
@@ -128,6 +149,15 @@ ps_inet_startcb(void *arg)
        if (ctx->options & DHCPCD_IPV6) {
                if (ipv6nd_open(ctx) == -1)
                        logerr("%s: ipv6nd_open", __func__);
+#ifdef HAVE_CAPSICUM
+               else if (cap_rights_limit(ctx->nd_fd, &rights) == -1
+                   && errno != ENOSYS)
+               {
+                       logerr("%s: cap_rights_limit", __func__);
+                       close(ctx->nd_fd);
+                       ctx->nd_fd = -1;
+               }
+#endif
                else if (eloop_event_add(ctx->eloop, ctx->nd_fd,
                    ps_inet_recvra, ctx) == -1)
                {
@@ -145,6 +175,15 @@ ps_inet_startcb(void *arg)
                ctx->dhcp6_fd = dhcp6_openudp(0, NULL);
                if (ctx->dhcp6_fd == -1)
                        logerr("%s: dhcp6_open", __func__);
+#ifdef HAVE_CAPSICUM
+               else if (cap_rights_limit(ctx->dhcp6_fd, &rights) == -1
+                   && errno != ENOSYS)
+               {
+                       logerr("%s: cap_rights_limit", __func__);
+                       close(ctx->dhcp6_fd);
+                       ctx->dhcp6_fd = -1;
+               }
+#endif
                else if (eloop_event_add(ctx->eloop, ctx->dhcp6_fd,
                    ps_inet_recvdhcp6, ctx) == -1)
                {
@@ -296,6 +335,11 @@ ps_inet_listenin(void *arg)
        struct ps_process *psp = arg;
        struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr;
        char buf[INET_ADDRSTRLEN];
+#ifdef HAVE_CAPSICUM
+       cap_rights_t rights;
+
+       cap_rights_init(&rights, CAP_RECV, CAP_CONNECT, CAP_SEND, CAP_EVENT);
+#endif
 
        inet_ntop(AF_INET, ia, buf, sizeof(buf));
        setproctitle("[network proxy] %s", buf);
@@ -306,6 +350,15 @@ ps_inet_listenin(void *arg)
                return -1;
        }
 
+#ifdef HAVE_CAPSICUM
+       if (cap_rights_limit(psp->psp_work_fd, &rights) == -1 &&
+           errno != ENOSYS)
+       {
+               logerr("%s: cap_rights_limit", __func__);
+               return -1;
+       }
+#endif
+
        if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd,
            ps_inet_recvinbootp, psp) == -1)
        {
@@ -313,9 +366,7 @@ ps_inet_listenin(void *arg)
                return -1;
        }
 
-#ifdef PRIVSEP_DEBUG
        logdebugx("spawned listener %s on PID %d", buf, getpid());
-#endif
        return 0;
 }
 #endif
@@ -335,6 +386,11 @@ static int
 ps_inet_listennd(void *arg)
 {
        struct ps_process *psp = arg;
+#ifdef HAVE_CAPSICUM
+       cap_rights_t rights;
+
+       cap_rights_init(&rights, CAP_RECV, CAP_CONNECT, CAP_SEND, CAP_EVENT);
+#endif
 
        setproctitle("[ND network proxy]");
 
@@ -344,6 +400,15 @@ ps_inet_listennd(void *arg)
                return -1;
        }
 
+#ifdef HAVE_CAPSICUM
+       if (cap_rights_limit(psp->psp_work_fd, &rights) == -1 &&
+           errno != ENOSYS)
+       {
+               logerr("%s: cap_rights_limit", __func__);
+               return -1;
+       }
+#endif
+
        if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd,
            ps_inet_recvin6nd, psp) == -1)
        {
@@ -373,6 +438,11 @@ ps_inet_listenin6(void *arg)
        struct ps_process *psp = arg;
        struct in6_addr *ia = &psp->psp_id.psi_addr.psa_in6_addr;
        char buf[INET6_ADDRSTRLEN];
+#ifdef HAVE_CAPSICUM
+       cap_rights_t rights;
+
+       cap_rights_init(&rights, CAP_RECV, CAP_CONNECT, CAP_SEND, CAP_EVENT);
+#endif
 
        inet_ntop(AF_INET6, ia, buf, sizeof(buf));
        setproctitle("[network proxy] %s", buf);
@@ -383,6 +453,15 @@ ps_inet_listenin6(void *arg)
                return -1;
        }
 
+#ifdef HAVE_CAPSICUM
+       if (cap_rights_limit(psp->psp_work_fd, &rights) == -1 &&
+           errno != ENOSYS)
+       {
+               logerr("%s: cap_rights_limit", __func__);
+               return -1;
+       }
+#endif
+
        if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd,
            ps_inet_recvin6dhcp6, psp) == -1)
        {
index 6ecc17f0a47c32b34ff2db40e9f6e32840246f0b..fd0cbd47440da70ff9881955c916ba41d0f5af95 100644 (file)
@@ -70,6 +70,9 @@
 #include "logerr.h"
 #include "privsep.h"
 
+#ifdef HAVE_CAPSICUM
+#include <sys/capsicum.h>
+#endif
 #ifdef HAVE_UTIL_H
 #include <util.h>
 #endif
@@ -143,7 +146,7 @@ ps_init(struct dhcpcd_ctx *ctx)
 }
 
 int
-ps_dropprivs(struct dhcpcd_ctx *ctx)
+ps_dropprivs(struct dhcpcd_ctx *ctx, unsigned int flags)
 {
        struct passwd *pw = ctx->ps_user;
 
@@ -163,6 +166,14 @@ ps_dropprivs(struct dhcpcd_ctx *ctx)
                return -1;
        }
 
+#ifdef HAVE_CAPSICUM
+       if (flags & PSF_CAP_ENTER) {
+               if (cap_enter() == -1 && errno != ENOSYS) {
+                       logerr("%s: cap_enter", __func__);
+                       return -1;
+               }
+       }
+#endif
        return 0;
 }
 
@@ -176,6 +187,11 @@ ps_dostart(struct dhcpcd_ctx *ctx,
        int stype;
        int fd[2];
        pid_t pid;
+#ifdef HAVE_CAPSICUM
+       cap_rights_t rights;
+
+       cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_SHUTDOWN);
+#endif
 
        stype = SOCK_CLOEXEC | SOCK_NONBLOCK;
        if (socketpair(AF_UNIX, SOCK_DGRAM | stype, 0, fd) == -1) {
@@ -195,8 +211,17 @@ ps_dostart(struct dhcpcd_ctx *ctx,
                *priv_pid = pid;
                *priv_fd = fd[0];
                close(fd[1]);
-               if (recv_unpriv_msg != NULL &&
-                   eloop_event_add(ctx->eloop, *priv_fd,
+               if (recv_unpriv_msg == NULL)
+                       ;
+#ifdef HAVE_CAPSICUM
+               else if (cap_rights_limit(*priv_fd, &rights) == -1
+                   && errno != ENOSYS)
+               {
+                       logerr("%s: cap_rights_limit", __func__);
+                       return -1;
+               }
+#endif
+               else if (eloop_event_add(ctx->eloop, *priv_fd,
                    recv_unpriv_msg, recv_ctx) == -1)
                {
                        logerr("%s: eloop_event_add", __func__);
@@ -240,6 +265,11 @@ ps_dostart(struct dhcpcd_ctx *ctx,
                goto errexit;
        }
 
+#ifdef HAVE_CAPSICUM
+       if (cap_rights_limit(*priv_fd, &rights) == -1 && errno != ENOSYS)
+               goto errexit;
+#endif
+
        if (eloop_event_add(ctx->eloop, *priv_fd, recv_msg, recv_ctx) == -1)
        {
                logerr("%s: eloop_event_add", __func__);
@@ -257,7 +287,7 @@ ps_dostart(struct dhcpcd_ctx *ctx,
        }
 
        if (flags & PSF_DROPPRIVS)
-               ps_dropprivs(ctx);
+               ps_dropprivs(ctx, flags);
 
        return 0;
 
index 9cdbb265797d45444f090ce5a9825aa94bee89c9..cb16ac1fadb00ef086d4c1501b6e03ed7d57b4de 100644 (file)
@@ -33,6 +33,7 @@
 
 /* Start flags */
 #define        PSF_DROPPRIVS           0x01
+#define        PSF_CAP_ENTER           0x02
 
 /* Commands */
 #define        PS_BOOTP                0x01
@@ -139,7 +140,7 @@ TAILQ_HEAD(ps_process_head, ps_process);
 
 int ps_mkdir(char *);
 int ps_init(struct dhcpcd_ctx *);
-int ps_dropprivs(struct dhcpcd_ctx *);
+int ps_dropprivs(struct dhcpcd_ctx *, unsigned int);
 int ps_start(struct dhcpcd_ctx *);
 int ps_stop(struct dhcpcd_ctx *);