2010-02-11 03:43:54 +03:00
/*
* security / tomoyo / gc . c
*
* Implementation of the Domain - Based Mandatory Access Control .
*
* Copyright ( C ) 2005 - 2010 NTT DATA CORPORATION
*
*/
# include "common.h"
# include <linux/kthread.h>
enum tomoyo_gc_id {
TOMOYO_ID_DOMAIN_INITIALIZER ,
TOMOYO_ID_DOMAIN_KEEPER ,
TOMOYO_ID_ALIAS ,
TOMOYO_ID_GLOBALLY_READABLE ,
TOMOYO_ID_PATTERN ,
TOMOYO_ID_NO_REWRITE ,
TOMOYO_ID_MANAGER ,
TOMOYO_ID_NAME ,
TOMOYO_ID_ACL ,
TOMOYO_ID_DOMAIN
} ;
struct tomoyo_gc_entry {
struct list_head list ;
int type ;
void * element ;
} ;
static LIST_HEAD ( tomoyo_gc_queue ) ;
static DEFINE_MUTEX ( tomoyo_gc_mutex ) ;
/* Caller holds tomoyo_policy_lock mutex. */
static bool tomoyo_add_to_gc ( const int type , void * element )
{
struct tomoyo_gc_entry * entry = kzalloc ( sizeof ( * entry ) , GFP_ATOMIC ) ;
if ( ! entry )
return false ;
entry - > type = type ;
entry - > element = element ;
list_add ( & entry - > list , & tomoyo_gc_queue ) ;
return true ;
}
static void tomoyo_del_allow_read
( struct tomoyo_globally_readable_file_entry * ptr )
{
tomoyo_put_name ( ptr - > filename ) ;
}
static void tomoyo_del_file_pattern ( struct tomoyo_pattern_entry * ptr )
{
tomoyo_put_name ( ptr - > pattern ) ;
}
static void tomoyo_del_no_rewrite ( struct tomoyo_no_rewrite_entry * ptr )
{
tomoyo_put_name ( ptr - > pattern ) ;
}
static void tomoyo_del_domain_initializer
( struct tomoyo_domain_initializer_entry * ptr )
{
tomoyo_put_name ( ptr - > domainname ) ;
tomoyo_put_name ( ptr - > program ) ;
}
static void tomoyo_del_domain_keeper ( struct tomoyo_domain_keeper_entry * ptr )
{
tomoyo_put_name ( ptr - > domainname ) ;
tomoyo_put_name ( ptr - > program ) ;
}
static void tomoyo_del_alias ( struct tomoyo_alias_entry * ptr )
{
tomoyo_put_name ( ptr - > original_name ) ;
tomoyo_put_name ( ptr - > aliased_name ) ;
}
static void tomoyo_del_manager ( struct tomoyo_policy_manager_entry * ptr )
{
tomoyo_put_name ( ptr - > manager ) ;
}
static void tomoyo_del_acl ( struct tomoyo_acl_info * acl )
{
switch ( acl - > type ) {
2010-02-16 02:03:30 +03:00
case TOMOYO_TYPE_PATH_ACL :
2010-02-11 03:43:54 +03:00
{
2010-02-16 02:03:30 +03:00
struct tomoyo_path_acl * entry
2010-02-11 03:43:54 +03:00
= container_of ( acl , typeof ( * entry ) , head ) ;
tomoyo_put_name ( entry - > filename ) ;
}
break ;
2010-02-16 02:03:30 +03:00
case TOMOYO_TYPE_PATH2_ACL :
2010-02-11 03:43:54 +03:00
{
2010-02-16 02:03:30 +03:00
struct tomoyo_path2_acl * entry
2010-02-11 03:43:54 +03:00
= container_of ( acl , typeof ( * entry ) , head ) ;
tomoyo_put_name ( entry - > filename1 ) ;
tomoyo_put_name ( entry - > filename2 ) ;
}
break ;
default :
printk ( KERN_WARNING " Unknown type \n " ) ;
break ;
}
}
static bool tomoyo_del_domain ( struct tomoyo_domain_info * domain )
{
struct tomoyo_acl_info * acl ;
struct tomoyo_acl_info * tmp ;
/*
* Since we don ' t protect whole execve ( ) operation using SRCU ,
* we need to recheck domain - > users at this point .
*
* ( 1 ) Reader starts SRCU section upon execve ( ) .
* ( 2 ) Reader traverses tomoyo_domain_list and finds this domain .
* ( 3 ) Writer marks this domain as deleted .
* ( 4 ) Garbage collector removes this domain from tomoyo_domain_list
* because this domain is marked as deleted and used by nobody .
* ( 5 ) Reader saves reference to this domain into
* " struct linux_binprm " - > cred - > security .
* ( 6 ) Reader finishes SRCU section , although execve ( ) operation has
* not finished yet .
* ( 7 ) Garbage collector waits for SRCU synchronization .
* ( 8 ) Garbage collector kfree ( ) this domain because this domain is
* used by nobody .
* ( 9 ) Reader finishes execve ( ) operation and restores this domain from
* " struct linux_binprm " - > cred - > security .
*
* By updating domain - > users at ( 5 ) , we can solve this race problem
* by rechecking domain - > users at ( 8 ) .
*/
if ( atomic_read ( & domain - > users ) )
return false ;
list_for_each_entry_safe ( acl , tmp , & domain - > acl_info_list , list ) {
tomoyo_del_acl ( acl ) ;
tomoyo_memory_free ( acl ) ;
}
tomoyo_put_name ( domain - > domainname ) ;
return true ;
}
static void tomoyo_del_name ( const struct tomoyo_name_entry * ptr )
{
}
static void tomoyo_collect_entry ( void )
{
mutex_lock ( & tomoyo_policy_lock ) ;
{
struct tomoyo_globally_readable_file_entry * ptr ;
list_for_each_entry_rcu ( ptr , & tomoyo_globally_readable_list ,
list ) {
if ( ! ptr - > is_deleted )
continue ;
if ( tomoyo_add_to_gc ( TOMOYO_ID_GLOBALLY_READABLE , ptr ) )
list_del_rcu ( & ptr - > list ) ;
else
break ;
}
}
{
struct tomoyo_pattern_entry * ptr ;
list_for_each_entry_rcu ( ptr , & tomoyo_pattern_list , list ) {
if ( ! ptr - > is_deleted )
continue ;
if ( tomoyo_add_to_gc ( TOMOYO_ID_PATTERN , ptr ) )
list_del_rcu ( & ptr - > list ) ;
else
break ;
}
}
{
struct tomoyo_no_rewrite_entry * ptr ;
list_for_each_entry_rcu ( ptr , & tomoyo_no_rewrite_list , list ) {
if ( ! ptr - > is_deleted )
continue ;
if ( tomoyo_add_to_gc ( TOMOYO_ID_NO_REWRITE , ptr ) )
list_del_rcu ( & ptr - > list ) ;
else
break ;
}
}
{
struct tomoyo_domain_initializer_entry * ptr ;
list_for_each_entry_rcu ( ptr , & tomoyo_domain_initializer_list ,
list ) {
if ( ! ptr - > is_deleted )
continue ;
if ( tomoyo_add_to_gc ( TOMOYO_ID_DOMAIN_INITIALIZER , ptr ) )
list_del_rcu ( & ptr - > list ) ;
else
break ;
}
}
{
struct tomoyo_domain_keeper_entry * ptr ;
list_for_each_entry_rcu ( ptr , & tomoyo_domain_keeper_list , list ) {
if ( ! ptr - > is_deleted )
continue ;
if ( tomoyo_add_to_gc ( TOMOYO_ID_DOMAIN_KEEPER , ptr ) )
list_del_rcu ( & ptr - > list ) ;
else
break ;
}
}
{
struct tomoyo_alias_entry * ptr ;
list_for_each_entry_rcu ( ptr , & tomoyo_alias_list , list ) {
if ( ! ptr - > is_deleted )
continue ;
if ( tomoyo_add_to_gc ( TOMOYO_ID_ALIAS , ptr ) )
list_del_rcu ( & ptr - > list ) ;
else
break ;
}
}
{
struct tomoyo_policy_manager_entry * ptr ;
list_for_each_entry_rcu ( ptr , & tomoyo_policy_manager_list ,
list ) {
if ( ! ptr - > is_deleted )
continue ;
if ( tomoyo_add_to_gc ( TOMOYO_ID_MANAGER , ptr ) )
list_del_rcu ( & ptr - > list ) ;
else
break ;
}
}
{
struct tomoyo_domain_info * domain ;
list_for_each_entry_rcu ( domain , & tomoyo_domain_list , list ) {
struct tomoyo_acl_info * acl ;
list_for_each_entry_rcu ( acl , & domain - > acl_info_list ,
list ) {
switch ( acl - > type ) {
2010-02-16 02:03:30 +03:00
case TOMOYO_TYPE_PATH_ACL :
2010-02-11 03:43:54 +03:00
if ( container_of ( acl ,
2010-02-16 02:03:30 +03:00
struct tomoyo_path_acl ,
2010-02-11 03:43:54 +03:00
head ) - > perm | |
container_of ( acl ,
2010-02-16 02:03:30 +03:00
struct tomoyo_path_acl ,
2010-02-11 03:43:54 +03:00
head ) - > perm_high )
continue ;
break ;
2010-02-16 02:03:30 +03:00
case TOMOYO_TYPE_PATH2_ACL :
2010-02-11 03:43:54 +03:00
if ( container_of ( acl ,
2010-02-16 02:03:30 +03:00
struct tomoyo_path2_acl ,
2010-02-11 03:43:54 +03:00
head ) - > perm )
continue ;
break ;
default :
continue ;
}
if ( tomoyo_add_to_gc ( TOMOYO_ID_ACL , acl ) )
list_del_rcu ( & acl - > list ) ;
else
break ;
}
if ( ! domain - > is_deleted | | atomic_read ( & domain - > users ) )
continue ;
/*
* Nobody is referring this domain . But somebody may
* refer this domain after successful execve ( ) .
* We recheck domain - > users after SRCU synchronization .
*/
if ( tomoyo_add_to_gc ( TOMOYO_ID_DOMAIN , domain ) )
list_del_rcu ( & domain - > list ) ;
else
break ;
}
}
mutex_unlock ( & tomoyo_policy_lock ) ;
mutex_lock ( & tomoyo_name_list_lock ) ;
{
int i ;
for ( i = 0 ; i < TOMOYO_MAX_HASH ; i + + ) {
struct tomoyo_name_entry * ptr ;
list_for_each_entry_rcu ( ptr , & tomoyo_name_list [ i ] ,
list ) {
if ( atomic_read ( & ptr - > users ) )
continue ;
if ( tomoyo_add_to_gc ( TOMOYO_ID_NAME , ptr ) )
list_del_rcu ( & ptr - > list ) ;
else {
i = TOMOYO_MAX_HASH ;
break ;
}
}
}
}
mutex_unlock ( & tomoyo_name_list_lock ) ;
}
static void tomoyo_kfree_entry ( void )
{
struct tomoyo_gc_entry * p ;
struct tomoyo_gc_entry * tmp ;
list_for_each_entry_safe ( p , tmp , & tomoyo_gc_queue , list ) {
switch ( p - > type ) {
case TOMOYO_ID_DOMAIN_INITIALIZER :
tomoyo_del_domain_initializer ( p - > element ) ;
break ;
case TOMOYO_ID_DOMAIN_KEEPER :
tomoyo_del_domain_keeper ( p - > element ) ;
break ;
case TOMOYO_ID_ALIAS :
tomoyo_del_alias ( p - > element ) ;
break ;
case TOMOYO_ID_GLOBALLY_READABLE :
tomoyo_del_allow_read ( p - > element ) ;
break ;
case TOMOYO_ID_PATTERN :
tomoyo_del_file_pattern ( p - > element ) ;
break ;
case TOMOYO_ID_NO_REWRITE :
tomoyo_del_no_rewrite ( p - > element ) ;
break ;
case TOMOYO_ID_MANAGER :
tomoyo_del_manager ( p - > element ) ;
break ;
case TOMOYO_ID_NAME :
tomoyo_del_name ( p - > element ) ;
break ;
case TOMOYO_ID_ACL :
tomoyo_del_acl ( p - > element ) ;
break ;
case TOMOYO_ID_DOMAIN :
if ( ! tomoyo_del_domain ( p - > element ) )
continue ;
break ;
default :
printk ( KERN_WARNING " Unknown type \n " ) ;
break ;
}
tomoyo_memory_free ( p - > element ) ;
list_del ( & p - > list ) ;
kfree ( p ) ;
}
}
static int tomoyo_gc_thread ( void * unused )
{
daemonize ( " GC for TOMOYO " ) ;
if ( mutex_trylock ( & tomoyo_gc_mutex ) ) {
int i ;
for ( i = 0 ; i < 10 ; i + + ) {
tomoyo_collect_entry ( ) ;
if ( list_empty ( & tomoyo_gc_queue ) )
break ;
synchronize_srcu ( & tomoyo_ss ) ;
tomoyo_kfree_entry ( ) ;
}
mutex_unlock ( & tomoyo_gc_mutex ) ;
}
do_exit ( 0 ) ;
}
void tomoyo_run_gc ( void )
{
struct task_struct * task = kthread_create ( tomoyo_gc_thread , NULL ,
" GC for TOMOYO " ) ;
if ( ! IS_ERR ( task ) )
wake_up_process ( task ) ;
}