aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/tun.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r--drivers/net/tun.c72
1 files changed, 34 insertions, 38 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 027f7aba26a..87214a257d2 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -130,17 +130,10 @@ static inline struct tun_sock *tun_sk(struct sock *sk)
static int tun_attach(struct tun_struct *tun, struct file *file)
{
struct tun_file *tfile = file->private_data;
- const struct cred *cred = current_cred();
int err;
ASSERT_RTNL();
- /* Check permissions */
- if (((tun->owner != -1 && cred->euid != tun->owner) ||
- (tun->group != -1 && !in_egroup_p(tun->group))) &&
- !capable(CAP_NET_ADMIN))
- return -EPERM;
-
netif_tx_lock_bh(tun->dev);
err = -EINVAL;
@@ -926,6 +919,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
dev = __dev_get_by_name(net, ifr->ifr_name);
if (dev) {
+ const struct cred *cred = current_cred();
+
if (ifr->ifr_flags & IFF_TUN_EXCL)
return -EBUSY;
if ((ifr->ifr_flags & IFF_TUN) && dev->netdev_ops == &tun_netdev_ops)
@@ -935,6 +930,14 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
else
return -EINVAL;
+ if (((tun->owner != -1 && cred->euid != tun->owner) ||
+ (tun->group != -1 && !in_egroup_p(tun->group))) &&
+ !capable(CAP_NET_ADMIN))
+ return -EPERM;
+ err = security_tun_dev_attach(tun->sk);
+ if (err < 0)
+ return err;
+
err = tun_attach(tun, file);
if (err < 0)
return err;
@@ -947,6 +950,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
if (!capable(CAP_NET_ADMIN))
return -EPERM;
+ err = security_tun_dev_create();
+ if (err < 0)
+ return err;
/* Set dev type */
if (ifr->ifr_flags & IFF_TUN) {
@@ -989,6 +995,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
tun->sk = sk;
container_of(sk, struct tun_sock, sk)->tun = tun;
+ security_tun_dev_post_create(sk);
+
tun_net_init(dev);
if (strchr(dev->name, '%')) {
@@ -1048,20 +1056,15 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
return err;
}
-static int tun_get_iff(struct net *net, struct file *file, struct ifreq *ifr)
+static int tun_get_iff(struct net *net, struct tun_struct *tun,
+ struct ifreq *ifr)
{
- struct tun_struct *tun = tun_get(file);
-
- if (!tun)
- return -EBADFD;
-
DBG(KERN_INFO "%s: tun_get_iff\n", tun->dev->name);
strcpy(ifr->ifr_name, tun->dev->name);
ifr->ifr_flags = tun_flags(tun);
- tun_put(tun);
return 0;
}
@@ -1105,8 +1108,8 @@ static int set_offload(struct net_device *dev, unsigned long arg)
return 0;
}
-static int tun_chr_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static long tun_chr_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
struct tun_file *tfile = file->private_data;
struct tun_struct *tun;
@@ -1128,34 +1131,32 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
(unsigned int __user*)argp);
}
+ rtnl_lock();
+
tun = __tun_get(tfile);
if (cmd == TUNSETIFF && !tun) {
- int err;
-
ifr.ifr_name[IFNAMSIZ-1] = '\0';
- rtnl_lock();
- err = tun_set_iff(tfile->net, file, &ifr);
- rtnl_unlock();
+ ret = tun_set_iff(tfile->net, file, &ifr);
- if (err)
- return err;
+ if (ret)
+ goto unlock;
if (copy_to_user(argp, &ifr, sizeof(ifr)))
- return -EFAULT;
- return 0;
+ ret = -EFAULT;
+ goto unlock;
}
-
+ ret = -EBADFD;
if (!tun)
- return -EBADFD;
+ goto unlock;
DBG(KERN_INFO "%s: tun_chr_ioctl cmd %d\n", tun->dev->name, cmd);
ret = 0;
switch (cmd) {
case TUNGETIFF:
- ret = tun_get_iff(current->nsproxy->net_ns, file, &ifr);
+ ret = tun_get_iff(current->nsproxy->net_ns, tun, &ifr);
if (ret)
break;
@@ -1201,7 +1202,6 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
case TUNSETLINK:
/* Only allow setting the type when the interface is down */
- rtnl_lock();
if (tun->dev->flags & IFF_UP) {
DBG(KERN_INFO "%s: Linktype set failed because interface is up\n",
tun->dev->name);
@@ -1211,7 +1211,6 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
DBG(KERN_INFO "%s: linktype set to %d\n", tun->dev->name, tun->dev->type);
ret = 0;
}
- rtnl_unlock();
break;
#ifdef TUN_DEBUG
@@ -1220,9 +1219,7 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
break;
#endif
case TUNSETOFFLOAD:
- rtnl_lock();
ret = set_offload(tun->dev, arg);
- rtnl_unlock();
break;
case TUNSETTXFILTER:
@@ -1230,9 +1227,7 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
ret = -EINVAL;
if ((tun->flags & TUN_TYPE_MASK) != TUN_TAP_DEV)
break;
- rtnl_lock();
ret = update_filter(&tun->txflt, (void __user *)arg);
- rtnl_unlock();
break;
case SIOCGIFHWADDR:
@@ -1248,9 +1243,7 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
DBG(KERN_DEBUG "%s: set hw address: %pM\n",
tun->dev->name, ifr.ifr_hwaddr.sa_data);
- rtnl_lock();
ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr);
- rtnl_unlock();
break;
case TUNGETSNDBUF:
@@ -1273,7 +1266,10 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
break;
};
- tun_put(tun);
+unlock:
+ rtnl_unlock();
+ if (tun)
+ tun_put(tun);
return ret;
}
@@ -1361,7 +1357,7 @@ static const struct file_operations tun_fops = {
.write = do_sync_write,
.aio_write = tun_chr_aio_write,
.poll = tun_chr_poll,
- .ioctl = tun_chr_ioctl,
+ .unlocked_ioctl = tun_chr_ioctl,
.open = tun_chr_open,
.release = tun_chr_close,
.fasync = tun_chr_fasync