2005-04-17 02:20:36 +04:00
/*
* Implementation of the SID table type .
*
2017-08-17 20:32:36 +03:00
* Author : Stephen Smalley , < sds @ tycho . nsa . gov >
2005-04-17 02:20:36 +04:00
*/
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
# include <linux/errno.h>
# include "flask.h"
# include "security.h"
# include "sidtab.h"
# define SIDTAB_HASH(sid) \
( sid & SIDTAB_HASH_MASK )
int sidtab_init ( struct sidtab * s )
{
int i ;
2017-01-15 15:13:19 +03:00
s - > htable = kmalloc_array ( SIDTAB_SIZE , sizeof ( * s - > htable ) , GFP_ATOMIC ) ;
2005-04-17 02:20:36 +04:00
if ( ! s - > htable )
return - ENOMEM ;
for ( i = 0 ; i < SIDTAB_SIZE ; i + + )
s - > htable [ i ] = NULL ;
s - > nel = 0 ;
s - > next_sid = 1 ;
s - > shutdown = 0 ;
2008-06-06 12:50:12 +04:00
spin_lock_init ( & s - > lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
int sidtab_insert ( struct sidtab * s , u32 sid , struct context * context )
{
2017-04-04 12:33:53 +03:00
int hvalue ;
2005-04-17 02:20:36 +04:00
struct sidtab_node * prev , * cur , * newnode ;
2017-04-04 12:33:53 +03:00
if ( ! s )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
hvalue = SIDTAB_HASH ( sid ) ;
prev = NULL ;
cur = s - > htable [ hvalue ] ;
2008-08-07 04:18:20 +04:00
while ( cur & & sid > cur - > sid ) {
2005-04-17 02:20:36 +04:00
prev = cur ;
cur = cur - > next ;
}
2017-04-04 12:33:53 +03:00
if ( cur & & sid = = cur - > sid )
return - EEXIST ;
2005-04-17 02:20:36 +04:00
newnode = kmalloc ( sizeof ( * newnode ) , GFP_ATOMIC ) ;
2017-04-04 12:33:53 +03:00
if ( ! newnode )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
newnode - > sid = sid ;
if ( context_cpy ( & newnode - > context , context ) ) {
kfree ( newnode ) ;
2017-04-04 12:33:53 +03:00
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
}
if ( prev ) {
newnode - > next = prev - > next ;
wmb ( ) ;
prev - > next = newnode ;
} else {
newnode - > next = s - > htable [ hvalue ] ;
wmb ( ) ;
s - > htable [ hvalue ] = newnode ;
}
s - > nel + + ;
if ( sid > = s - > next_sid )
s - > next_sid = sid + 1 ;
2017-04-04 12:33:53 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-05-07 21:03:20 +04:00
static struct context * sidtab_search_core ( struct sidtab * s , u32 sid , int force )
2005-04-17 02:20:36 +04:00
{
int hvalue ;
struct sidtab_node * cur ;
if ( ! s )
return NULL ;
hvalue = SIDTAB_HASH ( sid ) ;
cur = s - > htable [ hvalue ] ;
2008-08-07 04:18:20 +04:00
while ( cur & & sid > cur - > sid )
2005-04-17 02:20:36 +04:00
cur = cur - > next ;
2008-05-07 21:03:20 +04:00
if ( force & & cur & & sid = = cur - > sid & & cur - > context . len )
return & cur - > context ;
2017-01-15 15:30:20 +03:00
if ( ! cur | | sid ! = cur - > sid | | cur - > context . len ) {
2005-04-17 02:20:36 +04:00
/* Remap invalid SIDs to the unlabeled SID. */
sid = SECINITSID_UNLABELED ;
hvalue = SIDTAB_HASH ( sid ) ;
cur = s - > htable [ hvalue ] ;
2008-08-07 04:18:20 +04:00
while ( cur & & sid > cur - > sid )
2005-04-17 02:20:36 +04:00
cur = cur - > next ;
if ( ! cur | | sid ! = cur - > sid )
return NULL ;
}
return & cur - > context ;
}
2008-05-07 21:03:20 +04:00
struct context * sidtab_search ( struct sidtab * s , u32 sid )
{
return sidtab_search_core ( s , sid , 0 ) ;
}
struct context * sidtab_search_force ( struct sidtab * s , u32 sid )
{
return sidtab_search_core ( s , sid , 1 ) ;
}
2005-04-17 02:20:36 +04:00
int sidtab_map ( struct sidtab * s ,
int ( * apply ) ( u32 sid ,
struct context * context ,
void * args ) ,
void * args )
{
int i , rc = 0 ;
struct sidtab_node * cur ;
if ( ! s )
goto out ;
for ( i = 0 ; i < SIDTAB_SIZE ; i + + ) {
cur = s - > htable [ i ] ;
2008-08-07 04:18:20 +04:00
while ( cur ) {
2005-04-17 02:20:36 +04:00
rc = apply ( cur - > sid , & cur - > context , args ) ;
if ( rc )
goto out ;
cur = cur - > next ;
}
}
out :
return rc ;
}
2010-12-08 00:17:28 +03:00
static void sidtab_update_cache ( struct sidtab * s , struct sidtab_node * n , int loc )
{
BUG_ON ( loc > = SIDTAB_CACHE_LEN ) ;
while ( loc > 0 ) {
s - > cache [ loc ] = s - > cache [ loc - 1 ] ;
loc - - ;
}
s - > cache [ 0 ] = n ;
}
2005-04-17 02:20:36 +04:00
static inline u32 sidtab_search_context ( struct sidtab * s ,
struct context * context )
{
int i ;
struct sidtab_node * cur ;
for ( i = 0 ; i < SIDTAB_SIZE ; i + + ) {
cur = s - > htable [ i ] ;
2008-08-07 04:18:20 +04:00
while ( cur ) {
2010-12-08 00:17:28 +03:00
if ( context_cmp ( & cur - > context , context ) ) {
sidtab_update_cache ( s , cur , SIDTAB_CACHE_LEN - 1 ) ;
2005-04-17 02:20:36 +04:00
return cur - > sid ;
2010-12-08 00:17:28 +03:00
}
2005-04-17 02:20:36 +04:00
cur = cur - > next ;
}
}
return 0 ;
}
2010-12-08 00:17:28 +03:00
static inline u32 sidtab_search_cache ( struct sidtab * s , struct context * context )
{
int i ;
struct sidtab_node * node ;
for ( i = 0 ; i < SIDTAB_CACHE_LEN ; i + + ) {
node = s - > cache [ i ] ;
if ( unlikely ( ! node ) )
return 0 ;
if ( context_cmp ( & node - > context , context ) ) {
sidtab_update_cache ( s , node , i ) ;
return node - > sid ;
}
}
return 0 ;
}
2005-04-17 02:20:36 +04:00
int sidtab_context_to_sid ( struct sidtab * s ,
struct context * context ,
u32 * out_sid )
{
u32 sid ;
int ret = 0 ;
unsigned long flags ;
* out_sid = SECSID_NULL ;
2010-12-08 00:17:28 +03:00
sid = sidtab_search_cache ( s , context ) ;
if ( ! sid )
sid = sidtab_search_context ( s , context ) ;
2005-04-17 02:20:36 +04:00
if ( ! sid ) {
2008-06-06 12:50:12 +04:00
spin_lock_irqsave ( & s - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
/* Rescan now that we hold the lock. */
sid = sidtab_search_context ( s , context ) ;
if ( sid )
goto unlock_out ;
/* No SID exists for the context. Allocate a new one. */
if ( s - > next_sid = = UINT_MAX | | s - > shutdown ) {
ret = - ENOMEM ;
goto unlock_out ;
}
sid = s - > next_sid + + ;
2008-05-07 21:03:20 +04:00
if ( context - > len )
printk ( KERN_INFO
" SELinux: Context %s is not valid (left unmapped). \n " ,
context - > str ) ;
2005-04-17 02:20:36 +04:00
ret = sidtab_insert ( s , sid , context ) ;
if ( ret )
s - > next_sid - - ;
unlock_out :
2008-06-06 12:50:12 +04:00
spin_unlock_irqrestore ( & s - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
if ( ret )
return ret ;
* out_sid = sid ;
return 0 ;
}
void sidtab_hash_eval ( struct sidtab * h , char * tag )
{
int i , chain_len , slots_used , max_chain_len ;
struct sidtab_node * cur ;
slots_used = 0 ;
max_chain_len = 0 ;
for ( i = 0 ; i < SIDTAB_SIZE ; i + + ) {
cur = h - > htable [ i ] ;
if ( cur ) {
slots_used + + ;
chain_len = 0 ;
while ( cur ) {
chain_len + + ;
cur = cur - > next ;
}
if ( chain_len > max_chain_len )
max_chain_len = chain_len ;
}
}
2007-02-23 02:11:31 +03:00
printk ( KERN_DEBUG " %s: %d entries and %d/%d buckets used, longest "
2005-04-17 02:20:36 +04:00
" chain length %d \n " , tag , h - > nel , slots_used , SIDTAB_SIZE ,
max_chain_len ) ;
}
void sidtab_destroy ( struct sidtab * s )
{
int i ;
struct sidtab_node * cur , * temp ;
if ( ! s )
return ;
for ( i = 0 ; i < SIDTAB_SIZE ; i + + ) {
cur = s - > htable [ i ] ;
2008-08-07 04:18:20 +04:00
while ( cur ) {
2005-04-17 02:20:36 +04:00
temp = cur ;
cur = cur - > next ;
context_destroy ( & temp - > context ) ;
kfree ( temp ) ;
}
s - > htable [ i ] = NULL ;
}
kfree ( s - > htable ) ;
s - > htable = NULL ;
s - > nel = 0 ;
s - > next_sid = 1 ;
}
void sidtab_set ( struct sidtab * dst , struct sidtab * src )
{
unsigned long flags ;
2010-12-08 00:17:28 +03:00
int i ;
2005-04-17 02:20:36 +04:00
2008-06-06 12:50:12 +04:00
spin_lock_irqsave ( & src - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
dst - > htable = src - > htable ;
dst - > nel = src - > nel ;
dst - > next_sid = src - > next_sid ;
dst - > shutdown = 0 ;
2010-12-08 00:17:28 +03:00
for ( i = 0 ; i < SIDTAB_CACHE_LEN ; i + + )
dst - > cache [ i ] = NULL ;
2008-06-06 12:50:12 +04:00
spin_unlock_irqrestore ( & src - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
void sidtab_shutdown ( struct sidtab * s )
{
unsigned long flags ;
2008-06-06 12:50:12 +04:00
spin_lock_irqsave ( & s - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
s - > shutdown = 1 ;
2008-06-06 12:50:12 +04:00
spin_unlock_irqrestore ( & s - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
}