Skip to content

Commit

Permalink
selinux: make the netif cache namespace aware
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Paul Moore <[email protected]>
  • Loading branch information
pcmoore committed Sep 10, 2014
1 parent 25db6be commit cbe0d6e
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 36 deletions.
33 changes: 18 additions & 15 deletions security/selinux/hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -4307,15 +4307,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,
Expand Down Expand Up @@ -4408,8 +4408,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;
Expand Down Expand Up @@ -4748,7 +4748,8 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)

#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;
Expand All @@ -4774,14 +4775,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;
Expand Down Expand Up @@ -4810,7 +4811,7 @@ static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops,
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)
Expand All @@ -4820,7 +4821,7 @@ static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops,
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 */

Expand Down Expand Up @@ -4908,11 +4909,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,};
Expand Down Expand Up @@ -5025,7 +5028,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))
Expand All @@ -5047,7 +5050,7 @@ static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops,
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)
Expand All @@ -5057,7 +5060,7 @@ static unsigned int selinux_ipv6_postroute(const struct nf_hook_ops *ops,
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 */

Expand Down
4 changes: 3 additions & 1 deletion security/selinux/include/netif.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
#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_ */

2 changes: 2 additions & 0 deletions security/selinux/include/objsec.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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 */
};
Expand Down
43 changes: 23 additions & 20 deletions security/selinux/netif.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,36 +45,38 @@ 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:
* This is the hashing function for the network interface table, it returns the
* 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:
* Search the network interface table and return the record matching @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;
Expand All @@ -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++;

Expand All @@ -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
*
Expand All @@ -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;
Expand All @@ -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(),"
Expand All @@ -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;
Expand All @@ -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)
Expand All @@ -184,6 +188,7 @@ static int sel_netif_sid_slow(int ifindex, u32 *sid)

/**
* sel_netif_sid - Lookup the SID of a network interface
* @ns: the network namespace
* @ifindex: the network interface
* @sid: interface SID
*
Expand All @@ -195,38 +200,39 @@ static int sel_netif_sid_slow(int ifindex, u32 *sid)
* 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();
return 0;
}
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:
* This function removes the entry matching @ifindex from the network interface
* 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);
Expand Down Expand Up @@ -257,11 +263,8 @@ static int sel_netif_netdev_notifier_handler(struct notifier_block *this,
{
struct net_device *dev = netdev_notifier_info_to_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;
}
Expand Down

0 comments on commit cbe0d6e

Please sign in to comment.