aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/tun.c
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2009-01-20 05:57:48 -0500
committerDavid S. Miller <davem@davemloft.net>2009-01-21 19:00:43 -0500
commita7385ba21102a90f902055f9f185ca02bf62fa43 (patch)
tree93aa0f83a70f704e33ed378b2ad4636533bd7bde /drivers/net/tun.c
parent74a3e5a71c9b54c63bff978e9cafbcef67600f0b (diff)
tun: Fix races in tun_set_iff
It is possible for two different tasks with access to the same file descriptor to call tun_set_iff on it at the same time and race to attach to a tap device. Prevent this by placing all of the logic to attach to a file descriptor in one function and testing the file descriptor to be certain it is not already attached to another tun device. Signed-off-by: Eric W. Biederman <ebiederm@aristanetworks.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r--drivers/net/tun.c48
1 files changed, 32 insertions, 16 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 17923a508535..20ef14dc5603 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -106,6 +106,31 @@ struct tun_struct {
106#endif 106#endif
107}; 107};
108 108
109static int tun_attach(struct tun_struct *tun, struct file *file)
110{
111 const struct cred *cred = current_cred();
112
113 ASSERT_RTNL();
114
115 if (file->private_data)
116 return -EINVAL;
117
118 if (tun->attached)
119 return -EBUSY;
120
121 /* Check permissions */
122 if (((tun->owner != -1 && cred->euid != tun->owner) ||
123 (tun->group != -1 && cred->egid != tun->group)) &&
124 !capable(CAP_NET_ADMIN))
125 return -EPERM;
126
127 file->private_data = tun;
128 tun->attached = 1;
129 get_net(dev_net(tun->dev));
130
131 return 0;
132}
133
109/* TAP filterting */ 134/* TAP filterting */
110static void addr_hash_set(u32 *mask, const u8 *addr) 135static void addr_hash_set(u32 *mask, const u8 *addr)
111{ 136{
@@ -695,7 +720,6 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
695{ 720{
696 struct tun_struct *tun; 721 struct tun_struct *tun;
697 struct net_device *dev; 722 struct net_device *dev;
698 const struct cred *cred = current_cred();
699 int err; 723 int err;
700 724
701 dev = __dev_get_by_name(net, ifr->ifr_name); 725 dev = __dev_get_by_name(net, ifr->ifr_name);
@@ -707,17 +731,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
707 else 731 else
708 return -EINVAL; 732 return -EINVAL;
709 733
710 if (tun->attached) 734 err = tun_attach(tun, file);
711 return -EBUSY; 735 if (err < 0)
712 736 return err;
713 /* Check permissions */
714 if (((tun->owner != -1 &&
715 cred->euid != tun->owner) ||
716 (tun->group != -1 &&
717 cred->egid != tun->group)) &&
718 !capable(CAP_NET_ADMIN)) {
719 return -EPERM;
720 }
721 } 737 }
722 else { 738 else {
723 char *name; 739 char *name;
@@ -766,6 +782,10 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
766 err = register_netdevice(tun->dev); 782 err = register_netdevice(tun->dev);
767 if (err < 0) 783 if (err < 0)
768 goto err_free_dev; 784 goto err_free_dev;
785
786 err = tun_attach(tun, file);
787 if (err < 0)
788 goto err_free_dev;
769 } 789 }
770 790
771 DBG(KERN_INFO "%s: tun_set_iff\n", tun->dev->name); 791 DBG(KERN_INFO "%s: tun_set_iff\n", tun->dev->name);
@@ -785,10 +805,6 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
785 else 805 else
786 tun->flags &= ~TUN_VNET_HDR; 806 tun->flags &= ~TUN_VNET_HDR;
787 807
788 file->private_data = tun;
789 tun->attached = 1;
790 get_net(dev_net(tun->dev));
791
792 /* Make sure persistent devices do not get stuck in 808 /* Make sure persistent devices do not get stuck in
793 * xoff state. 809 * xoff state.
794 */ 810 */