diff options
Diffstat (limited to 'drivers/net/tun.c')
| -rw-r--r-- | drivers/net/tun.c | 23 |
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 |
