Module Name: src Committed By: maxv Date: Thu Mar 8 07:06:13 UTC 2018
Modified Files: src/sys/net/npf: npf.h npf_handler.c npf_inet.c Log Message: 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. To generate a diff of this commit: cvs rdiff -u -r1.55 -r1.56 src/sys/net/npf/npf.h cvs rdiff -u -r1.37 -r1.38 src/sys/net/npf/npf_handler.c \ src/sys/net/npf/npf_inet.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/net/npf/npf.h diff -u src/sys/net/npf/npf.h:1.55 src/sys/net/npf/npf.h:1.56 --- src/sys/net/npf/npf.h:1.55 Fri Dec 15 21:00:26 2017 +++ src/sys/net/npf/npf.h Thu Mar 8 07:06:13 2018 @@ -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 @@ int nbuf_find_tag(nbuf_t *, uint32_t *) #define NPC_ALG_EXEC 0x100 /* ALG execution. */ +#define NPC_FMTERR 0x200 /* Format error. */ + #define NPC_IP46 (NPC_IP4|NPC_IP6) typedef struct { Index: src/sys/net/npf/npf_handler.c diff -u src/sys/net/npf/npf_handler.c:1.37 src/sys/net/npf/npf_handler.c:1.38 --- src/sys/net/npf/npf_handler.c:1.37 Sun Feb 19 20:27:22 2017 +++ src/sys/net/npf/npf_handler.c Thu Mar 8 07:06:13 2018 @@ -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 @@ npf_reassembly(npf_t *npf, npf_cache_t * 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 @@ npf_packet_handler(npf_t *npf, struct mb 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 @@ out: error = ENETUNREACH; } +fastout: if (*mp) { /* Free the mbuf chain. */ m_freem(*mp); Index: src/sys/net/npf/npf_inet.c diff -u src/sys/net/npf/npf_inet.c:1.37 src/sys/net/npf/npf_inet.c:1.38 --- src/sys/net/npf/npf_inet.c:1.37 Sun Feb 19 20:27:22 2017 +++ src/sys/net/npf/npf_inet.c Thu Mar 8 07:06:13 2018 @@ -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 @@ npf_cache_ip(npf_cache_t *npc, nbuf_t *n 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 @@ npf_cache_ip(npf_cache_t *npc, nbuf_t *n 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 @@ npf_cache_ip(npf_cache_t *npc, nbuf_t *n 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 @@ npf_cache_ip(npf_cache_t *npc, nbuf_t *n 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 @@ npf_cache_ip(npf_cache_t *npc, nbuf_t *n * 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 @@ npf_cache_ip(npf_cache_t *npc, nbuf_t *n 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 @@ again: * 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;