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 <[EMAIL PROTECTED]>
> + *
> + */
> +
> +/*
> + * (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 <[EMAIL PROTECTED]>
> + *
> + * This code is heavily based on the "netif" concept originally developed by
> + * James Morris <[EMAIL PROTECTED]>
> + *   (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 [EMAIL PROTECTED] with
> the words "unsubscribe selinux" without quotes as the message.
-- 
Stephen Smalley
National Security Agency

-
To unsubscribe from this list: send the line "unsubscribe 
linux-security-module" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to