Commit fc66de8e authored by Christoph Hellwig's avatar Christoph Hellwig Committed by David S. Miller
Browse files

netfilter/ebtables: clean up compat {get, set}sockopt handling



Merge the native and compat {get,set}sockopt handlers using
in_compat_syscall().  Note that this required moving a fair
amout of code around to be done sanely.

Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f415e76f
Loading
Loading
Loading
Loading
+98 −116
Original line number Diff line number Diff line
@@ -1451,86 +1451,6 @@ static int copy_everything_to_user(struct ebt_table *t, void __user *user,
	   ebt_entry_to_user, entries, tmp.entries);
}

static int do_ebt_set_ctl(struct sock *sk,
	int cmd, void __user *user, unsigned int len)
{
	int ret;
	struct net *net = sock_net(sk);

	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
		return -EPERM;

	switch (cmd) {
	case EBT_SO_SET_ENTRIES:
		ret = do_replace(net, user, len);
		break;
	case EBT_SO_SET_COUNTERS:
		ret = update_counters(net, user, len);
		break;
	default:
		ret = -EINVAL;
	}
	return ret;
}

static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
{
	int ret;
	struct ebt_replace tmp;
	struct ebt_table *t;
	struct net *net = sock_net(sk);

	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
		return -EPERM;

	if (copy_from_user(&tmp, user, sizeof(tmp)))
		return -EFAULT;

	tmp.name[sizeof(tmp.name) - 1] = '\0';

	t = find_table_lock(net, tmp.name, &ret, &ebt_mutex);
	if (!t)
		return ret;

	switch (cmd) {
	case EBT_SO_GET_INFO:
	case EBT_SO_GET_INIT_INFO:
		if (*len != sizeof(struct ebt_replace)) {
			ret = -EINVAL;
			mutex_unlock(&ebt_mutex);
			break;
		}
		if (cmd == EBT_SO_GET_INFO) {
			tmp.nentries = t->private->nentries;
			tmp.entries_size = t->private->entries_size;
			tmp.valid_hooks = t->valid_hooks;
		} else {
			tmp.nentries = t->table->nentries;
			tmp.entries_size = t->table->entries_size;
			tmp.valid_hooks = t->table->valid_hooks;
		}
		mutex_unlock(&ebt_mutex);
		if (copy_to_user(user, &tmp, *len) != 0) {
			ret = -EFAULT;
			break;
		}
		ret = 0;
		break;

	case EBT_SO_GET_ENTRIES:
	case EBT_SO_GET_INIT_ENTRIES:
		ret = copy_everything_to_user(t, user, len, cmd);
		mutex_unlock(&ebt_mutex);
		break;

	default:
		mutex_unlock(&ebt_mutex);
		ret = -EINVAL;
	}

	return ret;
}

#ifdef CONFIG_COMPAT
/* 32 bit-userspace compatibility definitions. */
struct compat_ebt_replace {
@@ -2314,28 +2234,6 @@ static int compat_update_counters(struct net *net, void __user *user,
					hlp.num_counters, user, len);
}

static int compat_do_ebt_set_ctl(struct sock *sk,
		int cmd, void __user *user, unsigned int len)
{
	int ret;
	struct net *net = sock_net(sk);

	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
		return -EPERM;

	switch (cmd) {
	case EBT_SO_SET_ENTRIES:
		ret = compat_do_replace(net, user, len);
		break;
	case EBT_SO_SET_COUNTERS:
		ret = compat_update_counters(net, user, len);
		break;
	default:
		ret = -EINVAL;
	}
	return ret;
}

static int compat_do_ebt_get_ctl(struct sock *sk, int cmd,
		void __user *user, int *len)
{
@@ -2344,14 +2242,6 @@ static int compat_do_ebt_get_ctl(struct sock *sk, int cmd,
	struct ebt_table *t;
	struct net *net = sock_net(sk);

	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
		return -EPERM;

	/* try real handler in case userland supplied needed padding */
	if ((cmd == EBT_SO_GET_INFO ||
	     cmd == EBT_SO_GET_INIT_INFO) && *len != sizeof(tmp))
			return do_ebt_get_ctl(sk, cmd, user, len);

	if (copy_from_user(&tmp, user, sizeof(tmp)))
		return -EFAULT;

@@ -2413,20 +2303,112 @@ static int compat_do_ebt_get_ctl(struct sock *sk, int cmd,
}
#endif

static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
{
	struct net *net = sock_net(sk);
	struct ebt_replace tmp;
	struct ebt_table *t;
	int ret;

	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
		return -EPERM;

#ifdef CONFIG_COMPAT
	/* try real handler in case userland supplied needed padding */
	if (in_compat_syscall() &&
	    ((cmd != EBT_SO_GET_INFO && cmd != EBT_SO_GET_INIT_INFO) ||
	     *len != sizeof(tmp)))
		return compat_do_ebt_get_ctl(sk, cmd, user, len);
#endif

	if (copy_from_user(&tmp, user, sizeof(tmp)))
		return -EFAULT;

	tmp.name[sizeof(tmp.name) - 1] = '\0';

	t = find_table_lock(net, tmp.name, &ret, &ebt_mutex);
	if (!t)
		return ret;

	switch (cmd) {
	case EBT_SO_GET_INFO:
	case EBT_SO_GET_INIT_INFO:
		if (*len != sizeof(struct ebt_replace)) {
			ret = -EINVAL;
			mutex_unlock(&ebt_mutex);
			break;
		}
		if (cmd == EBT_SO_GET_INFO) {
			tmp.nentries = t->private->nentries;
			tmp.entries_size = t->private->entries_size;
			tmp.valid_hooks = t->valid_hooks;
		} else {
			tmp.nentries = t->table->nentries;
			tmp.entries_size = t->table->entries_size;
			tmp.valid_hooks = t->table->valid_hooks;
		}
		mutex_unlock(&ebt_mutex);
		if (copy_to_user(user, &tmp, *len) != 0) {
			ret = -EFAULT;
			break;
		}
		ret = 0;
		break;

	case EBT_SO_GET_ENTRIES:
	case EBT_SO_GET_INIT_ENTRIES:
		ret = copy_everything_to_user(t, user, len, cmd);
		mutex_unlock(&ebt_mutex);
		break;

	default:
		mutex_unlock(&ebt_mutex);
		ret = -EINVAL;
	}

	return ret;
}

static int do_ebt_set_ctl(struct sock *sk, int cmd, void __user *user,
		unsigned int len)
{
	struct net *net = sock_net(sk);
	int ret;

	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
		return -EPERM;

	switch (cmd) {
	case EBT_SO_SET_ENTRIES:
#ifdef CONFIG_COMPAT
		if (in_compat_syscall())
			ret = compat_do_replace(net, user, len);
		else
#endif
			ret = do_replace(net, user, len);
		break;
	case EBT_SO_SET_COUNTERS:
#ifdef CONFIG_COMPAT
		if (in_compat_syscall())
			ret = compat_update_counters(net, user, len);
		else
#endif
			ret = update_counters(net, user, len);
		break;
	default:
		ret = -EINVAL;
	}
	return ret;
}

static struct nf_sockopt_ops ebt_sockopts = {
	.pf		= PF_INET,
	.set_optmin	= EBT_BASE_CTL,
	.set_optmax	= EBT_SO_SET_MAX + 1,
	.set		= do_ebt_set_ctl,
#ifdef CONFIG_COMPAT
	.compat_set	= compat_do_ebt_set_ctl,
#endif
	.get_optmin	= EBT_BASE_CTL,
	.get_optmax	= EBT_SO_GET_MAX + 1,
	.get		= do_ebt_get_ctl,
#ifdef CONFIG_COMPAT
	.compat_get	= compat_do_ebt_get_ctl,
#endif
	.owner		= THIS_MODULE,
};