Author: cieciwa Date: Thu Sep 15 08:47:16 2005 GMT Module: SOURCES Tag: LINUX_2_6 ---- Log message: - [extra] unclean - kernel patch.
---- Files affected: SOURCES: linux-2.6-nf-unclean.patch (NONE -> 1.1.2.1) (NEW) ---- Diffs: ================================================================ Index: SOURCES/linux-2.6-nf-unclean.patch diff -u /dev/null SOURCES/linux-2.6-nf-unclean.patch:1.1.2.1 --- /dev/null Thu Sep 15 10:47:16 2005 +++ SOURCES/linux-2.6-nf-unclean.patch Thu Sep 15 10:47:11 2005 @@ -0,0 +1,648 @@ + Kconfig | 14 + + Makefile | 1 + ipt_unclean.c | 611 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 626 insertions(+) + +diff -Nur --exclude '*.orig' linux-2.6.13.1.org/net/ipv4/netfilter/Kconfig linux-2.6.13.1/net/ipv4/netfilter/Kconfig +--- linux-2.6.13.1.org/net/ipv4/netfilter/Kconfig 2005-09-10 04:42:58.000000000 +0200 ++++ linux-2.6.13.1/net/ipv4/netfilter/Kconfig 2005-09-15 10:39:29.000000000 +0200 +@@ -692,5 +692,19 @@ + Allows altering the ARP packet payload: source and destination + hardware and network addresses. + ++config IP_NF_MATCH_UNCLEAN ++ tristate 'Unclean match support (DANGEROUS)' ++ depends on EXPERIMENTAL && IP_NF_IPTABLES ++ help ++ Unclean packet matching matches any strange or invalid packets, by ++ looking at a series of fields in the IP, TCP, UDP and ICMP headers. ++ ++ Please note that this kind of matching is considered dangerous and ++ might harm the future compatibility of your packet filter. ++ ++ It has happened before, search on the net for ECN blackholes :( ++ ++ ++ + endmenu + +diff -Nur --exclude '*.orig' linux-2.6.13.1.org/net/ipv4/netfilter/Makefile linux-2.6.13.1/net/ipv4/netfilter/Makefile +--- linux-2.6.13.1.org/net/ipv4/netfilter/Makefile 2005-09-10 04:42:58.000000000 +0200 ++++ linux-2.6.13.1/net/ipv4/netfilter/Makefile 2005-09-15 10:39:29.000000000 +0200 +@@ -0,0 +0,1 @@ ++obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o +diff -Nur --exclude '*.orig' linux-2.6.13.1.org/net/ipv4/netfilter/ipt_unclean.c linux-2.6.13.1/net/ipv4/netfilter/ipt_unclean.c +--- linux-2.6.13.1.org/net/ipv4/netfilter/ipt_unclean.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.13.1/net/ipv4/netfilter/ipt_unclean.c 2005-09-15 10:39:29.000000000 +0200 +@@ -0,0 +1,611 @@ ++/* Kernel module to match suspect packets. */ ++#include <linux/module.h> ++#include <linux/skbuff.h> ++#include <linux/ip.h> ++#include <linux/udp.h> ++#include <linux/tcp.h> ++#include <linux/icmp.h> ++#include <net/checksum.h> ++#include <net/ip.h> ++ ++#include <linux/netfilter_ipv4/ip_tables.h> ++ ++#define limpk(format, args...) \ ++do { \ ++ if (net_ratelimit()) \ ++ printk("ipt_unclean: %s" format, \ ++ embedded ? "(embedded packet) " : "" , ## args); \ ++} while(0) ++ ++enum icmp_error_status ++{ ++ ICMP_MAY_BE_ERROR, ++ ICMP_IS_ERROR, ++ ICMP_NOT_ERROR ++}; ++ ++struct icmp_info ++{ ++ size_t min_len, max_len; ++ enum icmp_error_status err; ++ u_int8_t min_code, max_code; ++}; ++ ++static int ++check_ip(const struct sk_buff *skb, unsigned int offset); ++ ++/* ICMP-specific checks. */ ++static int ++check_icmp(const struct sk_buff *skb, ++ unsigned int offset, ++ unsigned int fragoff, ++ int more_frags, ++ int embedded) ++{ ++ struct icmphdr icmph; ++ static struct icmp_info info[] ++ = { [ICMP_ECHOREPLY] ++ = { 8, 65536, ICMP_NOT_ERROR, 0, 0 }, ++ [ICMP_DEST_UNREACH] ++ = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 15 }, ++ [ICMP_SOURCE_QUENCH] ++ = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 0 }, ++ [ICMP_REDIRECT] ++ = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 3 }, ++ [ICMP_ECHO] ++ = { 8, 65536, ICMP_NOT_ERROR, 0, 0 }, ++ /* Router advertisement. */ ++ [9] ++ = { 8, 8 + 255 * 8, ICMP_NOT_ERROR, 0, 0 }, ++ /* Router solicitation. */ ++ [10] ++ = { 8, 8, ICMP_NOT_ERROR, 0, 0 }, ++ [ICMP_TIME_EXCEEDED] ++ = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 1 }, ++ [ICMP_PARAMETERPROB] ++ = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 1 }, ++ [ICMP_TIMESTAMP] ++ = { 20, 20, ICMP_NOT_ERROR, 0, 0 }, ++ [ICMP_TIMESTAMPREPLY] ++ = { 20, 20, ICMP_NOT_ERROR, 0, 0 }, ++ [ICMP_INFO_REQUEST] ++ = { 8, 65536, ICMP_NOT_ERROR, 0, 0 }, ++ [ICMP_INFO_REPLY] ++ = { 8, 65536, ICMP_NOT_ERROR, 0, 0 }, ++ [ICMP_ADDRESS] ++ = { 12, 12, ICMP_NOT_ERROR, 0, 0 }, ++ [ICMP_ADDRESSREPLY] ++ = { 12, 12, ICMP_NOT_ERROR, 0, 0 } }; ++ ++ /* Can't do anything if it's a fragment. */ ++ if (fragoff) ++ return 1; ++ ++ /* CHECK: Must have whole header.. */ ++ if (skb_copy_bits(skb, offset, &icmph, sizeof(icmph)) < 0) { ++ limpk("ICMP len=%u too short\n", skb->len - offset); ++ return 0; ++ } ++ ++ /* If not embedded in an ICMP error already. */ ++ if (!embedded) { ++ /* CHECK: Truncated ICMP (even if first fragment). */ ++ if (icmph.type < sizeof(info)/sizeof(struct icmp_info) ++ && info[icmph.type].min_len != 0 ++ && skb->len - offset < info[icmph.type].min_len) { ++ limpk("ICMP type %u len %u too short\n", ++ icmph.type, skb->len - offset); ++ return 0; ++ } ++ ++ /* CHECK: Check within known error ICMPs. */ ++ if (icmph.type < sizeof(info)/sizeof(struct icmp_info) ++ && info[icmph.type].err == ICMP_IS_ERROR) { ++ /* Max IP header size = 60 */ ++ char inner[60 + 8]; ++ struct iphdr *inner_ip = (struct iphdr *)inner; ++ ++ /* CHECK: Embedded packet must be at least ++ length of iph + 8 bytes. */ ++ if (skb_copy_bits(skb, offset + sizeof(icmph), ++ inner, sizeof(struct iphdr)+8) < 0) { ++ limpk("ICMP error internal way too short\n"); ++ return 0; ++ } ++ ++ /* iphhdr may actually be longer: still need 8 ++ actual protocol bytes. */ ++ if (offset + sizeof(icmph) + inner_ip->ihl*4 + 8 ++ > skb->len) { ++ limpk("ICMP error internal too short\n"); ++ return 0; ++ } ++ if (!check_ip(skb, offset + sizeof(icmph))) ++ return 0; ++ } ++ } else { ++ /* CHECK: Can't embed ICMP unless known non-error. */ ++ if (icmph.type >= sizeof(info)/sizeof(struct icmp_info) ++ || info[icmph.type].err != ICMP_NOT_ERROR) { ++ limpk("ICMP type %u not embeddable\n", ++ icmph.type); ++ return 0; ++ } ++ } ++ ++ /* CHECK: Invalid ICMP codes. */ ++ if (icmph.type < sizeof(info)/sizeof(struct icmp_info) ++ && (icmph.code < info[icmph.type].min_code ++ || icmph.code > info[icmph.type].max_code)) { ++ limpk("ICMP type=%u code=%u\n", ++ icmph.type, icmph.code); ++ return 0; ++ } ++ ++ /* CHECK: Above maximum length. */ ++ if (icmph.type < sizeof(info)/sizeof(struct icmp_info) ++ && info[icmph.type].max_len != 0 ++ && skb->len - offset > info[icmph.type].max_len) { ++ limpk("ICMP type=%u too long: %u bytes\n", ++ icmph.type, skb->len - offset); ++ return 0; ++ } ++ ++ switch (icmph.type) { ++ case ICMP_PARAMETERPROB: { ++ /* CHECK: Problem param must be within error packet's ++ * IP header. */ ++ u_int32_t arg = ntohl(icmph.un.gateway); ++ ++ if (icmph.code == 0) { ++ /* We've already made sure it's long enough. */ ++ struct iphdr iph; ++ skb_copy_bits(skb, offset + sizeof(icmph), &iph, ++ sizeof(iph)); ++ /* Code 0 means that upper 8 bits is pointer ++ to problem. */ ++ if ((arg >> 24) >= iph.ihl*4) { ++ limpk("ICMP PARAMETERPROB ptr = %u\n", ++ ntohl(icmph.un.gateway) >> 24); ++ return 0; ++ } ++ arg &= 0x00FFFFFF; ++ } ++ ++ /* CHECK: Rest must be zero. */ ++ if (arg) { ++ limpk("ICMP PARAMETERPROB nonzero arg = %u\n", ++ arg); ++ return 0; ++ } ++ break; ++ } ++ ++ case ICMP_TIME_EXCEEDED: ++ case ICMP_SOURCE_QUENCH: ++ /* CHECK: Unused must be zero. */ ++ if (icmph.un.gateway != 0) { ++ limpk("ICMP type=%u unused = %u\n", ++ icmph.type, ntohl(icmph.un.gateway)); ++ return 0; ++ } ++ break; ++ } ++ ++ return 1; ++} ++ ++/* UDP-specific checks. */ ++static int ++check_udp(const struct sk_buff *skb, ++ unsigned int offset, ++ unsigned int fragoff, ++ int more_frags, ++ int embedded) ++{ ++ struct udphdr udph; ++ ++ /* Can't do anything if it's a fragment. */ ++ if (fragoff) ++ return 1; ++ ++ /* CHECK: Must cover UDP header. */ ++ if (skb_copy_bits(skb, offset, &udph, sizeof(udph)) < 0) { ++ limpk("UDP len=%u too short\n", skb->len - offset); ++ return 0; ++ } ++ ++ /* CHECK: Destination port can't be zero. */ ++ if (!udph.dest) { ++ limpk("UDP zero destination port\n"); ++ return 0; ++ } ++ ++ if (!more_frags) { ++ if (!embedded) { ++ /* CHECK: UDP length must match. */ ++ if (ntohs(udph.len) != skb->len - offset) { ++ limpk("UDP len too short %u vs %u\n", ++ ntohs(udph.len), skb->len - offset); ++ return 0; ++ } ++ } else { ++ /* CHECK: UDP length be >= this truncated pkt. */ ++ if (ntohs(udph.len) < skb->len - offset) { ++ limpk("UDP len too long %u vs %u\n", ++ ntohs(udph.len), skb->len - offset); ++ return 0; ++ } ++ } ++ } else { ++ /* CHECK: UDP length must be > this frag's length. */ ++ if (ntohs(udph.len) <= skb->len - offset) { ++ limpk("UDP fragment len too short %u vs %u\n", ++ ntohs(udph.len), skb->len - offset); ++ return 0; ++ } ++ } ++ ++ return 1; ++} ++ ++/* TCP-specific checks. */ ++static int ++check_tcp(const struct sk_buff *skb, ++ unsigned int offset, ++ unsigned int fragoff, ++ int more_frags, ++ int embedded) ++{ ++ struct tcphdr tcph; ++ unsigned char opt[15 * 4 - sizeof(struct tcphdr)]; ++ u32 tcpflags; ++ int end_of_options = 0; ++ unsigned int i, optlen; ++ ++ /* CHECK: Can't have offset=1: used to override TCP syn-checks. */ ++ /* In fact, this is caught below (offset < 516). */ ++ ++ /* Can't do anything if it's a fragment. */ ++ if (fragoff) ++ return 1; ++ ++ /* CHECK: Smaller than minimal TCP hdr. */ ++ if (skb_copy_bits(skb, offset, &tcph, sizeof(tcph)) < 0) { ++ u16 ports[2]; ++ ++ if (!embedded) { ++ limpk("Packet length %u < TCP header.\n", ++ skb->len - offset); ++ return 0; ++ } ++ ++ /* Must have ports available (datalen >= 8), from ++ check_icmp which set embedded = 1 */ ++ /* CHECK: TCP ports inside ICMP error */ ++ skb_copy_bits(skb, offset, ports, sizeof(ports)); ++ if (!ports[0] || !ports[1]) { ++ limpk("Zero TCP ports %u/%u.\n", ++ htons(ports[0]), htons(ports[1])); ++ return 0; ++ } ++ return 1; ++ } ++ ++ /* CHECK: TCP header claims tiny size. */ ++ if (tcph.doff * 4 < sizeof(tcph)) { ++ limpk("TCP header claims tiny size %u\n", tcph.doff * 4); ++ return 0; ++ } ++ ++ /* CHECK: Packet smaller than actual TCP hdr. */ ++ optlen = tcph.doff*4 - sizeof(tcph); ++ if (skb_copy_bits(skb, offset + sizeof(tcph), opt, optlen) < 0) { ++ if (!embedded) { ++ limpk("Packet length %u < actual TCP header.\n", ++ skb->len - offset); ++ return 0; ++ } else ++ return 1; ++ } ++ ++ /* CHECK: TCP ports non-zero */ ++ if (!tcph.source || !tcph.dest) { ++ limpk("Zero TCP ports %u/%u.\n", ++ htons(tcph.source), htons(tcph.dest)); ++ return 0; ++ } ++ ++ tcpflags = tcp_flag_word(&tcph); ++ ++ /* CHECK: TCP reserved bits zero. */ ++ if (tcpflags & TCP_RESERVED_BITS) { ++ limpk("TCP reserved bits not zero\n"); ++ return 0; ++ } ++ ++ tcpflags &= ~(TCP_DATA_OFFSET | TCP_FLAG_CWR | TCP_FLAG_ECE ++ | __constant_htonl(0x0000FFFF)); ++ ++ /* CHECK: TCP flags. */ ++ if (tcpflags != TCP_FLAG_SYN ++ && tcpflags != (TCP_FLAG_SYN|TCP_FLAG_ACK) ++ && tcpflags != TCP_FLAG_RST ++ && tcpflags != (TCP_FLAG_RST|TCP_FLAG_ACK) ++ && tcpflags != (TCP_FLAG_RST|TCP_FLAG_ACK|TCP_FLAG_PSH) ++ && tcpflags != (TCP_FLAG_FIN|TCP_FLAG_ACK) ++ && tcpflags != TCP_FLAG_ACK ++ && tcpflags != (TCP_FLAG_ACK|TCP_FLAG_PSH) ++ && tcpflags != (TCP_FLAG_ACK|TCP_FLAG_URG) ++ && tcpflags != (TCP_FLAG_ACK|TCP_FLAG_URG|TCP_FLAG_PSH) ++ && tcpflags != (TCP_FLAG_FIN|TCP_FLAG_ACK|TCP_FLAG_PSH) ++ && tcpflags != (TCP_FLAG_FIN|TCP_FLAG_ACK|TCP_FLAG_URG) ++ && tcpflags != (TCP_FLAG_FIN|TCP_FLAG_ACK|TCP_FLAG_URG ++ |TCP_FLAG_PSH)) { ++ limpk("TCP flags bad: 0x%04X\n", ntohl(tcpflags) >> 16); ++ return 0; ++ } ++ ++ for (i = 0; i < optlen; ) { ++ switch (opt[i]) { ++ case 0: ++ end_of_options = 1; ++ i++; ++ break; ++ case 1: ++ i++; ++ break; ++ default: ++ /* CHECK: options after EOO. */ ++ if (end_of_options) { ++ limpk("TCP option %u after end\n", ++ opt[i]); ++ return 0; ++ } ++ /* CHECK: options at tail. */ ++ else if (i+1 >= optlen) { ++ limpk("TCP option %u at tail\n", ++ opt[i]); ++ return 0; ++ } ++ /* CHECK: zero-length options. */ ++ else if (opt[i+1] == 0) { ++ limpk("TCP option %u 0 len\n", ++ opt[i]); ++ return 0; ++ } ++ /* CHECK: oversize options. */ ++ else if (i + opt[i+1] > optlen) { ++ limpk("TCP option %u at %u too long\n", ++ (unsigned int) opt[i], i); ++ return 0; ++ } ++ /* Move to next option */ ++ i += opt[i+1]; ++ } ++ } ++ ++ return 1; ++} ++ ++/* Returns 1 if ok */ ++/* Standard IP checks. */ ++static int ++check_ip(const struct sk_buff *skb, unsigned int offset) ++{ ++ int end_of_options = 0; ++ unsigned int datalen, optlen; ++ unsigned int i; ++ unsigned int fragoff; ++ struct iphdr iph; ++ unsigned char opt[15 * 4 - sizeof(struct iphdr)]; ++ int embedded = offset; ++ ++ /* Should only happen for local outgoing raw-socket packets. */ ++ /* CHECK: length >= ip header. */ ++ if (skb_copy_bits(skb, offset, &iph, sizeof(iph)) < 0) { ++ limpk("Packet length %u < IP header.\n", skb->len - offset); ++ return 0; ++ } ++ if (iph.ihl * 4 < sizeof(iph)) { ++ limpk("IP len %u < minimum IP header.\n", iph.ihl*4); ++ return 0; ++ } ++ ++ optlen = iph.ihl * 4 - sizeof(iph); ++ if (skb_copy_bits(skb, offset+sizeof(struct iphdr), opt, optlen)<0) { ++ limpk("Packet length %u < IP header %u.\n", ++ skb->len - offset, iph.ihl * 4); ++ return 0; ++ } ++ ++ fragoff = (ntohs(iph.frag_off) & IP_OFFSET); ++ datalen = skb->len - (offset + sizeof(struct iphdr) + optlen); ++ ++ /* CHECK: Embedded fragment. */ ++ if (offset && fragoff) { ++ limpk("Embedded fragment.\n"); ++ return 0; ++ } ++ ++ for (i = 0; i < optlen; ) { ++ switch (opt[i]) { ++ case 0: ++ end_of_options = 1; ++ i++; ++ break; ++ case 1: ++ i++; ++ break; ++ default: ++ /* CHECK: options after EOO. */ ++ if (end_of_options) { ++ limpk("IP option %u after end\n", ++ opt[i]); ++ return 0; ++ } ++ /* CHECK: options at tail. */ ++ else if (i+1 >= optlen) { ++ limpk("IP option %u at tail\n", ++ opt[i]); ++ return 0; ++ } ++ /* CHECK: zero-length or one-length options. */ ++ else if (opt[i+1] < 2) { ++ limpk("IP option %u %u len\n", ++ opt[i], opt[i+1]); ++ return 0; ++ } ++ /* CHECK: oversize options. */ ++ else if (i + opt[i+1] > optlen) { ++ limpk("IP option %u at %u too long\n", ++ opt[i], i); ++ return 0; ++ } ++ /* Move to next option */ ++ i += opt[i+1]; ++ } ++ } ++ ++ /* Fragment checks. */ ++ ++ /* CHECK: More fragments, but doesn't fill 8-byte boundary. */ ++ if ((ntohs(iph.frag_off) & IP_MF) ++ && (ntohs(iph.tot_len) % 8) != 0) { ++ limpk("Truncated fragment %u long.\n", ntohs(iph.tot_len)); ++ return 0; ++ } ++ ++ /* CHECK: Oversize fragment a-la Ping of Death. */ ++ if (fragoff * 8 + datalen > 65535) { ++ limpk("Oversize fragment to %u.\n", fragoff * 8); ++ return 0; ++ } ++ ++ /* CHECK: DF set and fragoff or MF set. */ ++ if ((ntohs(iph.frag_off) & IP_DF) ++ && (fragoff || (ntohs(iph.frag_off) & IP_MF))) { ++ limpk("DF set and offset=%u, MF=%u.\n", ++ fragoff, ntohs(iph.frag_off) & IP_MF); ++ return 0; ++ } ++ ++ /* CHECK: Zero-sized fragments. */ ++ if ((fragoff || (ntohs(iph.frag_off) & IP_MF)) ++ && datalen == 0) { ++ limpk("Zero size fragment offset=%u\n", fragoff); ++ return 0; ++ } ++ ++ /* Note: we can have even middle fragments smaller than this: ++ consider a large packet passing through a 600MTU then ++ 576MTU link: this gives a fragment of 24 data bytes. But ++ everyone packs fragments largest first, hence a fragment ++ can't START before 576 - MAX_IP_HEADER_LEN. */ ++ ++ /* Used to be min-size 576: I recall Alan Cox saying ax25 goes ++ down to 128 (576 taken from RFC 791: All hosts must be ++ prepared to accept datagrams of up to 576 octets). Use 128 ++ here. */ ++#define MIN_LIKELY_MTU 128 ++ /* CHECK: Min size of first frag = 128. */ ++ if ((ntohs(iph.frag_off) & IP_MF) ++ && fragoff == 0 ++ && ntohs(iph.tot_len) < MIN_LIKELY_MTU) { ++ limpk("First fragment size %u < %u\n", ntohs(iph.tot_len), ++ MIN_LIKELY_MTU); ++ return 0; ++ } ++ ++ /* CHECK: Min offset of frag = 128 - IP hdr len. */ ++ if (fragoff && fragoff * 8 < MIN_LIKELY_MTU - iph.ihl * 4) { ++ limpk("Fragment starts at %u < %u\n", fragoff * 8, ++ MIN_LIKELY_MTU - iph.ihl * 4); ++ return 0; ++ } ++ ++ /* CHECK: Protocol specification non-zero. */ ++ if (iph.protocol == 0) { ++ limpk("Zero protocol\n"); ++ return 0; ++ } ++ ++ /* FIXME: This is already checked for in "Oversize fragment" ++ above --RR */ ++ /* CHECK: Do not use what is unused. ++ * First bit of fragmentation flags should be unused. ++ * May be used by OS fingerprinting tools. ++ * 04 Jun 2002, Maciej Soltysiak, [EMAIL PROTECTED] ++ */ ++ if (ntohs(iph.frag_off)>>15) { ++ limpk("IP unused bit set\n"); ++ return 0; ++ } ++ ++ /* Per-protocol checks. */ ++ switch (iph.protocol) { ++ case IPPROTO_ICMP: ++ return check_icmp(skb, offset + iph.ihl*4, fragoff, ++ (ntohs(iph.frag_off) & IP_MF), ++ embedded); ++ ++ case IPPROTO_UDP: ++ return check_udp(skb, offset + iph.ihl*4, fragoff, ++ (ntohs(iph.frag_off) & IP_MF), <<Diff was trimmed, longer than 597 lines>> _______________________________________________ pld-cvs-commit mailing list pld-cvs-commit@lists.pld-linux.org http://lists.pld-linux.org/mailman/listinfo/pld-cvs-commit