diff options
author | Can Erkin Acar <canacar@cvs.openbsd.org> | 2003-10-22 18:42:41 +0000 |
---|---|---|
committer | Can Erkin Acar <canacar@cvs.openbsd.org> | 2003-10-22 18:42:41 +0000 |
commit | 6693f734d4d99c32579a9cef43ff0f9a39e4dc62 (patch) | |
tree | edbf003bbcf42419432abe8645aca40cac4fcdd9 | |
parent | 2e46b2f39d9833b32544b6e61959e55ae2d0700d (diff) |
Add locking and write filtering to bpf descriptors.
Locking prevents dangerous ioctls such as changing the
interface and sending signals to be executed by an
unprivileged process. A filter can also be applied
to packets injected through a bpf descriptor.
These features allow programs using bpf descriptors to
safely drop/seperate privileges.
ok frantzen@ henning@ mcbride@
-rw-r--r-- | share/man/man4/bpf.4 | 88 | ||||
-rw-r--r-- | sys/net/bpf.c | 97 | ||||
-rw-r--r-- | sys/net/bpf.h | 4 | ||||
-rw-r--r-- | sys/net/bpfdesc.h | 8 |
4 files changed, 167 insertions, 30 deletions
diff --git a/share/man/man4/bpf.4 b/share/man/man4/bpf.4 index 70070224b47..e251d2ea9e9 100644 --- a/share/man/man4/bpf.4 +++ b/share/man/man4/bpf.4 @@ -1,4 +1,4 @@ -.\" $OpenBSD: bpf.4,v 1.18 2003/07/09 11:45:44 jmc Exp $ +.\" $OpenBSD: bpf.4,v 1.19 2003/10/22 18:42:40 canacar Exp $ .\" $NetBSD: bpf.4,v 1.7 1995/09/27 18:31:50 thorpej Exp $ .\" .\" Copyright (c) 1990 The Regents of the University of California. @@ -43,7 +43,7 @@ The packet filter appears as a character special device, etc. After opening the device, the file descriptor must be bound to a specific network interface with the -.Dv BIOSETIF +.Dv BIOCSETIF ioctl. A given interface can be shared between multiple listeners, and the filter underlying each descriptor will see an identical packet stream. @@ -90,8 +90,15 @@ macros to extract multi-byte values. A packet can be sent out on the network by writing to a .Nm file descriptor. +Each descriptor can also have a user-settable filter +for controlling the writes. +Only packets matching the filter are sent out of the interface. The writes are unbuffered, meaning only one packet can be processed per write. -Currently, only writes to Ethernets and SLIP links are supported. +.Pp +Once a descriptor is configured, further changes to the configuration +can be prevented using the +.Dv BIOCLOCK +ioctl. .Ss Ioctls The ioctl command codes below are defined in .Aq Pa net/bpf.h . @@ -108,6 +115,8 @@ Additionally, and .Dv BIOCSETIF require +.Aq Pa sys/socket.h +and .Aq Pa net/if.h . .Pp The (third) argument to the @@ -150,6 +159,55 @@ promiscuously are closed. Flushes the buffer of incoming packets and resets the statistics that are returned by .Dv BIOCGSTATS . +.It Dv BIOCLOCK +This ioctl is designed to prevent the security issues associated +with an open +.Nm +descriptor in unprivileged programs. +Even with dropped privileges, an open +.Nm +descriptor can be abused by a rogue program to listen on any interface +on the system, send packets on these interfaces if the descriptor was +opened read-write and send signals to arbitrary processes using the +signaling mechanism of +.Nm bpf . +By allowing only +.Dq known safe +ioctls, the +.DV BIOCLOCK +ioctl prevents this abuse. +The allowable ioctls are +.Dv BIOCGBLEN , +.Dv BIOCFLUSH , +.Dv BIOCGDLT , +.Dv BIOCGETIF , +.Dv BIOCGRTIMEOUT , +.Dv BIOCSRTIMEOUT , +.Dv BIOCIMMEDIATE , +.Dv BIOCGSTATS , +.Dv BIOCVERSION , +.Dv BIOCGRSIG , +.Dv BIOCGHDRCMPLT , +.Dv TIOCGPGRP , +and +.Dv FIONREAD . +Use of any other ioctl is denied with error +.Er EPERM . +Once a descriptor is locked, it is not possible to unlock it. +A process with root privileges is not affected by the lock. +.Pp +A privileged program can open a +.Nm +device, drop privileges, set the interface, filters and modes on the +descriptor, and lock it. +Once the descriptor is locked, the system is safe +from further abuse through the descriptor. +Locking a descriptor does not prevent writes. +If the application does not need to send packets through +.Nm bpf , +it can open the device read-only to prevent writing. +If sending packets is necessary, a write-filter can be set before locking the +descriptor to prevent arbitrary packets from being sent out. .It Dv BIOCGETIF ( Li "struct ifreq" ) Returns the name of the hardware interface that the file is listening on. The name is returned in the @@ -229,6 +287,21 @@ are performed. See section .Sx FILTER MACHINE for an explanation of the filter language. +.It Dv BIOCSETWF ( Li "struct bpf_program" ) +Sets the filter program used by the kernel to filter the packets +written to the descriptor before the packets are sent out on the +network. +See +.Dv BIOCSETF +for a description of the filter program. +This ioctl also acts as +.Dv BIOCFLUSH . +.Pp +Note that the filter operates on the packet data written to the descriptor. +If the +.Dq header complete +flag is not set, the kernel sets the link-layer source address +of the packet after filtering. .It Dv BIOCVERSION ( Li "struct bpf_version" ) Returns the major and minor version numbers of the filter language currently recognized by the kernel. @@ -756,9 +829,12 @@ pc += (A == X) ? jt : jf pc += (A & X) ? jt : jf .El .It Dv BPF_RET -The return instructions terminate the filter program and specify the amount -of packet to accept (i.e., they return the truncation amount). -A return value of zero indicates that the packet should be ignored. +The return instructions terminate the filter program and specify the +amount of packet to accept (i.e., they return the truncation amount) +or, for the write filter, the maximum acceptable size for the packet +(i.e., the packet is dropped if it is larger than the returned +amount). +A return value of zero indicates that the packet should be ignored/dropped. The return value is either a constant .Pf ( Dv BPF_K ) or the accumulator diff --git a/sys/net/bpf.c b/sys/net/bpf.c index 425a2d41b75..3c171166439 100644 --- a/sys/net/bpf.c +++ b/sys/net/bpf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bpf.c,v 1.39 2003/10/04 01:03:49 deraadt Exp $ */ +/* $OpenBSD: bpf.c,v 1.40 2003/10/22 18:42:40 canacar Exp $ */ /* $NetBSD: bpf.c,v 1.33 1997/02/21 23:59:35 thorpej Exp $ */ /* @@ -80,7 +80,8 @@ int bpf_allocbufs(struct bpf_d *); void bpf_freed(struct bpf_d *); void bpf_ifname(struct ifnet *, struct ifreq *); void bpf_mcopy(const void *, void *, size_t); -int bpf_movein(struct uio *, int, struct mbuf **, struct sockaddr *); +int bpf_movein(struct uio *, int, struct mbuf **, + struct sockaddr *, struct bpf_insn *); void bpf_attachd(struct bpf_d *, struct bpf_if *); void bpf_detachd(struct bpf_d *); int bpf_setif(struct bpf_d *, struct ifreq *); @@ -95,16 +96,18 @@ void filt_bpfrdetach(struct knote *); int filt_bpfread(struct knote *, long); int -bpf_movein(uio, linktype, mp, sockp) +bpf_movein(uio, linktype, mp, sockp, filter) register struct uio *uio; int linktype; register struct mbuf **mp; register struct sockaddr *sockp; + struct bpf_insn *filter; { struct mbuf *m; int error; int len; int hlen; + int slen; /* XXX u_int ? */ /* * Build a sockaddr based on the data link layer type. @@ -181,19 +184,31 @@ bpf_movein(uio, linktype, mp, sockp) } m->m_len = len; *mp = m; + + error = uiomove(mtod(m, caddr_t), len, uio); + if (error) + goto bad; + + slen = bpf_filter(filter, mtod(m, u_char *), len, len); + if (slen == 0 || slen < len) { + error = EPERM; + goto bad; + } + + if (m->m_len < hlen) { + error = EPERM; + goto bad; + } /* - * Make room for link header. + * Make room for link header, and copy it to sockaddr */ if (hlen != 0) { + bcopy(m->m_data, sockp->sa_data, hlen); m->m_len -= hlen; m->m_data += hlen; /* XXX */ - error = uiomove((caddr_t)sockp->sa_data, hlen, uio); - if (error) - goto bad; } - error = uiomove(mtod(m, caddr_t), len - hlen, uio); - if (!error) - return (0); + + return (0); bad: m_freem(m); return (error); @@ -511,7 +526,7 @@ bpfwrite(dev, uio, ioflag) return (0); error = bpf_movein(uio, (int)d->bd_bif->bif_dlt, &m, - (struct sockaddr *)&dst); + (struct sockaddr *)&dst, d->bd_wfilter); if (error) return (error); @@ -581,6 +596,29 @@ bpfioctl(dev, cmd, addr, flag, p) register struct bpf_d *d = &bpf_dtab[minor(dev)]; int s, error = 0; + if (d->bd_locked && suser(p, 0) != 0) { + /* list of allowed ioctls when locked and not root */ + switch (cmd) { + case BIOCGBLEN: + case BIOCFLUSH: + case BIOCGDLT: + case BIOCGETIF: + case BIOCGRTIMEOUT: + case BIOCGSTATS: + case BIOCVERSION: + case BIOCGRSIG: + case BIOCGHDRCMPLT: + case FIONREAD: + case BIOCLOCK: + case BIOCSRTIMEOUT: + case BIOCIMMEDIATE: + case TIOCGPGRP: + break; + default: + return (EPERM); + } + } + switch (cmd) { default: @@ -632,7 +670,14 @@ bpfioctl(dev, cmd, addr, flag, p) * Set link layer read filter. */ case BIOCSETF: - error = bpf_setf(d, (struct bpf_program *)addr); + error = bpf_setf(d, (struct bpf_program *)addr, 0); + break; + + /* + * Set link layer write filter. + */ + case BIOCSETWF: + error = bpf_setf(d, (struct bpf_program *)addr, 1); break; /* @@ -753,6 +798,9 @@ bpfioctl(dev, cmd, addr, flag, p) d->bd_hdrcmplt = *(u_int *)addr ? 1 : 0; break; + case BIOCLOCK: /* set "locked" flag (no reset) */ + d->bd_locked = 1; + break; case FIONBIO: /* Non-blocking I/O */ if (*(int *)addr) @@ -807,20 +855,24 @@ bpfioctl(dev, cmd, addr, flag, p) * free it and replace it. Returns EINVAL for bogus requests. */ int -bpf_setf(d, fp) +bpf_setf(d, fp, wf) struct bpf_d *d; struct bpf_program *fp; + int wf; { struct bpf_insn *fcode, *old; u_int flen, size; int s; - old = d->bd_filter; + old = wf ? d->bd_wfilter : d->bd_rfilter; if (fp->bf_insns == 0) { if (fp->bf_len != 0) return (EINVAL); s = splimp(); - d->bd_filter = 0; + if (wf) + d->bd_wfilter = 0; + else + d->bd_rfilter = 0; bpf_reset_d(d); splx(s); if (old != 0) @@ -836,7 +888,10 @@ bpf_setf(d, fp) if (copyin((caddr_t)fp->bf_insns, (caddr_t)fcode, size) == 0 && bpf_validate(fcode, (int)flen)) { s = splimp(); - d->bd_filter = fcode; + if (wf) + d->bd_wfilter = fcode; + else + d->bd_rfilter = fcode; bpf_reset_d(d); splx(s); if (old != 0) @@ -1050,7 +1105,7 @@ bpf_tap(arg, pkt, pktlen) bp = (struct bpf_if *)arg; for (d = bp->bif_dlist; d != 0; d = d->bd_next) { ++d->bd_rcount; - slen = bpf_filter(d->bd_filter, pkt, pktlen, pktlen); + slen = bpf_filter(d->bd_rfilter, pkt, pktlen, pktlen); if (slen != 0) bpf_catchpacket(d, pkt, pktlen, slen, bcopy); } @@ -1105,7 +1160,7 @@ bpf_mtap(arg, m) for (d = bp->bif_dlist; d != 0; d = d->bd_next) { ++d->bd_rcount; - slen = bpf_filter(d->bd_filter, (u_char *)m, pktlen, 0); + slen = bpf_filter(d->bd_rfilter, (u_char *)m, pktlen, 0); if (slen != 0) bpf_catchpacket(d, (u_char *)m, pktlen, slen, bpf_mcopy); } @@ -1242,8 +1297,10 @@ bpf_freed(d) if (d->bd_fbuf != 0) free(d->bd_fbuf, M_DEVBUF); } - if (d->bd_filter) - free((caddr_t)d->bd_filter, M_DEVBUF); + if (d->bd_rfilter) + free((caddr_t)d->bd_rfilter, M_DEVBUF); + if (d->bd_wfilter) + free((caddr_t)d->bd_wfilter, M_DEVBUF); D_MARKFREE(d); } diff --git a/sys/net/bpf.h b/sys/net/bpf.h index e1738dfb4f2..50e210ce5b6 100644 --- a/sys/net/bpf.h +++ b/sys/net/bpf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bpf.h,v 1.23 2003/08/25 08:16:41 fgsch Exp $ */ +/* $OpenBSD: bpf.h,v 1.24 2003/10/22 18:42:40 canacar Exp $ */ /* $NetBSD: bpf.h,v 1.15 1996/12/13 07:57:33 mikel Exp $ */ /* @@ -111,6 +111,8 @@ struct bpf_version { #define BIOCGRSIG _IOR('B',115, u_int) #define BIOCGHDRCMPLT _IOR('B',116, u_int) #define BIOCSHDRCMPLT _IOW('B',117, u_int) +#define BIOCLOCK _IO('B',118) +#define BIOCSETWF _IOW('B',119, struct bpf_program) struct bpf_timeval { u_int32_t tv_sec; diff --git a/sys/net/bpfdesc.h b/sys/net/bpfdesc.h index 9060bc33915..9c3eb6d3a39 100644 --- a/sys/net/bpfdesc.h +++ b/sys/net/bpfdesc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bpfdesc.h,v 1.10 2003/06/02 23:28:11 millert Exp $ */ +/* $OpenBSD: bpfdesc.h,v 1.11 2003/10/22 18:42:40 canacar Exp $ */ /* $NetBSD: bpfdesc.h,v 1.11 1995/09/27 18:30:42 thorpej Exp $ */ /* @@ -67,13 +67,15 @@ struct bpf_d { struct bpf_if * bd_bif; /* interface descriptor */ u_long bd_rtout; /* Read timeout in 'ticks' */ u_long bd_rdStart; /* when the read started */ - struct bpf_insn *bd_filter; /* filter code */ + struct bpf_insn *bd_rfilter; /* read filter code */ + struct bpf_insn *bd_wfilter; /* write filter code */ u_long bd_rcount; /* number of packets received */ u_long bd_dcount; /* number of packets dropped */ u_char bd_promisc; /* true if listening promiscuously */ u_char bd_state; /* idle, waiting, or timed out */ u_char bd_immediate; /* true to return on packet arrival */ + u_char bd_locked; /* true if descriptor is locked */ int bd_hdrcmplt; /* false to fill in src lladdr automatically */ int bd_async; /* non-zero if packet reception should generate signal */ int bd_sig; /* signal to send upon packet reception */ @@ -97,6 +99,6 @@ struct bpf_if { }; #ifdef _KERNEL -int bpf_setf(struct bpf_d *, struct bpf_program *); +int bpf_setf(struct bpf_d *, struct bpf_program *, int); #endif /* _KERNEL */ #endif /* _NET_BPFDESC_H_ */ |