summaryrefslogtreecommitdiff
path: root/drivers/net/tun.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r--drivers/net/tun.c23
1 files changed, 15 insertions, 8 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 242899f7fd07..439565a264ee 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -513,14 +513,18 @@ static u16 tun_select_queue(struct net_device *dev, struct sk_buff *skb,
return ret;
}
-static inline bool tun_not_capable(struct tun_struct *tun)
+static inline bool tun_permitted(struct tun_struct *tun)
{
const struct cred *cred = current_cred();
struct net *net = dev_net(tun->dev);
- return ((uid_valid(tun->owner) && !uid_eq(cred->euid, tun->owner)) ||
- (gid_valid(tun->group) && !in_egroup_p(tun->group))) &&
- !ns_capable(net->user_ns, CAP_NET_ADMIN);
+ /* If both uid and gid are set, *both* must match */
+ if ((uid_valid(tun->owner) || gid_valid(tun->group)) &&
+ (!uid_valid(tun->owner) || uid_eq(cred->euid, tun->owner)) &&
+ (!gid_valid(tun->group) || in_egroup_p(tun->group)))
+ return true;
+
+ return ns_capable(net->user_ns, CAP_NET_ADMIN);
}
static void tun_set_real_num_queues(struct tun_struct *tun)
@@ -1903,7 +1907,8 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
if (tun->flags & IFF_NO_PI) {
u8 ip_version;
- if (!pskb_may_pull(skb, 1)) {
+ drop_reason = pskb_may_pull_reason(skb, 1);
+ if (drop_reason) {
err = -EINVAL;
goto drop;
}
@@ -1926,9 +1931,9 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
skb->protocol = pi.proto;
break;
case IFF_TAP:
- if (!pskb_may_pull(skb, ETH_HLEN)) {
+ drop_reason = pskb_may_pull_reason(skb, ETH_HLEN);
+ if (drop_reason) {
err = -ENOMEM;
- drop_reason = SKB_DROP_REASON_HDR_TRUNC;
goto drop;
}
skb_set_network_header(skb, ETH_HLEN);
@@ -2065,6 +2070,7 @@ drop:
dev_core_stats_rx_dropped_inc(tun->dev);
free_skb:
+ drop_reason = drop_reason ?: SKB_DROP_REASON_NOT_SPECIFIED;
if (!IS_ERR_OR_NULL(skb))
kfree_skb_reason(skb, drop_reason);
@@ -2877,7 +2883,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
!!(tun->flags & IFF_MULTI_QUEUE))
return -EINVAL;
- if (tun_not_capable(tun))
+ if (!tun_permitted(tun))
return -EPERM;
err = security_tun_dev_open(tun->security);
if (err < 0)
@@ -3860,6 +3866,7 @@ static void __exit tun_cleanup(void)
misc_deregister(&tun_miscdev);
rtnl_link_unregister(&tun_link_ops);
unregister_netdevice_notifier(&tun_notifier_block);
+ rcu_barrier();
}
/* Get an underlying socket object from tun file. Returns error unless file is