From owner-freebsd-bugs@FreeBSD.ORG Thu Apr 15 21:00:14 2010 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 22FE51065674 for ; Thu, 15 Apr 2010 21:00:14 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:4f8:fff6::28]) by mx1.freebsd.org (Postfix) with ESMTP id F40FB8FC12 for ; Thu, 15 Apr 2010 21:00:13 +0000 (UTC) Received: from freefall.freebsd.org (localhost [127.0.0.1]) by freefall.freebsd.org (8.14.4/8.14.4) with ESMTP id o3FL0Dx5071750 for ; Thu, 15 Apr 2010 21:00:13 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.4/8.14.4/Submit) id o3FL0DU9071743; Thu, 15 Apr 2010 21:00:13 GMT (envelope-from gnats) Resent-Date: Thu, 15 Apr 2010 21:00:13 GMT Resent-Message-Id: <201004152100.o3FL0DU9071743@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Matthew Luckie Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 87BDA1065672 for ; Thu, 15 Apr 2010 20:59:36 +0000 (UTC) (envelope-from mjl@luckie.org.nz) Received: from mailfilter4.ihug.co.nz (mailfilter4.ihug.co.nz [203.109.136.4]) by mx1.freebsd.org (Postfix) with ESMTP id 13BE28FC12 for ; Thu, 15 Apr 2010 20:59:34 +0000 (UTC) Received: from 118-92-126-17.dsl.dyn.ihug.co.nz (HELO spandex.luckie.org.nz) ([118.92.126.17]) by cust.filter4.content.vf.net.nz with ESMTP/TLS/DHE-RSA-AES256-SHA; 16 Apr 2010 08:58:38 +1200 Received: from mjl by spandex.luckie.org.nz with local (Exim 4.71 (FreeBSD)) (envelope-from ) id 1O2W9s-000C3X-72 for FreeBSD-gnats-submit@freebsd.org; Fri, 16 Apr 2010 08:59:32 +1200 Message-Id: Date: Fri, 16 Apr 2010 08:59:32 +1200 From: Matthew Luckie To: FreeBSD-gnats-submit@FreeBSD.org X-Send-Pr-Version: 3.113 Cc: Subject: kern/145733: [patch] ipfw flaws with ipv6 fragments X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list Reply-To: Matthew Luckie List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 15 Apr 2010 21:00:14 -0000 >Number: 145733 >Category: kern >Synopsis: [patch] ipfw flaws with ipv6 fragments >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Thu Apr 15 21:00:13 UTC 2010 >Closed-Date: >Last-Modified: >Originator: Matthew Luckie >Release: FreeBSD 8.0-RELEASE-p1 i386 >Organization: >Environment: System: FreeBSD spandex.luckie.org.nz 8.0-RELEASE-p1 FreeBSD 8.0-RELEASE-p1 #1: Fri Dec 4 08:21:29 NZDT 2009 mjl@spandex.luckie.org.nz:/usr/obj/usr/src/sys/spandex i386 >Description: IPFW has two flaws in its handling of IPv6 packets that arrive in fragments. First, it will deny an IPv6 packet that arrives with a fragmentation header which has an offset of zero, but no further fragments. This type of packet is explicitly allowed in RFC 2460: In response to an IPv6 packet that is sent to an IPv4 destination (i.e., a packet that undergoes translation from IPv6 to IPv4), the originating IPv6 node may receive an ICMP Packet Too Big message reporting a Next-Hop MTU less than 1280. In that case, the IPv6 node is not required to reduce the size of subsequent packets to less than 1280, but must include a Fragment header in those packets so that the IPv6-to-IPv4 translating router can obtain a suitable Identification value to use in resulting IPv4 fragments. Note that this means the payload may have to be reduced to 1232 octets (1280 minus 40 for the IPv6 header and 8 for the Fragment header), and smaller still if additional extension headers are used. The second flaw is that the code allows IPv6 packets that arrive in fragments to avoid transport-layer rules. For example, consider this ruleset: 00001 deny tcp from 2607:f0b0:0:6:209:87:239:67 80 to 2404:138:4002:4000:205:1cff:fe11:beff dst-port 37822 65534 allow ip from any to any 65535 deny ip from any to any Rule 1 will not be applied to the fragment with offset zero because the MF bit is intentionally included in the offset variable used in ipfw_chk, so the check to see if the transport header is found in fragment zero will fail. Instead, the rule will be skipped over, and the next rule which in this example is an allow will accept the fragment. Where an administrator might have expected the traffic to be blocked it will instead be allowed through the firewall. >How-To-Repeat: As above. I can provide code to reproduce this problem out of band. >Fix: Apply this patch. --- patch-ip_fw2-8.c begins here --- --- ip_fw2.c.orig 2010-04-16 08:38:52.000000000 +1200 +++ ip_fw2.c 2010-04-16 08:42:03.000000000 +1200 @@ -804,6 +804,7 @@ ipfw_log(struct ip_fw *f, u_int hlen, st char *action; int limit_reached = 0; char action2[40], proto[128], fragment[32]; + u_short mf = 0; fragment[0] = '\0'; proto[0] = '\0'; @@ -952,6 +953,8 @@ ipfw_log(struct ip_fw *f, u_int hlen, st snprintf(dst, sizeof(dst), "[%s]", ip6_sprintf(ip6buf, &args->f_id.dst_ip6)); + mf = offset & IP6F_MORE_FRAG; + offset &= IP6F_OFF_MASK; ip6 = (struct ip6_hdr *)ip; tcp = (struct tcphdr *)(((char *)ip) + hlen); udp = (struct udphdr *)(((char *)ip) + hlen); @@ -1021,13 +1024,13 @@ ipfw_log(struct ip_fw *f, u_int hlen, st #ifdef INET6 if (IS_IP6_FLOW_ID(&(args->f_id))) { - if (offset & (IP6F_OFF_MASK | IP6F_MORE_FRAG)) + if (offset || mf) snprintf(SNPARGS(fragment, 0), " (frag %08x:%d@%d%s)", args->f_id.frag_id6, ntohs(ip6->ip6_plen) - hlen, - ntohs(offset & IP6F_OFF_MASK) << 3, - (offset & IP6F_MORE_FRAG) ? "+" : ""); + ntohs(offset) << 3, + mf ? "+" : ""); } else #endif { @@ -2184,16 +2187,13 @@ ipfw_chk(struct ip_fw_args *args) /* * offset The offset of a fragment. offset != 0 means that - * we have a fragment at this offset of an IPv4 packet. - * offset == 0 means that (if this is an IPv4 packet) - * this is the first or only fragment. - * For IPv6 offset == 0 means there is no Fragment Header. - * If offset != 0 for IPv6 always use correct mask to - * get the correct offset because we add IP6F_MORE_FRAG - * to be able to dectect the first fragment which would - * otherwise have offset = 0. + * we have a fragment at this offset. + * offset == 0 means that this is the first or only fragment. + * + * mf The MF bit masked out of IPv6 packets. */ u_short offset = 0; + u_short mf = 0; /* * Local copies of addresses. They are only valid if we have @@ -2345,17 +2345,8 @@ do { \ proto = ((struct ip6_frag *)ulp)->ip6f_nxt; offset = ((struct ip6_frag *)ulp)->ip6f_offlg & IP6F_OFF_MASK; - /* Add IP6F_MORE_FRAG for offset of first - * fragment to be != 0. */ - offset |= ((struct ip6_frag *)ulp)->ip6f_offlg & + mf = ((struct ip6_frag *)ulp)->ip6f_offlg & IP6F_MORE_FRAG; - if (offset == 0) { - printf("IPFW2: IPV6 - Invalid Fragment " - "Header\n"); - if (V_fw_deny_unknown_exthdrs) - return (IP_FW_DENY); - break; - } args->f_id.frag_id6 = ntohl(((struct ip6_frag *)ulp)->ip6f_ident); ulp = NULL; @@ -2941,7 +2932,7 @@ check_body: case O_LOG: if (V_fw_verbose) ipfw_log(f, hlen, args, m, - oif, offset, tablearg, ip); + oif, offset|mf, tablearg, ip); match = 1; break; --- patch-ip_fw2-8.c ends here --- >Release-Note: >Audit-Trail: >Unformatted: