bin/102422: ipfw & kernel problems where firewall rules aren't interpreted correctly

Stephen Halpin seh-10lzx4 at mail.quadrizen.com
Wed Aug 23 07:00:30 UTC 2006


>Number:         102422
>Category:       bin
>Synopsis:       ipfw & kernel problems where firewall rules aren't interpreted correctly
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Aug 23 07:00:28 GMT 2006
>Closed-Date:
>Last-Modified:
>Originator:     Stephen Halpin
>Release:        FreeBSD 6.1 RELEASE i386
>Organization:
None
>Environment:
FreeBSD kira 6.1-RELEASE FreeBSD 6.1-RELEASE #0: Sun May  7 04:42:56 UTC 2006     root at opus.cse.buffalo.edu:/usr/obj/usr/src/sys/SMP  i386

>Description:
I'm including three ipfw problems.  The first appears to be new.  The second appears to have been addressed to some degree in 7.0 long before the 6.1 release, and never made it to 6.1.  The third is an odd side effect which results in incorrect rule matching in the kernel, and can be covered with a quick hack to ipfw, or fixed in the kernel proper.  My hope is the simple fix/workaround set might be small enough to make it into 6.2, with the hope that higher level fixes are provided over time.

The first can easily be seen with the following command:

    ipfw add allow ipv6-icmp from any to 2002::1 icmp6types 1,2,128,129

The rule is accepted with icmp6types 1,2,32,33,34,...94,95,128,129.  The problem is the data structure in /usr/src/sbin/ipfw/ipfw2.c:fill_icmp6types() is not properly initialized.  The last address must be a host address to see this behavior, where the address and netmask are written to the command buffer but the netmask isn't actually to be sent to the kernel.  The fix is to initialize the array cmd->d[] in fill_icmp6types(), which could be poorly written "for (type = 0; type < 7; type++) cmd->d[type]=0;" in place of "cmd->d[0]=0;".  Ideally one would want to leverage ICMP6_MAXTYPE, which /usr/include/netinet/ip_fw.h assumes is 203, but is actually defined as 201 in icmp6.h and 205 in ip_compat.h.  The actual array d[] in cmd is hardcoded as a length of 7 32-bit words, and should probably be scaled according to a singular definition of ICMP6_MAXTYPE, which should probably allow for all 256 possible type values.  The extra 32-bits on a 2,700 rule firewall with more icmp6types
  based rules than should be necessary due to the third bug below results in an additional 2,900 bytes of extra kernel memory usage.

The second can easily be seen with the following command:

    ipfw add deny tcp from any to 2000::/3

which fails with "ipfw: bad netmask ``:/3''".  This apparently was addressed with bug number 91245, which the query interface won't bring up for anything.  I was able to find it using the global Google.  I found a set of diffs at:

    http://lists.freebsd.org/pipermail/freebsd-bugs/2006-January/016359.html

It's still not going to do the right thing when an IPv6 hostname is used as a "from" or "to" argument.  It appears when the IPv4 and IPv6 syntax was merged, there was a desire to allow rules like 'tcp from any <port-list> to any <port-list>" could be shared across the two underlying network protocols.  I've never seen any significant rule set where this simple case occurred at all, and the result is the ability to end up with an ambiguous rule specification with IPv4 and IPv6 bits which won't match anything, and that fact may not be at all obvious to the author.  What's worse, the syntax is no longer completely compatible with either the preexisting ipfw or ipfw6 syntax, which means the user may have to have to recode their rules anyway.  I think using something like 'ipfw -4' and 'ipfw -6' or some other easily macroized form for use in rc.firewall{,6} to drive the original syntax of each would have allowed the existing rules (minus the renumbering/interleaving) to move forwa
 rd.  The syntax would no longer be ambiguous, oddness with dst-ip6 options and the like wouldn't be necessary, and the result should be safer firewalls.

The third problem can be demonstrated (thought not easily seen) with the following command:

    ipfw add allow ip6 from any to 2000::/16,2002::/16

It turns out that ipfw appears to take a list of IPv6 addresses, but the kernel only processes one address when it applies the rule.  The end result is the firewall does not behave as expected.  The simple fix here is to deny lists of ipv6 addresses until the kernel can  be brought up to date, though I'm hoping in time the kernel can be fixed.  It looks like ipfw allocates a small buffer for the entire context going to the kernel, so there may be a buffer overflow issue with sets of 256-bit address/netmasks hiding here as well in the existing ipfw implementation.
>How-To-Repeat:
Problem 1:

    ipfw add allow ipv6-icmp from any to 2002::1 icmp6types 1,2,128,129

The rule includes icmp6types 32..95 in addition to those listed above.  This can be seen on the console output when ipfw is run, either when adding the rule or when list/showing the rule.

Problem 2:

        ipfw add deny tcp from any to 2000::/3

An error message is displayed at the console and the rule is not accepted.

Problem 3:

        ipfw add allow ip6 from any to 2000::/16,2002::/16

The rule is echoed back as being accepted with multiple IPv6 addresses, but the kernel only matches one.  This requires passing traffic to see, which could be a security risk.
>Fix:
Problem 1 just requires proper intialization as described above.  A "better" solution is creating a single definition of ICMP6_MAXTYPE and sizing the array and it's initialization based on it, though there may be dependency issues with the header files which would change.

Problem 2 can be partially addressed with the patch from the in the description, which should be sufficient for the short term (6.2).  In the long term, I would strongly advocate an unambiguous syntax, as this is a security tool, and therefore must be as predictable and easy to use as possible.

Problem 3 could be addressed by having ipfw only accept a single IPv6 address as a parameter.  This would prevent the user from having what appears to be a valid firewall rule which does not properly match packets in the kernel.  A cursory look at the kernel code makes it seem fairly straightforward to have the kernel actually match multiple IPv6 addresses, though the fixed command buffer size in ipfw may have some scaling or buffer overflow issues.
>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list