Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/net bpf(4): Add ioctls BIOCSETWF and BIOCLOCK



details:   https://anonhg.NetBSD.org/src/rev/582f7f5307c3
branches:  trunk
changeset: 934411:582f7f5307c3
user:      roy <roy%NetBSD.org@localhost>
date:      Thu Jun 11 13:36:20 2020 +0000

description:
bpf(4): Add ioctls BIOCSETWF and BIOCLOCK

Once BIOCLOCK is executed, the device becomes locked which prevents the
execution of ioctl(2) commands which can change the underlying parameters
of the bpf(4) device. An example might be the setting of bpf(4) filter
programs or attaching to different network interfaces.

BIOCSETWF can be used to set write filters for outgoing packets.
Currently if a bpf(4) consumer is compromised, the bpf(4) descriptor can
essentially be used as a raw socket, regardless of consumer's UID.
Write filters give users the ability to constrain which packets can be sent
through the bpf(4) descriptor.

Taken from OpenBSD.

diffstat:

 share/man/man4/bpf.4 |   12 +++-
 sys/net/bpf.c        |  135 ++++++++++++++++++++++++++++++++++++--------------
 sys/net/bpf.h        |    4 +-
 sys/net/bpfdesc.h    |    7 +-
 4 files changed, 114 insertions(+), 44 deletions(-)

diffs (truncated from 371 to 300 lines):

diff -r 47cf23f1bc91 -r 582f7f5307c3 share/man/man4/bpf.4
--- a/share/man/man4/bpf.4      Thu Jun 11 13:08:07 2020 +0000
+++ b/share/man/man4/bpf.4      Thu Jun 11 13:36:20 2020 +0000
@@ -1,6 +1,6 @@
 .\" -*- nroff -*-
 .\"
-.\"    $NetBSD: bpf.4,v 1.61 2018/06/26 06:47:57 msaitoh Exp $
+.\"    $NetBSD: bpf.4,v 1.62 2020/06/11 13:36:20 roy Exp $
 .\"
 .\" Copyright (c) 1990, 1991, 1992, 1993, 1994
 .\"    The Regents of the University of California.  All rights reserved.
@@ -24,7 +24,7 @@
 .\" This document is derived in part from the enet man page (enet.4)
 .\" distributed with 4.3BSD Unix.
 .\"
-.Dd June 22, 2018
+.Dd June 11, 2020
 .Dt BPF 4
 .Os
 .Sh NAME
@@ -231,6 +231,10 @@
 .Xr rarpd 8 ,
 which must respond to messages in real time.
 The default for a new file is off.
+.Dv BIOCLOCK
+Set the locked flag on the bpf descriptor.
+This prevents the execution of ioctl commands which could change the
+underlying operating parameters of the device.
 .It Dv BIOCSETF ( struct bpf_program )
 Sets the filter program used by the kernel to discard uninteresting
 packets.
@@ -256,6 +260,10 @@
 See section
 .Sy FILTER MACHINE
 for an explanation of the filter language.
+.It Dv BIOCSETWF ( struct bpf_program )
+Sets the write filter program used by the kernel to control what type
+of packets can be written to the interface.
+See the BIOCSETF command for more information on the bpf filter program.
 .It Dv BIOCVERSION ( struct bpf_version )
 Returns the major and minor version numbers of the filter language currently
 recognized by the kernel.
diff -r 47cf23f1bc91 -r 582f7f5307c3 sys/net/bpf.c
--- a/sys/net/bpf.c     Thu Jun 11 13:08:07 2020 +0000
+++ b/sys/net/bpf.c     Thu Jun 11 13:36:20 2020 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: bpf.c,v 1.236 2020/03/16 21:20:11 pgoyette Exp $       */
+/*     $NetBSD: bpf.c,v 1.237 2020/06/11 13:36:20 roy Exp $    */
 
 /*
  * Copyright (c) 1990, 1991, 1993
@@ -39,7 +39,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: bpf.c,v 1.236 2020/03/16 21:20:11 pgoyette Exp $");
+__KERNEL_RCSID(0, "$NetBSD: bpf.c,v 1.237 2020/06/11 13:36:20 roy Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_bpf.h"
@@ -236,6 +236,7 @@
        PSLIST_ENTRY_DESTROY((__d), bd_bif_dlist_entry)
 
 static int     bpf_allocbufs(struct bpf_d *);
+static u_int   bpf_xfilter(struct bpf_filter **, void *, u_int, u_int);
 static void    bpf_deliver(struct bpf_if *,
                            void *(*cpfn)(void *, const void *, size_t),
                            void *, u_int, u_int, const u_int);
@@ -244,11 +245,12 @@
 static void    bpf_ifname(struct ifnet *, struct ifreq *);
 static void    *bpf_mcpy(void *, const void *, size_t);
 static int     bpf_movein(struct uio *, int, uint64_t,
-                               struct mbuf **, struct sockaddr *);
+                               struct mbuf **, struct sockaddr *,
+                               struct bpf_filter **);
 static void    bpf_attachd(struct bpf_d *, struct bpf_if *);
 static void    bpf_detachd(struct bpf_d *);
 static int     bpf_setif(struct bpf_d *, struct ifreq *);
-static int     bpf_setf(struct bpf_d *, struct bpf_program *);
+static int     bpf_setf(struct bpf_d *, struct bpf_program *, u_long);
 static void    bpf_timed_out(void *);
 static inline void
                bpf_wakeup(struct bpf_d *);
@@ -322,13 +324,14 @@
 
 static int
 bpf_movein(struct uio *uio, int linktype, uint64_t mtu, struct mbuf **mp,
-          struct sockaddr *sockp)
+          struct sockaddr *sockp, struct bpf_filter **wfilter)
 {
        struct mbuf *m, *m0, *n;
        int error;
        size_t len;
        size_t hlen;
        size_t align;
+       u_int slen;
 
        /*
         * Build a sockaddr based on the data link layer type.
@@ -431,6 +434,12 @@
                m = n;
        }
 
+       slen = bpf_xfilter(wfilter, mtod(m, u_char *), len, len);
+       if (slen == 0) {
+               error = EPERM;
+               goto bad;
+       }
+
        if (hlen != 0) {
                /* move link level header in the top of mbuf to sa_data */
                memcpy(sockp->sa_data, mtod(m0, void *), hlen);
@@ -572,7 +581,9 @@
        callout_init(&d->bd_callout, CALLOUT_MPSAFE);
        selinit(&d->bd_sel);
        d->bd_jitcode = NULL;
-       d->bd_filter = NULL;
+       d->bd_rfilter = NULL;
+       d->bd_wfilter = NULL;
+       d->bd_locked = 0;
        BPF_DLIST_ENTRY_INIT(d);
        BPFIF_DLIST_ENTRY_INIT(d);
        d->bd_mtx = mutex_obj_alloc(MUTEX_DEFAULT, IPL_SOFTNET);
@@ -820,7 +831,7 @@
        }
 
        error = bpf_movein(uio, (int)bp->bif_dlt, ifp->if_mtu, &m,
-               (struct sockaddr *) &dst);
+               (struct sockaddr *) &dst, &d->bd_wfilter);
        if (error)
                goto out;
 
@@ -934,6 +945,28 @@
        d->bd_state = BPF_IDLE;
        mutex_exit(d->bd_mtx);
 
+       if (d->bd_locked) {
+               switch (cmd) {
+               case BIOCGBLEN:         /* FALLTHROUGH */
+               case BIOCFLUSH:         /* FALLTHROUGH */
+               case BIOCGDLT:          /* FALLTHROUGH */
+               case BIOCGDLTLIST:      /* FALLTHROUGH */
+               case BIOCGETIF:         /* FALLTHROUGH */
+               case BIOCGRTIMEOUT:     /* FALLTHROUGH */
+               case BIOCGSTATS:        /* FALLTHROUGH */
+               case BIOCVERSION:       /* FALLTHROUGH */
+               case BIOCGHDRCMPLT:     /* FALLTHROUGH */
+               case FIONREAD:          /* FALLTHROUGH */
+               case BIOCLOCK:          /* FALLTHROUGH */
+               case BIOCSRTIMEOUT:     /* FALLTHROUGH */
+               case BIOCIMMEDIATE:     /* FALLTHROUGH */
+               case TIOCGPGRP:
+                       break;
+               default:
+                       return EPERM;
+               }
+       }
+
        switch (cmd) {
 
        default:
@@ -992,8 +1025,13 @@
        /*
         * Set link layer read filter.
         */
-       case BIOCSETF:
-               error = bpf_setf(d, addr);
+       case BIOCSETF:          /* FALLTHROUGH */
+       case BIOCSETWF:
+               error = bpf_setf(d, addr, cmd);
+               break;
+
+       case BIOCLOCK:
+               d->bd_locked = 1;
                break;
 
        /*
@@ -1267,12 +1305,12 @@
  * free it and replace it.  Returns EINVAL for bogus requests.
  */
 static int
-bpf_setf(struct bpf_d *d, struct bpf_program *fp)
+bpf_setf(struct bpf_d *d, struct bpf_program *fp, u_long cmd)
 {
        struct bpf_insn *fcode;
        bpfjit_func_t jcode;
        size_t flen, size = 0;
-       struct bpf_filter *oldf, *newf;
+       struct bpf_filter *oldf, *newf, **storef;
 
        jcode = NULL;
        flen = fp->bf_len;
@@ -1303,13 +1341,20 @@
        newf->bf_insn = fcode;
        newf->bf_size = size;
        newf->bf_jitcode = jcode;
-       d->bd_jitcode = jcode; /* XXX just for kvm(3) users */
+       if (cmd == BIOCSETF)
+               d->bd_jitcode = jcode; /* XXX just for kvm(3) users */
 
        /* Need to hold bpf_mtx for pserialize_perform */
        mutex_enter(&bpf_mtx);
        mutex_enter(d->bd_mtx);
-       oldf = d->bd_filter;
-       atomic_store_release(&d->bd_filter, newf);
+       if (cmd == BIOCSETWF) {
+               oldf = d->bd_wfilter;
+               storef = &d->bd_wfilter;
+       } else {
+               oldf = d->bd_rfilter;
+               storef = &d->bd_rfilter;
+       }
+       atomic_store_release(storef, newf);
        reset_d(d);
        pserialize_perform(bpf_psz);
        mutex_exit(d->bd_mtx);
@@ -1560,6 +1605,31 @@
        return dst_arg;
 }
 
+static inline u_int
+bpf_xfilter(struct bpf_filter **filter, void *pkt, u_int pktlen, u_int buflen)
+{
+       struct bpf_filter *filt;
+       uint32_t mem[BPF_MEMWORDS];
+       bpf_args_t args = {
+               .pkt = (const uint8_t *)pkt,
+               .wirelen = pktlen,
+               .buflen = buflen,
+               .mem = mem,
+               .arg = NULL
+       };
+       u_int slen;
+
+       filt = atomic_load_consume(filter);
+       if (filt == NULL) /* No filter means accept all. */
+               return (u_int)-1;
+
+       if (filt->bf_jitcode != NULL)
+               slen = filt->bf_jitcode(NULL, &args);
+       else
+               slen = bpf_filter_ext(NULL, filt->bf_insn, &args);
+       return slen;
+}
+
 /*
  * Dispatch a packet to all the listeners on interface bp.
  *
@@ -1573,18 +1643,11 @@
 bpf_deliver(struct bpf_if *bp, void *(*cpfn)(void *, const void *, size_t),
     void *pkt, u_int pktlen, u_int buflen, const u_int direction)
 {
-       uint32_t mem[BPF_MEMWORDS];
-       bpf_args_t args = {
-               .pkt = (const uint8_t *)pkt,
-               .wirelen = pktlen,
-               .buflen = buflen,
-               .mem = mem,
-               .arg = NULL
-       };
        bool gottime = false;
        struct timespec ts;
        struct bpf_d *d;
        int s;
+       u_int slen;
 
        KASSERT(!cpu_intr_p());
 
@@ -1595,9 +1658,6 @@
         */
        s = pserialize_read_enter();
        BPFIF_DLIST_READER_FOREACH(d, bp) {
-               u_int slen = 0;
-               struct bpf_filter *filter;
-
                if (direction == BPF_D_IN) {
                        if (d->bd_direction == BPF_D_OUT)
                                continue;
@@ -1609,18 +1669,10 @@
                atomic_inc_ulong(&d->bd_rcount);
                BPF_STATINC(recv);
 
-               filter = atomic_load_consume(&d->bd_filter);
-               if (filter != NULL) {
-                       if (filter->bf_jitcode != NULL)
-                               slen = filter->bf_jitcode(NULL, &args);
-                       else
-                               slen = bpf_filter_ext(NULL, filter->bf_insn,
-                                   &args);
-               }
+               slen = bpf_xfilter(&d->bd_rfilter, pkt, pktlen, buflen);
+               if (slen == 0)
+                       continue;
 
-               if (!slen) {
-                       continue;
-               }
                if (!gottime) {
                        gottime = true;
                        nanotime(&ts);



Home | Main Index | Thread Index | Old Index