#!/bin/bash #Copyright 2003 William Stearns #Released under the GPL. #Many thanks to the denizens of the Sans handlers list for their input #on this module, especially Chris Brenton, Gary Kessler, George Bakos, #Kevin Timm, Robert Wagner, Remko Lodder, Johannes Ullrich, and Vasily #Tomilin. Thanks too to Don Cohen for his input on u32 issues. Me='plength' MyVersion='0.4.0' #FIXME - check this module carefully, then make DefaultActions='DROP' DefaultActions='NONE' [ -r /etc/modwall/modwall.conf ] && . /etc/modwall/modwall.conf [ -r /etc/modwall/$Me.conf ] && . /etc/modwall/$Me.conf [ -r ${MWLibDir:-'/usr/lib/modwall/'}/modwalllib ] && . ${MWLibDir:-'/usr/lib/modwall/'}/modwalllib if [ -z "$MWLibVer" ]; then echo 'It looks like modwalllib was not loaded, why? Exiting' >&2 exit 1 fi for OneTask in $Tasks ; do case "$OneTask" in link) $IptablesBin -N $Me >/dev/null 2>&1 $IptablesBin $AppIn INPUT -i \! lo -m length --length 0:512 -j $Me $IptablesBin $AppIn FORWARD -m length --length 0:512 -j $Me $IptablesBin $AppIn OUTPUT -m length --length 0:512 -j $Me ;; unlink) $IptablesBin -D INPUT -i \! lo -m length --length 0:512 -j $Me $IptablesBin -D FORWARD -m length --length 0:512 -j $Me $IptablesBin -D OUTPUT -m length --length 0:512 -j $Me $IptablesBin -X $Me >/dev/null 2>&1 ;; create) echo "Starting $Me" >&2 FlushOrNewChain $Me #Drop fragmented syn or syn/ack packets: LogAs='FragmentedSyn' $Ipt -A $Me -p tcp --tcp-flags SYN SYN -m u32 --u32 '"4&0x3FFF=1:0x3FFF"' $Tail #Check for payload on a syn/syn+ack. To do so, we use u32 #to jump over the ip and tcp headers, and simply test the first #payload byte for 0:255. If there is any payload at all, this #will return true; if there is no payload, u32 will realize it's #being asked to test a non-existant byte and return false. #Marvelously elegant; thanks to Don Cohen for this. #Well, with the minor detail that the module doesn't perform #the check as I described. #LogAs='PayloadOnSyn' $Ipt -A $Me -p tcp --tcp-flags SYN SYN -m u32 --u32 '"0>>22&0x3C@12>>26&0x3C@-3&0xFF=0:255"' $Tail #rfc791: #Every internet module must be able to forward a datagram of 68 octets #without further fragmentation. This is because an internet header may #be up to 60 octets, and the minimum fragment is 8 octets. #So, any fragment except the last that is less than 68 bytes was #artificially fragmented. #We can't have MF set and have a packet smaller than 68 bytes. LogAs='TooSmallNonTerminalFrag' $Ipt -A $Me -m u32 --u32 '"3&0x20>>5=1"' -m length --length 0:67 $Tail #MF set, 68-511 bytes, technically legal, but _way_ unlikely. LogAs='SmallNonTerminalFrag' $Ipt -A $Me -m u32 --u32 '"3&0x20>>5=1"' -m length --length 68:511 $Tail #Last fragment (MF clear, non-zero frag offset) must be >=21 bytes LogAs='TooSmallTerminalFrag' $Ipt -A $Me -m u32 --u32 '"3&0x20>>5=0 && 4&0x1FFF=1:65535"' -m length --length 0:20 $Tail #OK, we're done testing fragments, so we go back if MF is set #$IptablesBin -A $Me -m u32 --u32 '3&0x20>>5=1' -j RETURN #...or the Frag offset is >0 #$IptablesBin -A $Me -m u32 --u32 '4&0x1FFF=1:65535' -j RETURN #Actually, check for set MF or non-zero offset all at once: $IptablesBin -A $Me -m u32 --u32 '4&0x3FFF=1:65535' -j RETURN #Now we're left with unfragmented packets #Complete UDP >=28 bytes LogAs='Toosmalludp' $Ipt -A $Me -p udp -m length --length 0:27 $Tail #Complete TCP >=40 bytes LogAs='Toosmalltcp' $Ipt -A $Me -p tcp -m length --length 0:39 $Tail #Complete ICMP; 28 bytes for no payload ping or chargen request; smaller ones? LogAs='Toosmallicmp' $Ipt -A $Me -p icmp -m length --length 0:27 $Tail #protocol 30, should be >=32 bytes LogAs='Toosmallproto30' $Ipt -A $Me -p 30 -m length --length 0:31 $Tail #protocol 47 GRE, >=40 LogAs='Toosmallproto47' $Ipt -A $Me -p 47 -m length --length 0:39 $Tail #protocol 50 ESP, >=50 LogAs='Toosmallproto50' $Ipt -A $Me -p 50 -m length --length 0:49 $Tail #protocol 51 AH, >=36 LogAs='Toosmallproto51' $Ipt -A $Me -p 51 -m length --length 0:35 $Tail #IP packets, >=20 LogAs='ToosmallIP' $Ipt -A $Me -m length --length 0:19 $Tail ;; destroy) echo "Stopping $Me" >&2 DestroyChain $Me ;; renamechain) TempChain="$Me-$RANDOM" echo "Replacing existing rules in $Me with new rules" >&2 $IptablesBin -E $Me $TempChain ;; replacelinks) if [ -z "$TempChain" ]; then echo "No temporary chain to relink in $Me replacelinks, replace operation incomplete." >&2 elif ! $IptablesBin -L $Me -n >/dev/null 2>&1 ; then echo "No $Me chain in $Me, replace operation incomplete." >&2 elif ! $IptablesBin -L $TempChain -n >/dev/null 2>&1 ; then echo "No $TempChain chain in $Me, replace operation incomplete." >&2 elif [ "`$IptablesBin -L INPUT -n --line-numbers | grep $TempChain | wc -l`" -ne 1 ]; then echo "Too few/many references to $TempChain in INPUT in $Me replacelinks, replace operation incomplete." >&2 elif [ "`$IptablesBin -L FORWARD -n --line-numbers | grep $TempChain | wc -l`" -ne 1 ]; then echo "Too few/many references to $TempChain in FORWARD in $Me replacelinks, replace operation incomplete." >&2 elif [ "`$IptablesBin -L OUTPUT -n --line-numbers | grep $TempChain | wc -l`" -ne 1 ]; then echo "Too few/many references to $TempChain in OUTPUT in $Me replacelinks, replace operation incomplete." >&2 else $IptablesBin -R INPUT `$IptablesBin -L INPUT -n --line-numbers | grep $TempChain | awk '{print $1}'` -i \! lo -m length --length 0:512 -j $Me $IptablesBin -R FORWARD `$IptablesBin -L FORWARD -n --line-numbers | grep $TempChain | awk '{print $1}'` -m length --length 0:512 -j $Me $IptablesBin -R OUTPUT `$IptablesBin -L OUTPUT -n --line-numbers | grep $TempChain | awk '{print $1}'` -m length --length 0:512 -j $Me DestroyChain $TempChain unset TempChain fi ;; status) if $IptablesBin -L $Me -n >/dev/null 2>&1 ; then echo "$Me created" >&2 else echo "$Me destroyed" >&2 fi ;; version) echo "$Me $MyVersion, modwalllib $MWLibVer" >&2 ;; help) DefaultHelp cat <&2 The $Me module takes a closer look at the lengths of certain types of packets. Certain types of packets should be _at least_ N bytes long. For example, since normal fragmentation produces non-terminal fragments that are at least the MTU large, we should never see a non-terminal packet smaller than 68 bytes (the rfc791 minimum MTU) or even less than 512 bytes (the Internet practical minimum MTU). See the notes in this file for more details about the packet types and reasons for their minimums lengths. We have put enough thought into this module that it _should_ be safe to use, but it could really use a few days on an unprotected ISP router to be sure I haven\'t missed some subtle issue. It might be worth running this with an action of NONE for a few hours before putting it into production use. EOTEXT ;; *) echo "Unknown action $Action in $Me, no action taken." >&2 ;; esac done