]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
BPF: Set write filters where supported
authorRoy Marples <roy@marples.name>
Fri, 15 May 2020 19:23:55 +0000 (20:23 +0100)
committerRoy Marples <roy@marples.name>
Fri, 15 May 2020 19:23:55 +0000 (20:23 +0100)
While here make Capsicum and Pledge more granular so we can
easily Pledge the BPF BOOTP process but not the ARP one.

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

index c9f3b084f240bdcc8e1bb92d9a5470eb8540114b..1517355fbec0edce4177f641271ad7ba277e3336 100644 (file)
--- a/src/bpf.c
+++ b/src/bpf.c
@@ -303,14 +303,22 @@ next:
 int
 bpf_attach(int fd, void *filter, unsigned int filter_len)
 {
-       struct bpf_program pf;
+       struct bpf_program pf = { .bf_insns = filter, .bf_len = filter_len };
 
        /* Install the filter. */
-       memset(&pf, 0, sizeof(pf));
-       pf.bf_insns = filter;
-       pf.bf_len = filter_len;
        return ioctl(fd, BIOCSETF, &pf);
 }
+
+#ifdef BIOCSETWF
+static int
+bpf_wattach(int fd, void *filter, unsigned int filter_len)
+{
+       struct bpf_program pf = { .bf_insns = filter, .bf_len = filter_len };
+
+       /* Install the filter. */
+       return ioctl(fd, BIOCSETWF, &pf);
+}
+#endif
 #endif
 
 #ifndef __sun
@@ -488,8 +496,8 @@ static const struct bpf_insn bpf_arp_filter [] = {
 #define BPF_ARP_LEN            BPF_ARP_ETHER_LEN + BPF_ARP_FILTER_LEN + \
                                BPF_CMP_HWADDR_LEN + BPF_ARP_ADDRS_LEN
 
-int
-bpf_arp(struct interface *ifp, int fd)
+static int
+bpf_arp_rw(struct interface *ifp, int fd, bool read)
 {
        struct bpf_insn bpf[BPF_ARP_LEN];
        struct bpf_insn *bp;
@@ -518,6 +526,18 @@ bpf_arp(struct interface *ifp, int fd)
        memcpy(bp, bpf_arp_filter, sizeof(bpf_arp_filter));
        bp += BPF_ARP_FILTER_LEN;
 
+#ifdef BIOCSETWF
+       if (!read) {
+               /* All passed, return the packet. */
+               BPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET);
+               bp++;
+
+               return bpf_wattach(fd, bpf, (unsigned int)(bp - bpf));
+       }
+#else
+       UNUSED(read);
+#endif
+
        /* Ensure it's not from us. */
        bp += bpf_cmp_hwaddr(bp, BPF_CMP_HWADDR_LEN, sizeof(struct arphdr),
                             false, ifp->hwaddr, ifp->hwlen);
@@ -579,6 +599,19 @@ noaddrs:
 
        return bpf_attach(fd, bpf, (unsigned int)(bp - bpf));
 }
+
+int
+bpf_arp(struct interface *ifp, int fd)
+{
+
+#ifdef BIOCSETWF
+       if (bpf_arp_rw(ifp, fd, false) == -1)
+               return -1;
+#endif
+
+       return bpf_arp_rw(ifp, fd, true);
+}
+
 #endif
 
 #ifdef ARPHRD_NONE
@@ -602,7 +635,7 @@ static const struct bpf_insn bpf_bootp_ether[] = {
 #define BOOTP_MIN_SIZE         sizeof(struct ip) + sizeof(struct udphdr) + \
                                sizeof(struct bootp)
 
-static const struct bpf_insn bpf_bootp_filter[] = {
+static const struct bpf_insn bpf_bootp_base[] = {
        /* Make sure it's an IPv4 packet. */
        BPF_STMT(BPF_LD + BPF_B + BPF_IND, 0),
        BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0xf0),
@@ -625,22 +658,36 @@ static const struct bpf_insn bpf_bootp_filter[] = {
        BPF_STMT(BPF_ALU + BPF_MUL + BPF_K, 4),
        BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0),
        BPF_STMT(BPF_MISC + BPF_TAX, 0),
+};
+#define BPF_BOOTP_BASE_LEN     __arraycount(bpf_bootp_base)
 
+static const struct bpf_insn bpf_bootp_read[] = {
        /* Make sure it's from and to the right port. */
        BPF_STMT(BPF_LD + BPF_W + BPF_IND, 0),
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (BOOTPS << 16) + BOOTPC, 1, 0),
        BPF_STMT(BPF_RET + BPF_K, 0),
 };
+#define BPF_BOOTP_READ_LEN     __arraycount(bpf_bootp_read)
+
+#ifdef BIOCSETWF
+static const struct bpf_insn bpf_bootp_write[] = {
+       /* Make sure it's from and to the right port. */
+       BPF_STMT(BPF_LD + BPF_W + BPF_IND, 0),
+       BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (BOOTPC << 16) + BOOTPS, 1, 0),
+       BPF_STMT(BPF_RET + BPF_K, 0),
+};
+#define BPF_BOOTP_WRITE_LEN    __arraycount(bpf_bootp_write)
+#endif
 
-#define BPF_BOOTP_FILTER_LEN   __arraycount(bpf_bootp_filter)
 #define BPF_BOOTP_CHADDR_LEN   ((BOOTP_CHADDR_LEN / 4) * 3)
 #define        BPF_BOOTP_XID_LEN       4 /* BOUND check is 4 instructions */
 
-#define BPF_BOOTP_LEN          BPF_BOOTP_ETHER_LEN + BPF_BOOTP_FILTER_LEN \
-                               + BPF_BOOTP_XID_LEN + BPF_BOOTP_CHADDR_LEN + 4
+#define BPF_BOOTP_LEN          BPF_BOOTP_ETHER_LEN + \
+                               BPF_BOOTP_BASE_LEN + BPF_BOOTP_READ_LEN + \
+                               BPF_BOOTP_XID_LEN + BPF_BOOTP_CHADDR_LEN + 4
 
-int
-bpf_bootp(struct interface *ifp, int fd)
+static int
+bpf_bootp_rw(struct interface *ifp, int fd, bool read)
 {
 #if 0
        const struct dhcp_state *state = D_CSTATE(ifp);
@@ -670,8 +717,26 @@ bpf_bootp(struct interface *ifp, int fd)
        }
 
        /* Copy in the main filter. */
-       memcpy(bp, bpf_bootp_filter, sizeof(bpf_bootp_filter));
-       bp += BPF_BOOTP_FILTER_LEN;
+       memcpy(bp, bpf_bootp_base, sizeof(bpf_bootp_base));
+       bp += BPF_BOOTP_BASE_LEN;
+
+#ifdef BIOCSETWF
+       if (!read) {
+               memcpy(bp, bpf_bootp_write, sizeof(bpf_bootp_write));
+               bp += BPF_BOOTP_WRITE_LEN;
+
+               /* All passed, return the packet. */
+               BPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET);
+               bp++;
+
+               return bpf_wattach(fd, bpf, (unsigned int)(bp - bpf));
+       }
+#else
+       UNUSED(read);
+#endif
+
+       memcpy(bp, bpf_bootp_read, sizeof(bpf_bootp_read));
+       bp += BPF_BOOTP_READ_LEN;
 
        /* These checks won't work when same IP exists on other interfaces. */
 #if 0
@@ -715,3 +780,18 @@ bpf_bootp(struct interface *ifp, int fd)
 
        return bpf_attach(fd, bpf, (unsigned int)(bp - bpf));
 }
+
+int
+bpf_bootp(struct interface *ifp, int fd)
+{
+
+#ifdef BIOCSETWF
+       if (bpf_bootp_rw(ifp, fd, true) == -1 ||
+           bpf_bootp_rw(ifp, fd, false) == -1 ||
+           ioctl(fd, BIOCLOCK) == -1)
+               return -1;
+       return 0;
+#else
+       return bpf_bootp_rw(ifp, fd, true);
+#endif
+}
index 3f5c35d5cf6eb26845b27c68b04ebb8e6eb9b620..10e5e6ee226b11d1db96565fa4f2975d8003a2aa 100644 (file)
@@ -2269,10 +2269,22 @@ printpidfile:
 
 #ifdef PRIVSEP
        if (ctx.options & DHCPCD_PRIVSEP) {
-               if (ps_dropprivs(&ctx, PSF_CAP_ENTER | PSF_PLEDGE) == -1) {
+               if (ps_dropprivs(&ctx) == -1) {
                        logerr("ps_dropprivs");
                        goto exit_failure;
                }
+#ifdef HAVE_CAPSICUM
+               if (cap_enter() == -1 && errno != ENOSYS) {
+                       logerr("%s: cap_enter", __func__);
+                       goto exit_failure;
+               }
+#endif
+#ifdef HAVE_PLEDGE
+               if (pledge("stdio inet route dns", NULL) == -1) {
+                       logerr("%s: pledge", __func__);
+                       goto exit_failure;
+               }
+#endif
        }
 #endif
 
index 8326425d03ae973425e137c51f63644434844c6d..06247c0f8ec4049811f6731589e72fb4d1dcb400 100644 (file)
@@ -203,7 +203,6 @@ ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
        struct iovec *iov = msg->msg_iov;
        struct interface *ifp;
        struct ipv4_state *istate;
-       unsigned int flags = PSF_DROPPRIVS | PSF_CAP_ENTER;
 
        cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
        psp = ps_findprocess(ctx, &psm->ps_id);
@@ -257,19 +256,12 @@ ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
                psp->psp_proto = ETHERTYPE_ARP;
                psp->psp_protostr = "ARP";
                psp->psp_filter = bpf_arp;
-               /*
-                * Pledge is currently useless for BPF ARP because we cannot
-                * change the filter:
-                * http://openbsd-archive.7691.n7.nabble.com/ \
-                *        pledge-bpf-32bit-arch-unbreak-td299901.html
-                */
                break;
 #endif
        case PS_BPF_BOOTP:
                psp->psp_proto = ETHERTYPE_IP;
                psp->psp_protostr = "BOOTP";
                psp->psp_filter = bpf_bootp;
-               flags |= PSF_PLEDGE;
                break;
        }
 
@@ -277,12 +269,21 @@ ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
            &psp->psp_pid, &psp->psp_fd,
            ps_bpf_recvmsg, NULL, psp,
            ps_bpf_start_bpf, ps_bpf_signal_bpfcb,
-           flags);
+           PSF_DROPPRIVS);
        switch (start) {
        case -1:
                ps_freeprocess(psp);
                return -1;
        case 0:
+#ifdef HAVE_CAPSICUM
+       if (cap_enter() == -1 && errno != ENOSYS)
+               logerr("%s: cap_enter", __func__);
+#endif
+#ifdef HAVE_PLEDGE
+       /* Cant change BPF fitler for ARP yet. */
+       if (cmd != PS_BPF_ARP && pledge("stdio", NULL) == -1)
+               logerr("%s: pledge", __func__);
+#endif
                break;
        default:
 #ifdef PRIVSEP_DEBUG
index 70074160b1b02bea7b5aed1d237428953fa64a19..74cdf722520bfd39ec3ca0dec0951d810ae6ec65 100644 (file)
@@ -46,9 +46,6 @@
 #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
 
@@ -305,11 +302,25 @@ ps_inet_dodispatch(void *arg)
 pid_t
 ps_inet_start(struct dhcpcd_ctx *ctx)
 {
+       pid_t pid;
 
-       return ps_dostart(ctx, &ctx->ps_inet_pid, &ctx->ps_inet_fd,
+       pid = ps_dostart(ctx, &ctx->ps_inet_pid, &ctx->ps_inet_fd,
            ps_inet_recvmsg, ps_inet_dodispatch, ctx,
            ps_inet_startcb, ps_inet_signalcb,
-           PSF_DROPPRIVS | PSF_PLEDGE);
+           PSF_DROPPRIVS);
+
+#ifdef HAVE_CAPSICUM
+#if 0          /* This breaks sendmsg() */
+       if (cap_enter() == -1 && errno != ENOSYS)
+               logerr("%s: cap_enter", __func__);
+#endif
+#endif
+#ifdef HAVE_PLEDGE
+       if (pid == 0 && pledge("stdio inet", NULL) == -1)
+               logerr("%s: pledge", __func__);
+#endif
+
+       return pid;
 }
 
 int
@@ -555,12 +566,22 @@ ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm,
            &psp->psp_pid, &psp->psp_fd,
            ps_inet_recvmsgpsp, NULL, psp,
            start_func, ps_inet_signalcb,
-           PSF_DROPPRIVS | PSF_PLEDGE);
+           PSF_DROPPRIVS);
        switch (start) {
        case -1:
                ps_freeprocess(psp);
                return -1;
        case 0:
+#ifdef HAVE_CAPSICUM
+#if 0          /* This breaks sendmsg() */
+               if (cap_enter() == -1 && errno != ENOSYS)
+                       logerr("%s: cap_enter", __func__);
+#endif
+#endif
+#ifdef HAVE_PLEDGE
+               if (pledge("stdio inet", NULL) == -1)
+                       logerr("%s: pledge", __func__);
+#endif
                break;
        default:
                break;
index 1bf068e990eafc992f314f1e7daee8591cb2e39a..621aed28e777b7cf1831701b0b54ee9fb94af66f 100644 (file)
@@ -108,7 +108,7 @@ ps_init(struct dhcpcd_ctx *ctx)
 }
 
 int
-ps_dropprivs(struct dhcpcd_ctx *ctx, unsigned int flags)
+ps_dropprivs(struct dhcpcd_ctx *ctx)
 {
        struct passwd *pw = ctx->ps_user;
 
@@ -127,33 +127,6 @@ ps_dropprivs(struct dhcpcd_ctx *ctx, unsigned int flags)
                return -1;
        }
 
-#ifdef HAVE_CAPSICUM
-       if (flags & PSF_CAP_ENTER) {
-               if (cap_enter() == -1 && errno != ENOSYS) {
-                       logerr("%s: cap_enter", __func__);
-                       return -1;
-               }
-       }
-#else
-       UNUSED(flags);
-#endif
-
-#ifdef HAVE_PLEDGE
-       if (flags & PSF_PLEDGE) {
-               const char *promises;
-
-               if (ctx->options & DHCPCD_UNPRIV)
-                       promises = "stdio dns bpf";
-               else
-                       /* SIOCGIFGROUP requires inet */
-                       promises = "stdio dns inet route";
-                if (pledge(promises, NULL) == -1) {
-                        logerr("%s: pledge", __func__);
-                        return -1;
-                }
-       }
-#endif
-
        return 0;
 }
 
@@ -267,7 +240,7 @@ ps_dostart(struct dhcpcd_ctx *ctx,
        }
 
        if (flags & PSF_DROPPRIVS)
-               ps_dropprivs(ctx, flags);
+               ps_dropprivs(ctx);
 
        return 0;
 
index 94346caca9aa1b72600c3bb01c5b0137d4d31826..7fb5ca9b590ef8f11ee7a85ade40fdef504b6110 100644 (file)
@@ -33,8 +33,6 @@
 
 /* Start flags */
 #define        PSF_DROPPRIVS           0x01
-#define        PSF_CAP_ENTER           0x02
-#define        PSF_PLEDGE              0x04
 
 /* Protocols */
 #define        PS_BOOTP                0x0001
@@ -147,7 +145,7 @@ TAILQ_HEAD(ps_process_head, ps_process);
 #endif
 
 int ps_init(struct dhcpcd_ctx *);
-int ps_dropprivs(struct dhcpcd_ctx *, unsigned int);
+int ps_dropprivs(struct dhcpcd_ctx *);
 int ps_start(struct dhcpcd_ctx *);
 int ps_stop(struct dhcpcd_ctx *);