]> Pileus Git - ~andy/linux/commitdiff
net: Allow userns root control of the core of the network stack.
authorEric W. Biederman <ebiederm@xmission.com>
Fri, 16 Nov 2012 03:03:04 +0000 (03:03 +0000)
committerDavid S. Miller <davem@davemloft.net>
Mon, 19 Nov 2012 01:32:45 +0000 (20:32 -0500)
Allow an unpriviled user who has created a user namespace, and then
created a network namespace to effectively use the new network
namespace, by reducing capable(CAP_NET_ADMIN) and
capable(CAP_NET_RAW) calls to be ns_capable(net->user_ns,
CAP_NET_ADMIN), or capable(net->user_ns, CAP_NET_RAW) calls.

Settings that merely control a single network device are allowed.
Either the network device is a logical network device where
restrictions make no difference or the network device is hardware NIC
that has been explicity moved from the initial network namespace.

In general policy and network stack state changes are allowed
while resource control is left unchanged.

Allow ethtool ioctls.

Allow binding to network devices.
Allow setting the socket mark.
Allow setting the socket priority.

Allow setting the network device alias via sysfs.
Allow setting the mtu via sysfs.
Allow changing the network device flags via sysfs.
Allow setting the network device group via sysfs.

Allow the following network device ioctls.
SIOCGMIIPHY
SIOCGMIIREG
SIOCSIFNAME
SIOCSIFFLAGS
SIOCSIFMETRIC
SIOCSIFMTU
SIOCSIFHWADDR
SIOCSIFSLAVE
SIOCADDMULTI
SIOCDELMULTI
SIOCSIFHWBROADCAST
SIOCSMIIREG
SIOCBONDENSLAVE
SIOCBONDRELEASE
SIOCBONDSETHWADDR
SIOCBONDCHANGEACTIVE
SIOCBRADDIF
SIOCBRDELIF
SIOCSHWTSTAMP

Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/core/dev.c
net/core/ethtool.c
net/core/net-sysfs.c
net/core/sock.c

index 974199daa911488df13c8902344ee857f23440e4..0afae8ba413e4976966f357bea2783727761ac1a 100644 (file)
@@ -5279,7 +5279,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
        case SIOCGMIIPHY:
        case SIOCGMIIREG:
        case SIOCSIFNAME:
-               if (!capable(CAP_NET_ADMIN))
+               if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
                        return -EPERM;
                dev_load(net, ifr.ifr_name);
                rtnl_lock();
@@ -5300,16 +5300,25 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
         *      - require strict serialization.
         *      - do not return a value
         */
+       case SIOCSIFMAP:
+       case SIOCSIFTXQLEN:
+               if (!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+               /* fall through */
+       /*
+        *      These ioctl calls:
+        *      - require local superuser power.
+        *      - require strict serialization.
+        *      - do not return a value
+        */
        case SIOCSIFFLAGS:
        case SIOCSIFMETRIC:
        case SIOCSIFMTU:
-       case SIOCSIFMAP:
        case SIOCSIFHWADDR:
        case SIOCSIFSLAVE:
        case SIOCADDMULTI:
        case SIOCDELMULTI:
        case SIOCSIFHWBROADCAST:
-       case SIOCSIFTXQLEN:
        case SIOCSMIIREG:
        case SIOCBONDENSLAVE:
        case SIOCBONDRELEASE:
@@ -5318,7 +5327,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
        case SIOCBRADDIF:
        case SIOCBRDELIF:
        case SIOCSHWTSTAMP:
-               if (!capable(CAP_NET_ADMIN))
+               if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
                        return -EPERM;
                /* fall through */
        case SIOCBONDSLAVEINFOQUERY:
index 4d64cc2e3fa9bf1246ea3504f582616f98060bf1..a8705432e4b1923073b5ed3565daa3baa9d39fa7 100644 (file)
@@ -1460,7 +1460,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
        case ETHTOOL_GEEE:
                break;
        default:
-               if (!capable(CAP_NET_ADMIN))
+               if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
                        return -EPERM;
        }
 
index bcf02f608cbfa76ad06da490f2dbf423e728fc17..c66b8c2f3b22ee721c3e4a80be7e15f0d0f61fb3 100644 (file)
@@ -73,11 +73,12 @@ static ssize_t netdev_store(struct device *dev, struct device_attribute *attr,
                            const char *buf, size_t len,
                            int (*set)(struct net_device *, unsigned long))
 {
-       struct net_device *net = to_net_dev(dev);
+       struct net_device *netdev = to_net_dev(dev);
+       struct net *net = dev_net(netdev);
        unsigned long new;
        int ret = -EINVAL;
 
-       if (!capable(CAP_NET_ADMIN))
+       if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
                return -EPERM;
 
        ret = kstrtoul(buf, 0, &new);
@@ -87,8 +88,8 @@ static ssize_t netdev_store(struct device *dev, struct device_attribute *attr,
        if (!rtnl_trylock())
                return restart_syscall();
 
-       if (dev_isalive(net)) {
-               if ((ret = (*set)(net, new)) == 0)
+       if (dev_isalive(netdev)) {
+               if ((ret = (*set)(netdev, new)) == 0)
                        ret = len;
        }
        rtnl_unlock();
@@ -264,6 +265,9 @@ static ssize_t store_tx_queue_len(struct device *dev,
                                  struct device_attribute *attr,
                                  const char *buf, size_t len)
 {
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
        return netdev_store(dev, attr, buf, len, change_tx_queue_len);
 }
 
@@ -271,10 +275,11 @@ static ssize_t store_ifalias(struct device *dev, struct device_attribute *attr,
                             const char *buf, size_t len)
 {
        struct net_device *netdev = to_net_dev(dev);
+       struct net *net = dev_net(netdev);
        size_t count = len;
        ssize_t ret;
 
-       if (!capable(CAP_NET_ADMIN))
+       if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
                return -EPERM;
 
        /* ignore trailing newline */
index 06286006a2cc2be32a472a4453df7d088e1e17b6..d4f7b58b3866e4763e50de6e81c7c82e1b3a7160 100644 (file)
@@ -515,7 +515,7 @@ static int sock_bindtodevice(struct sock *sk, char __user *optval, int optlen)
 
        /* Sorry... */
        ret = -EPERM;
-       if (!capable(CAP_NET_RAW))
+       if (!ns_capable(net->user_ns, CAP_NET_RAW))
                goto out;
 
        ret = -EINVAL;
@@ -696,7 +696,8 @@ set_rcvbuf:
                break;
 
        case SO_PRIORITY:
-               if ((val >= 0 && val <= 6) || capable(CAP_NET_ADMIN))
+               if ((val >= 0 && val <= 6) ||
+                   ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
                        sk->sk_priority = val;
                else
                        ret = -EPERM;
@@ -813,7 +814,7 @@ set_rcvbuf:
                        clear_bit(SOCK_PASSSEC, &sock->flags);
                break;
        case SO_MARK:
-               if (!capable(CAP_NET_ADMIN))
+               if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
                        ret = -EPERM;
                else
                        sk->sk_mark = val;