Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f6677f43 authored by Dmitry Mishin's avatar Dmitry Mishin Committed by David S. Miller
Browse files

[NETFILTER]: Fix iptables compat hook validation



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: default avatarDmitry Mishin <dim@openvz.org>
Signed-off-by; Patrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 74c9c0c1
Loading
Loading
Loading
Loading
+51 −27
Original line number Diff line number Diff line
@@ -1516,25 +1516,8 @@ static inline int compat_copy_match_from_user(struct ipt_entry_match *m,
	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(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 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(struct ipt_entry *e, void **dstptr,
		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;

	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;
}

	t = ipt_get_target(de);
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)