2005-04-16 15:20:36 -07:00
/*
* Network interface table .
*
* Network interfaces ( devices ) do not have a security field , so we
* maintain a table associating each interface with a SID .
*
* Author : James Morris < jmorris @ redhat . com >
*
* Copyright ( C ) 2003 Red Hat , Inc . , James Morris < jmorris @ redhat . com >
2008-01-29 08:38:08 -05:00
* Copyright ( C ) 2007 Hewlett - Packard Development Company , L . P .
2008-04-18 17:38:22 -04:00
* Paul Moore < paul . moore @ hp . com >
2005-04-16 15:20:36 -07:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 ,
* as published by the Free Software Foundation .
*/
# include <linux/init.h>
# include <linux/types.h>
# include <linux/stddef.h>
# include <linux/kernel.h>
# include <linux/list.h>
# include <linux/notifier.h>
# include <linux/netdevice.h>
# include <linux/rcupdate.h>
2007-09-12 13:02:17 +02:00
# include <net/net_namespace.h>
2005-04-16 15:20:36 -07:00
# include "security.h"
# include "objsec.h"
# include "netif.h"
# define SEL_NETIF_HASH_SIZE 64
# define SEL_NETIF_HASH_MAX 1024
2008-04-18 17:38:22 -04:00
struct sel_netif {
2005-04-16 15:20:36 -07:00
struct list_head list ;
struct netif_security_struct nsec ;
struct rcu_head rcu_head ;
} ;
static u32 sel_netif_total ;
static LIST_HEAD ( sel_netif_list ) ;
static DEFINE_SPINLOCK ( sel_netif_lock ) ;
static struct list_head sel_netif_hash [ SEL_NETIF_HASH_SIZE ] ;
2008-01-29 08:38:08 -05:00
/**
* sel_netif_hashfn - Hashing function for the interface table
* @ 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 )
2005-04-16 15:20:36 -07:00
{
2008-01-29 08:38:08 -05:00
return ( ifindex & ( SEL_NETIF_HASH_SIZE - 1 ) ) ;
2005-04-16 15:20:36 -07:00
}
2008-01-29 08:38:08 -05:00
/**
* sel_netif_find - Search for an interface record
* @ 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 .
*
2005-04-16 15:20:36 -07:00
*/
2008-01-29 08:38:08 -05:00
static inline struct sel_netif * sel_netif_find ( int ifindex )
2005-04-16 15:20:36 -07:00
{
2008-01-29 08:38:08 -05:00
int idx = sel_netif_hashfn ( ifindex ) ;
struct sel_netif * netif ;
2005-04-16 15:20:36 -07:00
2008-01-29 08:38:08 -05:00
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 ) )
2005-04-16 15:20:36 -07:00
return netif ;
2008-01-29 08:38:08 -05:00
2005-04-16 15:20:36 -07:00
return NULL ;
}
2008-01-29 08:38:08 -05:00
/**
* sel_netif_insert - Insert a new interface into the table
* @ netif : the new interface record
*
* Description :
* Add a new interface record to the network interface hash table . Returns
* zero on success , negative values on failure .
*
*/
2005-04-16 15:20:36 -07:00
static int sel_netif_insert ( struct sel_netif * netif )
{
2008-01-29 08:38:08 -05:00
int idx ;
2008-04-18 17:38:22 -04:00
2008-01-29 08:38:08 -05:00
if ( sel_netif_total > = SEL_NETIF_HASH_MAX )
return - ENOSPC ;
2008-04-18 17:38:22 -04:00
2008-01-29 08:38:08 -05:00
idx = sel_netif_hashfn ( netif - > nsec . ifindex ) ;
2005-04-16 15:20:36 -07:00
list_add_rcu ( & netif - > list , & sel_netif_hash [ idx ] ) ;
sel_netif_total + + ;
2008-01-29 08:38:08 -05:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2008-01-29 08:38:08 -05:00
/**
* sel_netif_free - Frees an interface 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 interface entry can be
* released safely .
*
*/
2005-04-16 15:20:36 -07:00
static void sel_netif_free ( struct rcu_head * p )
{
struct sel_netif * netif = container_of ( p , struct sel_netif , rcu_head ) ;
kfree ( netif ) ;
}
2008-01-29 08:38:08 -05:00
/**
* sel_netif_destroy - Remove an interface record from the table
* @ netif : the existing interface record
*
* Description :
* Remove an existing interface record from the network interface table .
*
*/
2005-04-16 15:20:36 -07:00
static void sel_netif_destroy ( struct sel_netif * netif )
{
list_del_rcu ( & netif - > list ) ;
sel_netif_total - - ;
call_rcu ( & netif - > rcu_head , sel_netif_free ) ;
}
2008-01-29 08:38:08 -05:00
/**
* sel_netif_sid_slow - Lookup the SID of a network interface using the policy
* @ ifindex : the network interface
* @ sid : interface SID
*
* Description :
* This function determines the SID of a network interface by quering the
* security policy . The result is added to the network interface table to
* speedup future queries . Returns zero on success , negative values on
* failure .
*
*/
static int sel_netif_sid_slow ( int ifindex , u32 * sid )
2005-04-16 15:20:36 -07:00
{
int ret ;
2008-01-29 08:38:08 -05:00
struct sel_netif * netif ;
struct sel_netif * new = NULL ;
struct net_device * dev ;
2005-04-16 15:20:36 -07:00
2008-01-29 08:38:08 -05:00
/* NOTE: we always use init's network namespace since we don't
* currently support containers */
2005-04-16 15:20:36 -07:00
2008-01-29 08:38:08 -05:00
dev = dev_get_by_index ( & init_net , ifindex ) ;
2008-01-29 08:51:16 -05:00
if ( unlikely ( dev = = NULL ) ) {
printk ( KERN_WARNING
" SELinux: failure in sel_netif_sid_slow(), "
" invalid network interface (%d) \n " , ifindex ) ;
2008-01-29 08:38:08 -05:00
return - ENOENT ;
2008-01-29 08:51:16 -05:00
}
2005-04-16 15:20:36 -07:00
spin_lock_bh ( & sel_netif_lock ) ;
2008-01-29 08:38:08 -05:00
netif = sel_netif_find ( ifindex ) ;
if ( netif ! = NULL ) {
* sid = netif - > nsec . sid ;
ret = 0 ;
2005-04-16 15:20:36 -07:00
goto out ;
}
2008-01-29 08:38:08 -05:00
new = kzalloc ( sizeof ( * new ) , GFP_ATOMIC ) ;
if ( new = = NULL ) {
ret = - ENOMEM ;
2005-04-16 15:20:36 -07:00
goto out ;
}
2008-01-29 08:38:08 -05:00
ret = security_netif_sid ( dev - > name , & new - > nsec . sid ) ;
if ( ret ! = 0 )
goto out ;
new - > nsec . ifindex = ifindex ;
ret = sel_netif_insert ( new ) ;
if ( ret ! = 0 )
goto out ;
* sid = new - > nsec . sid ;
2005-04-16 15:20:36 -07:00
out :
2008-01-29 08:38:08 -05:00
spin_unlock_bh ( & sel_netif_lock ) ;
dev_put ( dev ) ;
2008-01-29 08:51:16 -05:00
if ( unlikely ( ret ) ) {
printk ( KERN_WARNING
" SELinux: failure in sel_netif_sid_slow(), "
" unable to determine network interface label (%d) \n " ,
ifindex ) ;
2008-01-29 08:38:08 -05:00
kfree ( new ) ;
2008-01-29 08:51:16 -05:00
}
2005-04-16 15:20:36 -07:00
return ret ;
}
2008-01-29 08:38:08 -05:00
/**
* sel_netif_sid - Lookup the SID of a network interface
* @ ifindex : the network interface
* @ sid : interface SID
*
* Description :
* This function determines the SID of a network interface using the fastest
* method possible . First the interface 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_netif_sid ( int ifindex , u32 * sid )
2005-04-16 15:20:36 -07:00
{
struct sel_netif * netif ;
rcu_read_lock ( ) ;
2008-01-29 08:38:08 -05:00
netif = sel_netif_find ( ifindex ) ;
if ( likely ( netif ! = NULL ) ) {
* sid = netif - > nsec . sid ;
2005-04-16 15:20:36 -07:00
rcu_read_unlock ( ) ;
2008-01-29 08:38:08 -05:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
rcu_read_unlock ( ) ;
2008-01-29 08:38:08 -05:00
return sel_netif_sid_slow ( ifindex , sid ) ;
2005-04-16 15:20:36 -07:00
}
2008-01-29 08:38:08 -05:00
/**
* sel_netif_kill - Remove an entry from the network interface table
* @ 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 )
2005-04-16 15:20:36 -07:00
{
struct sel_netif * netif ;
2008-04-21 18:12:33 -07:00
rcu_read_lock ( ) ;
2005-04-16 15:20:36 -07:00
spin_lock_bh ( & sel_netif_lock ) ;
2008-01-29 08:38:08 -05:00
netif = sel_netif_find ( ifindex ) ;
2005-04-16 15:20:36 -07:00
if ( netif )
sel_netif_destroy ( netif ) ;
spin_unlock_bh ( & sel_netif_lock ) ;
2008-04-21 18:12:33 -07:00
rcu_read_unlock ( ) ;
2005-04-16 15:20:36 -07:00
}
2008-01-29 08:38:08 -05:00
/**
* sel_netif_flush - Flush the entire network interface table
*
* Description :
* Remove all entries from the network interface table .
*
*/
2005-04-16 15:20:36 -07:00
static void sel_netif_flush ( void )
{
int idx ;
2008-01-29 08:38:08 -05:00
struct sel_netif * netif ;
2005-04-16 15:20:36 -07:00
spin_lock_bh ( & sel_netif_lock ) ;
2008-01-29 08:38:08 -05:00
for ( idx = 0 ; idx < SEL_NETIF_HASH_SIZE ; idx + + )
2005-04-16 15:20:36 -07:00
list_for_each_entry ( netif , & sel_netif_hash [ idx ] , list )
sel_netif_destroy ( netif ) ;
spin_unlock_bh ( & sel_netif_lock ) ;
}
static int sel_netif_avc_callback ( u32 event , u32 ssid , u32 tsid ,
2008-04-18 17:38:22 -04:00
u16 class , u32 perms , u32 * retained )
2005-04-16 15:20:36 -07:00
{
if ( event = = AVC_CALLBACK_RESET ) {
sel_netif_flush ( ) ;
synchronize_net ( ) ;
}
return 0 ;
}
static int sel_netif_netdev_notifier_handler ( struct notifier_block * this ,
2008-04-18 17:38:22 -04:00
unsigned long event , void * ptr )
2005-04-16 15:20:36 -07:00
{
struct net_device * dev = ptr ;
2008-03-25 21:47:49 +09:00
if ( dev_net ( dev ) ! = & init_net )
2007-09-12 13:02:17 +02:00
return NOTIFY_DONE ;
2005-04-16 15:20:36 -07:00
if ( event = = NETDEV_DOWN )
2008-01-29 08:38:08 -05:00
sel_netif_kill ( dev - > ifindex ) ;
2005-04-16 15:20:36 -07:00
return NOTIFY_DONE ;
}
static struct notifier_block sel_netif_netdev_notifier = {
. notifier_call = sel_netif_netdev_notifier_handler ,
} ;
static __init int sel_netif_init ( void )
{
2008-01-29 08:38:08 -05:00
int i , err ;
2008-04-18 17:38:22 -04:00
2005-04-16 15:20:36 -07:00
if ( ! selinux_enabled )
2008-01-29 08:38:08 -05:00
return 0 ;
2005-04-16 15:20:36 -07:00
for ( i = 0 ; i < SEL_NETIF_HASH_SIZE ; i + + )
INIT_LIST_HEAD ( & sel_netif_hash [ i ] ) ;
register_netdevice_notifier ( & sel_netif_netdev_notifier ) ;
2008-04-18 17:38:22 -04:00
2005-04-16 15:20:36 -07:00
err = avc_add_callback ( sel_netif_avc_callback , AVC_CALLBACK_RESET ,
2008-04-18 17:38:22 -04:00
SECSID_NULL , SECSID_NULL , SECCLASS_NULL , 0 ) ;
2005-04-16 15:20:36 -07:00
if ( err )
panic ( " avc_add_callback() failed, error %d \n " , err ) ;
return err ;
}
__initcall ( sel_netif_init ) ;