selinux: make the netif cache namespace aware
authorPaul Moore <pmoore@redhat.com>
Wed, 10 Sep 2014 21:09:57 +0000 (17:09 -0400)
committerStephen Smalley <sds@tycho.nsa.gov>
Tue, 20 Jan 2015 20:39:33 +0000 (20:39 +0000)
While SELinux largely ignores namespaces, for good reason, there are
some places where it needs to at least be aware of namespaces in order
to function correctly.  Network namespaces are one example.  Basic
awareness of network namespaces are necessary in order to match a
network interface's index number to an actual network device.

This patch corrects a problem with network interfaces added to a
non-init namespace, and can be reproduced with the following commands:

 [NOTE: the NetLabel configuration is here only to active the dynamic
        networking controls ]

 # netlabelctl unlbl add default address:0.0.0.0/0 \
   label:system_u:object_r:unlabeled_t:s0
 # netlabelctl unlbl add default address:::/0 \
   label:system_u:object_r:unlabeled_t:s0
 # netlabelctl cipsov4 add pass doi:100 tags:1
 # netlabelctl map add domain:lspp_test_netlabel_t \
   protocol:cipsov4,100

 # ip link add type veth
 # ip netns add myns
 # ip link set veth1 netns myns
 # ip a add dev veth0 10.250.13.100/24
 # ip netns exec myns ip a add dev veth1 10.250.13.101/24
 # ip l set veth0 up
 # ip netns exec myns ip l set veth1 up

 # ping -c 1 10.250.13.101
 # ip netns exec myns ping -c 1 10.250.13.100

Reported-by: Jiri Jaburek <jjaburek@redhat.com>
Signed-off-by: Paul Moore <pmoore@redhat.com>
security/selinux/hooks.c
security/selinux/include/netif.h
security/selinux/include/objsec.h
security/selinux/netif.c

index 65d7aa40adf3cea039ef6183fbeeda6bdf3c9d18..d5cba5989f632c4eb720592a181d8c2d1336f608 100644 (file)
@@ -4200,15 +4200,15 @@ static int selinux_socket_unix_may_send(struct socket *sock,
                            &ad);
 }
 
-static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family,
-                                   u32 peer_sid,
+static int selinux_inet_sys_rcv_skb(struct net *ns, int ifindex,
+                                   char *addrp, u16 family, u32 peer_sid,
                                    struct common_audit_data *ad)
 {
        int err;
        u32 if_sid;
        u32 node_sid;
 
-       err = sel_netif_sid(ifindex, &if_sid);
+       err = sel_netif_sid(ns, ifindex, &if_sid);
        if (err)
                return err;
        err = avc_has_perm(peer_sid, if_sid,
@@ -4301,8 +4301,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
                err = selinux_skb_peerlbl_sid(skb, family, &peer_sid);
                if (err)
                        return err;
-               err = selinux_inet_sys_rcv_skb(skb->skb_iif, addrp, family,
-                                              peer_sid, &ad);
+               err = selinux_inet_sys_rcv_skb(sock_net(sk), skb->skb_iif,
+                                              addrp, family, peer_sid, &ad);
                if (err) {
                        selinux_netlbl_err(skb, err, 0);
                        return err;
@@ -4648,7 +4648,8 @@ out:
 
 #ifdef CONFIG_NETFILTER
 
-static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
+static unsigned int selinux_ip_forward(struct sk_buff *skb,
+                                      const struct net_device *indev,
                                       u16 family)
 {
        int err;
@@ -4674,14 +4675,14 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
 
        ad.type = LSM_AUDIT_DATA_NET;
        ad.u.net = &net;
-       ad.u.net->netif = ifindex;
+       ad.u.net->netif = indev->ifindex;
        ad.u.net->family = family;
        if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0)
                return NF_DROP;
 
        if (peerlbl_active) {
-               err = selinux_inet_sys_rcv_skb(ifindex, addrp, family,
-                                              peer_sid, &ad);
+               err = selinux_inet_sys_rcv_skb(dev_net(indev), indev->ifindex,
+                                              addrp, family, peer_sid, &ad);
                if (err) {
                        selinux_netlbl_err(skb, err, 1);
                        return NF_DROP;
@@ -4710,7 +4711,7 @@ static unsigned int selinux_ipv4_forward(unsigned int hooknum,
                                         const struct net_device *out,
                                         int (*okfn)(struct sk_buff *))
 {
-       return selinux_ip_forward(skb, in->ifindex, PF_INET);
+       return selinux_ip_forward(skb, in, PF_INET);
 }
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
@@ -4720,7 +4721,7 @@ static unsigned int selinux_ipv6_forward(unsigned int hooknum,
                                         const struct net_device *out,
                                         int (*okfn)(struct sk_buff *))
 {
-       return selinux_ip_forward(skb, in->ifindex, PF_INET6);
+       return selinux_ip_forward(skb, in, PF_INET6);
 }
 #endif /* IPV6 */
 
@@ -4788,11 +4789,13 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
        return NF_ACCEPT;
 }
 
-static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
+static unsigned int selinux_ip_postroute(struct sk_buff *skb,
+                                        const struct net_device *outdev,
                                         u16 family)
 {
        u32 secmark_perm;
        u32 peer_sid;
+       int ifindex = outdev->ifindex;
        struct sock *sk;
        struct common_audit_data ad;
        struct lsm_network_audit net = {0,};
@@ -4857,7 +4860,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
                u32 if_sid;
                u32 node_sid;
 
-               if (sel_netif_sid(ifindex, &if_sid))
+               if (sel_netif_sid(dev_net(outdev), ifindex, &if_sid))
                        return NF_DROP;
                if (avc_has_perm(peer_sid, if_sid,
                                 SECCLASS_NETIF, NETIF__EGRESS, &ad))
@@ -4879,7 +4882,7 @@ static unsigned int selinux_ipv4_postroute(unsigned int hooknum,
                                           const struct net_device *out,
                                           int (*okfn)(struct sk_buff *))
 {
-       return selinux_ip_postroute(skb, out->ifindex, PF_INET);
+       return selinux_ip_postroute(skb, out, PF_INET);
 }
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
@@ -4889,7 +4892,7 @@ static unsigned int selinux_ipv6_postroute(unsigned int hooknum,
                                           const struct net_device *out,
                                           int (*okfn)(struct sk_buff *))
 {
-       return selinux_ip_postroute(skb, out->ifindex, PF_INET6);
+       return selinux_ip_postroute(skb, out, PF_INET6);
 }
 #endif /* IPV6 */
 
index 57c6eae81eacac6226273963ee89602cdf736983..c7214544409015a3fbc6cda9a7386e394f50be2b 100644 (file)
 #ifndef _SELINUX_NETIF_H_
 #define _SELINUX_NETIF_H_
 
+#include <net/net_namespace.h>
+
 void sel_netif_flush(void);
 
-int sel_netif_sid(int ifindex, u32 *sid);
+int sel_netif_sid(struct net *ns, int ifindex, u32 *sid);
 
 #endif /* _SELINUX_NETIF_H_ */
 
index 6fd9dd256a6288af158b68591cb16496c13ef7bd..2a3be7adc071daacf7981a4829692298633fbe4c 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/binfmts.h>
 #include <linux/in.h>
 #include <linux/spinlock.h>
+#include <net/net_namespace.h>
 #include "flask.h"
 #include "avc.h"
 
@@ -78,6 +79,7 @@ struct ipc_security_struct {
 };
 
 struct netif_security_struct {
+       struct net *ns;                 /* network namespace */
        int ifindex;                    /* device index */
        u32 sid;                        /* SID for this interface */
 };
index 6c814ec4b01ae9f1fae1f3909167ae0c24c9f002..548651a7387623d886267dd7979841aa1f4b84ee 100644 (file)
@@ -45,6 +45,7 @@ static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE];
 
 /**
  * sel_netif_hashfn - Hashing function for the interface table
+ * @ns: the network namespace
  * @ifindex: the network interface
  *
  * Description:
@@ -52,13 +53,14 @@ static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE];
  * bucket number for the given interface.
  *
  */
-static inline u32 sel_netif_hashfn(int ifindex)
+static inline u32 sel_netif_hashfn(const struct net *ns, int ifindex)
 {
-       return (ifindex & (SEL_NETIF_HASH_SIZE - 1));
+       return (((uintptr_t)ns + ifindex) & (SEL_NETIF_HASH_SIZE - 1));
 }
 
 /**
  * sel_netif_find - Search for an interface record
+ * @ns: the network namespace
  * @ifindex: the network interface
  *
  * Description:
@@ -66,15 +68,15 @@ static inline u32 sel_netif_hashfn(int ifindex)
  * If an entry can not be found in the table return NULL.
  *
  */
-static inline struct sel_netif *sel_netif_find(int ifindex)
+static inline struct sel_netif *sel_netif_find(const struct net *ns,
+                                              int ifindex)
 {
-       int idx = sel_netif_hashfn(ifindex);
+       int idx = sel_netif_hashfn(ns, ifindex);
        struct sel_netif *netif;
 
        list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list)
-               /* all of the devices should normally fit in the hash, so we
-                * optimize for that case */
-               if (likely(netif->nsec.ifindex == ifindex))
+               if (net_eq(netif->nsec.ns, ns) &&
+                   netif->nsec.ifindex == ifindex)
                        return netif;
 
        return NULL;
@@ -96,7 +98,7 @@ static int sel_netif_insert(struct sel_netif *netif)
        if (sel_netif_total >= SEL_NETIF_HASH_MAX)
                return -ENOSPC;
 
-       idx = sel_netif_hashfn(netif->nsec.ifindex);
+       idx = sel_netif_hashfn(netif->nsec.ns, netif->nsec.ifindex);
        list_add_rcu(&netif->list, &sel_netif_hash[idx]);
        sel_netif_total++;
 
@@ -120,6 +122,7 @@ static void sel_netif_destroy(struct sel_netif *netif)
 
 /**
  * sel_netif_sid_slow - Lookup the SID of a network interface using the policy
+ * @ns: the network namespace
  * @ifindex: the network interface
  * @sid: interface SID
  *
@@ -130,7 +133,7 @@ static void sel_netif_destroy(struct sel_netif *netif)
  * failure.
  *
  */
-static int sel_netif_sid_slow(int ifindex, u32 *sid)
+static int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid)
 {
        int ret;
        struct sel_netif *netif;
@@ -140,7 +143,7 @@ static int sel_netif_sid_slow(int ifindex, u32 *sid)
        /* NOTE: we always use init's network namespace since we don't
         * currently support containers */
 
-       dev = dev_get_by_index(&init_net, ifindex);
+       dev = dev_get_by_index(ns, ifindex);
        if (unlikely(dev == NULL)) {
                printk(KERN_WARNING
                       "SELinux: failure in sel_netif_sid_slow(),"
@@ -149,7 +152,7 @@ static int sel_netif_sid_slow(int ifindex, u32 *sid)
        }
 
        spin_lock_bh(&sel_netif_lock);
-       netif = sel_netif_find(ifindex);
+       netif = sel_netif_find(ns, ifindex);
        if (netif != NULL) {
                *sid = netif->nsec.sid;
                ret = 0;
@@ -163,6 +166,7 @@ static int sel_netif_sid_slow(int ifindex, u32 *sid)
        ret = security_netif_sid(dev->name, &new->nsec.sid);
        if (ret != 0)
                goto out;
+       new->nsec.ns = ns;
        new->nsec.ifindex = ifindex;
        ret = sel_netif_insert(new);
        if (ret != 0)
@@ -184,6 +188,7 @@ out:
 
 /**
  * sel_netif_sid - Lookup the SID of a network interface
+ * @ns: the network namespace
  * @ifindex: the network interface
  * @sid: interface SID
  *
@@ -195,12 +200,12 @@ out:
  * on failure.
  *
  */
-int sel_netif_sid(int ifindex, u32 *sid)
+int sel_netif_sid(struct net *ns, int ifindex, u32 *sid)
 {
        struct sel_netif *netif;
 
        rcu_read_lock();
-       netif = sel_netif_find(ifindex);
+       netif = sel_netif_find(ns, ifindex);
        if (likely(netif != NULL)) {
                *sid = netif->nsec.sid;
                rcu_read_unlock();
@@ -208,11 +213,12 @@ int sel_netif_sid(int ifindex, u32 *sid)
        }
        rcu_read_unlock();
 
-       return sel_netif_sid_slow(ifindex, sid);
+       return sel_netif_sid_slow(ns, ifindex, sid);
 }
 
 /**
  * sel_netif_kill - Remove an entry from the network interface table
+ * @ns: the network namespace
  * @ifindex: the network interface
  *
  * Description:
@@ -220,13 +226,13 @@ int sel_netif_sid(int ifindex, u32 *sid)
  * table if it exists.
  *
  */
-static void sel_netif_kill(int ifindex)
+static void sel_netif_kill(const struct net *ns, int ifindex)
 {
        struct sel_netif *netif;
 
        rcu_read_lock();
        spin_lock_bh(&sel_netif_lock);
-       netif = sel_netif_find(ifindex);
+       netif = sel_netif_find(ns, ifindex);
        if (netif)
                sel_netif_destroy(netif);
        spin_unlock_bh(&sel_netif_lock);
@@ -257,11 +263,8 @@ static int sel_netif_netdev_notifier_handler(struct notifier_block *this,
 {
        struct net_device *dev = ptr;
 
-       if (dev_net(dev) != &init_net)
-               return NOTIFY_DONE;
-
        if (event == NETDEV_DOWN)
-               sel_netif_kill(dev->ifindex);
+               sel_netif_kill(dev_net(dev), dev->ifindex);
 
        return NOTIFY_DONE;
 }