2009-02-04 17:06:58 +03:00
/*
* Copyright ( C ) 2008 IBM Corporation
* Author : Mimi Zohar < zohar @ us . ibm . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , version 2 of the License .
*
* ima_policy . c
* - initialize default measure policy rules
*
*/
# include <linux/module.h>
# include <linux/list.h>
# include <linux/security.h>
# include <linux/magic.h>
2009-02-04 17:07:00 +03:00
# include <linux/parser.h>
2009-02-04 17:06:58 +03:00
# include "ima.h"
/* flags definitions */
# define IMA_FUNC 0x0001
# define IMA_MASK 0x0002
# define IMA_FSMAGIC 0x0004
# define IMA_UID 0x0008
2009-02-04 17:07:00 +03:00
enum ima_action { UNKNOWN = - 1 , DONT_MEASURE = 0 , MEASURE } ;
# define MAX_LSM_RULES 6
enum lsm_rule_types { LSM_OBJ_USER , LSM_OBJ_ROLE , LSM_OBJ_TYPE ,
LSM_SUBJ_USER , LSM_SUBJ_ROLE , LSM_SUBJ_TYPE
} ;
2009-02-04 17:06:58 +03:00
struct ima_measure_rule_entry {
struct list_head list ;
enum ima_action action ;
unsigned int flags ;
enum ima_hooks func ;
int mask ;
unsigned long fsmagic ;
uid_t uid ;
2009-02-04 17:07:00 +03:00
struct {
void * rule ; /* LSM file metadata specific */
int type ; /* audit type */
} lsm [ MAX_LSM_RULES ] ;
2009-02-04 17:06:58 +03:00
} ;
2009-05-21 23:47:06 +04:00
/*
* Without LSM specific knowledge , the default policy can only be
2009-02-04 17:07:00 +03:00
* written in terms of . action , . func , . mask , . fsmagic , and . uid
*/
2009-05-21 23:47:06 +04:00
/*
* The minimum rule set to allow for full TCB coverage . Measures all files
* opened or mmap for exec and everything read by root . Dangerous because
* normal users can easily run the machine out of memory simply building
* and running executables .
*/
2009-02-04 17:06:58 +03:00
static struct ima_measure_rule_entry default_rules [ ] = {
2009-05-18 18:26:10 +04:00
{ . action = DONT_MEASURE , . fsmagic = PROC_SUPER_MAGIC , . flags = IMA_FSMAGIC } ,
2009-02-04 17:06:58 +03:00
{ . action = DONT_MEASURE , . fsmagic = SYSFS_MAGIC , . flags = IMA_FSMAGIC } ,
{ . action = DONT_MEASURE , . fsmagic = DEBUGFS_MAGIC , . flags = IMA_FSMAGIC } ,
{ . action = DONT_MEASURE , . fsmagic = TMPFS_MAGIC , . flags = IMA_FSMAGIC } ,
2009-05-18 18:26:10 +04:00
{ . action = DONT_MEASURE , . fsmagic = SECURITYFS_MAGIC , . flags = IMA_FSMAGIC } ,
{ . action = DONT_MEASURE , . fsmagic = SELINUX_MAGIC , . flags = IMA_FSMAGIC } ,
2009-02-04 17:06:58 +03:00
{ . action = MEASURE , . func = FILE_MMAP , . mask = MAY_EXEC ,
. flags = IMA_FUNC | IMA_MASK } ,
{ . action = MEASURE , . func = BPRM_CHECK , . mask = MAY_EXEC ,
. flags = IMA_FUNC | IMA_MASK } ,
2010-01-27 01:02:41 +03:00
{ . action = MEASURE , . func = FILE_CHECK , . mask = MAY_READ , . uid = 0 ,
2009-05-21 23:47:06 +04:00
. flags = IMA_FUNC | IMA_MASK | IMA_UID } ,
2009-02-04 17:06:58 +03:00
} ;
static LIST_HEAD ( measure_default_rules ) ;
2009-02-04 17:07:00 +03:00
static LIST_HEAD ( measure_policy_rules ) ;
2009-02-04 17:06:58 +03:00
static struct list_head * ima_measure ;
2009-02-04 17:07:00 +03:00
static DEFINE_MUTEX ( ima_measure_mutex ) ;
2009-05-21 23:47:06 +04:00
static bool ima_use_tcb __initdata ;
static int __init default_policy_setup ( char * str )
{
ima_use_tcb = 1 ;
return 1 ;
}
__setup ( " ima_tcb " , default_policy_setup ) ;
2009-02-04 17:06:58 +03:00
/**
* ima_match_rules - determine whether an inode matches the measure rule .
* @ rule : a pointer to a rule
* @ inode : a pointer to an inode
* @ func : LIM hook identifier
* @ mask : requested action ( MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC )
*
* Returns true on rule match , false on failure .
*/
static bool ima_match_rules ( struct ima_measure_rule_entry * rule ,
struct inode * inode , enum ima_hooks func , int mask )
{
struct task_struct * tsk = current ;
2009-02-04 17:07:00 +03:00
int i ;
2009-02-04 17:06:58 +03:00
if ( ( rule - > flags & IMA_FUNC ) & & rule - > func ! = func )
return false ;
if ( ( rule - > flags & IMA_MASK ) & & rule - > mask ! = mask )
return false ;
if ( ( rule - > flags & IMA_FSMAGIC )
& & rule - > fsmagic ! = inode - > i_sb - > s_magic )
return false ;
if ( ( rule - > flags & IMA_UID ) & & rule - > uid ! = tsk - > cred - > uid )
return false ;
2009-02-04 17:07:00 +03:00
for ( i = 0 ; i < MAX_LSM_RULES ; i + + ) {
2009-05-05 21:12:48 +04:00
int rc = 0 ;
2009-02-04 17:07:00 +03:00
u32 osid , sid ;
if ( ! rule - > lsm [ i ] . rule )
continue ;
switch ( i ) {
case LSM_OBJ_USER :
case LSM_OBJ_ROLE :
case LSM_OBJ_TYPE :
security_inode_getsecid ( inode , & osid ) ;
rc = security_filter_rule_match ( osid ,
rule - > lsm [ i ] . type ,
2009-05-05 21:12:48 +04:00
Audit_equal ,
2009-02-04 17:07:00 +03:00
rule - > lsm [ i ] . rule ,
NULL ) ;
break ;
case LSM_SUBJ_USER :
case LSM_SUBJ_ROLE :
case LSM_SUBJ_TYPE :
security_task_getsecid ( tsk , & sid ) ;
rc = security_filter_rule_match ( sid ,
rule - > lsm [ i ] . type ,
2009-05-05 21:12:48 +04:00
Audit_equal ,
2009-02-04 17:07:00 +03:00
rule - > lsm [ i ] . rule ,
NULL ) ;
default :
break ;
}
if ( ! rc )
return false ;
}
2009-02-04 17:06:58 +03:00
return true ;
}
/**
* ima_match_policy - decision based on LSM and other conditions
* @ inode : pointer to an inode for which the policy decision is being made
* @ func : IMA hook identifier
* @ mask : requested action ( MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC )
*
* Measure decision based on func / mask / fsmagic and LSM ( subj / obj / type )
* conditions .
*
* ( There is no need for locking when walking the policy list ,
* as elements in the list are never deleted , nor does the list
* change . )
*/
int ima_match_policy ( struct inode * inode , enum ima_hooks func , int mask )
{
struct ima_measure_rule_entry * entry ;
list_for_each_entry ( entry , ima_measure , list ) {
bool rc ;
rc = ima_match_rules ( entry , inode , func , mask ) ;
if ( rc )
return entry - > action ;
}
return 0 ;
}
/**
* ima_init_policy - initialize the default measure rules .
*
* ima_measure points to either the measure_default_rules or the
2009-02-04 17:07:00 +03:00
* the new measure_policy_rules .
2009-02-04 17:06:58 +03:00
*/
2009-05-21 23:43:32 +04:00
void __init ima_init_policy ( void )
2009-02-04 17:06:58 +03:00
{
2009-05-21 23:47:06 +04:00
int i , entries ;
/* if !ima_use_tcb set entries = 0 so we load NO default rules */
if ( ima_use_tcb )
entries = ARRAY_SIZE ( default_rules ) ;
else
entries = 0 ;
2009-02-04 17:06:58 +03:00
2009-05-21 23:47:06 +04:00
for ( i = 0 ; i < entries ; i + + )
2009-02-04 17:06:58 +03:00
list_add_tail ( & default_rules [ i ] . list , & measure_default_rules ) ;
ima_measure = & measure_default_rules ;
}
2009-02-04 17:07:00 +03:00
/**
* ima_update_policy - update default_rules with new measure rules
*
* Called on file . release to update the default rules with a complete new
* policy . Once updated , the policy is locked , no additional rules can be
* added to the policy .
*/
void ima_update_policy ( void )
{
const char * op = " policy_update " ;
const char * cause = " already exists " ;
int result = 1 ;
int audit_info = 0 ;
if ( ima_measure = = & measure_default_rules ) {
ima_measure = & measure_policy_rules ;
cause = " complete " ;
result = 0 ;
}
integrity_audit_msg ( AUDIT_INTEGRITY_STATUS , NULL ,
NULL , op , cause , result , audit_info ) ;
}
enum {
Opt_err = - 1 ,
Opt_measure = 1 , Opt_dont_measure ,
Opt_obj_user , Opt_obj_role , Opt_obj_type ,
Opt_subj_user , Opt_subj_role , Opt_subj_type ,
Opt_func , Opt_mask , Opt_fsmagic , Opt_uid
} ;
static match_table_t policy_tokens = {
{ Opt_measure , " measure " } ,
{ Opt_dont_measure , " dont_measure " } ,
{ Opt_obj_user , " obj_user=%s " } ,
{ Opt_obj_role , " obj_role=%s " } ,
{ Opt_obj_type , " obj_type=%s " } ,
{ Opt_subj_user , " subj_user=%s " } ,
{ Opt_subj_role , " subj_role=%s " } ,
{ Opt_subj_type , " subj_type=%s " } ,
{ Opt_func , " func=%s " } ,
{ Opt_mask , " mask=%s " } ,
{ Opt_fsmagic , " fsmagic=%s " } ,
{ Opt_uid , " uid=%s " } ,
{ Opt_err , NULL }
} ;
static int ima_lsm_rule_init ( struct ima_measure_rule_entry * entry ,
char * args , int lsm_rule , int audit_type )
{
int result ;
2010-04-20 18:21:01 +04:00
if ( entry - > lsm [ lsm_rule ] . rule )
return - EINVAL ;
2009-02-04 17:07:00 +03:00
entry - > lsm [ lsm_rule ] . type = audit_type ;
result = security_filter_rule_init ( entry - > lsm [ lsm_rule ] . type ,
2009-05-05 21:12:48 +04:00
Audit_equal , args ,
2009-02-04 17:07:00 +03:00
& entry - > lsm [ lsm_rule ] . rule ) ;
return result ;
}
static int ima_parse_rule ( char * rule , struct ima_measure_rule_entry * entry )
{
struct audit_buffer * ab ;
char * p ;
int result = 0 ;
2009-02-11 19:12:28 +03:00
ab = audit_log_start ( NULL , GFP_KERNEL , AUDIT_INTEGRITY_RULE ) ;
2009-02-04 17:07:00 +03:00
2010-04-20 18:21:01 +04:00
entry - > uid = - 1 ;
2010-04-20 18:21:07 +04:00
entry - > action = UNKNOWN ;
2010-04-20 18:20:54 +04:00
while ( ( p = strsep ( & rule , " " ) ) ! = NULL ) {
2009-02-04 17:07:00 +03:00
substring_t args [ MAX_OPT_ARGS ] ;
int token ;
unsigned long lnum ;
if ( result < 0 )
break ;
if ( ! * p )
2010-04-20 18:20:54 +04:00
break ;
2009-02-04 17:07:00 +03:00
token = match_token ( p , policy_tokens , args ) ;
switch ( token ) {
case Opt_measure :
audit_log_format ( ab , " %s " , " measure " ) ;
2010-04-20 18:21:01 +04:00
if ( entry - > action ! = UNKNOWN )
result = - EINVAL ;
2009-02-04 17:07:00 +03:00
entry - > action = MEASURE ;
break ;
case Opt_dont_measure :
audit_log_format ( ab , " %s " , " dont_measure " ) ;
2010-04-20 18:21:01 +04:00
if ( entry - > action ! = UNKNOWN )
result = - EINVAL ;
2009-02-04 17:07:00 +03:00
entry - > action = DONT_MEASURE ;
break ;
case Opt_func :
audit_log_format ( ab , " func=%s " , args [ 0 ] . from ) ;
2010-04-20 18:21:01 +04:00
if ( entry - > func )
result = - EINVAL ;
2010-01-27 01:02:41 +03:00
if ( strcmp ( args [ 0 ] . from , " FILE_CHECK " ) = = 0 )
entry - > func = FILE_CHECK ;
/* PATH_CHECK is for backwards compat */
else if ( strcmp ( args [ 0 ] . from , " PATH_CHECK " ) = = 0 )
entry - > func = FILE_CHECK ;
2009-02-04 17:07:00 +03:00
else if ( strcmp ( args [ 0 ] . from , " FILE_MMAP " ) = = 0 )
entry - > func = FILE_MMAP ;
else if ( strcmp ( args [ 0 ] . from , " BPRM_CHECK " ) = = 0 )
entry - > func = BPRM_CHECK ;
else
result = - EINVAL ;
if ( ! result )
entry - > flags | = IMA_FUNC ;
break ;
case Opt_mask :
audit_log_format ( ab , " mask=%s " , args [ 0 ] . from ) ;
2010-04-20 18:21:01 +04:00
if ( entry - > mask )
result = - EINVAL ;
2009-02-04 17:07:00 +03:00
if ( ( strcmp ( args [ 0 ] . from , " MAY_EXEC " ) ) = = 0 )
entry - > mask = MAY_EXEC ;
else if ( strcmp ( args [ 0 ] . from , " MAY_WRITE " ) = = 0 )
entry - > mask = MAY_WRITE ;
else if ( strcmp ( args [ 0 ] . from , " MAY_READ " ) = = 0 )
entry - > mask = MAY_READ ;
else if ( strcmp ( args [ 0 ] . from , " MAY_APPEND " ) = = 0 )
entry - > mask = MAY_APPEND ;
else
result = - EINVAL ;
if ( ! result )
entry - > flags | = IMA_MASK ;
break ;
case Opt_fsmagic :
audit_log_format ( ab , " fsmagic=%s " , args [ 0 ] . from ) ;
2010-04-20 18:21:01 +04:00
if ( entry - > fsmagic ) {
result = - EINVAL ;
break ;
}
2009-02-04 17:07:00 +03:00
result = strict_strtoul ( args [ 0 ] . from , 16 ,
& entry - > fsmagic ) ;
if ( ! result )
entry - > flags | = IMA_FSMAGIC ;
break ;
case Opt_uid :
audit_log_format ( ab , " uid=%s " , args [ 0 ] . from ) ;
2010-04-20 18:21:01 +04:00
if ( entry - > uid ! = - 1 ) {
result = - EINVAL ;
break ;
}
2009-02-04 17:07:00 +03:00
result = strict_strtoul ( args [ 0 ] . from , 10 , & lnum ) ;
if ( ! result ) {
entry - > uid = ( uid_t ) lnum ;
if ( entry - > uid ! = lnum )
result = - EINVAL ;
else
entry - > flags | = IMA_UID ;
}
break ;
case Opt_obj_user :
audit_log_format ( ab , " obj_user=%s " , args [ 0 ] . from ) ;
result = ima_lsm_rule_init ( entry , args [ 0 ] . from ,
LSM_OBJ_USER ,
AUDIT_OBJ_USER ) ;
break ;
case Opt_obj_role :
audit_log_format ( ab , " obj_role=%s " , args [ 0 ] . from ) ;
result = ima_lsm_rule_init ( entry , args [ 0 ] . from ,
LSM_OBJ_ROLE ,
AUDIT_OBJ_ROLE ) ;
break ;
case Opt_obj_type :
audit_log_format ( ab , " obj_type=%s " , args [ 0 ] . from ) ;
result = ima_lsm_rule_init ( entry , args [ 0 ] . from ,
LSM_OBJ_TYPE ,
AUDIT_OBJ_TYPE ) ;
break ;
case Opt_subj_user :
audit_log_format ( ab , " subj_user=%s " , args [ 0 ] . from ) ;
result = ima_lsm_rule_init ( entry , args [ 0 ] . from ,
LSM_SUBJ_USER ,
AUDIT_SUBJ_USER ) ;
break ;
case Opt_subj_role :
audit_log_format ( ab , " subj_role=%s " , args [ 0 ] . from ) ;
result = ima_lsm_rule_init ( entry , args [ 0 ] . from ,
LSM_SUBJ_ROLE ,
AUDIT_SUBJ_ROLE ) ;
break ;
case Opt_subj_type :
audit_log_format ( ab , " subj_type=%s " , args [ 0 ] . from ) ;
result = ima_lsm_rule_init ( entry , args [ 0 ] . from ,
LSM_SUBJ_TYPE ,
AUDIT_SUBJ_TYPE ) ;
break ;
case Opt_err :
2010-04-20 18:21:13 +04:00
result = - EINVAL ;
2009-02-11 19:12:28 +03:00
audit_log_format ( ab , " UNKNOWN=%s " , p ) ;
2009-02-04 17:07:00 +03:00
break ;
}
}
2010-04-20 18:21:01 +04:00
if ( ! result & & ( entry - > action = = UNKNOWN ) )
2009-02-04 17:07:00 +03:00
result = - EINVAL ;
2010-04-20 18:20:54 +04:00
audit_log_format ( ab , " res=%d " , ! ! result ) ;
2009-02-04 17:07:00 +03:00
audit_log_end ( ab ) ;
return result ;
}
/**
* ima_parse_add_rule - add a rule to measure_policy_rules
* @ rule - ima measurement policy rule
*
* Uses a mutex to protect the policy list from multiple concurrent writers .
2010-04-20 18:20:54 +04:00
* Returns the length of the rule parsed , an error code on failure
2009-02-04 17:07:00 +03:00
*/
2010-04-20 18:20:54 +04:00
ssize_t ima_parse_add_rule ( char * rule )
2009-02-04 17:07:00 +03:00
{
2009-02-11 19:12:28 +03:00
const char * op = " update_policy " ;
2010-04-20 18:20:54 +04:00
char * p ;
2009-02-04 17:07:00 +03:00
struct ima_measure_rule_entry * entry ;
2010-04-20 18:20:54 +04:00
ssize_t result , len ;
2009-02-04 17:07:00 +03:00
int audit_info = 0 ;
/* Prevent installed policy from changing */
if ( ima_measure ! = & measure_default_rules ) {
integrity_audit_msg ( AUDIT_INTEGRITY_STATUS , NULL ,
NULL , op , " already exists " ,
- EACCES , audit_info ) ;
return - EACCES ;
}
entry = kzalloc ( sizeof ( * entry ) , GFP_KERNEL ) ;
if ( ! entry ) {
integrity_audit_msg ( AUDIT_INTEGRITY_STATUS , NULL ,
NULL , op , " -ENOMEM " , - ENOMEM , audit_info ) ;
return - ENOMEM ;
}
INIT_LIST_HEAD ( & entry - > list ) ;
2010-04-20 18:20:54 +04:00
p = strsep ( & rule , " \n " ) ;
len = strlen ( p ) + 1 ;
result = ima_parse_rule ( p , entry ) ;
2009-02-04 17:07:00 +03:00
if ( ! result ) {
2010-04-20 18:20:54 +04:00
result = len ;
2009-02-04 17:07:00 +03:00
mutex_lock ( & ima_measure_mutex ) ;
list_add_tail ( & entry - > list , & measure_policy_rules ) ;
mutex_unlock ( & ima_measure_mutex ) ;
2009-02-11 19:12:28 +03:00
} else {
2009-02-04 17:07:00 +03:00
kfree ( entry ) ;
2009-02-11 19:12:28 +03:00
integrity_audit_msg ( AUDIT_INTEGRITY_STATUS , NULL ,
NULL , op , " invalid policy " , result ,
audit_info ) ;
}
2009-02-04 17:07:00 +03:00
return result ;
}
/* ima_delete_rules called to cleanup invalid policy */
2009-02-05 01:28:26 +03:00
void ima_delete_rules ( void )
2009-02-04 17:07:00 +03:00
{
struct ima_measure_rule_entry * entry , * tmp ;
mutex_lock ( & ima_measure_mutex ) ;
list_for_each_entry_safe ( entry , tmp , & measure_policy_rules , list ) {
list_del ( & entry - > list ) ;
kfree ( entry ) ;
}
mutex_unlock ( & ima_measure_mutex ) ;
}