Source-Changes-HG archive

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

[src/netbsd-7-0]: src/sys/netinet6 Pull up following revision(s) (requested b...



details:   https://anonhg.NetBSD.org/src/rev/f65b3d716c1f
branches:  netbsd-7-0
changeset: 801425:f65b3d716c1f
user:      martin <martin%NetBSD.org@localhost>
date:      Tue Jan 30 18:31:53 2018 +0000

description:
Pull up following revision(s) (requested by maxv in ticket #1560):
        sys/netinet6/frag6.c: revision 1.65
        sys/netinet6/ip6_input.c: revision 1.187
        sys/netinet6/ip6_var.h: revision 1.78
        sys/netinet6/raw_ip6.c: revision 1.160 (patch)
Fix a buffer overflow in ip6_get_prevhdr. Doing
        mtod(m, char *) + len
is wrong, an option is allowed to be located in another mbuf of the chain.
If the offset of an option within the chain is bigger than the length of
the first mbuf in that chain, we are reading/writing one byte of packet-
controlled data beyond the end of the first mbuf.
The length of this first mbuf depends on the layout the network driver
chose. In the most difficult case, it will allocate a 2KB cluster, which
is bigger than the Ethernet MTU.
But there is at least one way of exploiting this case: by sending a
special combination of nested IPv6 fragments, the packet can control a
good bunch of 'len'. By luck, the memory pool containing clusters does not
embed the pool header in front of the items, so it is not straightforward
to predict what is located at 'mtod(m, char *) + len'.
However, by sending offending fragments in a loop, it is possible to
crash the kernel - at some point we will hit important data structures.
As far as I can tell, PF protects against this difficult case, because
it kicks nested fragments. NPF does not protect against this. IPF I don't
know.
Then there are the more easy cases, if the MTU is bigger than a cluster,
or if the network driver did not allocate a cluster, or perhaps if the
fragments are received via a tunnel; I haven't investigated these cases.
Change ip6_get_prevhdr so that it returns an offset in the chain, and
always use IP6_EXTHDR_GET to get a writable pointer. IP6_EXTHDR_GET
leaves M_PKTHDR untouched.
This place is still fragile.

diffstat:

 sys/netinet6/frag6.c     |  33 ++++++++++++++++++++++-----------
 sys/netinet6/ip6_input.c |  46 ++++++++++++++++++++--------------------------
 sys/netinet6/ip6_var.h   |   4 ++--
 sys/netinet6/raw_ip6.c   |   8 ++++----
 4 files changed, 48 insertions(+), 43 deletions(-)

diffs (202 lines):

diff -r cf176b83bddb -r f65b3d716c1f sys/netinet6/frag6.c
--- a/sys/netinet6/frag6.c      Mon Jan 29 19:44:06 2018 +0000
+++ b/sys/netinet6/frag6.c      Tue Jan 30 18:31:53 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: frag6.c,v 1.55 2013/08/30 07:42:08 christos Exp $      */
+/*     $NetBSD: frag6.c,v 1.55.6.1 2018/01/30 18:31:53 martin Exp $    */
 /*     $KAME: frag6.c,v 1.40 2002/05/27 21:40:31 itojun Exp $  */
 
 /*
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: frag6.c,v 1.55 2013/08/30 07:42:08 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: frag6.c,v 1.55.6.1 2018/01/30 18:31:53 martin Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -441,14 +441,6 @@
                m_cat(m, t);
        }
 
-       /*
-        * Store NXT to the original.
-        */
-       {
-               u_int8_t *prvnxtp = ip6_get_prevhdr(m, offset); /* XXX */
-               *prvnxtp = nxt;
-       }
-
        frag6_remque(q6);
        frag6_nfrags -= q6->ip6q_nfrag;
        kmem_intr_free(q6, sizeof(struct ip6q));
@@ -456,11 +448,30 @@
 
        if (m->m_flags & M_PKTHDR) { /* Isn't it always true? */
                int plen = 0;
-               for (t = m; t; t = t->m_next)
+               for (t = m; t; t = t->m_next) {
+                       /*
+                        * XXX XXX Why don't we remove M_PKTHDR?
+                        */
                        plen += t->m_len;
+               }
                m->m_pkthdr.len = plen;
        }
 
+       /*
+        * Restore NXT to the original.
+        */
+       {
+               const int prvnxt = ip6_get_prevhdr(m, offset);
+               uint8_t *prvnxtp;
+
+               IP6_EXTHDR_GET(prvnxtp, uint8_t *, m, prvnxt,
+                   sizeof(*prvnxtp));
+               if (prvnxtp == NULL) {
+                       goto dropfrag;
+               }
+               *prvnxtp = nxt;
+       }
+
        IP6_STATINC(IP6_STAT_REASSEMBLED);
        in6_ifstat_inc(dstifp, ifs6_reass_ok);
 
diff -r cf176b83bddb -r f65b3d716c1f sys/netinet6/ip6_input.c
--- a/sys/netinet6/ip6_input.c  Mon Jan 29 19:44:06 2018 +0000
+++ b/sys/netinet6/ip6_input.c  Tue Jan 30 18:31:53 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: ip6_input.c,v 1.149.2.1 2015/01/23 09:27:15 martin Exp $       */
+/*     $NetBSD: ip6_input.c,v 1.149.2.1.2.1 2018/01/30 18:31:53 martin Exp $   */
 /*     $KAME: ip6_input.c,v 1.188 2001/03/29 05:34:31 itojun Exp $     */
 
 /*
@@ -62,7 +62,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ip6_input.c,v 1.149.2.1 2015/01/23 09:27:15 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ip6_input.c,v 1.149.2.1.2.1 2018/01/30 18:31:53 martin Exp $");
 
 #include "opt_gateway.h"
 #include "opt_inet.h"
@@ -1384,50 +1384,44 @@
 }
 
 /*
- * Get pointer to the previous header followed by the header
+ * Get offset to the previous header followed by the header
  * currently processed.
- * XXX: This function supposes that
- *     M includes all headers,
- *     the next header field and the header length field of each header
- *     are valid, and
- *     the sum of each header length equals to OFF.
- * Because of these assumptions, this function must be called very
- * carefully. Moreover, it will not be used in the near future when
- * we develop `neater' mechanism to process extension headers.
  */
-u_int8_t *
+int
 ip6_get_prevhdr(struct mbuf *m, int off)
 {
        struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
 
-       if (off == sizeof(struct ip6_hdr))
-               return (&ip6->ip6_nxt);
-       else {
-               int len, nxt;
-               struct ip6_ext *ip6e = NULL;
+       if (off == sizeof(struct ip6_hdr)) {
+               return offsetof(struct ip6_hdr, ip6_nxt);
+       } else if (off < sizeof(struct ip6_hdr)) {
+               panic("%s: off < sizeof(struct ip6_hdr)", __func__);
+       } else {
+               int len, nlen, nxt;
+               struct ip6_ext ip6e;
 
                nxt = ip6->ip6_nxt;
                len = sizeof(struct ip6_hdr);
+               nlen = 0;
                while (len < off) {
-                       ip6e = (struct ip6_ext *)(mtod(m, char *) + len);
+                       m_copydata(m, len, sizeof(ip6e), &ip6e);
 
                        switch (nxt) {
                        case IPPROTO_FRAGMENT:
-                               len += sizeof(struct ip6_frag);
+                               nlen = sizeof(struct ip6_frag);
                                break;
                        case IPPROTO_AH:
-                               len += (ip6e->ip6e_len + 2) << 2;
+                               nlen = (ip6e.ip6e_len + 2) << 2;
                                break;
                        default:
-                               len += (ip6e->ip6e_len + 1) << 3;
+                               nlen = (ip6e.ip6e_len + 1) << 3;
                                break;
                        }
-                       nxt = ip6e->ip6e_nxt;
+                       len += nlen;
+                       nxt = ip6e.ip6e_nxt;
                }
-               if (ip6e)
-                       return (&ip6e->ip6e_nxt);
-               else
-                       return NULL;
+
+               return (len - nlen);
        }
 }
 
diff -r cf176b83bddb -r f65b3d716c1f sys/netinet6/ip6_var.h
--- a/sys/netinet6/ip6_var.h    Mon Jan 29 19:44:06 2018 +0000
+++ b/sys/netinet6/ip6_var.h    Tue Jan 30 18:31:53 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: ip6_var.h,v 1.62.2.1 2015/01/23 09:27:15 martin Exp $  */
+/*     $NetBSD: ip6_var.h,v 1.62.2.1.2.1 2018/01/30 18:31:53 martin Exp $      */
 /*     $KAME: ip6_var.h,v 1.33 2000/06/11 14:59:20 jinmei Exp $        */
 
 /*
@@ -327,7 +327,7 @@
 void   ip6_freepcbopts(struct ip6_pktopts *);
 void   ip6_freemoptions(struct ip6_moptions *);
 int    ip6_unknown_opt(u_int8_t *, struct mbuf *, int);
-u_int8_t *ip6_get_prevhdr(struct mbuf *, int);
+int    ip6_get_prevhdr(struct mbuf *, int);
 int    ip6_nexthdr(struct mbuf *, int, int, int *);
 int    ip6_lasthdr(struct mbuf *, int, int, int *);
 
diff -r cf176b83bddb -r f65b3d716c1f sys/netinet6/raw_ip6.c
--- a/sys/netinet6/raw_ip6.c    Mon Jan 29 19:44:06 2018 +0000
+++ b/sys/netinet6/raw_ip6.c    Tue Jan 30 18:31:53 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: raw_ip6.c,v 1.136 2014/08/09 05:33:01 rtr Exp $        */
+/*     $NetBSD: raw_ip6.c,v 1.136.6.1 2018/01/30 18:31:53 martin Exp $ */
 /*     $KAME: raw_ip6.c,v 1.82 2001/07/23 18:57:56 jinmei Exp $        */
 
 /*
@@ -62,7 +62,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: raw_ip6.c,v 1.136 2014/08/09 05:33:01 rtr Exp $");
+__KERNEL_RCSID(0, "$NetBSD: raw_ip6.c,v 1.136.6.1 2018/01/30 18:31:53 martin Exp $");
 
 #include "opt_ipsec.h"
 
@@ -259,11 +259,11 @@
                if (proto == IPPROTO_NONE)
                        m_freem(m);
                else {
-                       u_int8_t *prvnxtp = ip6_get_prevhdr(m, *offp); /* XXX */
+                       const int prvnxt = ip6_get_prevhdr(m, *offp);
                        in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_protounknown);
                        icmp6_error(m, ICMP6_PARAM_PROB,
                            ICMP6_PARAMPROB_NEXTHEADER,
-                           prvnxtp - mtod(m, u_int8_t *));
+                           prvnxt);
                }
                IP6_STATDEC(IP6_STAT_DELIVERED);
        }



Home | Main Index | Thread Index | Old Index