On Thu, Nov 14, 2013 at 4:27 AM, Alexander Bluhm
<[email protected]> wrote:
> On Fri, Oct 18, 2013 at 08:45:02PM +0200, Alexander Bluhm wrote:
>> Our IPv6 stack scans all extension headers for routing header type
>> 0 and drops the packet if it finds one.  RFC 5095 demands to handle
>> a routing header type 0 like an unrecognised routing type.  This
>> is enough to protect the own machine.
>>
>> To protect a network as a firewall, we have pf which does the same
>> full scan in pf_walk_header6().  As pf is enabled by default, nothing
>> changes for most users.  If you turn off pf on your router, you
>> should not expect extra protection.
>>
>> I would like to get rid of the double scanning and the old disabled
>> code in the IPv6 stack.
>
> Theo and others don't like that change as it decreases security.
> There are hosts out there that still process RH0 and there are
> OpenBSD routers with pf disabled.
>
> This diff brings back the header chain scanning.  As an improvement
> it only scans if pf has not done that before.
>
> Note that ip6_check_rh0hdr() can be easily tricked by hiding the
> routing header type 0 behind a fragment header.  Only pf can protect
> you correctly as it reassembles on the forwarding path.  So I am
> not sure wether it is worth adding it again.
>
> Comments?

I guess it would help people who decide to disable pf for slight
performance benefit ?


>
> 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 {
>



-- 
This message is strictly personal and the opinions expressed do not
represent those of my employers, either past or present.

Reply via email to