netdev
[Top] [All Lists]

[PATCH] fix for netfilter/nat/pppoe crashes (hopefully)

To: netfilter@xxxxxxxxxxxxxxx, netfilter-devel@xxxxxxxxxxxxxxx, netdev@xxxxxxxxxxx
Subject: [PATCH] fix for netfilter/nat/pppoe crashes (hopefully)
From: Marc Boucher <marc@xxxxxxx>
Date: Tue, 31 Jul 2001 12:42:59 -0400
Cc: mostrows@xxxxxxxxxx, mostrows@xxxxxxxxxxxxx
Sender: owner-netdev@xxxxxxxxxxx
User-agent: Mutt/1.3.19i
Hi folks,

Enclosed is a patch which should eliminate the crashes involving
netfilter/iptables/nat (and often pppoe) that several people have been
experiencing under kernels >= 2.4.4.

Basically the nat manip_pkt handlers were corrupting skb_shinfo(skb)->frag_list
(thus causing a crash in skb_drop_fraglist()) by stupidly writing beyond
skb->end when attempting to update fields (like tcp->check) in truncated
inner-ICMP headers.

Cheers
Marc

--- linux-2.4.7-mb/net/ipv4/netfilter/ip_nat_core.c     2001/07/31 15:31:51     
1.1
+++ linux-2.4.7-mb/net/ipv4/netfilter/ip_nat_core.c     2001/07/31 16:12:45
@@ -824,6 +824,16 @@
                               ? "DST" : "SRC",
                               NIPQUAD(info->manips[i].manip.ip),
                               ntohs(info->manips[i].manip.u.udp.port));
+
+                       if((skb->len <= ((void *)inner - (void *)iph)) ||
+                               (skb->len < ((void *)(
+                                       (u_int32_t *)inner + inner->ihl
+                                       ) - (void *)iph))) {
+                               DEBUGP("icmp_reply_translation: skb too small 
for inner IP header\n");
+                               READ_UNLOCK(&ip_nat_lock);
+                               return NF_DROP;
+                       }
+
                        manip_pkt(inner->protocol, inner,
                                  skb->len - ((void *)inner - (void *)iph),
                                  &info->manips[i].manip,
--- linux-2.4.7-mb/net/ipv4/netfilter/ip_nat_proto_icmp.c       2001/07/31 
15:37:44     1.1
+++ linux-2.4.7-mb/net/ipv4/netfilter/ip_nat_proto_icmp.c       2001/07/31 
15:55:15
@@ -9,6 +9,12 @@
 #include <linux/netfilter_ipv4/ip_nat_rule.h>
 #include <linux/netfilter_ipv4/ip_nat_protocol.h>
 
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
 static int
 icmp_in_range(const struct ip_conntrack_tuple *tuple,
              enum ip_nat_manip_type maniptype,
@@ -48,6 +54,11 @@
               enum ip_nat_manip_type maniptype)
 {
        struct icmphdr *hdr = (struct icmphdr *)((u_int32_t *)iph + iph->ihl);
+
+       if(((void *)(hdr+1) - (void *)iph) < len) {
+               DEBUGP("icmp_manip_pkt: too small\n");
+               return;
+       }
 
        hdr->checksum = ip_nat_cheat_check(hdr->un.echo.id ^ 0xFFFF,
                                           manip->u.icmp.id,
--- linux-2.4.7-mb/net/ipv4/netfilter/ip_nat_proto_tcp.c        2001/07/31 
15:37:45     1.1
+++ linux-2.4.7-mb/net/ipv4/netfilter/ip_nat_proto_tcp.c        2001/07/31 
16:19:40
@@ -9,6 +9,12 @@
 #include <linux/netfilter_ipv4/ip_nat_rule.h>
 #include <linux/netfilter_ipv4/ip_nat_protocol.h>
 
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
 static int
 tcp_in_range(const struct ip_conntrack_tuple *tuple,
             enum ip_nat_manip_type maniptype,
@@ -83,6 +89,12 @@
        u_int32_t oldip;
        u_int16_t *portptr;
 
+
+       if(((void *)&hdr->dest + sizeof(hdr->dest) - (void *)iph) < len) {
+               DEBUGP("tcp_manip_pkt: too small\n");
+               return;
+       }
+
        if (maniptype == IP_NAT_MANIP_SRC) {
                /* Get rid of src ip and src pt */
                oldip = iph->saddr;
@@ -92,10 +104,17 @@
                oldip = iph->daddr;
                portptr = &hdr->dest;
        }
-       hdr->check = ip_nat_cheat_check(~oldip, manip->ip,
+
+       /* this could be a inner header returned in icmp packet; in such
+          cases we cannot update the checksum field since it is outside of
+          the 64 bits of transport layer headers typically included */
+       if(((void *)&hdr->check + sizeof(hdr->check) - (void *)iph) >= len) {
+               hdr->check = ip_nat_cheat_check(~oldip, manip->ip,
                                        ip_nat_cheat_check(*portptr ^ 0xFFFF,
                                                           manip->u.tcp.port,
                                                           hdr->check));
+       }
+
        *portptr = manip->u.tcp.port;
 }
 
--- linux-2.4.7-mb/net/ipv4/netfilter/ip_nat_proto_udp.c        2001/07/31 
15:37:45     1.1
+++ linux-2.4.7-mb/net/ipv4/netfilter/ip_nat_proto_udp.c        2001/07/31 
16:19:23
@@ -9,6 +9,12 @@
 #include <linux/netfilter_ipv4/ip_nat_rule.h>
 #include <linux/netfilter_ipv4/ip_nat_protocol.h>
 
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
 static int
 udp_in_range(const struct ip_conntrack_tuple *tuple,
             enum ip_nat_manip_type maniptype,
@@ -80,6 +86,11 @@
        struct udphdr *hdr = (struct udphdr *)((u_int32_t *)iph + iph->ihl);
        u_int32_t oldip;
        u_int16_t *portptr;
+
+       if(((void *)&hdr->check + sizeof(hdr->check) - (void *)iph) < len) {
+               DEBUGP("udp_manip_pkt: too small\n");
+               return;
+       }
 
        if (maniptype == IP_NAT_MANIP_SRC) {
                /* Get rid of src ip and src pt */

<Prev in Thread] Current Thread [Next in Thread>