]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
netfilter: x_tables: check for bogus target offset
authorFlorian Westphal <fw@strlen.de>
Fri, 1 Apr 2016 12:17:28 +0000 (14:17 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 24 Jun 2016 17:18:23 +0000 (10:18 -0700)
commit ce683e5f9d045e5d67d1312a42b359cb2ab2a13c upstream.

We're currently asserting that targetoff + targetsize <= nextoff.

Extend it to also check that targetoff is >= sizeof(xt_entry).
Since this is generic code, add an argument pointing to the start of the
match/target, we can then derive the base structure size from the delta.

We also need the e->elems pointer in a followup change to validate matches.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/linux/netfilter/x_tables.h
net/ipv4/netfilter/arp_tables.c
net/ipv4/netfilter/ip_tables.c
net/ipv6/netfilter/ip6_tables.c
net/netfilter/x_tables.c

index e3a3d0594742210626992d95968195f2a1552551..41b38534293cecf43e8a67c26200e55d0dbc678e 100644 (file)
@@ -239,7 +239,7 @@ void xt_unregister_match(struct xt_match *target);
 int xt_register_matches(struct xt_match *match, unsigned int n);
 void xt_unregister_matches(struct xt_match *match, unsigned int n);
 
-int xt_check_entry_offsets(const void *base,
+int xt_check_entry_offsets(const void *base, const char *elems,
                           unsigned int target_offset,
                           unsigned int next_offset);
 
@@ -492,7 +492,7 @@ void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr,
                                unsigned int *size);
 int xt_compat_target_to_user(const struct xt_entry_target *t,
                             void __user **dstptr, unsigned int *size);
-int xt_compat_check_entry_offsets(const void *base,
+int xt_compat_check_entry_offsets(const void *base, const char *elems,
                                  unsigned int target_offset,
                                  unsigned int next_offset);
 
index b05405fd9f9413c36c84322c8254e94674bd73de..fa907f24616e69212e8469db0c9be4fbe75aebf6 100644 (file)
@@ -592,7 +592,8 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e,
        if (!arp_checkentry(&e->arp))
                return -EINVAL;
 
-       err = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
+       err = xt_check_entry_offsets(e, e->elems, e->target_offset,
+                                    e->next_offset);
        if (err)
                return err;
 
@@ -1253,7 +1254,7 @@ check_compat_entry_size_and_hooks(struct compat_arpt_entry *e,
        if (!arp_checkentry(&e->arp))
                return -EINVAL;
 
-       ret = xt_compat_check_entry_offsets(e, e->target_offset,
+       ret = xt_compat_check_entry_offsets(e, e->elems, e->target_offset,
                                            e->next_offset);
        if (ret)
                return ret;
index b83a3369b8a4de9b788eaaf09200fb27584772a8..fa06250faf7fb2984e396518c8e6c0d5db1af506 100644 (file)
@@ -754,7 +754,8 @@ check_entry_size_and_hooks(struct ipt_entry *e,
        if (!ip_checkentry(&e->ip))
                return -EINVAL;
 
-       err = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
+       err = xt_check_entry_offsets(e, e->elems, e->target_offset,
+                                    e->next_offset);
        if (err)
                return err;
 
@@ -1512,7 +1513,7 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,
        if (!ip_checkentry(&e->ip))
                return -EINVAL;
 
-       ret = xt_compat_check_entry_offsets(e,
+       ret = xt_compat_check_entry_offsets(e, e->elems,
                                            e->target_offset, e->next_offset);
        if (ret)
                return ret;
index d8035191ea1a9b7f3590df3489805bd433fcdfe0..84eb76fedf8ca23821f52d18f0cfd1c597e2ea79 100644 (file)
@@ -766,7 +766,8 @@ check_entry_size_and_hooks(struct ip6t_entry *e,
        if (!ip6_checkentry(&e->ipv6))
                return -EINVAL;
 
-       err = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
+       err = xt_check_entry_offsets(e, e->elems, e->target_offset,
+                                    e->next_offset);
        if (err)
                return err;
 
@@ -1524,7 +1525,7 @@ check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,
        if (!ip6_checkentry(&e->ipv6))
                return -EINVAL;
 
-       ret = xt_compat_check_entry_offsets(e,
+       ret = xt_compat_check_entry_offsets(e, e->elems,
                                            e->target_offset, e->next_offset);
        if (ret)
                return ret;
index 1f8cfe7379f7018c8a4a95dfbeb3068b3faeaadb..7b46dd6a20a5981c25b46eaae710490773dc8798 100644 (file)
@@ -545,14 +545,17 @@ struct compat_xt_standard_target {
        compat_uint_t verdict;
 };
 
-/* see xt_check_entry_offsets */
-int xt_compat_check_entry_offsets(const void *base,
+int xt_compat_check_entry_offsets(const void *base, const char *elems,
                                  unsigned int target_offset,
                                  unsigned int next_offset)
 {
+       long size_of_base_struct = elems - (const char *)base;
        const struct compat_xt_entry_target *t;
        const char *e = base;
 
+       if (target_offset < size_of_base_struct)
+               return -EINVAL;
+
        if (target_offset + sizeof(*t) > next_offset)
                return -EINVAL;
 
@@ -576,12 +579,16 @@ EXPORT_SYMBOL(xt_compat_check_entry_offsets);
  * xt_check_entry_offsets - validate arp/ip/ip6t_entry
  *
  * @base: pointer to arp/ip/ip6t_entry
+ * @elems: pointer to first xt_entry_match, i.e. ip(6)t_entry->elems
  * @target_offset: the arp/ip/ip6_t->target_offset
  * @next_offset: the arp/ip/ip6_t->next_offset
  *
  * validates that target_offset and next_offset are sane.
  * Also see xt_compat_check_entry_offsets for CONFIG_COMPAT version.
  *
+ * This function does not validate the targets or matches themselves, it
+ * only tests that all the offsets and sizes are correct.
+ *
  * The arp/ip/ip6t_entry structure @base must have passed following tests:
  * - it must point to a valid memory location
  * - base to base + next_offset must be accessible, i.e. not exceed allocated
@@ -590,12 +597,18 @@ EXPORT_SYMBOL(xt_compat_check_entry_offsets);
  * Return: 0 on success, negative errno on failure.
  */
 int xt_check_entry_offsets(const void *base,
+                          const char *elems,
                           unsigned int target_offset,
                           unsigned int next_offset)
 {
+       long size_of_base_struct = elems - (const char *)base;
        const struct xt_entry_target *t;
        const char *e = base;
 
+       /* target start is within the ip/ip6/arpt_entry struct */
+       if (target_offset < size_of_base_struct)
+               return -EINVAL;
+
        if (target_offset + sizeof(*t) > next_offset)
                return -EINVAL;