[NETFILTER]: Missed and reordered checks in {arp,ip,ip6}_tables
[powerpc.git] / net / ipv6 / netfilter / ip6_tables.c
index 4ab368f..167c2ea 100644 (file)
@@ -111,7 +111,7 @@ ip6_packet_match(const struct sk_buff *skb,
                 const char *outdev,
                 const struct ip6t_ip6 *ip6info,
                 unsigned int *protoff,
-                int *fragoff)
+                int *fragoff, int *hotdrop)
 {
        size_t i;
        unsigned long ret;
@@ -169,9 +169,11 @@ ip6_packet_match(const struct sk_buff *skb,
                unsigned short _frag_off;
 
                protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
-               if (protohdr < 0)
+               if (protohdr < 0) {
+                       if (_frag_off == 0)
+                               *hotdrop = 1;
                        return 0;
-
+               }
                *fragoff = _frag_off;
 
                dprintf("Packet protocol %hi ?= %s%hi.\n",
@@ -290,7 +292,7 @@ ip6t_do_table(struct sk_buff **pskb,
                IP_NF_ASSERT(e);
                IP_NF_ASSERT(back);
                if (ip6_packet_match(*pskb, indev, outdev, &e->ipv6,
-                       &protoff, &offset)) {
+                       &protoff, &offset, &hotdrop)) {
                        struct ip6t_entry_target *t;
 
                        if (IP6T_MATCH_ITERATE(e, do_match,
@@ -584,12 +586,19 @@ check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
                return -EINVAL;
        }
 
+       if (e->target_offset + sizeof(struct ip6t_entry_target) >
+                                                               e->next_offset)
+               return -EINVAL;
+
        j = 0;
        ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
        if (ret != 0)
                goto cleanup_matches;
 
        t = ip6t_get_target(e);
+       ret = -EINVAL;
+       if (e->target_offset + t->u.target_size > e->next_offset)
+                       goto cleanup_matches;
        target = try_then_request_module(xt_find_target(AF_INET6,
                                                        t->u.user.name,
                                                        t->u.user.revision),
@@ -749,19 +758,17 @@ translate_table(const char *name,
                }
        }
 
-       if (!mark_source_chains(newinfo, valid_hooks, entry0))
-               return -ELOOP;
-
        /* Finally, each sanity check must pass */
        i = 0;
        ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
                                check_entry, name, size, &i);
 
-       if (ret != 0) {
-               IP6T_ENTRY_ITERATE(entry0, newinfo->size,
-                                 cleanup_entry, &i);
-               return ret;
-       }
+       if (ret != 0)
+               goto cleanup;
+
+       ret = -ELOOP;
+       if (!mark_source_chains(newinfo, valid_hooks, entry0))
+               goto cleanup;
 
        /* And one copy for every other CPU */
        for_each_possible_cpu(i) {
@@ -769,6 +776,9 @@ translate_table(const char *name,
                        memcpy(newinfo->entries[i], entry0, newinfo->size);
        }
 
+       return 0;
+cleanup:
+       IP6T_ENTRY_ITERATE(entry0, newinfo->size, cleanup_entry, &i);
        return ret;
 }
 
@@ -1438,6 +1448,9 @@ static void __exit ip6_tables_fini(void)
  * If target header is found, its offset is set in *offset and return protocol
  * number. Otherwise, return -1.
  *
+ * If the first fragment doesn't contain the final protocol header or
+ * NEXTHDR_NONE it is considered invalid.
+ *
  * Note that non-1st fragment is special case that "the protocol number
  * of last header" is "next header" field in Fragment header. In this case,
  * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
@@ -1461,12 +1474,12 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
                if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
                        if (target < 0)
                                break;
-                       return -1;
+                       return -ENOENT;
                }
 
                hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
                if (hp == NULL)
-                       return -1;
+                       return -EBADMSG;
                if (nexthdr == NEXTHDR_FRAGMENT) {
                        unsigned short _frag_off, *fp;
                        fp = skb_header_pointer(skb,
@@ -1475,7 +1488,7 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
                                                sizeof(_frag_off),
                                                &_frag_off);
                        if (fp == NULL)
-                               return -1;
+                               return -EBADMSG;
 
                        _frag_off = ntohs(*fp) & ~0x7;
                        if (_frag_off) {
@@ -1486,7 +1499,7 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
                                                *fragoff = _frag_off;
                                        return hp->nexthdr;
                                }
-                               return -1;
+                               return -ENOENT;
                        }
                        hdrlen = 8;
                } else if (nexthdr == NEXTHDR_AUTH)