diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2009-01-20 05:57:48 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-01-21 19:00:43 -0500 |
commit | a7385ba21102a90f902055f9f185ca02bf62fa43 (patch) | |
tree | 93aa0f83a70f704e33ed378b2ad4636533bd7bde /drivers/net/tun.c | |
parent | 74a3e5a71c9b54c63bff978e9cafbcef67600f0b (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.c | 48 |
1 files changed, 32 insertions, 16 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 17923a50853..20ef14dc560 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 | ||
109 | static 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 */ |
110 | static void addr_hash_set(u32 *mask, const u8 *addr) | 135 | static 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 | */ |