@ -33,6 +33,7 @@
# include "include/policy.h"
# include "include/policy_ns.h"
# include "include/resource.h"
# include "include/policy_unpack.h"
/**
* aa_mangle_name - mangle a profile name to std profile layout form
@ -84,11 +85,13 @@ static int mangle_name(const char *name, char *target)
* Returns : kernel buffer containing copy of user buffer data or an
* ERR_PTR on failure .
*/
static char * aa_simple_write_to_buffer ( int op , const char __user * userbuf ,
size_t alloc_size , size_t copy_size ,
loff_t * pos )
static struct aa_loaddata * aa_simple_write_to_buffer ( int op ,
const char __user * userbuf ,
size_t alloc_size ,
size_t copy_size ,
loff_t * pos )
{
char * data ;
struct aa_loaddata * data ;
BUG_ON ( copy_size > alloc_size ) ;
@ -96,19 +99,16 @@ static char *aa_simple_write_to_buffer(int op, const char __user *userbuf,
/* only writes from pos 0, that is complete writes */
return ERR_PTR ( - ESPIPE ) ;
/*
* Don ' t allow profile load / replace / remove from profiles that don ' t
* have CAP_MAC_ADMIN
*/
if ( ! aa_may_manage_policy ( __aa_current_profile ( ) , NULL , op ) )
return ERR_PTR ( - EACCES ) ;
/* freed by caller to simple_write_to_buffer */
data = kvmalloc ( alloc_size ) ;
data = kvmalloc ( sizeof ( * data ) + alloc_size ) ;
if ( data = = NULL )
return ERR_PTR ( - ENOMEM ) ;
kref_init ( & data - > count ) ;
data - > size = copy_size ;
data - > hash = NULL ;
data - > abi = 0 ;
if ( copy_from_user ( data , userbuf , copy_size ) ) {
if ( copy_from_user ( data - > data , userbuf , copy_size ) ) {
kvfree ( data ) ;
return ERR_PTR ( - EFAULT ) ;
}
@ -116,26 +116,38 @@ static char *aa_simple_write_to_buffer(int op, const char __user *userbuf,
return data ;
}
/* .load file hook fn to load policy */
static ssize_t profile_load ( struct file * f , const char __user * buf , size_t size ,
loff_t * pos )
static ssize_t policy_update ( int binop , const char __user * buf , size_t size ,
loff_t * pos )
{
char * data ;
ssize_t error ;
struct aa_loaddata * data ;
struct aa_profile * profile = aa_current_profile ( ) ;
int op = binop = = PROF_ADD ? OP_PROF_LOAD : OP_PROF_REPL ;
/* high level check about policy management - fine grained in
* below after unpack
*/
error = aa_may_manage_policy ( profile , profile - > ns , op ) ;
if ( error )
return error ;
data = aa_simple_write_to_buffer ( OP_PROF_LOAD , buf , size , size , pos ) ;
data = aa_simple_write_to_buffer ( op , buf , size , size , pos ) ;
error = PTR_ERR ( data ) ;
if ( ! IS_ERR ( data ) ) {
error = aa_replace_profiles ( __aa_current_profile ( ) - > ns , data ,
size , PROF_ADD ) ;
kvfree ( data ) ;
error = aa_replace_profiles ( profile - > ns , binop , data ) ;
aa_put_loaddata ( data ) ;
}
return error ;
}
static ssize_t profile_load ( struct file * f , const char __user * buf , size_t size ,
loff_t * pos )
{
int error = policy_update ( PROF_ADD , buf , size , pos ) ;
return error ;
}
static const struct file_operations aa_fs_profile_load = {
. write = profile_load ,
. llseek = default_llseek ,
@ -145,16 +157,7 @@ static const struct file_operations aa_fs_profile_load = {
static ssize_t profile_replace ( struct file * f , const char __user * buf ,
size_t size , loff_t * pos )
{
char * data ;
ssize_t error ;
data = aa_simple_write_to_buffer ( OP_PROF_REPL , buf , size , size , pos ) ;
error = PTR_ERR ( data ) ;
if ( ! IS_ERR ( data ) ) {
error = aa_replace_profiles ( __aa_current_profile ( ) - > ns , data ,
size , PROF_REPLACE ) ;
kvfree ( data ) ;
}
int error = policy_update ( PROF_REPLACE , buf , size , pos ) ;
return error ;
}
@ -164,27 +167,35 @@ static const struct file_operations aa_fs_profile_replace = {
. llseek = default_llseek ,
} ;
/* .remove file hook fn to remove loaded policy */
static ssize_t profile_remove ( struct file * f , const char __user * buf ,
size_t size , loff_t * pos )
{
char * data ;
struct aa_loaddata * data ;
struct aa_profile * profile ;
ssize_t error ;
profile = aa_current_profile ( ) ;
/* high level check about policy management - fine grained in
* below after unpack
*/
error = aa_may_manage_policy ( profile , profile - > ns , OP_PROF_RM ) ;
if ( error )
goto out ;
/*
* aa_remove_profile needs a null terminated string so 1 extra
* byte is allocated and the copied data is null terminated .
*/
data = aa_simple_write_to_buffer ( OP_PROF_RM , buf , size + 1 , size , pos ) ;
data = aa_simple_write_to_buffer ( OP_PROF_RM , buf , size + 1 , size ,
pos ) ;
error = PTR_ERR ( data ) ;
if ( ! IS_ERR ( data ) ) {
data [ size ] = 0 ;
error = aa_remove_profiles ( __aa_current_profile ( ) - > ns , data ,
size ) ;
kvfree ( data ) ;
data - > data [ size ] = 0 ;
error = aa_remove_profiles ( profile - > ns , data - > data , size ) ;
aa_put_loaddata ( data ) ;
}
out :
return error ;
}
@ -401,6 +412,100 @@ static const struct file_operations aa_fs_ns_name = {
. release = single_release ,
} ;
static int rawdata_release ( struct inode * inode , struct file * file )
{
/* TODO: switch to loaddata when profile switched to symlink */
aa_put_loaddata ( file - > private_data ) ;
return 0 ;
}
static int aa_fs_seq_raw_abi_show ( struct seq_file * seq , void * v )
{
struct aa_proxy * proxy = seq - > private ;
struct aa_profile * profile = aa_get_profile_rcu ( & proxy - > profile ) ;
if ( profile - > rawdata - > abi ) {
seq_printf ( seq , " v%d " , profile - > rawdata - > abi ) ;
seq_puts ( seq , " \n " ) ;
}
aa_put_profile ( profile ) ;
return 0 ;
}
static int aa_fs_seq_raw_abi_open ( struct inode * inode , struct file * file )
{
return aa_fs_seq_profile_open ( inode , file , aa_fs_seq_raw_abi_show ) ;
}
static const struct file_operations aa_fs_seq_raw_abi_fops = {
. owner = THIS_MODULE ,
. open = aa_fs_seq_raw_abi_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = aa_fs_seq_profile_release ,
} ;
static int aa_fs_seq_raw_hash_show ( struct seq_file * seq , void * v )
{
struct aa_proxy * proxy = seq - > private ;
struct aa_profile * profile = aa_get_profile_rcu ( & proxy - > profile ) ;
unsigned int i , size = aa_hash_size ( ) ;
if ( profile - > rawdata - > hash ) {
for ( i = 0 ; i < size ; i + + )
seq_printf ( seq , " %.2x " , profile - > rawdata - > hash [ i ] ) ;
seq_puts ( seq , " \n " ) ;
}
aa_put_profile ( profile ) ;
return 0 ;
}
static int aa_fs_seq_raw_hash_open ( struct inode * inode , struct file * file )
{
return aa_fs_seq_profile_open ( inode , file , aa_fs_seq_raw_hash_show ) ;
}
static const struct file_operations aa_fs_seq_raw_hash_fops = {
. owner = THIS_MODULE ,
. open = aa_fs_seq_raw_hash_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = aa_fs_seq_profile_release ,
} ;
static ssize_t rawdata_read ( struct file * file , char __user * buf , size_t size ,
loff_t * ppos )
{
struct aa_loaddata * rawdata = file - > private_data ;
return simple_read_from_buffer ( buf , size , ppos , rawdata - > data ,
rawdata - > size ) ;
}
static int rawdata_open ( struct inode * inode , struct file * file )
{
struct aa_proxy * proxy = inode - > i_private ;
struct aa_profile * profile ;
if ( ! policy_view_capable ( NULL ) )
return - EACCES ;
profile = aa_get_profile_rcu ( & proxy - > profile ) ;
file - > private_data = aa_get_loaddata ( profile - > rawdata ) ;
aa_put_profile ( profile ) ;
return 0 ;
}
static const struct file_operations aa_fs_rawdata_fops = {
. open = rawdata_open ,
. read = rawdata_read ,
. llseek = generic_file_llseek ,
. release = rawdata_release ,
} ;
/** fns to setup dynamic per profile/namespace files **/
void __aa_fs_profile_rmdir ( struct aa_profile * profile )
{
@ -512,6 +617,29 @@ int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
profile - > dents [ AAFS_PROF_HASH ] = dent ;
}
if ( profile - > rawdata ) {
dent = create_profile_file ( dir , " raw_sha1 " , profile ,
& aa_fs_seq_raw_hash_fops ) ;
if ( IS_ERR ( dent ) )
goto fail ;
profile - > dents [ AAFS_PROF_RAW_HASH ] = dent ;
dent = create_profile_file ( dir , " raw_abi " , profile ,
& aa_fs_seq_raw_abi_fops ) ;
if ( IS_ERR ( dent ) )
goto fail ;
profile - > dents [ AAFS_PROF_RAW_ABI ] = dent ;
dent = securityfs_create_file ( " raw_data " , S_IFREG | 0444 , dir ,
profile - > proxy ,
& aa_fs_rawdata_fops ) ;
if ( IS_ERR ( dent ) )
goto fail ;
profile - > dents [ AAFS_PROF_RAW_DATA ] = dent ;
d_inode ( dent ) - > i_size = profile - > rawdata - > size ;
aa_get_proxy ( profile - > proxy ) ;
}
list_for_each_entry ( child , & profile - > base . profiles , base . list ) {
error = __aa_fs_profile_mkdir ( child , prof_child_dir ( profile ) ) ;
if ( error )
@ -817,6 +945,9 @@ static const struct seq_operations aa_fs_profiles_op = {
static int profiles_open ( struct inode * inode , struct file * file )
{
if ( ! policy_view_capable ( NULL ) )
return - EACCES ;
return seq_open ( file , & aa_fs_profiles_op ) ;
}