Re: [RFC PATCH v8 10/18] SELinux: Add a network node caching mechanism similar to the sel_netif_*() functions

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, 2007-12-14 at 16:50 -0500, Paul Moore wrote:
> This patch adds a SELinux IP address/node SID caching mechanism similar to the
> sel_netif_*() functions.  The node SID queries in the SELinux hooks files are
> also modified to take advantage of this new functionality.  In addition, remove
> the address length information from the sk_buff parsing routines as it is
> redundant since we already have the address family.

This is very nice - we also need the same kind of cache for port SIDs.

> ---
> 
>  security/selinux/Makefile          |    9 +
>  security/selinux/hooks.c           |   33 ++-
>  security/selinux/include/netnode.h |   32 +++
>  security/selinux/include/objsec.h  |    9 +
>  security/selinux/netnode.c         |  351 ++++++++++++++++++++++++++++++++++++
>  5 files changed, 417 insertions(+), 17 deletions(-)
> 
> diff --git a/security/selinux/Makefile b/security/selinux/Makefile
> index dc3502e..00afd85 100644
> --- a/security/selinux/Makefile
> +++ b/security/selinux/Makefile
> @@ -4,7 +4,14 @@
>  
>  obj-$(CONFIG_SECURITY_SELINUX) := selinux.o ss/
>  
> -selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o exports.o
> +selinux-y := avc.o \
> +	     hooks.o \
> +	     selinuxfs.o \
> +	     netlink.o \
> +	     nlmsgtab.o \
> +	     netif.o \
> +	     netnode.o \
> +	     exports.o
>  
>  selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o
>  
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index e429a8c..05b58cc 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -76,6 +76,7 @@
>  #include "avc.h"
>  #include "objsec.h"
>  #include "netif.h"
> +#include "netnode.h"
>  #include "xfrm.h"
>  #include "netlabel.h"
>  
> @@ -3158,7 +3159,7 @@ out:
>  #endif /* IPV6 */
>  
>  static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
> -			     char **addrp, int *len, int src, u8 *proto)
> +			     char **addrp, int src, u8 *proto)
>  {
>  	int ret = 0;
>  
> @@ -3167,7 +3168,6 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
>  		ret = selinux_parse_skb_ipv4(skb, ad, proto);
>  		if (ret || !addrp)
>  			break;
> -		*len = 4;
>  		*addrp = (char *)(src ? &ad->u.net.v4info.saddr :
>  					&ad->u.net.v4info.daddr);
>  		break;
> @@ -3177,7 +3177,6 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
>  		ret = selinux_parse_skb_ipv6(skb, ad, proto);
>  		if (ret || !addrp)
>  			break;
> -		*len = 16;
>  		*addrp = (char *)(src ? &ad->u.net.v6info.saddr :
>  					&ad->u.net.v6info.daddr);
>  		break;
> @@ -3377,7 +3376,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
>  			break;
>  		}
>  		
> -		err = security_node_sid(family, addrp, addrlen, &sid);
> +		err = sel_netnode_sid(addrp, family, &sid);
>  		if (err)
>  			goto out;
>  		
> @@ -3589,7 +3588,8 @@ static int selinux_socket_unix_may_send(struct socket *sock,
>  }
>  
>  static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
> -		struct avc_audit_data *ad, u16 family, char *addrp, int len)
> +				       struct avc_audit_data *ad,
> +				       u16 family, char *addrp)
>  {
>  	int err = 0;
>  	u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0;
> @@ -3649,7 +3649,7 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
>  	if (err)
>  		goto out;
>  	
> -	err = security_node_sid(family, addrp, len, &node_sid);
> +	err = sel_netnode_sid(addrp, family, &node_sid);
>  	if (err)
>  		goto out;
>  	
> @@ -3678,7 +3678,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
>  {
>  	u16 family;
>  	char *addrp;
> -	int len, err = 0;
> +	int err = 0;
>  	struct avc_audit_data ad;
>  	struct sk_security_struct *sksec = sk->sk_security;
>  
> @@ -3694,13 +3694,12 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
>  	ad.u.net.netif = skb->iif;
>  	ad.u.net.family = family;
>  
> -	err = selinux_parse_skb(skb, &ad, &addrp, &len, 1, NULL);
> +	err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL);
>  	if (err)
>  		goto out;
>  
>  	if (selinux_compat_net)
> -		err = selinux_sock_rcv_skb_compat(sk, skb, &ad, family,
> -						  addrp, len);
> +		err = selinux_sock_rcv_skb_compat(sk, skb, &ad, family, addrp);
>  	else
>  		err = avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET,
>  				   PACKET__RECV, &ad);
> @@ -3922,9 +3921,11 @@ out:
>  
>  #ifdef CONFIG_NETFILTER
>  
> -static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device *dev,
> +static int selinux_ip_postroute_last_compat(struct sock *sk,
> +					    struct net_device *dev,
>  					    struct avc_audit_data *ad,
> -					    u16 family, char *addrp, int len)
> +					    u16 family,
> +					    char *addrp)
>  {
>  	int err = 0;
>  	u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0;
> @@ -3975,7 +3976,7 @@ static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device *
>  	if (err)
>  		goto out;
>  		
> -	err = security_node_sid(family, addrp, len, &node_sid);
> +	err = sel_netnode_sid(addrp, family, &node_sid);
>  	if (err)
>  		goto out;
>  	
> @@ -4009,7 +4010,7 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
>                                                u16 family)
>  {
>  	char *addrp;
> -	int len, err = 0;
> +	int err = 0;
>  	struct sock *sk;
>  	struct avc_audit_data ad;
>  	struct net_device *dev = (struct net_device *)out;
> @@ -4026,13 +4027,13 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
>  	ad.u.net.netif = dev->ifindex;
>  	ad.u.net.family = family;
>  
> -	err = selinux_parse_skb(skb, &ad, &addrp, &len, 0, &proto);
> +	err = selinux_parse_skb(skb, &ad, &addrp, 0, &proto);
>  	if (err)
>  		goto out;
>  
>  	if (selinux_compat_net)
>  		err = selinux_ip_postroute_last_compat(sk, dev, &ad,
> -						       family, addrp, len);
> +						       family, addrp);
>  	else
>  		err = avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET,
>  				   PACKET__SEND, &ad);
> diff --git a/security/selinux/include/netnode.h b/security/selinux/include/netnode.h
> new file mode 100644
> index 0000000..1b94450
> --- /dev/null
> +++ b/security/selinux/include/netnode.h
> @@ -0,0 +1,32 @@
> +/*
> + * Network node table
> + *
> + * SELinux must keep a mapping of network nodes to labels/SIDs.  This
> + * mapping is maintained as part of the normal policy but a fast cache is
> + * needed to reduce the lookup overhead since most of these queries happen on
> + * a per-packet basis.
> + *
> + * Author: Paul Moore <paul.moore@xxxxxx>
> + *
> + */
> +
> +/*
> + * (c) Copyright Hewlett-Packard Development Company, L.P., 2007
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of version 2 of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _SELINUX_NETNODE_H
> +#define _SELINUX_NETNODE_H
> +
> +int sel_netnode_sid(void *addr, u16 family, u32 *sid);
> +
> +#endif
> diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
> index e41a2aa..6140734 100644
> --- a/security/selinux/include/objsec.h
> +++ b/security/selinux/include/objsec.h
> @@ -99,6 +99,15 @@ struct netif_security_struct {
>  	u32 sid;			/* SID for this interface */
>  };
>  
> +struct netnode_security_struct {
> +	union {
> +		__be32 ipv4;		/* IPv4 node address */
> +		struct in6_addr ipv6;	/* IPv6 node address */
> +	} addr;
> +	u32 sid;			/* SID for this node */
> +	u16 family;			/* address family */
> +};
> +
>  struct sk_security_struct {
>  	struct sock *sk;		/* back pointer to sk object */
>  	u32 sid;			/* SID of this object */
> diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c
> new file mode 100644
> index 0000000..0cd66b8
> --- /dev/null
> +++ b/security/selinux/netnode.c
> @@ -0,0 +1,351 @@
> +/*
> + * Network node table
> + *
> + * SELinux must keep a mapping of network nodes to labels/SIDs.  This
> + * mapping is maintained as part of the normal policy but a fast cache is
> + * needed to reduce the lookup overhead since most of these queries happen on
> + * a per-packet basis.
> + *
> + * Author: Paul Moore <paul.moore@xxxxxx>
> + *
> + * This code is heavily based on the "netif" concept originally developed by
> + * James Morris <jmorris@xxxxxxxxxx>
> + *   (see security/selinux/netif.c for more information)
> + * 
> + */
> +
> +/*
> + * (c) Copyright Hewlett-Packard Development Company, L.P., 2007
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of version 2 of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/types.h>
> +#include <linux/rcupdate.h>
> +#include <linux/list.h>
> +#include <linux/spinlock.h>
> +#include <linux/in.h>
> +#include <linux/in6.h>
> +#include <linux/ip.h>
> +#include <linux/ipv6.h>
> +#include <net/ip.h>
> +#include <net/ipv6.h>
> +#include <asm/bug.h>
> +
> +#include "objsec.h"
> +
> +#define SEL_NETNODE_HASH_SIZE       256
> +#define SEL_NETNODE_HASH_BKT_LIMIT   16
> +
> +struct sel_netnode {
> +	struct netnode_security_struct nsec;
> +
> +	struct list_head list;
> +	struct rcu_head rcu;
> +};
> +
> +/* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason
> + * for this is that I suspect most users will not make heavy use of both
> + * address families at the same time so one table will usually end up wasted,
> + * if this becomes a problem we can always add a hash table for each address
> + * family later */
> +
> +static LIST_HEAD(sel_netnode_list);
> +static DEFINE_SPINLOCK(sel_netnode_lock);
> +static struct list_head sel_netnode_hash[SEL_NETNODE_HASH_SIZE];
> +
> +/**
> + * sel_netnode_free - Frees a node entry
> + * @p: the entry's RCU field
> + *
> + * Description:
> + * This function is designed to be used as a callback to the call_rcu()
> + * function so that memory allocated to a hash table node entry can be
> + * released safely.
> + *
> + */
> +static void sel_netnode_free(struct rcu_head *p)
> +{
> +	struct sel_netnode *node = container_of(p, struct sel_netnode, rcu);
> +	kfree(node);
> +}
> +
> +/**
> + * sel_netnode_hashfn_ipv4 - IPv4 hashing function for the node table
> + * @addr: IPv4 address
> + *
> + * Description:
> + * This is the IPv4 hashing function for the node interface table, it returns
> + * the bucket number for the given IP address.
> + *
> + */
> +static u32 sel_netnode_hashfn_ipv4(__be32 addr)
> +{
> +	/* at some point we should determine if the mismatch in byte order
> +	 * affects the hash function dramatically */
> +	return (addr & (SEL_NETNODE_HASH_SIZE - 1));
> +}
> +
> +/**
> + * sel_netnode_hashfn_ipv6 - IPv6 hashing function for the node table
> + * @addr: IPv6 address
> + *
> + * Description:
> + * This is the IPv6 hashing function for the node interface table, it returns
> + * the bucket number for the given IP address.
> + *
> + */
> +static u32 sel_netnode_hashfn_ipv6(const struct in6_addr *addr)
> +{
> +	/* just hash the least significant 32 bits to keep things fast (they
> +	 * are the most likely to be different anyway), we can revisit this
> +	 * later if needed */
> +	return (addr->s6_addr32[3] & (SEL_NETNODE_HASH_SIZE - 1));
> +}
> +
> +/**
> + * sel_netnode_find - Search for a node record
> + * @addr: IP address
> + * @family: address family
> + *
> + * Description:
> + * Search the network node table and return the record matching @addr.  If an
> + * entry can not be found in the table return NULL.
> + *
> + */
> +static struct sel_netnode *sel_netnode_find(const void *addr, u16 family)
> +{
> +	u32 idx;
> +	struct sel_netnode *node;
> +
> +	switch (family) {
> +	case PF_INET:
> +		idx = sel_netnode_hashfn_ipv4(*(__be32 *)addr);
> +		break;
> +	case PF_INET6:
> +		idx = sel_netnode_hashfn_ipv6(addr);
> +		break;
> +	default:
> +		BUG();
> +	}
> +
> +	list_for_each_entry_rcu(node, &sel_netnode_hash[idx], list)
> +		if (node->nsec.family == family)
> +			switch (family) {
> +			case PF_INET:
> +				if (node->nsec.addr.ipv4 == *(__be32 *)addr)
> +					return node;
> +				break;
> +			case PF_INET6:
> +				if (ipv6_addr_cmp(&node->nsec.addr.ipv6,
> +						  addr) == 0)
> +					return node;
> +				break;
> +			}
> +
> +	return NULL;
> +}
> +
> +/**
> + * sel_netnode_insert - Insert a new node into the table
> + * @node: the new node record
> + *
> + * Description:
> + * Add a new node record to the network address hash table.  Returns zero on
> + * success, negative values on failure.
> + *
> + */
> +static int sel_netnode_insert(struct sel_netnode *node)
> +{
> +	u32 idx;
> +	u32 count = 0;
> +	struct sel_netnode *iter;
> +
> +	switch (node->nsec.family) {
> +	case PF_INET:
> +		idx = sel_netnode_hashfn_ipv4(node->nsec.addr.ipv4);
> +		break;
> +	case PF_INET6:
> +		idx = sel_netnode_hashfn_ipv6(&node->nsec.addr.ipv6);
> +		break;
> +	default:
> +		BUG();
> +	}
> +	list_add_rcu(&node->list, &sel_netnode_hash[idx]);
> +
> +	/* we need to impose a limit on the growth of the hash table so check
> +	 * this bucket to make sure it is within the specified bounds */
> +	list_for_each_entry(iter, &sel_netnode_hash[idx], list)
> +		if (++count > SEL_NETNODE_HASH_BKT_LIMIT) {
> +			list_del_rcu(&iter->list);
> +			call_rcu(&iter->rcu, sel_netnode_free);
> +			break;
> +		}
> +
> +	return 0;
> +}
> +
> +/**
> + * sel_netnode_destroy - Remove a node record from the table
> + * @node: the existing node record
> + *
> + * Description:
> + * Remove an existing node record from the network address table.
> + *
> + */
> +static void sel_netnode_destroy(struct sel_netnode *node)
> +{
> +	list_del_rcu(&node->list);
> +	call_rcu(&node->rcu, sel_netnode_free);
> +}
> +
> +/**
> + * sel_netnode_sid_slow - Lookup the SID of a network address using the policy
> + * @addr: the IP address
> + * @family: the address family
> + * @sid: node SID
> + *
> + * Description:
> + * This function determines the SID of a network address by quering the
> + * security policy.  The result is added to the network address table to
> + * speedup future queries.  Returns zero on success, negative values on
> + * failure.
> + *
> + */
> +static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
> +{
> +	int ret;
> +	struct sel_netnode *node;
> +	struct sel_netnode *new = NULL;
> +
> +	spin_lock_bh(&sel_netnode_lock);
> +	node = sel_netnode_find(addr, family);
> +	if (node != NULL) {
> +		*sid = node->nsec.sid;
> +		ret = 0;
> +		goto out;
> +	}
> +	new = kzalloc(sizeof(*new), GFP_ATOMIC);
> +	if (new == NULL) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +	switch (family) {
> +	case PF_INET:
> +		ret = security_node_sid(PF_INET,
> +					addr, sizeof(struct in_addr),
> +					&new->nsec.sid);
> +		new->nsec.addr.ipv4 = *(__be32 *)addr;
> +		break;
> +	case PF_INET6:
> +		ret = security_node_sid(PF_INET6,
> +					addr, sizeof(struct in6_addr),
> +					&new->nsec.sid);
> +		memcpy(&new->nsec.addr.ipv6, addr,
> +		       sizeof(new->nsec.addr.ipv6));
> +		break;
> +	default:
> +		BUG();
> +	}
> +	if (ret != 0)
> +		goto out;
> +	new->nsec.family = family;
> +	ret = sel_netnode_insert(new);
> +	if (ret != 0)
> +		goto out;
> +	*sid = new->nsec.sid;
> +
> +out:
> +	spin_unlock_bh(&sel_netnode_lock);
> +	if (ret != 0)
> +		kfree(new);
> +	return ret;
> +}
> +
> +/**
> + * sel_netnode_sid - Lookup the SID of a network address
> + * @addr: the IP address
> + * @family: the address family
> + * @sid: node SID
> + *
> + * Description:
> + * This function determines the SID of a network address using the fastest
> + * method possible.  First the address table is queried, but if an entry
> + * can't be found then the policy is queried and the result is added to the
> + * table to speedup future queries.  Returns zero on success, negative values
> + * on failure.
> + *
> + */
> +int sel_netnode_sid(void *addr, u16 family, u32 *sid)
> +{
> +	struct sel_netnode *node;
> +
> +	rcu_read_lock();
> +	node = sel_netnode_find(addr, family);
> +	if (node != NULL) {
> +		*sid = node->nsec.sid;
> +		rcu_read_unlock();
> +		return 0;
> +	}
> +	rcu_read_unlock();
> +
> +	return sel_netnode_sid_slow(addr, family, sid);
> +}
> +
> +/**
> + * sel_netnode_flush - Flush the entire network address table
> + *
> + * Description:
> + * Remove all entries from the network address table.
> + *
> + */
> +static void sel_netnode_flush(void)
> +{
> +	u32 idx;
> +	struct sel_netnode *node;
> +
> +	spin_lock_bh(&sel_netnode_lock);
> +	for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++)
> +		list_for_each_entry(node, &sel_netnode_hash[idx], list)
> +			sel_netnode_destroy(node);
> +	spin_unlock_bh(&sel_netnode_lock);
> +}
> +
> +static int sel_netnode_avc_callback(u32 event, u32 ssid, u32 tsid,
> +				    u16 class, u32 perms, u32 *retained)
> +{
> +	if (event == AVC_CALLBACK_RESET) {
> +		sel_netnode_flush();
> +		synchronize_net();
> +	}
> +	return 0;
> +}
> +
> +static __init int sel_netnode_init(void)
> +{
> +	int iter;
> +	int ret;
> +	
> +	if (!selinux_enabled)
> +		return 0;
> +
> +	for (iter = 0; iter < SEL_NETNODE_HASH_SIZE; iter++)
> +		INIT_LIST_HEAD(&sel_netnode_hash[iter]);
> +
> +	ret = avc_add_callback(sel_netnode_avc_callback, AVC_CALLBACK_RESET,
> +	                       SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
> +	if (ret != 0)
> +		panic("avc_add_callback() failed, error %d\n", ret);
> +
> +	return ret;
> +}
> +
> +__initcall(sel_netnode_init);
> 
> 
> --
> This message was distributed to subscribers of the selinux mailing list.
> If you no longer wish to subscribe, send mail to majordomo@xxxxxxxxxxxxx with
> the words "unsubscribe selinux" without quotes as the message.
-- 
Stephen Smalley
National Security Agency


--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@xxxxxxxxxxxxx with
the words "unsubscribe selinux" without quotes as the message.

[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux