summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCan Erkin Acar <canacar@cvs.openbsd.org>2003-10-22 18:42:41 +0000
committerCan Erkin Acar <canacar@cvs.openbsd.org>2003-10-22 18:42:41 +0000
commit6693f734d4d99c32579a9cef43ff0f9a39e4dc62 (patch)
treeedbf003bbcf42419432abe8645aca40cac4fcdd9
parent2e46b2f39d9833b32544b6e61959e55ae2d0700d (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.488
-rw-r--r--sys/net/bpf.c97
-rw-r--r--sys/net/bpf.h4
-rw-r--r--sys/net/bpfdesc.h8
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_ */