2010-07-29 14:48:06 -07:00
/*
* AppArmor security module
*
* This file contains AppArmor policy attachment and domain transitions
*
* Copyright ( C ) 2002 - 2008 Novell / SUSE
* Copyright 2009 - 2010 Canonical Ltd .
*
* 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 .
*/
# include <linux/errno.h>
# include <linux/fdtable.h>
# include <linux/file.h>
# include <linux/mount.h>
# include <linux/syscalls.h>
# include <linux/tracehook.h>
# include <linux/personality.h>
# include "include/audit.h"
# include "include/apparmorfs.h"
# include "include/context.h"
# include "include/domain.h"
# include "include/file.h"
# include "include/ipc.h"
# include "include/match.h"
# include "include/path.h"
# include "include/policy.h"
2017-01-16 00:42:15 -08:00
# include "include/policy_ns.h"
2010-07-29 14:48:06 -07:00
/**
* aa_free_domain_entries - free entries in a domain table
* @ domain : the domain table to free ( MAYBE NULL )
*/
void aa_free_domain_entries ( struct aa_domain * domain )
{
int i ;
if ( domain ) {
if ( ! domain - > table )
return ;
for ( i = 0 ; i < domain - > size ; i + + )
kzfree ( domain - > table [ i ] ) ;
kzfree ( domain - > table ) ;
domain - > table = NULL ;
}
}
/**
* may_change_ptraced_domain - check if can change profile on ptraced task
2017-06-09 14:22:14 -07:00
* @ to_label : profile to change to ( NOT NULL )
* @ info : message if there is an error
2010-07-29 14:48:06 -07:00
*
2013-10-08 05:46:03 -07:00
* Check if current is ptraced and if so if the tracing task is allowed
2010-07-29 14:48:06 -07:00
* to trace the new domain
*
* Returns : % 0 or error if change not allowed
*/
2017-06-09 14:22:14 -07:00
static int may_change_ptraced_domain ( struct aa_label * to_label ,
const char * * info )
2010-07-29 14:48:06 -07:00
{
struct task_struct * tracer ;
2017-06-09 08:14:28 -07:00
struct aa_label * tracerl = NULL ;
2010-07-29 14:48:06 -07:00
int error = 0 ;
rcu_read_lock ( ) ;
2013-10-08 05:46:03 -07:00
tracer = ptrace_parent ( current ) ;
2013-02-18 16:03:34 -08:00
if ( tracer )
2010-07-29 14:48:06 -07:00
/* released below */
2017-06-09 08:14:28 -07:00
tracerl = aa_get_task_label ( tracer ) ;
2010-07-29 14:48:06 -07:00
/* not ptraced */
2017-06-09 08:14:28 -07:00
if ( ! tracer | | unconfined ( tracerl ) )
2010-07-29 14:48:06 -07:00
goto out ;
2017-06-09 14:22:14 -07:00
error = aa_may_ptrace ( tracerl , to_label , PTRACE_MODE_ATTACH ) ;
2010-07-29 14:48:06 -07:00
out :
2011-06-28 15:06:38 +01:00
rcu_read_unlock ( ) ;
2017-06-09 08:14:28 -07:00
aa_put_label ( tracerl ) ;
2010-07-29 14:48:06 -07:00
2017-06-09 14:22:14 -07:00
if ( error )
* info = " ptrace prevents transition " ;
2010-07-29 14:48:06 -07:00
return error ;
}
2017-06-09 16:55:04 -07:00
/**** TODO: dedup to aa_label_match - needs perm and dfa, merging
* specifically this is an exact copy of aa_label_match except
* aa_compute_perms is replaced with aa_compute_fperms
* and policy . dfa with file . dfa
* * * */
/* match a profile and its associated ns component if needed
* Assumes visibility test has already been done .
* If a subns profile is not to be matched should be prescreened with
* visibility test .
*/
static inline unsigned int match_component ( struct aa_profile * profile ,
struct aa_profile * tp ,
bool stack , unsigned int state )
{
const char * ns_name ;
if ( stack )
state = aa_dfa_match ( profile - > file . dfa , state , " & " ) ;
if ( profile - > ns = = tp - > ns )
return aa_dfa_match ( profile - > file . dfa , state , tp - > base . hname ) ;
/* try matching with namespace name and then profile */
ns_name = aa_ns_name ( profile - > ns , tp - > ns , true ) ;
state = aa_dfa_match_len ( profile - > file . dfa , state , " : " , 1 ) ;
state = aa_dfa_match ( profile - > file . dfa , state , ns_name ) ;
state = aa_dfa_match_len ( profile - > file . dfa , state , " : " , 1 ) ;
return aa_dfa_match ( profile - > file . dfa , state , tp - > base . hname ) ;
}
/**
* label_compound_match - find perms for full compound label
* @ profile : profile to find perms for
* @ label : label to check access permissions for
* @ stack : whether this is a stacking request
* @ start : state to start match in
* @ subns : whether to do permission checks on components in a subns
* @ request : permissions to request
* @ perms : perms struct to set
*
* Returns : 0 on success else ERROR
*
* For the label A //&B//&C this does the perm match for A//&B//&C
* @ perms should be preinitialized with allperms OR a previous permission
* check to be stacked .
*/
static int label_compound_match ( struct aa_profile * profile ,
struct aa_label * label , bool stack ,
unsigned int state , bool subns , u32 request ,
struct aa_perms * perms )
{
struct aa_profile * tp ;
struct label_it i ;
struct path_cond cond = { } ;
/* find first subcomponent that is visible */
label_for_each ( i , label , tp ) {
if ( ! aa_ns_visible ( profile - > ns , tp - > ns , subns ) )
continue ;
state = match_component ( profile , tp , stack , state ) ;
if ( ! state )
goto fail ;
goto next ;
}
/* no component visible */
* perms = allperms ;
return 0 ;
next :
label_for_each_cont ( i , label , tp ) {
if ( ! aa_ns_visible ( profile - > ns , tp - > ns , subns ) )
continue ;
state = aa_dfa_match ( profile - > file . dfa , state , " //& " ) ;
state = match_component ( profile , tp , false , state ) ;
if ( ! state )
goto fail ;
}
* perms = aa_compute_fperms ( profile - > file . dfa , state , & cond ) ;
aa_apply_modes_to_perms ( profile , perms ) ;
if ( ( perms - > allow & request ) ! = request )
return - EACCES ;
return 0 ;
fail :
* perms = nullperms ;
return - EACCES ;
}
/**
* label_components_match - find perms for all subcomponents of a label
* @ profile : profile to find perms for
* @ label : label to check access permissions for
* @ stack : whether this is a stacking request
* @ start : state to start match in
* @ subns : whether to do permission checks on components in a subns
* @ request : permissions to request
* @ perms : an initialized perms struct to add accumulation to
*
* Returns : 0 on success else ERROR
*
* For the label A //&B//&C this does the perm match for each of A and B and C
* @ perms should be preinitialized with allperms OR a previous permission
* check to be stacked .
*/
static int label_components_match ( struct aa_profile * profile ,
struct aa_label * label , bool stack ,
unsigned int start , bool subns , u32 request ,
struct aa_perms * perms )
{
struct aa_profile * tp ;
struct label_it i ;
struct aa_perms tmp ;
struct path_cond cond = { } ;
unsigned int state = 0 ;
/* find first subcomponent to test */
label_for_each ( i , label , tp ) {
if ( ! aa_ns_visible ( profile - > ns , tp - > ns , subns ) )
continue ;
state = match_component ( profile , tp , stack , start ) ;
if ( ! state )
goto fail ;
goto next ;
}
/* no subcomponents visible - no change in perms */
return 0 ;
next :
tmp = aa_compute_fperms ( profile - > file . dfa , state , & cond ) ;
aa_apply_modes_to_perms ( profile , & tmp ) ;
aa_perms_accum ( perms , & tmp ) ;
label_for_each_cont ( i , label , tp ) {
if ( ! aa_ns_visible ( profile - > ns , tp - > ns , subns ) )
continue ;
state = match_component ( profile , tp , stack , start ) ;
if ( ! state )
goto fail ;
tmp = aa_compute_fperms ( profile - > file . dfa , state , & cond ) ;
aa_apply_modes_to_perms ( profile , & tmp ) ;
aa_perms_accum ( perms , & tmp ) ;
}
if ( ( perms - > allow & request ) ! = request )
return - EACCES ;
return 0 ;
fail :
* perms = nullperms ;
return - EACCES ;
}
/**
* label_match - do a multi - component label match
* @ profile : profile to match against ( NOT NULL )
* @ label : label to match ( NOT NULL )
* @ stack : whether this is a stacking request
* @ state : state to start in
* @ subns : whether to match subns components
* @ request : permission request
* @ perms : Returns computed perms ( NOT NULL )
*
* Returns : the state the match finished in , may be the none matching state
*/
static int label_match ( struct aa_profile * profile , struct aa_label * label ,
bool stack , unsigned int state , bool subns , u32 request ,
struct aa_perms * perms )
{
int error ;
* perms = nullperms ;
error = label_compound_match ( profile , label , stack , state , subns ,
request , perms ) ;
if ( ! error )
return error ;
* perms = allperms ;
return label_components_match ( profile , label , stack , state , subns ,
request , perms ) ;
}
/******* end TODO: dedup *****/
2010-07-29 14:48:06 -07:00
/**
* change_profile_perms - find permissions for change_profile
* @ profile : the current profile ( NOT NULL )
2017-06-09 16:55:04 -07:00
* @ target : label to transition to ( NOT NULL )
* @ stack : whether this is a stacking request
2010-07-29 14:48:06 -07:00
* @ request : requested perms
* @ start : state to start matching in
*
2017-06-09 16:55:04 -07:00
*
2010-07-29 14:48:06 -07:00
* Returns : permission set
2017-06-09 16:55:04 -07:00
*
* currently only matches full label A //&B//&C or individual components A, B, C
* not arbitrary combinations . Eg . A //&B, C
2010-07-29 14:48:06 -07:00
*/
2017-06-09 16:55:04 -07:00
static int change_profile_perms ( struct aa_profile * profile ,
struct aa_label * target , bool stack ,
u32 request , unsigned int start ,
struct aa_perms * perms )
{
if ( profile_unconfined ( profile ) ) {
perms - > allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC ;
perms - > audit = perms - > quiet = perms - > kill = 0 ;
return 0 ;
}
/* TODO: add profile in ns screening */
return label_match ( profile , target , stack , start , true , request , perms ) ;
}
2010-07-29 14:48:06 -07:00
/**
* __attach_match_ - find an attachment match
* @ name - to match against ( NOT NULL )
* @ head - profile list to walk ( NOT NULL )
*
* Do a linear search on the profiles in the list . There is a matching
* preference where an exact match is preferred over a name which uses
* expressions to match , and matching expressions with the greatest
* xmatch_len are preferred .
*
* Requires : @ head not be shared or have appropriate locks held
*
* Returns : profile or NULL if no match found
*/
static struct aa_profile * __attach_match ( const char * name ,
struct list_head * head )
{
int len = 0 ;
struct aa_profile * profile , * candidate = NULL ;
2013-07-10 21:06:43 -07:00
list_for_each_entry_rcu ( profile , head , base . list ) {
2017-06-09 08:14:28 -07:00
if ( profile - > label . flags & FLAG_NULL )
2010-07-29 14:48:06 -07:00
continue ;
if ( profile - > xmatch & & profile - > xmatch_len > len ) {
unsigned int state = aa_dfa_match ( profile - > xmatch ,
DFA_START , name ) ;
u32 perm = dfa_user_allow ( profile - > xmatch , state ) ;
/* any accepting state means a valid match. */
if ( perm & MAY_EXEC ) {
candidate = profile ;
len = profile - > xmatch_len ;
}
} else if ( ! strcmp ( profile - > base . name , name ) )
/* exact non-re match, no more searching required */
return profile ;
}
return candidate ;
}
/**
* find_attach - do attachment search for unconfined processes
* @ ns : the current namespace ( NOT NULL )
* @ list : list to search ( NOT NULL )
* @ name : the executable name to match against ( NOT NULL )
*
2017-06-09 16:55:04 -07:00
* Returns : label or NULL if no match found
2010-07-29 14:48:06 -07:00
*/
2017-06-09 16:55:04 -07:00
static struct aa_label * find_attach ( struct aa_ns * ns , struct list_head * list ,
const char * name )
2010-07-29 14:48:06 -07:00
{
struct aa_profile * profile ;
2013-07-10 21:06:43 -07:00
rcu_read_lock ( ) ;
2010-07-29 14:48:06 -07:00
profile = aa_get_profile ( __attach_match ( name , list ) ) ;
2013-07-10 21:06:43 -07:00
rcu_read_unlock ( ) ;
2010-07-29 14:48:06 -07:00
2017-06-09 16:55:04 -07:00
return profile ? & profile - > label : NULL ;
2010-07-29 14:48:06 -07:00
}
static const char * next_name ( int xtype , const char * name )
{
return NULL ;
}
/**
* x_table_lookup - lookup an x transition name via transition table
* @ profile : current profile ( NOT NULL )
* @ xindex : index into x transition table
2017-06-09 16:55:04 -07:00
* @ name : returns : name tested to find label ( NOT NULL )
2010-07-29 14:48:06 -07:00
*
2017-06-09 16:55:04 -07:00
* Returns : refcounted label , or NULL on failure ( MAYBE NULL )
2010-07-29 14:48:06 -07:00
*/
2017-06-09 16:55:04 -07:00
static struct aa_label * x_table_lookup ( struct aa_profile * profile , u32 xindex ,
const char * * name )
2010-07-29 14:48:06 -07:00
{
2017-06-09 16:55:04 -07:00
struct aa_label * label = NULL ;
2010-07-29 14:48:06 -07:00
u32 xtype = xindex & AA_X_TYPE_MASK ;
int index = xindex & AA_X_INDEX_MASK ;
2017-06-09 16:55:04 -07:00
AA_BUG ( ! name ) ;
2010-07-29 14:48:06 -07:00
2017-06-09 16:55:04 -07:00
/* index is guaranteed to be in range, validated at load time */
/* TODO: move lookup parsing to unpack time so this is a straight
* index into the resultant label
*/
for ( * name = profile - > file . trans . table [ index ] ; ! label & & * name ;
* name = next_name ( xtype , * name ) ) {
2010-07-29 14:48:06 -07:00
if ( xindex & AA_X_CHILD ) {
2017-06-09 16:55:04 -07:00
struct aa_profile * new_profile ;
2010-07-29 14:48:06 -07:00
/* release by caller */
2017-06-09 16:55:04 -07:00
new_profile = aa_find_child ( profile , * name ) ;
if ( new_profile )
label = & new_profile - > label ;
2010-07-29 14:48:06 -07:00
continue ;
}
2017-06-09 16:55:04 -07:00
label = aa_label_parse ( & profile - > label , * name , GFP_ATOMIC ,
true , false ) ;
if ( IS_ERR ( label ) )
label = NULL ;
2010-07-29 14:48:06 -07:00
}
/* released by caller */
2017-06-09 16:55:04 -07:00
return label ;
2010-07-29 14:48:06 -07:00
}
/**
2017-06-09 16:55:04 -07:00
* x_to_label - get target label for a given xindex
2010-07-29 14:48:06 -07:00
* @ profile : current profile ( NOT NULL )
* @ name : name to lookup ( NOT NULL )
* @ xindex : index into x transition table
2017-06-09 16:55:04 -07:00
* @ lookupname : returns : name used in lookup if one was specified ( NOT NULL )
2010-07-29 14:48:06 -07:00
*
2017-06-09 16:55:04 -07:00
* find label for a transition index
2010-07-29 14:48:06 -07:00
*
2017-06-09 16:55:04 -07:00
* Returns : refcounted label or NULL if not found available
2010-07-29 14:48:06 -07:00
*/
2017-06-09 16:55:04 -07:00
static struct aa_label * x_to_label ( struct aa_profile * profile ,
const char * name , u32 xindex ,
const char * * lookupname ,
const char * * info )
2010-07-29 14:48:06 -07:00
{
2017-06-09 16:55:04 -07:00
struct aa_label * new = NULL ;
2017-01-16 00:42:16 -08:00
struct aa_ns * ns = profile - > ns ;
2010-07-29 14:48:06 -07:00
u32 xtype = xindex & AA_X_TYPE_MASK ;
2017-06-09 16:55:04 -07:00
const char * stack = NULL ;
2010-07-29 14:48:06 -07:00
switch ( xtype ) {
case AA_X_NONE :
/* fail exec unless ix || ux fallback - handled by caller */
2017-06-09 16:55:04 -07:00
* lookupname = NULL ;
break ;
case AA_X_TABLE :
/* TODO: fix when perm mapping done at unload */
stack = profile - > file . trans . table [ xindex & AA_X_INDEX_MASK ] ;
if ( * stack ! = ' & ' ) {
/* released by caller */
new = x_table_lookup ( profile , xindex , lookupname ) ;
stack = NULL ;
break ;
}
/* fall through to X_NAME */
2010-07-29 14:48:06 -07:00
case AA_X_NAME :
if ( xindex & AA_X_CHILD )
/* released by caller */
2017-06-09 16:55:04 -07:00
new = find_attach ( ns , & profile - > base . profiles ,
name ) ;
2010-07-29 14:48:06 -07:00
else
/* released by caller */
2017-06-09 16:55:04 -07:00
new = find_attach ( ns , & ns - > base . profiles ,
name ) ;
* lookupname = name ;
2010-07-29 14:48:06 -07:00
break ;
}
2017-06-09 16:55:04 -07:00
if ( ! new ) {
if ( xindex & AA_X_INHERIT ) {
/* (p|c|n)ix - don't change profile but do
* use the newest version
*/
* info = " ix fallback " ;
/* no profile && no error */
new = aa_get_newest_label ( & profile - > label ) ;
} else if ( xindex & AA_X_UNCONFINED ) {
new = aa_get_newest_label ( ns_unconfined ( profile - > ns ) ) ;
* info = " ux fallback " ;
}
}
if ( new & & stack ) {
/* base the stack on post domain transition */
struct aa_label * base = new ;
new = aa_label_parse ( base , stack , GFP_ATOMIC , true , false ) ;
if ( IS_ERR ( new ) )
new = NULL ;
aa_put_label ( base ) ;
}
2010-07-29 14:48:06 -07:00
/* released by caller */
2017-06-09 16:55:04 -07:00
return new ;
2010-07-29 14:48:06 -07:00
}
2017-06-09 16:55:04 -07:00
static struct aa_label * profile_transition ( struct aa_profile * profile ,
const struct linux_binprm * bprm ,
char * buffer , struct path_cond * cond ,
bool * secure_exec )
2010-07-29 14:48:06 -07:00
{
2017-06-09 16:55:04 -07:00
struct aa_label * new = NULL ;
const char * info = NULL , * name = NULL , * target = NULL ;
unsigned int state = profile - > file . start ;
2017-05-29 12:19:39 -07:00
struct aa_perms perms = { } ;
2017-06-09 16:55:04 -07:00
bool nonewprivs = false ;
2015-05-02 15:11:42 -07:00
int error = 0 ;
2010-07-29 14:48:06 -07:00
2017-06-09 16:55:04 -07:00
AA_BUG ( ! profile ) ;
AA_BUG ( ! bprm ) ;
AA_BUG ( ! buffer ) ;
2010-07-29 14:48:06 -07:00
2017-05-23 03:25:14 -07:00
error = aa_path_name ( & bprm - > file - > f_path , profile - > path_flags , buffer ,
2017-05-22 03:06:52 -07:00
& name , & info , profile - > disconnected ) ;
2010-07-29 14:48:06 -07:00
if ( error ) {
2017-06-09 08:14:28 -07:00
if ( profile_unconfined ( profile ) | |
2017-06-09 16:55:04 -07:00
( profile - > label . flags & FLAG_IX_ON_NAME_ERROR ) ) {
AA_DEBUG ( " name lookup ix on error " ) ;
2010-07-29 14:48:06 -07:00
error = 0 ;
2017-06-09 16:55:04 -07:00
new = aa_get_newest_label ( & profile - > label ) ;
}
2010-07-29 14:48:06 -07:00
name = bprm - > filename ;
goto audit ;
}
2017-06-09 08:14:28 -07:00
if ( profile_unconfined ( profile ) ) {
2017-06-09 16:55:04 -07:00
new = find_attach ( profile - > ns , & profile - > ns - > base . profiles ,
name ) ;
if ( new ) {
AA_DEBUG ( " unconfined attached to new label " ) ;
return new ;
}
AA_DEBUG ( " unconfined exec no attachment " ) ;
return aa_get_newest_label ( & profile - > label ) ;
2010-07-29 14:48:06 -07:00
}
/* find exec permissions for name */
2017-06-09 16:55:04 -07:00
state = aa_str_perms ( profile - > file . dfa , state , name , cond , & perms ) ;
2010-07-29 14:48:06 -07:00
if ( perms . allow & MAY_EXEC ) {
/* exec permission determine how to transition */
2017-06-09 16:55:04 -07:00
new = x_to_label ( profile , name , perms . xindex , & target , & info ) ;
if ( new & & new - > proxy = = profile - > label . proxy & & info ) {
/* hack ix fallback - improve how this is detected */
goto audit ;
} else if ( ! new ) {
error = - EACCES ;
info = " profile transition not found " ;
/* remove MAY_EXEC to audit as failure */
perms . allow & = ~ MAY_EXEC ;
2010-07-29 14:48:06 -07:00
}
} else if ( COMPLAIN_MODE ( profile ) ) {
2017-06-09 16:55:04 -07:00
/* no exec permission - learning mode */
struct aa_profile * new_profile = aa_new_null_profile ( profile ,
false , name ,
GFP_ATOMIC ) ;
2010-07-29 14:48:06 -07:00
if ( ! new_profile ) {
error = - ENOMEM ;
info = " could not create null profile " ;
2017-06-09 16:55:04 -07:00
} else {
2010-07-29 14:48:06 -07:00
error = - EACCES ;
2017-06-09 16:55:04 -07:00
new = & new_profile - > label ;
}
2010-07-29 14:48:06 -07:00
perms . xindex | = AA_X_UNSAFE ;
} else
/* fail exec */
error = - EACCES ;
2017-06-09 16:55:04 -07:00
if ( ! new )
goto audit ;
/* Policy has specified a domain transitions. if no_new_privs and
* confined and not transitioning to the current domain fail .
*
* NOTE : Domain transitions from unconfined and to stritly stacked
* subsets are allowed even when no_new_privs is set because this
* aways results in a further reduction of permissions .
2012-04-12 16:47:51 -05:00
*/
2017-06-09 16:55:04 -07:00
if ( ( bprm - > unsafe & LSM_UNSAFE_NO_NEW_PRIVS ) & &
! profile_unconfined ( profile ) & &
! aa_label_is_subset ( new , & profile - > label ) ) {
2012-04-12 16:47:51 -05:00
error = - EPERM ;
2017-06-09 16:55:04 -07:00
info = " no new privs " ;
nonewprivs = true ;
perms . allow & = ~ MAY_EXEC ;
goto audit ;
}
if ( ! ( perms . xindex & AA_X_UNSAFE ) ) {
if ( DEBUG_ON ) {
dbg_printk ( " apparmor: scrubbing environment variables "
" for %s profile= " , name ) ;
aa_label_printk ( new , GFP_ATOMIC ) ;
dbg_printk ( " \n " ) ;
}
* secure_exec = true ;
2012-04-12 16:47:51 -05:00
}
2017-06-09 16:55:04 -07:00
audit :
aa_audit_file ( profile , & perms , OP_EXEC , MAY_EXEC , name , target , new ,
cond - > uid , info , error ) ;
if ( ! new | | nonewprivs ) {
aa_put_label ( new ) ;
return ERR_PTR ( error ) ;
}
return new ;
}
static int profile_onexec ( struct aa_profile * profile , struct aa_label * onexec ,
bool stack , const struct linux_binprm * bprm ,
char * buffer , struct path_cond * cond ,
bool * secure_exec )
{
unsigned int state = profile - > file . start ;
struct aa_perms perms = { } ;
const char * xname = NULL , * info = " change_profile onexec " ;
int error = - EACCES ;
AA_BUG ( ! profile ) ;
AA_BUG ( ! onexec ) ;
AA_BUG ( ! bprm ) ;
AA_BUG ( ! buffer ) ;
if ( profile_unconfined ( profile ) ) {
/* change_profile on exec already granted */
/*
* NOTE : Domain transitions from unconfined are allowed
* even when no_new_privs is set because this aways results
* in a further reduction of permissions .
*/
return 0 ;
}
error = aa_path_name ( & bprm - > file - > f_path , profile - > path_flags , buffer ,
& xname , & info , profile - > disconnected ) ;
if ( error ) {
if ( profile_unconfined ( profile ) | |
( profile - > label . flags & FLAG_IX_ON_NAME_ERROR ) ) {
AA_DEBUG ( " name lookup ix on error " ) ;
error = 0 ;
}
xname = bprm - > filename ;
2010-07-29 14:48:06 -07:00
goto audit ;
2017-06-09 16:55:04 -07:00
}
/* find exec permissions for name */
state = aa_str_perms ( profile - > file . dfa , state , xname , cond , & perms ) ;
if ( ! ( perms . allow & AA_MAY_ONEXEC ) ) {
info = " no change_onexec valid for executable " ;
goto audit ;
}
/* test if this exec can be paired with change_profile onexec.
* onexec permission is linked to exec with a standard pairing
* exec \ 0 change_profile
*/
state = aa_dfa_null_transition ( profile - > file . dfa , state ) ;
error = change_profile_perms ( profile , onexec , stack , AA_MAY_ONEXEC ,
state , & perms ) ;
if ( error ) {
perms . allow & = ~ AA_MAY_ONEXEC ;
goto audit ;
}
/* Policy has specified a domain transitions. if no_new_privs and
* confined and not transitioning to the current domain fail .
*
* NOTE : Domain transitions from unconfined and to stritly stacked
* subsets are allowed even when no_new_privs is set because this
* aways results in a further reduction of permissions .
*/
if ( ( bprm - > unsafe & LSM_UNSAFE_NO_NEW_PRIVS ) & &
! profile_unconfined ( profile ) & &
! aa_label_is_subset ( onexec , & profile - > label ) ) {
error = - EPERM ;
info = " no new privs " ;
perms . allow & = ~ AA_MAY_ONEXEC ;
goto audit ;
}
if ( ! ( perms . xindex & AA_X_UNSAFE ) ) {
if ( DEBUG_ON ) {
dbg_printk ( " apparmor: scrubbing environment "
" variables for %s label= " , xname ) ;
aa_label_printk ( onexec , GFP_ATOMIC ) ;
dbg_printk ( " \n " ) ;
}
* secure_exec = true ;
}
audit :
return aa_audit_file ( profile , & perms , OP_EXEC , AA_MAY_ONEXEC , xname ,
NULL , onexec , cond - > uid , info , error ) ;
}
/* ensure none ns domain transitions are correctly applied with onexec */
static struct aa_label * handle_onexec ( struct aa_label * label ,
struct aa_label * onexec , bool stack ,
const struct linux_binprm * bprm ,
char * buffer , struct path_cond * cond ,
bool * unsafe )
{
struct aa_profile * profile ;
struct aa_label * new ;
int error ;
AA_BUG ( ! label ) ;
AA_BUG ( ! onexec ) ;
AA_BUG ( ! bprm ) ;
AA_BUG ( ! buffer ) ;
if ( ! stack ) {
error = fn_for_each_in_ns ( label , profile ,
profile_onexec ( profile , onexec , stack ,
bprm , buffer , cond , unsafe ) ) ;
if ( error )
return ERR_PTR ( error ) ;
new = fn_label_build_in_ns ( label , profile , GFP_ATOMIC ,
aa_get_newest_label ( onexec ) ,
profile_transition ( profile , bprm , buffer ,
cond , unsafe ) ) ;
} else {
/* TODO: determine how much we want to losen this */
error = fn_for_each_in_ns ( label , profile ,
profile_onexec ( profile , onexec , stack , bprm ,
buffer , cond , unsafe ) ) ;
if ( error )
return ERR_PTR ( error ) ;
new = fn_label_build_in_ns ( label , profile , GFP_ATOMIC ,
aa_label_merge ( & profile - > label , onexec ,
GFP_ATOMIC ) ,
profile_transition ( profile , bprm , buffer ,
cond , unsafe ) ) ;
}
if ( new )
return new ;
/* TODO: get rid of GLOBAL_ROOT_UID */
error = fn_for_each_in_ns ( label , profile ,
aa_audit_file ( profile , & nullperms , OP_CHANGE_ONEXEC ,
AA_MAY_ONEXEC , bprm - > filename , NULL ,
onexec , GLOBAL_ROOT_UID ,
" failed to build target label " , - ENOMEM ) ) ;
return ERR_PTR ( error ) ;
}
/**
* apparmor_bprm_set_creds - set the new creds on the bprm struct
* @ bprm : binprm for the exec ( NOT NULL )
*
* Returns : % 0 or error on failure
*
* TODO : once the other paths are done see if we can ' t refactor into a fn
*/
int apparmor_bprm_set_creds ( struct linux_binprm * bprm )
{
struct aa_task_ctx * ctx ;
struct aa_label * label , * new = NULL ;
struct aa_profile * profile ;
char * buffer = NULL ;
const char * info = NULL ;
int error = 0 ;
bool unsafe = false ;
struct path_cond cond = {
file_inode ( bprm - > file ) - > i_uid ,
file_inode ( bprm - > file ) - > i_mode
} ;
2017-07-18 15:25:23 -07:00
if ( bprm - > called_set_creds )
2017-06-09 16:55:04 -07:00
return 0 ;
ctx = cred_ctx ( bprm - > cred ) ;
AA_BUG ( ! ctx ) ;
label = aa_get_newest_label ( ctx - > label ) ;
/* buffer freed below, name is pointer into buffer */
get_buffers ( buffer ) ;
/* Test for onexec first as onexec override other x transitions. */
if ( ctx - > onexec )
new = handle_onexec ( label , ctx - > onexec , ctx - > token ,
bprm , buffer , & cond , & unsafe ) ;
else
new = fn_label_build ( label , profile , GFP_ATOMIC ,
profile_transition ( profile , bprm , buffer ,
& cond , & unsafe ) ) ;
AA_BUG ( ! new ) ;
if ( IS_ERR ( new ) ) {
error = PTR_ERR ( new ) ;
goto done ;
} else if ( ! new ) {
error = - ENOMEM ;
goto done ;
}
/* TODO: Add ns level no_new_privs subset test */
2010-07-29 14:48:06 -07:00
if ( bprm - > unsafe & LSM_UNSAFE_SHARE ) {
/* FIXME: currently don't mediate shared state */
;
}
2017-06-09 16:55:04 -07:00
if ( bprm - > unsafe & ( LSM_UNSAFE_PTRACE ) ) {
/* TODO: test needs to be profile of label to new */
error = may_change_ptraced_domain ( new , & info ) ;
2016-04-20 14:18:18 -07:00
if ( error )
2010-07-29 14:48:06 -07:00
goto audit ;
}
2017-06-09 16:55:04 -07:00
if ( unsafe ) {
if ( DEBUG_ON ) {
dbg_printk ( " scrubbing environment variables for %s "
" label= " , bprm - > filename ) ;
aa_label_printk ( new , GFP_ATOMIC ) ;
dbg_printk ( " \n " ) ;
}
2010-07-29 14:48:06 -07:00
bprm - > unsafe | = AA_SECURE_X_NEEDED ;
}
2017-06-09 16:55:04 -07:00
if ( label - > proxy ! = new - > proxy ) {
/* when transitioning clear unsafe personality bits */
if ( DEBUG_ON ) {
dbg_printk ( " apparmor: clearing unsafe personality "
" bits. %s label= " , bprm - > filename ) ;
aa_label_printk ( new , GFP_ATOMIC ) ;
dbg_printk ( " \n " ) ;
}
bprm - > per_clear | = PER_CLEAR_ON_SETID ;
}
2017-06-09 08:14:28 -07:00
aa_put_label ( ctx - > label ) ;
2017-06-09 16:55:04 -07:00
/* transfer reference, released when ctx is freed */
ctx - > label = new ;
2010-07-29 14:48:06 -07:00
2017-06-09 16:55:04 -07:00
done :
/* clear out temporary/transitional state from the context */
2017-01-16 00:43:00 -08:00
aa_clear_task_ctx_trans ( ctx ) ;
2010-07-29 14:48:06 -07:00
2017-06-09 08:14:28 -07:00
aa_put_label ( label ) ;
2017-05-23 03:25:14 -07:00
put_buffers ( buffer ) ;
2010-07-29 14:48:06 -07:00
return error ;
2017-06-09 16:55:04 -07:00
audit :
error = fn_for_each ( label , profile ,
aa_audit_file ( profile , & nullperms , OP_EXEC , MAY_EXEC ,
bprm - > filename , NULL , new ,
file_inode ( bprm - > file ) - > i_uid , info ,
error ) ) ;
aa_put_label ( new ) ;
goto done ;
2010-07-29 14:48:06 -07:00
}
/**
* apparmor_bprm_secureexec - determine if secureexec is needed
* @ bprm : binprm for exec ( NOT NULL )
*
* Returns : % 1 if secureexec is needed else % 0
*/
int apparmor_bprm_secureexec ( struct linux_binprm * bprm )
{
/* the decision to use secure exec is computed in set_creds
* and stored in bprm - > unsafe .
*/
2015-05-02 15:11:42 -07:00
if ( bprm - > unsafe & AA_SECURE_X_NEEDED )
return 1 ;
2010-07-29 14:48:06 -07:00
2015-05-02 15:11:42 -07:00
return 0 ;
2010-07-29 14:48:06 -07:00
}
/*
* Functions for self directed profile change
*/
2017-06-09 17:01:43 -07:00
/* helper fn for change_hat
2010-07-29 14:48:06 -07:00
*
2017-06-09 17:01:43 -07:00
* Returns : label for hat transition OR ERR_PTR . Does NOT return NULL
2010-07-29 14:48:06 -07:00
*/
2017-06-09 17:01:43 -07:00
static struct aa_label * build_change_hat ( struct aa_profile * profile ,
const char * name , bool sibling )
2010-07-29 14:48:06 -07:00
{
2017-06-09 17:01:43 -07:00
struct aa_profile * root , * hat = NULL ;
const char * info = NULL ;
int error = 0 ;
if ( sibling & & PROFILE_IS_HAT ( profile ) ) {
root = aa_get_profile_rcu ( & profile - > parent ) ;
} else if ( ! sibling & & ! PROFILE_IS_HAT ( profile ) ) {
root = aa_get_profile ( profile ) ;
} else {
info = " conflicting target types " ;
error = - EPERM ;
goto audit ;
}
hat = aa_find_child ( root , name ) ;
if ( ! hat ) {
error = - ENOENT ;
if ( COMPLAIN_MODE ( profile ) ) {
hat = aa_new_null_profile ( profile , true , name ,
GFP_KERNEL ) ;
if ( ! hat ) {
info = " failed null profile create " ;
error = - ENOMEM ;
}
}
}
aa_put_profile ( root ) ;
audit :
aa_audit_file ( profile , & nullperms , OP_CHANGE_HAT , AA_MAY_CHANGEHAT ,
name , hat ? hat - > base . hname : NULL ,
hat ? & hat - > label : NULL , GLOBAL_ROOT_UID , NULL ,
error ) ;
if ( ! hat | | ( error & & error ! = - ENOENT ) )
return ERR_PTR ( error ) ;
/* if hat && error - complain mode, already audited and we adjust for
* complain mode allow by returning hat - > label
*/
return & hat - > label ;
}
/* helper fn for changing into a hat
*
* Returns : label for hat transition or ERR_PTR . Does not return NULL
*/
static struct aa_label * change_hat ( struct aa_label * label , const char * hats [ ] ,
int count , int flags )
{
struct aa_profile * profile , * root , * hat = NULL ;
struct aa_label * new ;
struct label_it it ;
bool sibling = false ;
const char * name , * info = NULL ;
int i , error ;
AA_BUG ( ! label ) ;
AA_BUG ( ! hats ) ;
AA_BUG ( count < 1 ) ;
if ( PROFILE_IS_HAT ( labels_profile ( label ) ) )
sibling = true ;
/*find first matching hat */
for ( i = 0 ; i < count & & ! hat ; i + + ) {
name = hats [ i ] ;
label_for_each_in_ns ( it , labels_ns ( label ) , label , profile ) {
if ( sibling & & PROFILE_IS_HAT ( profile ) ) {
root = aa_get_profile_rcu ( & profile - > parent ) ;
} else if ( ! sibling & & ! PROFILE_IS_HAT ( profile ) ) {
root = aa_get_profile ( profile ) ;
} else { /* conflicting change type */
info = " conflicting targets types " ;
error = - EPERM ;
goto fail ;
}
hat = aa_find_child ( root , name ) ;
aa_put_profile ( root ) ;
if ( ! hat ) {
if ( ! COMPLAIN_MODE ( profile ) )
goto outer_continue ;
/* complain mode succeed as if hat */
} else if ( ! PROFILE_IS_HAT ( hat ) ) {
info = " target not hat " ;
error = - EPERM ;
aa_put_profile ( hat ) ;
goto fail ;
}
aa_put_profile ( hat ) ;
}
/* found a hat for all profiles in ns */
goto build ;
outer_continue :
;
}
/* no hats that match, find appropriate error
*
* In complain mode audit of the failure is based off of the first
* hat supplied . This is done due how userspace interacts with
* change_hat .
*/
name = NULL ;
label_for_each_in_ns ( it , labels_ns ( label ) , label , profile ) {
if ( ! list_empty ( & profile - > base . profiles ) ) {
info = " hat not found " ;
error = - ENOENT ;
goto fail ;
}
}
info = " no hats defined " ;
error = - ECHILD ;
fail :
label_for_each_in_ns ( it , labels_ns ( label ) , label , profile ) {
/*
* no target as it has failed to be found or built
*
* change_hat uses probing and should not log failures
* related to missing hats
*/
/* TODO: get rid of GLOBAL_ROOT_UID */
if ( count > 1 | | COMPLAIN_MODE ( profile ) ) {
aa_audit_file ( profile , & nullperms , OP_CHANGE_HAT ,
AA_MAY_CHANGEHAT , name , NULL , NULL ,
GLOBAL_ROOT_UID , info , error ) ;
}
}
return ERR_PTR ( error ) ;
build :
new = fn_label_build_in_ns ( label , profile , GFP_KERNEL ,
build_change_hat ( profile , name , sibling ) ,
aa_get_label ( & profile - > label ) ) ;
if ( ! new ) {
info = " label build failed " ;
error = - ENOMEM ;
goto fail ;
} /* else if (IS_ERR) build_change_hat has logged error so return new */
return new ;
2010-07-29 14:48:06 -07:00
}
/**
* aa_change_hat - change hat to / from subprofile
* @ hats : vector of hat names to try changing into ( MAYBE NULL if @ count = = 0 )
* @ count : number of hat names in @ hats
* @ token : magic value to validate the hat change
2017-06-09 11:36:48 -07:00
* @ flags : flags affecting behavior of the change
2010-07-29 14:48:06 -07:00
*
2017-06-09 17:01:43 -07:00
* Returns % 0 on success , error otherwise .
*
2010-07-29 14:48:06 -07:00
* Change to the first profile specified in @ hats that exists , and store
* the @ hat_magic in the current task context . If the count = = 0 and the
* @ token matches that stored in the current task context , return to the
* top level profile .
*
2017-06-09 17:01:43 -07:00
* change_hat only applies to profiles in the current ns , and each profile
* in the ns must make the same transition otherwise change_hat will fail .
2010-07-29 14:48:06 -07:00
*/
2017-06-09 11:36:48 -07:00
int aa_change_hat ( const char * hats [ ] , int count , u64 token , int flags )
2010-07-29 14:48:06 -07:00
{
const struct cred * cred ;
2017-01-16 00:43:00 -08:00
struct aa_task_ctx * ctx ;
2017-06-09 17:01:43 -07:00
struct aa_label * label , * previous , * new = NULL , * target = NULL ;
struct aa_profile * profile ;
2017-05-29 12:19:39 -07:00
struct aa_perms perms = { } ;
2017-06-09 17:01:43 -07:00
const char * info = NULL ;
2010-07-29 14:48:06 -07:00
int error = 0 ;
2012-04-12 16:47:51 -05:00
/*
* Fail explicitly requested domain transitions if no_new_privs .
* There is no exception for unconfined as change_hat is not
* available .
*/
2017-06-09 17:01:43 -07:00
if ( task_no_new_privs ( current ) ) {
/* not an apparmor denial per se, so don't log it */
AA_DEBUG ( " no_new_privs - change_hat denied " ) ;
2012-04-12 16:47:51 -05:00
return - EPERM ;
2017-06-09 17:01:43 -07:00
}
2012-04-12 16:47:51 -05:00
2010-07-29 14:48:06 -07:00
/* released below */
cred = get_current_cred ( ) ;
2017-01-16 00:43:00 -08:00
ctx = cred_ctx ( cred ) ;
2017-06-09 08:14:28 -07:00
label = aa_get_newest_cred_label ( cred ) ;
2017-06-09 17:01:43 -07:00
previous = aa_get_newest_label ( ctx - > previous ) ;
2010-07-29 14:48:06 -07:00
2017-06-09 08:14:28 -07:00
if ( unconfined ( label ) ) {
2017-06-09 17:01:43 -07:00
info = " unconfined can not change_hat " ;
2010-07-29 14:48:06 -07:00
error = - EPERM ;
2017-06-09 17:01:43 -07:00
goto fail ;
2010-07-29 14:48:06 -07:00
}
if ( count ) {
2017-06-09 17:01:43 -07:00
new = change_hat ( label , hats , count , flags ) ;
AA_BUG ( ! new ) ;
if ( IS_ERR ( new ) ) {
error = PTR_ERR ( new ) ;
new = NULL ;
/* already audited */
goto out ;
2010-07-29 14:48:06 -07:00
}
2017-06-09 17:01:43 -07:00
error = may_change_ptraced_domain ( new , & info ) ;
if ( error )
goto fail ;
2010-07-29 14:48:06 -07:00
2017-06-09 17:01:43 -07:00
if ( flags & AA_CHANGE_TEST )
goto out ;
target = new ;
error = aa_set_current_hat ( new , token ) ;
if ( error = = - EACCES )
/* kill task in case of brute force attacks */
goto kill ;
} else if ( previous & & ! ( flags & AA_CHANGE_TEST ) ) {
/* Return to saved label. Kill task if restore fails
2010-07-29 14:48:06 -07:00
* to avoid brute force attacks
*/
2017-06-09 17:01:43 -07:00
target = previous ;
2017-06-09 08:14:28 -07:00
error = aa_restore_previous_label ( token ) ;
2017-06-09 17:01:43 -07:00
if ( error ) {
if ( error = = - EACCES )
goto kill ;
goto fail ;
}
} /* else ignore @flags && restores when there is no saved profile */
2010-07-29 14:48:06 -07:00
out :
2017-06-09 17:01:43 -07:00
aa_put_label ( new ) ;
aa_put_label ( previous ) ;
2017-06-09 08:14:28 -07:00
aa_put_label ( label ) ;
2010-07-29 14:48:06 -07:00
put_cred ( cred ) ;
return error ;
2017-06-09 17:01:43 -07:00
kill :
info = " failed token match " ;
perms . kill = AA_MAY_CHANGEHAT ;
fail :
fn_for_each_in_ns ( label , profile ,
aa_audit_file ( profile , & perms , OP_CHANGE_HAT ,
AA_MAY_CHANGEHAT , NULL , NULL , target ,
GLOBAL_ROOT_UID , info , error ) ) ;
goto out ;
2010-07-29 14:48:06 -07:00
}
2017-06-09 17:01:43 -07:00
2017-06-09 17:07:58 -07:00
static int change_profile_perms_wrapper ( const char * op , const char * name ,
struct aa_profile * profile ,
struct aa_label * target , bool stack ,
u32 request , struct aa_perms * perms )
{
const char * info = NULL ;
int error = 0 ;
/*
* Fail explicitly requested domain transitions when no_new_privs
* and not unconfined OR the transition results in a stack on
* the current label .
* Stacking domain transitions and transitions from unconfined are
* allowed even when no_new_privs is set because this aways results
* in a reduction of permissions .
*/
if ( task_no_new_privs ( current ) & & ! stack & &
! profile_unconfined ( profile ) & &
! aa_label_is_subset ( target , & profile - > label ) ) {
info = " no new privs " ;
error = - EPERM ;
}
if ( ! error )
error = change_profile_perms ( profile , target , stack , request ,
profile - > file . start , perms ) ;
if ( error )
error = aa_audit_file ( profile , perms , op , request , name ,
NULL , target , GLOBAL_ROOT_UID , info ,
error ) ;
return error ;
}
2017-06-09 17:01:43 -07:00
2010-07-29 14:48:06 -07:00
/**
* aa_change_profile - perform a one - way profile transition
2017-01-16 00:43:06 -08:00
* @ fqname : name of profile may include namespace ( NOT NULL )
2010-07-29 14:48:06 -07:00
* @ onexec : whether this transition is to take place immediately or at exec
2017-06-09 11:36:48 -07:00
* @ flags : flags affecting change behavior
2010-07-29 14:48:06 -07:00
*
* Change to new profile @ name . Unlike with hats , there is no way
* to change back . If @ name isn ' t specified the current profile name is
* used .
* If @ onexec then the transition is delayed until
* the next exec .
*
* Returns % 0 on success , error otherwise .
*/
2017-06-09 11:36:48 -07:00
int aa_change_profile ( const char * fqname , int flags )
2010-07-29 14:48:06 -07:00
{
2017-06-09 17:07:58 -07:00
struct aa_label * label , * new = NULL , * target = NULL ;
struct aa_profile * profile ;
2017-05-29 12:19:39 -07:00
struct aa_perms perms = { } ;
2017-06-09 17:07:58 -07:00
const char * info = NULL ;
const char * auditname = fqname ; /* retain leading & if stack */
bool stack = flags & AA_CHANGE_STACK ;
2017-01-16 00:43:01 -08:00
int error = 0 ;
2017-06-09 17:07:58 -07:00
char * op ;
2010-07-29 14:48:06 -07:00
u32 request ;
2017-01-16 00:43:06 -08:00
if ( ! fqname | | ! * fqname ) {
AA_DEBUG ( " no profile name " ) ;
2010-07-29 14:48:06 -07:00
return - EINVAL ;
2017-01-16 00:43:06 -08:00
}
2010-07-29 14:48:06 -07:00
2017-06-09 11:36:48 -07:00
if ( flags & AA_CHANGE_ONEXEC ) {
2010-07-29 14:48:06 -07:00
request = AA_MAY_ONEXEC ;
2017-06-09 17:07:58 -07:00
if ( stack )
op = OP_STACK_ONEXEC ;
else
op = OP_CHANGE_ONEXEC ;
2010-07-29 14:48:06 -07:00
} else {
request = AA_MAY_CHANGE_PROFILE ;
2017-06-09 17:07:58 -07:00
if ( stack )
op = OP_STACK ;
else
op = OP_CHANGE_PROFILE ;
2010-07-29 14:48:06 -07:00
}
2017-06-09 17:07:58 -07:00
label = aa_get_current_label ( ) ;
2010-07-29 14:48:06 -07:00
2017-06-09 17:07:58 -07:00
if ( * fqname = = ' & ' ) {
stack = true ;
/* don't have label_parse() do stacking */
fqname + + ;
2012-04-12 16:47:51 -05:00
}
2017-06-09 17:07:58 -07:00
target = aa_label_parse ( label , fqname , GFP_KERNEL , true , false ) ;
if ( IS_ERR ( target ) ) {
struct aa_profile * tprofile ;
2012-04-12 16:47:51 -05:00
2017-06-09 17:07:58 -07:00
info = " label not found " ;
error = PTR_ERR ( target ) ;
target = NULL ;
/*
* TODO : fixme using labels_profile is not right - do profile
* per complain profile
*/
2017-06-09 11:36:48 -07:00
if ( ( flags & AA_CHANGE_TEST ) | |
2017-06-09 17:07:58 -07:00
! COMPLAIN_MODE ( labels_profile ( label ) ) )
2010-07-29 14:48:06 -07:00
goto audit ;
/* released below */
2017-06-09 17:07:58 -07:00
tprofile = aa_new_null_profile ( labels_profile ( label ) , false ,
fqname , GFP_KERNEL ) ;
if ( ! tprofile ) {
2010-07-29 14:48:06 -07:00
info = " failed null profile create " ;
error = - ENOMEM ;
goto audit ;
}
2017-06-09 17:07:58 -07:00
target = & tprofile - > label ;
goto check ;
2010-07-29 14:48:06 -07:00
}
2017-06-09 17:07:58 -07:00
/*
* self directed transitions only apply to current policy ns
* TODO : currently requiring perms for stacking and straight change
* stacking doesn ' t strictly need this . Determine how much
* we want to loosen this restriction for stacking
*
* if ( ! stack ) {
*/
error = fn_for_each_in_ns ( label , profile ,
change_profile_perms_wrapper ( op , auditname ,
profile , target , stack ,
request , & perms ) ) ;
if ( error )
/* auditing done in change_profile_perms_wrapper */
goto out ;
2017-01-16 00:43:06 -08:00
2017-06-09 17:07:58 -07:00
/* } */
check :
2010-07-29 14:48:06 -07:00
/* check if tracing task is allowed to trace target domain */
2017-06-09 17:07:58 -07:00
error = may_change_ptraced_domain ( target , & info ) ;
if ( error & & ! fn_for_each_in_ns ( label , profile ,
COMPLAIN_MODE ( profile ) ) )
2010-07-29 14:48:06 -07:00
goto audit ;
2017-06-09 17:07:58 -07:00
/* TODO: add permission check to allow this
* if ( ( flags & AA_CHANGE_ONEXEC ) & & ! current_is_single_threaded ( ) ) {
* info = " not a single threaded task " ;
* error = - EACCES ;
* goto audit ;
* }
*/
2017-06-09 11:36:48 -07:00
if ( flags & AA_CHANGE_TEST )
2017-06-09 17:07:58 -07:00
goto out ;
2010-07-29 14:48:06 -07:00
2017-06-09 17:07:58 -07:00
if ( ! ( flags & AA_CHANGE_ONEXEC ) ) {
/* only transition profiles in the current ns */
if ( stack )
new = aa_label_merge ( label , target , GFP_KERNEL ) ;
else
new = fn_label_build_in_ns ( label , profile , GFP_KERNEL ,
aa_get_label ( target ) ,
aa_get_label ( & profile - > label ) ) ;
if ( IS_ERR_OR_NULL ( new ) ) {
info = " failed to build target label " ;
error = PTR_ERR ( new ) ;
new = NULL ;
perms . allow = 0 ;
goto audit ;
}
error = aa_replace_current_label ( new ) ;
} else
/* full transition will be built in exec path */
error = aa_set_current_onexec ( target , stack ) ;
2010-07-29 14:48:06 -07:00
audit :
2017-06-09 17:07:58 -07:00
error = fn_for_each_in_ns ( label , profile ,
aa_audit_file ( profile , & perms , op , request , auditname ,
NULL , new ? new : target ,
GLOBAL_ROOT_UID , info , error ) ) ;
2010-07-29 14:48:06 -07:00
2017-06-09 17:07:58 -07:00
out :
aa_put_label ( new ) ;
aa_put_label ( target ) ;
2017-06-09 08:14:28 -07:00
aa_put_label ( label ) ;
2010-07-29 14:48:06 -07:00
return error ;
}