netfilter: x_tables: check for bogus target offset

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>
struct xt_table *xt_find_table_lock(struct net *net, u_int8_t af,
				    const char *name)
{
	struct xt_table *t, *found = NULL;

	mutex_lock(&xt[af].mutex);
	list_for_each_entry(t, &net->xt.tables[af], list)
		if (strcmp(t->name, name) == 0 && try_module_get(t->me))
			return t;

	if (net == &init_net)
		goto out;

	/* Table doesn't exist in this netns, re-try init */
	list_for_each_entry(t, &init_net.xt.tables[af], list) {
		if (strcmp(t->name, name))
			continue;
		if (!try_module_get(t->me))
			return NULL;

		mutex_unlock(&xt[af].mutex);
		if (t->table_init(net) != 0) {
			module_put(t->me);
			return NULL;
		}

		found = t;

		mutex_lock(&xt[af].mutex);
		break;
	}

	if (!found)
		goto out;

	/* and once again: */
	list_for_each_entry(t, &net->xt.tables[af], list)
		if (strcmp(t->name, name) == 0)
			return t;

	module_put(found->me);
 out:
	mutex_unlock(&xt[af].mutex);
	return NULL;
}

struct xt_table *xt_find_table_lock(struct net *net, u_int8_t af,
				    const char *name)
{
	struct xt_table *t, *found = NULL;

	mutex_lock(&xt[af].mutex);
	list_for_each_entry(t, &net->xt.tables[af], list)
		if (strcmp(t->name, name) == 0 && try_module_get(t->me))
			return t;

	if (net == &init_net)
		goto out;

	/* Table doesn't exist in this netns, re-try init */
	list_for_each_entry(t, &init_net.xt.tables[af], list) {
		if (strcmp(t->name, name))
			continue;
		if (!try_module_get(t->me))
			return NULL;

		mutex_unlock(&xt[af].mutex);
		if (t->table_init(net) != 0) {
			module_put(t->me);
			return NULL;
		}

		found = t;

		mutex_lock(&xt[af].mutex);
		break;
	}

	if (!found)
		goto out;

	/* and once again: */
	list_for_each_entry(t, &net->xt.tables[af], list)
		if (strcmp(t->name, name) == 0)
			return t;

	module_put(found->me);
 out:
	mutex_unlock(&xt[af].mutex);
	return NULL;
}
