[patch 13/32] NETFILTER: Fix iptables compat hook validation

Chris Wright
2006-12-08T23:58:51Z

[-- Attachment #1: netfilter-fix-iptables-compat-hook-validation.patch --]
[-- Type: text/plain, Size: 4623 bytes --]

-stable review patch. If anyone has any objections, please let us know.
------------------

From: Dmitry Mishin <dim@openvz.org>

In compat mode, matches and targets valid hooks checks always successful due
to not initialized e->comefrom field yet. This patch separates this checks from
translation code and moves them after mark_source_chains() call, where these
marks are initialized.

Signed-off-by: Dmitry Mishin <dim@openvz.org>
Signed-off-by; Patrick McHardy <kaber@trash.net>
Signed-off-by: Chris Wright <chrisw@sous-sol.org>
---
commit 14f5487cb9bd34cd59360d2cac7dccac9b27e8ce
tree fab7cabcdb7fe450ff47bf42918f845ff3da1b86
parent 756c508367e95d6f963502e4feecb8c76aeee332
author Dmitry Mishin <dim@openvz.org> Mon, 04 Dec 2006 12:19:35 +0100
committer Patrick McHardy <kaber@trash.net> Mon, 04 Dec 2006 12:19:35 +0100

net/ipv4/netfilter/ip_tables.c | 78 ++++++++++++++++++++++++++---------------
1 file changed, 51 insertions(+), 27 deletions(-)

--- linux-2.6.19.orig/net/ipv4/netfilter/ip_tables.c
+++ linux-2.6.19/net/ipv4/netfilter/ip_tables.c
@@ -1516,25 +1516,8 @@ static inline int compat_copy_match_from
void **dstptr, compat_uint_t *size, const char *name,
const struct ipt_ip *ip, unsigned int hookmask)
{
- struct ipt_entry_match *dm;
- struct ipt_match *match;
- int ret;
-
- dm = (struct ipt_entry_match *)*dstptr;
- match = m->u.kernel.match;
xt_compat_match_from_user(m, dstptr, size);
-
- ret = xt_check_match(match, AF_INET, dm->u.match_size - sizeof(*dm),
- name, hookmask, ip->proto,
- ip->invflags & IPT_INV_PROTO);
- if (!ret && m->u.kernel.match->checkentry
- && !m->u.kernel.match->checkentry(name, ip, match, dm->data,
- hookmask)) {
- duprintf("ip_tables: check failed for `%s'.\n",
- m->u.kernel.match->name);
- ret = -EINVAL;
- }
- return ret;
+ return 0;
}

static int compat_copy_entry_from_user(struct ipt_entry *e, void **dstptr,
@@ -1556,7 +1539,7 @@ static int compat_copy_entry_from_user(s
ret = IPT_MATCH_ITERATE(e, compat_copy_match_from_user, dstptr, size,
name, &de->ip, de->comefrom);
if (ret)
- goto err;
+ return ret;
de->target_offset = e->target_offset - (origsize - *size);
t = ipt_get_target(e);
target = t->u.kernel.target;
@@ -1569,26 +1552,62 @@ static int compat_copy_entry_from_user(s
if ((unsigned char *)de - base < newinfo->underflow[h])
newinfo->underflow[h] -= origsize - *size;
}
+ return ret;
+}
+
+static inline int compat_check_match(struct ipt_entry_match *m, const char *name,
+ const struct ipt_ip *ip, unsigned int hookmask)
+{
+ struct ipt_match *match;
+ int ret;

- t = ipt_get_target(de);
+ match = m->u.kernel.match;
+ ret = xt_check_match(match, AF_INET, m->u.match_size - sizeof(*m),
+ name, hookmask, ip->proto,
+ ip->invflags & IPT_INV_PROTO);
+ if (!ret && m->u.kernel.match->checkentry
+ && !m->u.kernel.match->checkentry(name, ip, match, m->data,
+ hookmask)) {
+ duprintf("ip_tables: compat: check failed for `%s'.\n",
+ m->u.kernel.match->name);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static inline int compat_check_target(struct ipt_entry *e, const char *name)
+{
+ struct ipt_entry_target *t;
+ struct ipt_target *target;
+ int ret;
+
+ t = ipt_get_target(e);
target = t->u.kernel.target;
ret = xt_check_target(target, AF_INET, t->u.target_size - sizeof(*t),
name, e->comefrom, e->ip.proto,
e->ip.invflags & IPT_INV_PROTO);
- if (ret)
- goto err;
-
- if (t->u.kernel.target->checkentry
- && !t->u.kernel.target->checkentry(name, de, target,
- t->data, de->comefrom)) {
+ if (!ret && t->u.kernel.target->checkentry
+ && !t->u.kernel.target->checkentry(name, e, target,
+ t->data, e->comefrom)) {
duprintf("ip_tables: compat: check failed for `%s'.\n",
t->u.kernel.target->name);
ret = -EINVAL;
}
-err:
return ret;
}

+static inline int compat_check_entry(struct ipt_entry *e, const char *name)
+{
+ int ret;
+
+ ret = IPT_MATCH_ITERATE(e, compat_check_match, name, &e->ip,
+ e->comefrom);
+ if (ret)
+ return ret;
+
+ return compat_check_target(e, name);
+}
+
static int
translate_compat_table(const char *name,
unsigned int valid_hooks,
@@ -1677,6 +1696,11 @@ translate_compat_table(const char *name,
if (!mark_source_chains(newinfo, valid_hooks, entry1))
goto free_newinfo;

+ ret = IPT_ENTRY_ITERATE(entry1, newinfo->size, compat_check_entry,
+ name);
+ if (ret)
+ goto free_newinfo;
+
/* And one copy for every other CPU */
for_each_possible_cpu(i)
if (newinfo->entries[i] && newinfo->entries[i] != entry1)

--