Merge to Fedora kernel-2.6.18-1.2255_FC5-vs2.0.2.2-rc9 patched with stable patch...
[linux-2.6.git] / net / ipv4 / netfilter / ip_tables.c
index 048514f..e964436 100644 (file)
@@ -404,6 +404,13 @@ mark_source_chains(struct xt_table_info *newinfo,
                            && unconditional(&e->ip)) {
                                unsigned int oldpos, size;
 
+                               if (t->verdict < -NF_MAX_VERDICT - 1) {
+                                       duprintf("mark_source_chains: bad "
+                                               "negative verdict (%i)\n",
+                                                               t->verdict);
+                                       return 0;
+                               }
+
                                /* Return: backtrack through the last
                                   big jump. */
                                do {
@@ -441,6 +448,13 @@ mark_source_chains(struct xt_table_info *newinfo,
                                if (strcmp(t->target.u.user.name,
                                           IPT_STANDARD_TARGET) == 0
                                    && newpos >= 0) {
+                                       if (newpos > newinfo->size -
+                                               sizeof(struct ipt_entry)) {
+                                               duprintf("mark_source_chains: "
+                                                       "bad verdict (%i)\n",
+                                                               newpos);
+                                               return 0;
+                                       }
                                        /* This a jump; chase it. */
                                        duprintf("Jump rule %u -> %u\n",
                                                 pos, newpos);
@@ -473,27 +487,6 @@ cleanup_match(struct ipt_entry_match *m, unsigned int *i)
        return 0;
 }
 
-static inline int
-standard_check(const struct ipt_entry_target *t,
-              unsigned int max_offset)
-{
-       struct ipt_standard_target *targ = (void *)t;
-
-       /* Check standard info. */
-       if (targ->verdict >= 0
-           && targ->verdict > max_offset - sizeof(struct ipt_entry)) {
-               duprintf("ipt_standard_check: bad verdict (%i)\n",
-                        targ->verdict);
-               return 0;
-       }
-       if (targ->verdict < -NF_MAX_VERDICT - 1) {
-               duprintf("ipt_standard_check: bad negative verdict (%i)\n",
-                        targ->verdict);
-               return 0;
-       }
-       return 1;
-}
-
 static inline int
 check_match(struct ipt_entry_match *m,
            const char *name,
@@ -552,12 +545,18 @@ check_entry(struct ipt_entry *e, const char *name, unsigned int size,
                return -EINVAL;
        }
 
+       if (e->target_offset + sizeof(struct ipt_entry_target) > e->next_offset)
+               return -EINVAL;
+
        j = 0;
        ret = IPT_MATCH_ITERATE(e, check_match, name, &e->ip, e->comefrom, &j);
        if (ret != 0)
                goto cleanup_matches;
 
        t = ipt_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_INET,
                                                     t->u.user.name,
                                                     t->u.user.revision),
@@ -575,12 +574,7 @@ check_entry(struct ipt_entry *e, const char *name, unsigned int size,
        if (ret)
                goto err;
 
-       if (t->u.kernel.target == &ipt_standard_target) {
-               if (!standard_check(t, size)) {
-                       ret = -EINVAL;
-                       goto cleanup_matches;
-               }
-       } else if (t->u.kernel.target->checkentry
+       if (t->u.kernel.target->checkentry
                   && !t->u.kernel.target->checkentry(name, e, target, t->data,
                                                      t->u.target_size
                                                      - sizeof(*t),
@@ -730,7 +724,7 @@ translate_table(const char *name,
 
        if (ret != 0) {
                IPT_ENTRY_ITERATE(entry0, newinfo->size,
-                                 cleanup_entry, &i);
+                               cleanup_entry, &i);
                return ret;
        }
 
@@ -1531,15 +1525,22 @@ check_compat_entry_size_and_hooks(struct ipt_entry *e,
                return -EINVAL;
        }
 
+       if (e->target_offset + sizeof(struct compat_xt_entry_target) >
+                                                               e->next_offset)
+               return -EINVAL;
+
        off = 0;
        entry_offset = (void *)e - (void *)base;
        j = 0;
        ret = IPT_MATCH_ITERATE(e, compat_check_calc_match, name, &e->ip,
                        e->comefrom, &off, &j);
        if (ret != 0)
-               goto out;
+               goto cleanup_matches;
 
        t = ipt_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_INET,
                                                     t->u.user.name,
                                                     t->u.user.revision),
@@ -1547,7 +1548,7 @@ check_compat_entry_size_and_hooks(struct ipt_entry *e,
        if (IS_ERR(target) || !target) {
                duprintf("check_entry: `%s' not found\n", t->u.user.name);
                ret = target ? PTR_ERR(target) : -ENOENT;
-               goto out;
+               goto cleanup_matches;
        }
        t->u.kernel.target = target;
 
@@ -1574,7 +1575,10 @@ check_compat_entry_size_and_hooks(struct ipt_entry *e,
 
        (*i)++;
        return 0;
+
 out:
+       module_put(t->u.kernel.target->me);
+cleanup_matches:
        IPT_MATCH_ITERATE(e, cleanup_match, &j);
        return ret;
 }
@@ -1597,18 +1601,16 @@ static inline int compat_copy_match_from_user(struct ipt_entry_match *m,
        ret = xt_check_match(match, AF_INET, dm->u.match_size - sizeof(*dm),
                             name, hookmask, ip->proto,
                             ip->invflags & IPT_INV_PROTO);
-       if (ret)
-               return ret;
 
-       if (m->u.kernel.match->checkentry
+       if (!ret && m->u.kernel.match->checkentry
            && !m->u.kernel.match->checkentry(name, ip, match, dm->data,
                                              dm->u.match_size - sizeof(*dm),
                                              hookmask)) {
                duprintf("ip_tables: check failed for `%s'.\n",
                         m->u.kernel.match->name);
-               return -EINVAL;
+               ret = -EINVAL;
        }
-       return 0;
+       return ret;
 }
 
 static int compat_copy_entry_from_user(struct ipt_entry *e, void **dstptr,
@@ -1630,7 +1632,7 @@ static int compat_copy_entry_from_user(struct ipt_entry *e, void **dstptr,
        ret = IPT_MATCH_ITERATE(e, compat_copy_match_from_user, dstptr, size,
                        name, &de->ip, de->comefrom);
        if (ret)
-               goto out;
+               goto err;
        de->target_offset = e->target_offset - (origsize - *size);
        t = ipt_get_target(e);
        target = t->u.kernel.target;
@@ -1653,22 +1655,18 @@ static int compat_copy_entry_from_user(struct ipt_entry *e, void **dstptr,
                              name, e->comefrom, e->ip.proto,
                              e->ip.invflags & IPT_INV_PROTO);
        if (ret)
-               goto out;
+               goto err;
 
-       ret = -EINVAL;
-       if (t->u.kernel.target == &ipt_standard_target) {
-               if (!standard_check(t, *size))
-                       goto out;
-       } else if (t->u.kernel.target->checkentry
+       if (t->u.kernel.target->checkentry
                   && !t->u.kernel.target->checkentry(name, de, target,
                                t->data, t->u.target_size - sizeof(*t),
                                de->comefrom)) {
                duprintf("ip_tables: compat: check failed for `%s'.\n",
                         t->u.kernel.target->name);
-               goto out;
+               ret = -EINVAL;
+               goto err;
        }
-       ret = 0;
-out:
+ err:
        return ret;
 }
 
@@ -1682,7 +1680,7 @@ translate_compat_table(const char *name,
                unsigned int *hook_entries,
                unsigned int *underflows)
 {
-       unsigned int i;
+       unsigned int i, j;
        struct xt_table_info *newinfo, *info;
        void *pos, *entry0, *entry1;
        unsigned int size;
@@ -1700,21 +1698,21 @@ translate_compat_table(const char *name,
        }
 
        duprintf("translate_compat_table: size %u\n", info->size);
-       i = 0;
+       j = 0;
        xt_compat_lock(AF_INET);
        /* Walk through entries, checking offsets. */
        ret = IPT_ENTRY_ITERATE(entry0, total_size,
                                check_compat_entry_size_and_hooks,
                                info, &size, entry0,
                                entry0 + total_size,
-                               hook_entries, underflows, &i, name);
+                               hook_entries, underflows, &j, name);
        if (ret != 0)
                goto out_unlock;
 
        ret = -EINVAL;
-       if (i != number) {
+       if (j != number) {
                duprintf("translate_compat_table: %u not %u entries\n",
-                        i, number);
+                        j, number);
                goto out_unlock;
        }
 
@@ -1773,8 +1771,10 @@ translate_compat_table(const char *name,
 free_newinfo:
        xt_free_table_info(newinfo);
 out:
+       IPT_ENTRY_ITERATE(entry0, total_size, cleanup_entry, &j);
        return ret;
 out_unlock:
+       compat_flush_offsets();
        xt_compat_unlock(AF_INET);
        goto out;
 }
@@ -1994,6 +1994,9 @@ compat_do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
 {
        int ret;
 
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
        switch (cmd) {
        case IPT_SO_GET_INFO:
                ret = get_info(user, len, 1);