Source-Changes-HG archive

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

[src/trunk]: src/sys/net/npf Declare NPC_FMTERR, and use it to kick malformed...



details:   https://anonhg.NetBSD.org/src/rev/772b2faa1190
branches:  trunk
changeset: 360375:772b2faa1190
user:      maxv <maxv%NetBSD.org@localhost>
date:      Thu Mar 08 07:06:13 2018 +0000

description:
Declare NPC_FMTERR, and use it to kick malformed packets. Several sanity
checks are added in IPv6; after we see the first IPPROTO_FRAGMENT header,
we are allowed to fail to advance, otherwise we kick the packet.

Sent on tech-net@ a few days ago, no response, but I'm committing it now
anyway.

diffstat:

 sys/net/npf/npf.h         |   4 ++-
 sys/net/npf/npf_handler.c |  17 ++++++++++---
 sys/net/npf/npf_inet.c    |  59 +++++++++++++++++++++++++++++++++++-----------
 3 files changed, 60 insertions(+), 20 deletions(-)

diffs (213 lines):

diff -r bddbd2488e29 -r 772b2faa1190 sys/net/npf/npf.h
--- a/sys/net/npf/npf.h Thu Mar 08 06:48:23 2018 +0000
+++ b/sys/net/npf/npf.h Thu Mar 08 07:06:13 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: npf.h,v 1.55 2017/12/15 21:00:26 maxv Exp $    */
+/*     $NetBSD: npf.h,v 1.56 2018/03/08 07:06:13 maxv Exp $    */
 
 /*-
  * Copyright (c) 2009-2014 The NetBSD Foundation, Inc.
@@ -143,6 +143,8 @@
 
 #define        NPC_ALG_EXEC    0x100   /* ALG execution. */
 
+#define        NPC_FMTERR      0x200   /* Format error. */
+
 #define        NPC_IP46        (NPC_IP4|NPC_IP6)
 
 typedef struct {
diff -r bddbd2488e29 -r 772b2faa1190 sys/net/npf/npf_handler.c
--- a/sys/net/npf/npf_handler.c Thu Mar 08 06:48:23 2018 +0000
+++ b/sys/net/npf/npf_handler.c Thu Mar 08 07:06:13 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: npf_handler.c,v 1.37 2017/02/19 20:27:22 christos Exp $        */
+/*     $NetBSD: npf_handler.c,v 1.38 2018/03/08 07:06:13 maxv Exp $    */
 
 /*-
  * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
@@ -37,7 +37,7 @@
 
 #ifdef _KERNEL
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.37 2017/02/19 20:27:22 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.38 2018/03/08 07:06:13 maxv Exp $");
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -109,7 +109,7 @@
        nbuf_init(npf, nbuf, *mp, nbuf->nb_ifp);
        npc->npc_info = 0;
 
-       if (npf_cache_all(npc) & NPC_IPFRAG) {
+       if (npf_cache_all(npc) & (NPC_IPFRAG|NPC_FMTERR)) {
                return EINVAL;
        }
        npf_stats_inc(npf, NPF_STAT_REASSEMBLY);
@@ -154,8 +154,16 @@
        error = 0;
        rp = NULL;
 
-       /* Cache everything.  Determine whether it is an IP fragment. */
+       /* Cache everything. */
        flags = npf_cache_all(&npc);
+
+       /* If error on the format, leave quickly. */
+       if (flags & NPC_FMTERR) {
+               error = EINVAL;
+               goto fastout;
+       }
+
+       /* Determine whether it is an IP fragment. */
        if (__predict_false(flags & NPC_IPFRAG)) {
                /*
                 * We pass IPv6 fragments unconditionally
@@ -308,6 +316,7 @@
                error = ENETUNREACH;
        }
 
+fastout:
        if (*mp) {
                /* Free the mbuf chain. */
                m_freem(*mp);
diff -r bddbd2488e29 -r 772b2faa1190 sys/net/npf/npf_inet.c
--- a/sys/net/npf/npf_inet.c    Thu Mar 08 06:48:23 2018 +0000
+++ b/sys/net/npf/npf_inet.c    Thu Mar 08 07:06:13 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: npf_inet.c,v 1.37 2017/02/19 20:27:22 christos Exp $   */
+/*     $NetBSD: npf_inet.c,v 1.38 2018/03/08 07:06:13 maxv Exp $       */
 
 /*-
  * Copyright (c) 2009-2014 The NetBSD Foundation, Inc.
@@ -40,7 +40,7 @@
 
 #ifdef _KERNEL
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.37 2017/02/19 20:27:22 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.38 2018/03/08 07:06:13 maxv Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -328,12 +328,12 @@
 
                ip = nbuf_ensure_contig(nbuf, sizeof(struct ip));
                if (ip == NULL) {
-                       return 0;
+                       return NPC_FMTERR;
                }
 
                /* Check header length and fragment offset. */
                if ((u_int)(ip->ip_hl << 2) < sizeof(struct ip)) {
-                       return 0;
+                       return NPC_FMTERR;
                }
                if (ip->ip_off & ~htons(IP_DF | IP_RF)) {
                        /* Note fragmentation. */
@@ -357,10 +357,14 @@
                struct ip6_ext *ip6e;
                struct ip6_frag *ip6f;
                size_t off, hlen;
+               int frag_present;
+               bool is_frag;
+               uint8_t onxt;
+               int fragoff;
 
                ip6 = nbuf_ensure_contig(nbuf, sizeof(struct ip6_hdr));
                if (ip6 == NULL) {
-                       return 0;
+                       return NPC_FMTERR;
                }
 
                /* Set initial next-protocol value. */
@@ -368,16 +372,14 @@
                npc->npc_proto = ip6->ip6_nxt;
                npc->npc_hlen = hlen;
 
+               frag_present = 0;
+               is_frag = false;
+
                /*
                 * Advance by the length of the current header.
                 */
                off = nbuf_offset(nbuf);
-               while (nbuf_advance(nbuf, hlen, 0) != NULL) {
-                       ip6e = nbuf_ensure_contig(nbuf, sizeof(*ip6e));
-                       if (ip6e == NULL) {
-                               return 0;
-                       }
-
+               while ((ip6e = nbuf_advance(nbuf, hlen, sizeof(*ip6e))) != NULL) {
                        /*
                         * Determine whether we are going to continue.
                         */
@@ -388,9 +390,23 @@
                                hlen = (ip6e->ip6e_len + 1) << 3;
                                break;
                        case IPPROTO_FRAGMENT:
+                               if (frag_present++)
+                                       return NPC_FMTERR;
                                ip6f = nbuf_ensure_contig(nbuf, sizeof(*ip6f));
                                if (ip6f == NULL)
-                                       return 0;
+                                       return NPC_FMTERR;
+
+                               hlen = sizeof(struct ip6_frag);
+
+                               /* RFC6946: Skip dummy fragments. */
+                               fragoff = ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK);
+                               if (fragoff == 0 &&
+                                   !(ip6f->ip6f_offlg & IP6F_MORE_FRAG)) {
+                                       break;
+                               }
+
+                               is_frag = true;
+
                                /*
                                 * We treat the first fragment as a regular
                                 * packet and then we pass the rest of the
@@ -399,10 +415,9 @@
                                 * be able to reassembled, if not they will
                                 * be ignored. We can do better later.
                                 */
-                               if (ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK) != 0)
+                               if (fragoff != 0)
                                        flags |= NPC_IPFRAG;
 
-                               hlen = sizeof(struct ip6_frag);
                                break;
                        case IPPROTO_AH:
                                hlen = (ip6e->ip6e_len + 2) << 2;
@@ -415,11 +430,24 @@
                        if (!hlen) {
                                break;
                        }
+                       onxt = npc->npc_proto;
                        npc->npc_proto = ip6e->ip6e_nxt;
                        npc->npc_hlen += hlen;
                }
 
                /*
+                * We failed to advance. If we are not a fragment, that's
+                * a format error and we leave. Otherwise, restore npc_hlen
+                * and npc_proto to their previous (and correct) values.
+                */
+               if (ip6e == NULL) {
+                       if (!is_frag)
+                               return NPC_FMTERR;
+                       npc->npc_proto = onxt;
+                       npc->npc_hlen -= hlen;
+               }
+
+               /*
                 * Re-fetch the header pointers (nbufs might have been
                 * reallocated).  Restore the original offset (if any).
                 */
@@ -469,7 +497,8 @@
         * fragmented, then we cannot look into L4.
         */
        flags = npf_cache_ip(npc, nbuf);
-       if ((flags & NPC_IP46) == 0 || (flags & NPC_IPFRAG) != 0) {
+       if ((flags & NPC_IP46) == 0 || (flags & NPC_IPFRAG) != 0 ||
+           (flags & NPC_FMTERR) != 0) {
                nbuf_unset_flag(nbuf, NBUF_DATAREF_RESET);
                npc->npc_info |= flags;
                return flags;



Home | Main Index | Thread Index | Old Index