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: Christoph Hellwig <hch@lst.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Christoph Hellwig 2020-07-17 08:23:19 +02:00 коммит произвёл David S. Miller
Родитель f415e76fd7
Коммит fc66de8e16
1 изменённых файлов: 98 добавлений и 116 удалений

Просмотреть файл

@ -1451,86 +1451,6 @@ static int copy_everything_to_user(struct ebt_table *t, void __user *user,
ebt_entry_to_user, entries, tmp.entries); 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 #ifdef CONFIG_COMPAT
/* 32 bit-userspace compatibility definitions. */ /* 32 bit-userspace compatibility definitions. */
struct compat_ebt_replace { struct compat_ebt_replace {
@ -2314,28 +2234,6 @@ static int compat_update_counters(struct net *net, void __user *user,
hlp.num_counters, user, len); 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, static int compat_do_ebt_get_ctl(struct sock *sk, int cmd,
void __user *user, int *len) 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 ebt_table *t;
struct net *net = sock_net(sk); 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))) if (copy_from_user(&tmp, user, sizeof(tmp)))
return -EFAULT; return -EFAULT;
@ -2413,20 +2303,112 @@ static int compat_do_ebt_get_ctl(struct sock *sk, int cmd,
} }
#endif #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 = { static struct nf_sockopt_ops ebt_sockopts = {
.pf = PF_INET, .pf = PF_INET,
.set_optmin = EBT_BASE_CTL, .set_optmin = EBT_BASE_CTL,
.set_optmax = EBT_SO_SET_MAX + 1, .set_optmax = EBT_SO_SET_MAX + 1,
.set = do_ebt_set_ctl, .set = do_ebt_set_ctl,
#ifdef CONFIG_COMPAT
.compat_set = compat_do_ebt_set_ctl,
#endif
.get_optmin = EBT_BASE_CTL, .get_optmin = EBT_BASE_CTL,
.get_optmax = EBT_SO_GET_MAX + 1, .get_optmax = EBT_SO_GET_MAX + 1,
.get = do_ebt_get_ctl, .get = do_ebt_get_ctl,
#ifdef CONFIG_COMPAT
.compat_get = compat_do_ebt_get_ctl,
#endif
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };