On Thu, Nov 14, 2013 at 05:38:14PM -0700, Theo de Raadt wrote:
> Beautiful.

I seems there was enough discussion.  The Security argument is more
important than the others.  The new diff has no performance impact
when pf is turned on.

So I need OKs.

bluhm

Index: net/pf.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/net/pf.c,v
retrieving revision 1.857
diff -u -p -u -p -r1.857 pf.c
--- net/pf.c    30 Oct 2013 11:35:10 -0000      1.857
+++ net/pf.c    13 Nov 2013 23:14:32 -0000
@@ -6490,6 +6490,7 @@ pf_test(sa_family_t af, int fwdir, struc
                }
        }
        pd.eh = eh;
+       pd.m->m_pkthdr.pf.flags |= PF_TAG_PROCESSED;
 
        switch (pd.virtual_proto) {
 
Index: netinet6/ip6_input.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_input.c,v
retrieving revision 1.120
diff -u -p -u -p -r1.120 ip6_input.c
--- netinet6/ip6_input.c        11 Nov 2013 09:15:35 -0000      1.120
+++ netinet6/ip6_input.c        13 Nov 2013 23:38:22 -0000
@@ -122,6 +122,7 @@ struct ifqueue ip6intrq;
 struct ip6stat ip6stat;
 
 void ip6_init2(void *);
+int ip6_check_rh0hdr(struct mbuf *, int *);
 
 int ip6_hopopts_input(u_int32_t *, u_int32_t *, struct mbuf **, int *);
 struct mbuf *ip6_pullexthdr(struct mbuf *, size_t, int);
@@ -331,6 +332,20 @@ ip6_input(struct mbuf *m)
        srcrt = !IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst);
 #endif
 
+       /*
+        * Be more secure than RFC5095 and scan for type 0 routing headers.
+        * If pf has already scanned the header chain, do not do it twice.
+        */
+       if (!(m->m_pkthdr.pf.flags & PF_TAG_PROCESSED) &&
+           ip6_check_rh0hdr(m, &off)) {
+               ip6stat.ip6s_badoptions++;
+               in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard);
+               in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr);
+               icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, off);
+               /* m is already freed */
+               return;
+       }
+
        if (IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) ||
            IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) {
                ours = 1;
@@ -698,6 +713,74 @@ ip6_input(struct mbuf *m)
        return;
  bad:
        m_freem(m);
+}
+
+/* scan packet for RH0 routing header. Mostly stolen from pf.c:pf_test() */
+int
+ip6_check_rh0hdr(struct mbuf *m, int *offp)
+{
+       struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+       struct ip6_rthdr rthdr;
+       struct ip6_ext opt6;
+       u_int8_t proto = ip6->ip6_nxt;
+       int done = 0, lim, off, rh_cnt = 0;
+
+       off = ((caddr_t)ip6 - m->m_data) + sizeof(struct ip6_hdr);
+       lim = min(m->m_pkthdr.len, ntohs(ip6->ip6_plen) + sizeof(*ip6));
+       do {
+               switch (proto) {
+               case IPPROTO_ROUTING:
+                       *offp = off;
+                       if (rh_cnt++) {
+                               /* more then one rh header present */
+                               return (1);
+                       }
+
+                       if (off + sizeof(rthdr) > lim) {
+                               /* packet to short to make sense */
+                               return (1);
+                       }
+
+                       m_copydata(m, off, sizeof(rthdr), (caddr_t)&rthdr);
+
+                       if (rthdr.ip6r_type == IPV6_RTHDR_TYPE_0) {
+                               *offp += offsetof(struct ip6_rthdr, ip6r_type);
+                               return (1);
+                       }
+
+                       off += (rthdr.ip6r_len + 1) * 8;
+                       proto = rthdr.ip6r_nxt;
+                       break;
+               case IPPROTO_AH:
+               case IPPROTO_HOPOPTS:
+               case IPPROTO_DSTOPTS:
+                       /* get next header and header length */
+                       if (off + sizeof(opt6) > lim) {
+                               /*
+                                * Packet to short to make sense, we could
+                                * reject the packet but as a router we 
+                                * should not do that so forward it.
+                                */
+                               return (0);
+                       }
+
+                       m_copydata(m, off, sizeof(opt6), (caddr_t)&opt6);
+
+                       if (proto == IPPROTO_AH)
+                               off += (opt6.ip6e_len + 2) * 4;
+                       else
+                               off += (opt6.ip6e_len + 1) * 8;
+                       proto = opt6.ip6e_nxt;
+                       break;
+               case IPPROTO_FRAGMENT:
+               default:
+                       /* end of header stack */
+                       done = 1;
+                       break;
+               }
+       } while (!done);
+
+       return (0);
 }
 
 /*
Index: sys/mbuf.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/sys/mbuf.h,v
retrieving revision 1.168
diff -u -p -u -p -r1.168 mbuf.h
--- sys/mbuf.h  13 Oct 2013 10:10:04 -0000      1.168
+++ sys/mbuf.h  13 Nov 2013 23:14:32 -0000
@@ -100,6 +100,7 @@ struct pkthdr_pf {
 #define        PF_TAG_DIVERTED_PACKET          0x10
 #define        PF_TAG_REROUTE                  0x20
 #define        PF_TAG_REFRAGMENTED             0x40    /* refragmented ipv6 
packet */
+#define        PF_TAG_PROCESSED                0x80    /* packet was checked 
by pf */
 
 /* record/packet header in first mbuf of chain; valid if M_PKTHDR set */
 struct pkthdr {

Reply via email to