2019-06-01 11:08:55 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2010-07-30 01:48:04 +04:00
/*
* AppArmor security module
*
* This file contains AppArmor mediation of files
*
* Copyright ( C ) 1998 - 2008 Novell / SUSE
* Copyright 2009 - 2010 Canonical Ltd .
*/
2017-06-09 21:58:42 +03:00
# include <linux/tty.h>
# include <linux/fdtable.h>
# include <linux/file.h>
2021-01-21 16:19:44 +03:00
# include <linux/fs.h>
# include <linux/mount.h>
2017-06-09 21:58:42 +03:00
2010-07-30 01:48:04 +04:00
# include "include/apparmor.h"
# include "include/audit.h"
2017-10-11 11:04:48 +03:00
# include "include/cred.h"
2010-07-30 01:48:04 +04:00
# include "include/file.h"
# include "include/match.h"
2017-07-19 09:18:33 +03:00
# include "include/net.h"
2010-07-30 01:48:04 +04:00
# include "include/path.h"
# include "include/policy.h"
2017-06-10 00:59:51 +03:00
# include "include/label.h"
2010-07-30 01:48:04 +04:00
2017-05-27 01:07:22 +03:00
static u32 map_mask_to_chr_mask ( u32 mask )
{
u32 m = mask & PERMS_CHRS_MASK ;
if ( mask & AA_MAY_GETATTR )
m | = MAY_READ ;
if ( mask & ( AA_MAY_SETATTR | AA_MAY_CHMOD | AA_MAY_CHOWN ) )
m | = MAY_WRITE ;
return m ;
}
2010-07-30 01:48:04 +04:00
/**
* file_audit_cb - call back for file specific audit fields
* @ ab : audit_buffer ( NOT NULL )
* @ va : audit struct to audit values of ( NOT NULL )
*/
static void file_audit_cb ( struct audit_buffer * ab , void * va )
{
struct common_audit_data * sa = va ;
2012-02-08 04:33:13 +04:00
kuid_t fsuid = current_fsuid ( ) ;
2020-07-13 22:51:59 +03:00
char str [ 10 ] ;
2010-07-30 01:48:04 +04:00
2017-05-29 22:16:04 +03:00
if ( aad ( sa ) - > request & AA_AUDIT_FILE_MASK ) {
2020-07-13 22:51:59 +03:00
aa_perm_mask_to_str ( str , sizeof ( str ) , aa_file_perm_chrs ,
map_mask_to_chr_mask ( aad ( sa ) - > request ) ) ;
audit_log_format ( ab , " requested_mask= \" %s \" " , str ) ;
2010-07-30 01:48:04 +04:00
}
2017-05-29 22:16:04 +03:00
if ( aad ( sa ) - > denied & AA_AUDIT_FILE_MASK ) {
2020-07-13 22:51:59 +03:00
aa_perm_mask_to_str ( str , sizeof ( str ) , aa_file_perm_chrs ,
map_mask_to_chr_mask ( aad ( sa ) - > denied ) ) ;
audit_log_format ( ab , " denied_mask= \" %s \" " , str ) ;
2010-07-30 01:48:04 +04:00
}
2017-05-29 22:16:04 +03:00
if ( aad ( sa ) - > request & AA_AUDIT_FILE_MASK ) {
2012-02-08 04:33:13 +04:00
audit_log_format ( ab , " fsuid=%d " ,
from_kuid ( & init_user_ns , fsuid ) ) ;
audit_log_format ( ab , " ouid=%d " ,
2017-01-16 11:43:02 +03:00
from_kuid ( & init_user_ns , aad ( sa ) - > fs . ouid ) ) ;
2010-07-30 01:48:04 +04:00
}
2017-06-10 01:48:20 +03:00
if ( aad ( sa ) - > peer ) {
audit_log_format ( ab , " target= " ) ;
aa_label_xaudit ( ab , labels_ns ( aad ( sa ) - > label ) , aad ( sa ) - > peer ,
2019-04-05 16:34:58 +03:00
FLAG_VIEW_SUBNS , GFP_KERNEL ) ;
2017-06-10 01:48:20 +03:00
} else if ( aad ( sa ) - > fs . target ) {
2010-07-30 01:48:04 +04:00
audit_log_format ( ab , " target= " ) ;
2017-01-16 11:43:02 +03:00
audit_log_untrustedstring ( ab , aad ( sa ) - > fs . target ) ;
2010-07-30 01:48:04 +04:00
}
}
/**
* aa_audit_file - handle the auditing of file operations
* @ profile : the profile being enforced ( NOT NULL )
* @ perms : the permissions computed for the request ( NOT NULL )
* @ op : operation being mediated
* @ request : permissions requested
* @ name : name of object being mediated ( MAYBE NULL )
* @ target : name of target ( MAYBE NULL )
2017-06-10 01:48:20 +03:00
* @ tlabel : target label ( MAY BE NULL )
2010-07-30 01:48:04 +04:00
* @ ouid : object uid
* @ info : extra information message ( MAYBE NULL )
* @ error : 0 if operation allowed else failure error code
*
* Returns : % 0 or error on failure
*/
2017-05-29 22:19:39 +03:00
int aa_audit_file ( struct aa_profile * profile , struct aa_perms * perms ,
2017-01-16 11:43:02 +03:00
const char * op , u32 request , const char * name ,
2017-06-10 01:48:20 +03:00
const char * target , struct aa_label * tlabel ,
kuid_t ouid , const char * info , int error )
2010-07-30 01:48:04 +04:00
{
int type = AUDIT_APPARMOR_AUTO ;
2017-01-16 11:43:02 +03:00
DEFINE_AUDIT_DATA ( sa , LSM_AUDIT_DATA_TASK , op ) ;
sa . u . tsk = NULL ;
2017-05-29 22:16:04 +03:00
aad ( & sa ) - > request = request ;
2017-01-16 11:43:02 +03:00
aad ( & sa ) - > name = name ;
aad ( & sa ) - > fs . target = target ;
2017-06-10 01:48:20 +03:00
aad ( & sa ) - > peer = tlabel ;
2017-01-16 11:43:02 +03:00
aad ( & sa ) - > fs . ouid = ouid ;
aad ( & sa ) - > info = info ;
aad ( & sa ) - > error = error ;
2014-06-08 22:20:54 +04:00
sa . u . tsk = NULL ;
2017-01-16 11:43:02 +03:00
if ( likely ( ! aad ( & sa ) - > error ) ) {
2010-07-30 01:48:04 +04:00
u32 mask = perms - > audit ;
if ( unlikely ( AUDIT_MODE ( profile ) = = AUDIT_ALL ) )
mask = 0xffff ;
/* mask off perms that are not being force audited */
2017-05-29 22:16:04 +03:00
aad ( & sa ) - > request & = mask ;
2010-07-30 01:48:04 +04:00
2017-05-29 22:16:04 +03:00
if ( likely ( ! aad ( & sa ) - > request ) )
2010-07-30 01:48:04 +04:00
return 0 ;
type = AUDIT_APPARMOR_AUDIT ;
} else {
/* only report permissions that were denied */
2017-05-29 22:16:04 +03:00
aad ( & sa ) - > request = aad ( & sa ) - > request & ~ perms - > allow ;
AA_BUG ( ! aad ( & sa ) - > request ) ;
2010-07-30 01:48:04 +04:00
2017-05-29 22:16:04 +03:00
if ( aad ( & sa ) - > request & perms - > kill )
2010-07-30 01:48:04 +04:00
type = AUDIT_APPARMOR_KILL ;
/* quiet known rejects, assumes quiet and kill do not overlap */
2017-05-29 22:16:04 +03:00
if ( ( aad ( & sa ) - > request & perms - > quiet ) & &
2010-07-30 01:48:04 +04:00
AUDIT_MODE ( profile ) ! = AUDIT_NOQUIET & &
AUDIT_MODE ( profile ) ! = AUDIT_ALL )
2017-05-29 22:16:04 +03:00
aad ( & sa ) - > request & = ~ perms - > quiet ;
2010-07-30 01:48:04 +04:00
2017-05-29 22:16:04 +03:00
if ( ! aad ( & sa ) - > request )
2017-06-10 01:48:20 +03:00
return aad ( & sa ) - > error ;
2010-07-30 01:48:04 +04:00
}
2017-05-29 22:16:04 +03:00
aad ( & sa ) - > denied = aad ( & sa ) - > request & ~ perms - > allow ;
2017-01-16 11:43:02 +03:00
return aa_audit ( type , profile , & sa , file_audit_cb ) ;
2010-07-30 01:48:04 +04:00
}
2017-06-10 02:02:25 +03:00
/**
* is_deleted - test if a file has been completely unlinked
* @ dentry : dentry of file to test for deletion ( NOT NULL )
*
2020-04-28 14:52:21 +03:00
* Returns : true if deleted else false
2017-06-10 02:02:25 +03:00
*/
static inline bool is_deleted ( struct dentry * dentry )
{
if ( d_unlinked ( dentry ) & & d_backing_inode ( dentry ) - > i_nlink = = 0 )
2020-04-28 14:52:21 +03:00
return true ;
return false ;
2017-06-10 02:02:25 +03:00
}
static int path_name ( const char * op , struct aa_label * label ,
const struct path * path , int flags , char * buffer ,
const char * * name , struct path_cond * cond , u32 request )
{
struct aa_profile * profile ;
const char * info = NULL ;
int error ;
error = aa_path_name ( path , flags , buffer , name , & info ,
labels_profile ( label ) - > disconnected ) ;
if ( error ) {
fn_for_each_confined ( label , profile ,
aa_audit_file ( profile , & nullperms , op , request , * name ,
NULL , NULL , cond - > uid , info , error ) ) ;
return error ;
}
return 0 ;
}
2010-07-30 01:48:04 +04:00
/**
* map_old_perms - map old file perms layout to the new layout
* @ old : permission set in old mapping
*
* Returns : new permission mapping
*/
static u32 map_old_perms ( u32 old )
{
u32 new = old & 0xf ;
if ( old & MAY_READ )
2017-05-27 01:07:22 +03:00
new | = AA_MAY_GETATTR | AA_MAY_OPEN ;
2010-07-30 01:48:04 +04:00
if ( old & MAY_WRITE )
2017-05-27 01:07:22 +03:00
new | = AA_MAY_SETATTR | AA_MAY_CREATE | AA_MAY_DELETE |
AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_OPEN ;
2010-07-30 01:48:04 +04:00
if ( old & 0x10 )
new | = AA_MAY_LINK ;
/* the old mapping lock and link_subset flags where overlaid
* and use was determined by part of a pair that they were in
*/
if ( old & 0x20 )
new | = AA_MAY_LOCK | AA_LINK_SUBSET ;
if ( old & 0x40 ) /* AA_EXEC_MMAP */
new | = AA_EXEC_MMAP ;
return new ;
}
/**
2017-05-29 22:19:39 +03:00
* aa_compute_fperms - convert dfa compressed perms to internal perms
2010-07-30 01:48:04 +04:00
* @ dfa : dfa to compute perms for ( NOT NULL )
* @ state : state in dfa
* @ cond : conditions to consider ( NOT NULL )
*
* TODO : convert from dfa + state to permission entry , do computation conversion
* at load time .
*
* Returns : computed permission set
*/
2017-05-29 22:19:39 +03:00
struct aa_perms aa_compute_fperms ( struct aa_dfa * dfa , unsigned int state ,
struct path_cond * cond )
2010-07-30 01:48:04 +04:00
{
/* FIXME: change over to new dfa format
* currently file perms are encoded in the dfa , new format
* splits the permissions from the dfa . This mapping can be
* done at profile load
*/
2017-09-15 22:55:46 +03:00
struct aa_perms perms = { } ;
2010-07-30 01:48:04 +04:00
2012-02-08 04:33:13 +04:00
if ( uid_eq ( current_fsuid ( ) , cond - > uid ) ) {
2010-07-30 01:48:04 +04:00
perms . allow = map_old_perms ( dfa_user_allow ( dfa , state ) ) ;
perms . audit = map_old_perms ( dfa_user_audit ( dfa , state ) ) ;
perms . quiet = map_old_perms ( dfa_user_quiet ( dfa , state ) ) ;
perms . xindex = dfa_user_xindex ( dfa , state ) ;
} else {
perms . allow = map_old_perms ( dfa_other_allow ( dfa , state ) ) ;
perms . audit = map_old_perms ( dfa_other_audit ( dfa , state ) ) ;
perms . quiet = map_old_perms ( dfa_other_quiet ( dfa , state ) ) ;
perms . xindex = dfa_other_xindex ( dfa , state ) ;
}
2017-05-27 01:07:22 +03:00
perms . allow | = AA_MAY_GETATTR ;
2010-07-30 01:48:04 +04:00
/* change_profile wasn't determined by ownership in old mapping */
if ( ACCEPT_TABLE ( dfa ) [ state ] & 0x80000000 )
perms . allow | = AA_MAY_CHANGE_PROFILE ;
2012-03-27 15:14:33 +04:00
if ( ACCEPT_TABLE ( dfa ) [ state ] & 0x40000000 )
perms . allow | = AA_MAY_ONEXEC ;
2010-07-30 01:48:04 +04:00
return perms ;
}
/**
* aa_str_perms - find permission that match @ name
* @ dfa : to match against ( MAYBE NULL )
* @ state : state to start matching in
* @ name : string to match against dfa ( NOT NULL )
* @ cond : conditions to consider for permission set computation ( NOT NULL )
* @ perms : Returns - the permissions found when matching @ name
*
* Returns : the final state in @ dfa when beginning @ start and walking @ name
*/
unsigned int aa_str_perms ( struct aa_dfa * dfa , unsigned int start ,
const char * name , struct path_cond * cond ,
2017-05-29 22:19:39 +03:00
struct aa_perms * perms )
2010-07-30 01:48:04 +04:00
{
unsigned int state ;
state = aa_dfa_match ( dfa , start , name ) ;
2017-05-29 22:19:39 +03:00
* perms = aa_compute_fperms ( dfa , state , cond ) ;
2010-07-30 01:48:04 +04:00
return state ;
}
2017-06-10 02:02:25 +03:00
int __aa_path_perm ( const char * op , struct aa_profile * profile , const char * name ,
u32 request , struct path_cond * cond , int flags ,
struct aa_perms * perms )
2010-07-30 01:48:04 +04:00
{
2017-06-10 02:02:25 +03:00
int e = 0 ;
if ( profile_unconfined ( profile ) )
return 0 ;
aa_str_perms ( profile - > file . dfa , profile - > file . start , name , cond , perms ) ;
if ( request & ~ perms - > allow )
e = - EACCES ;
return aa_audit_file ( profile , perms , op , request , name , NULL , NULL ,
cond - > uid , NULL , e ) ;
}
static int profile_path_perm ( const char * op , struct aa_profile * profile ,
const struct path * path , char * buffer , u32 request ,
struct path_cond * cond , int flags ,
struct aa_perms * perms )
{
const char * name ;
int error ;
if ( profile_unconfined ( profile ) )
return 0 ;
error = path_name ( op , & profile - > label , path ,
flags | profile - > path_flags , buffer , & name , cond ,
request ) ;
if ( error )
return error ;
return __aa_path_perm ( op , profile , name , request , cond , flags ,
perms ) ;
2010-07-30 01:48:04 +04:00
}
/**
* aa_path_perm - do permissions check & audit for @ path
* @ op : operation being checked
2017-06-10 02:02:25 +03:00
* @ label : profile being enforced ( NOT NULL )
2010-07-30 01:48:04 +04:00
* @ path : path to check permissions of ( NOT NULL )
* @ flags : any additional path flags beyond what the profile specifies
* @ request : requested permissions
* @ cond : conditional info for this request ( NOT NULL )
*
* Returns : % 0 else error if access denied or other error
*/
2017-06-10 02:02:25 +03:00
int aa_path_perm ( const char * op , struct aa_label * label ,
2017-01-16 11:43:01 +03:00
const struct path * path , int flags , u32 request ,
struct path_cond * cond )
2010-07-30 01:48:04 +04:00
{
2017-05-29 22:19:39 +03:00
struct aa_perms perms = { } ;
2017-06-10 02:02:25 +03:00
struct aa_profile * profile ;
char * buffer = NULL ;
2010-07-30 01:48:04 +04:00
int error ;
2017-06-10 02:02:25 +03:00
flags | = PATH_DELEGATE_DELETED | ( S_ISDIR ( cond - > mode ) ? PATH_IS_DIR :
0 ) ;
2019-09-14 13:34:06 +03:00
buffer = aa_get_buffer ( false ) ;
2019-05-03 17:12:21 +03:00
if ( ! buffer )
return - ENOMEM ;
2017-06-10 02:02:25 +03:00
error = fn_for_each_confined ( label , profile ,
profile_path_perm ( op , profile , path , buffer , request ,
cond , flags , & perms ) ) ;
2019-05-03 17:12:21 +03:00
aa_put_buffer ( buffer ) ;
2010-07-30 01:48:04 +04:00
return error ;
}
/**
* xindex_is_subset - helper for aa_path_link
* @ link : link permission set
* @ target : target permission set
*
* test target x permissions are equal OR a subset of link x permissions
* this is done as part of the subset test , where a hardlink must have
* a subset of permissions that the target has .
*
2020-04-28 14:52:21 +03:00
* Returns : true if subset else false
2010-07-30 01:48:04 +04:00
*/
static inline bool xindex_is_subset ( u32 link , u32 target )
{
if ( ( ( link & ~ AA_X_UNSAFE ) ! = ( target & ~ AA_X_UNSAFE ) ) | |
( ( link & AA_X_UNSAFE ) & & ! ( target & AA_X_UNSAFE ) ) )
2020-04-28 14:52:21 +03:00
return false ;
2010-07-30 01:48:04 +04:00
2020-04-28 14:52:21 +03:00
return true ;
2010-07-30 01:48:04 +04:00
}
2017-06-10 02:06:21 +03:00
static int profile_path_link ( struct aa_profile * profile ,
const struct path * link , char * buffer ,
const struct path * target , char * buffer2 ,
struct path_cond * cond )
2010-07-30 01:48:04 +04:00
{
2017-06-10 02:06:21 +03:00
const char * lname , * tname = NULL ;
struct aa_perms lperms = { } , perms ;
const char * info = NULL ;
2010-07-30 01:48:04 +04:00
u32 request = AA_MAY_LINK ;
unsigned int state ;
int error ;
2017-06-10 02:06:21 +03:00
error = path_name ( OP_LINK , & profile - > label , link , profile - > path_flags ,
buffer , & lname , cond , AA_MAY_LINK ) ;
2010-07-30 01:48:04 +04:00
if ( error )
goto audit ;
/* buffer2 freed below, tname is pointer in buffer2 */
2017-06-10 02:06:21 +03:00
error = path_name ( OP_LINK , & profile - > label , target , profile - > path_flags ,
buffer2 , & tname , cond , AA_MAY_LINK ) ;
2010-07-30 01:48:04 +04:00
if ( error )
goto audit ;
error = - EACCES ;
/* aa_str_perms - handles the case of the dfa being NULL */
state = aa_str_perms ( profile - > file . dfa , profile - > file . start , lname ,
2017-06-10 02:06:21 +03:00
cond , & lperms ) ;
2010-07-30 01:48:04 +04:00
if ( ! ( lperms . allow & AA_MAY_LINK ) )
goto audit ;
/* test to see if target can be paired with link */
state = aa_dfa_null_transition ( profile - > file . dfa , state ) ;
2017-06-10 02:06:21 +03:00
aa_str_perms ( profile - > file . dfa , state , tname , cond , & perms ) ;
2010-07-30 01:48:04 +04:00
/* force audit/quiet masks for link are stored in the second entry
* in the link pair .
*/
lperms . audit = perms . audit ;
lperms . quiet = perms . quiet ;
lperms . kill = perms . kill ;
if ( ! ( perms . allow & AA_MAY_LINK ) ) {
info = " target restricted " ;
2017-06-10 02:06:21 +03:00
lperms = perms ;
2010-07-30 01:48:04 +04:00
goto audit ;
}
/* done if link subset test is not required */
if ( ! ( perms . allow & AA_LINK_SUBSET ) )
goto done_tests ;
2017-06-10 02:06:21 +03:00
/* Do link perm subset test requiring allowed permission on link are
* a subset of the allowed permissions on target .
2010-07-30 01:48:04 +04:00
*/
2017-06-10 02:06:21 +03:00
aa_str_perms ( profile - > file . dfa , profile - > file . start , tname , cond ,
2010-07-30 01:48:04 +04:00
& perms ) ;
/* AA_MAY_LINK is not considered in the subset test */
request = lperms . allow & ~ AA_MAY_LINK ;
lperms . allow & = perms . allow | AA_MAY_LINK ;
request | = AA_AUDIT_FILE_MASK & ( lperms . allow & ~ perms . allow ) ;
if ( request & ~ lperms . allow ) {
goto audit ;
} else if ( ( lperms . allow & MAY_EXEC ) & &
! xindex_is_subset ( lperms . xindex , perms . xindex ) ) {
lperms . allow & = ~ MAY_EXEC ;
request | = MAY_EXEC ;
info = " link not subset of target " ;
goto audit ;
}
done_tests :
error = 0 ;
audit :
2017-06-10 02:06:21 +03:00
return aa_audit_file ( profile , & lperms , OP_LINK , request , lname , tname ,
NULL , cond - > uid , info , error ) ;
}
/**
* aa_path_link - Handle hard link permission check
* @ label : the label being enforced ( NOT NULL )
* @ old_dentry : the target dentry ( NOT NULL )
* @ new_dir : directory the new link will be created in ( NOT NULL )
* @ new_dentry : the link being created ( NOT NULL )
*
* Handle the permission test for a link & target pair . Permission
* is encoded as a pair where the link permission is determined
* first , and if allowed , the target is tested . The target test
* is done from the point of the link match ( not start of DFA )
* making the target permission dependent on the link permission match .
*
* The subset test if required forces that permissions granted
* on link are a subset of the permission granted to target .
*
* Returns : % 0 if allowed else error
*/
int aa_path_link ( struct aa_label * label , struct dentry * old_dentry ,
const struct path * new_dir , struct dentry * new_dentry )
{
2017-06-20 07:50:36 +03:00
struct path link = { . mnt = new_dir - > mnt , . dentry = new_dentry } ;
struct path target = { . mnt = new_dir - > mnt , . dentry = old_dentry } ;
2017-06-10 02:06:21 +03:00
struct path_cond cond = {
d_backing_inode ( old_dentry ) - > i_uid ,
d_backing_inode ( old_dentry ) - > i_mode
} ;
char * buffer = NULL , * buffer2 = NULL ;
struct aa_profile * profile ;
int error ;
/* buffer freed below, lname is pointer in buffer */
2019-09-14 13:34:06 +03:00
buffer = aa_get_buffer ( false ) ;
buffer2 = aa_get_buffer ( false ) ;
2019-05-03 17:12:21 +03:00
error = - ENOMEM ;
if ( ! buffer | | ! buffer2 )
goto out ;
2017-06-10 02:06:21 +03:00
error = fn_for_each_confined ( label , profile ,
profile_path_link ( profile , & link , buffer , & target ,
buffer2 , & cond ) ) ;
2019-05-03 17:12:21 +03:00
out :
aa_put_buffer ( buffer ) ;
aa_put_buffer ( buffer2 ) ;
2010-07-30 01:48:04 +04:00
return error ;
}
2017-06-10 02:19:02 +03:00
static void update_file_ctx ( struct aa_file_ctx * fctx , struct aa_label * label ,
u32 request )
{
struct aa_label * l , * old ;
/* update caching of label on file_ctx */
spin_lock ( & fctx - > lock ) ;
old = rcu_dereference_protected ( fctx - > label ,
2018-10-03 08:39:01 +03:00
lockdep_is_held ( & fctx - > lock ) ) ;
2017-06-10 02:19:02 +03:00
l = aa_label_merge ( old , label , GFP_ATOMIC ) ;
if ( l ) {
if ( l ! = old ) {
rcu_assign_pointer ( fctx - > label , l ) ;
aa_put_label ( old ) ;
} else
aa_put_label ( l ) ;
fctx - > allow | = request ;
}
spin_unlock ( & fctx - > lock ) ;
}
static int __file_path_perm ( const char * op , struct aa_label * label ,
struct aa_label * flabel , struct file * file ,
2019-09-14 13:34:06 +03:00
u32 request , u32 denied , bool in_atomic )
2017-06-10 02:19:02 +03:00
{
struct aa_profile * profile ;
struct aa_perms perms = { } ;
struct path_cond cond = {
2021-01-21 16:19:44 +03:00
. uid = i_uid_into_mnt ( file_mnt_user_ns ( file ) , file_inode ( file ) ) ,
2017-06-10 02:19:02 +03:00
. mode = file_inode ( file ) - > i_mode
} ;
char * buffer ;
int flags , error ;
/* revalidation due to label out of date. No revocation at this time */
if ( ! denied & & aa_label_is_subset ( flabel , label ) )
/* TODO: check for revocation on stale profiles */
return 0 ;
flags = PATH_DELEGATE_DELETED | ( S_ISDIR ( cond . mode ) ? PATH_IS_DIR : 0 ) ;
2019-09-14 13:34:06 +03:00
buffer = aa_get_buffer ( in_atomic ) ;
2019-05-03 17:12:21 +03:00
if ( ! buffer )
return - ENOMEM ;
2017-06-10 02:19:02 +03:00
/* check every profile in task label not in current cache */
error = fn_for_each_not_in_set ( flabel , label , profile ,
profile_path_perm ( op , profile , & file - > f_path , buffer ,
request , & cond , flags , & perms ) ) ;
if ( denied & & ! error ) {
/*
* check every profile in file label that was not tested
* in the initial check above .
*
* TODO : cache full perms so this only happens because of
* conditionals
* TODO : don ' t audit here
*/
if ( label = = flabel )
error = fn_for_each ( label , profile ,
profile_path_perm ( op , profile , & file - > f_path ,
buffer , request , & cond , flags ,
& perms ) ) ;
else
error = fn_for_each_not_in_set ( label , flabel , profile ,
profile_path_perm ( op , profile , & file - > f_path ,
buffer , request , & cond , flags ,
& perms ) ) ;
}
if ( ! error )
update_file_ctx ( file_ctx ( file ) , label , request ) ;
2019-05-03 17:12:21 +03:00
aa_put_buffer ( buffer ) ;
2017-06-10 02:19:02 +03:00
return error ;
}
2017-07-19 09:18:33 +03:00
static int __file_sock_perm ( const char * op , struct aa_label * label ,
struct aa_label * flabel , struct file * file ,
u32 request , u32 denied )
{
struct socket * sock = ( struct socket * ) file - > private_data ;
int error ;
AA_BUG ( ! sock ) ;
/* revalidation due to label out of date. No revocation at this time */
if ( ! denied & & aa_label_is_subset ( flabel , label ) )
return 0 ;
/* TODO: improve to skip profiles cached in flabel */
error = aa_sock_file_perm ( label , op , request , sock ) ;
if ( denied ) {
/* TODO: improve to skip profiles checked above */
/* check every profile in file label to is cached */
last_error ( error , aa_sock_file_perm ( flabel , op , request , sock ) ) ;
}
if ( ! error )
update_file_ctx ( file_ctx ( file ) , label , request ) ;
return error ;
}
2010-07-30 01:48:04 +04:00
/**
* aa_file_perm - do permission revalidation check & audit for @ file
* @ op : operation being checked
2017-06-10 00:59:51 +03:00
* @ label : label being enforced ( NOT NULL )
2010-07-30 01:48:04 +04:00
* @ file : file to revalidate access permissions on ( NOT NULL )
* @ request : requested permissions
2019-09-14 13:34:06 +03:00
* @ in_atomic : whether allocations need to be done in atomic context
2010-07-30 01:48:04 +04:00
*
* Returns : % 0 if access allowed else error
*/
2017-06-10 00:59:51 +03:00
int aa_file_perm ( const char * op , struct aa_label * label , struct file * file ,
2019-09-14 13:34:06 +03:00
u32 request , bool in_atomic )
2010-07-30 01:48:04 +04:00
{
2017-06-10 00:59:51 +03:00
struct aa_file_ctx * fctx ;
struct aa_label * flabel ;
u32 denied ;
int error = 0 ;
AA_BUG ( ! label ) ;
AA_BUG ( ! file ) ;
fctx = file_ctx ( file ) ;
rcu_read_lock ( ) ;
2019-12-18 22:04:07 +03:00
flabel = rcu_dereference ( fctx - > label ) ;
2017-06-10 00:59:51 +03:00
AA_BUG ( ! flabel ) ;
/* revalidate access, if task is unconfined, or the cached cred
* doesn ' t match or if the request is for more permissions than
* was granted .
*
* Note : the test for ! unconfined ( flabel ) is to handle file
* delegation from unconfined tasks
*/
denied = request & ~ fctx - > allow ;
if ( unconfined ( label ) | | unconfined ( flabel ) | |
2019-12-18 22:04:07 +03:00
( ! denied & & aa_label_is_subset ( flabel , label ) ) ) {
rcu_read_unlock ( ) ;
2017-06-10 00:59:51 +03:00
goto done ;
2019-12-18 22:04:07 +03:00
}
2017-06-10 00:59:51 +03:00
2019-12-18 22:04:07 +03:00
flabel = aa_get_newest_label ( flabel ) ;
rcu_read_unlock ( ) ;
2017-06-10 00:59:51 +03:00
/* TODO: label cross check */
if ( file - > f_path . mnt & & path_mediated_fs ( file - > f_path . dentry ) )
2017-06-10 02:19:02 +03:00
error = __file_path_perm ( op , label , flabel , file , request ,
2019-09-14 13:34:06 +03:00
denied , in_atomic ) ;
2010-07-30 01:48:04 +04:00
2017-07-19 09:18:33 +03:00
else if ( S_ISSOCK ( file_inode ( file ) - > i_mode ) )
error = __file_sock_perm ( op , label , flabel , file , request ,
denied ) ;
2019-09-14 08:24:23 +03:00
aa_put_label ( flabel ) ;
2019-12-18 22:04:07 +03:00
done :
2017-06-10 00:59:51 +03:00
return error ;
2010-07-30 01:48:04 +04:00
}
2017-06-09 21:58:42 +03:00
2017-06-09 18:14:28 +03:00
static void revalidate_tty ( struct aa_label * label )
2017-06-09 21:58:42 +03:00
{
struct tty_struct * tty ;
int drop_tty = 0 ;
tty = get_current_tty ( ) ;
if ( ! tty )
return ;
spin_lock ( & tty - > files_lock ) ;
if ( ! list_empty ( & tty - > tty_files ) ) {
struct tty_file_private * file_priv ;
struct file * file ;
/* TODO: Revalidate access to controlling tty. */
file_priv = list_first_entry ( & tty - > tty_files ,
struct tty_file_private , list ) ;
file = file_priv - > file ;
2019-09-14 13:34:06 +03:00
if ( aa_file_perm ( OP_INHERIT , label , file , MAY_READ | MAY_WRITE ,
IN_ATOMIC ) )
2017-06-09 21:58:42 +03:00
drop_tty = 1 ;
}
spin_unlock ( & tty - > files_lock ) ;
tty_kref_put ( tty ) ;
if ( drop_tty )
no_tty ( ) ;
}
static int match_file ( const void * p , struct file * file , unsigned int fd )
{
2017-06-09 18:14:28 +03:00
struct aa_label * label = ( struct aa_label * ) p ;
2017-06-09 21:58:42 +03:00
2019-09-14 13:34:06 +03:00
if ( aa_file_perm ( OP_INHERIT , label , file , aa_map_file_to_perms ( file ) ,
IN_ATOMIC ) )
2017-06-09 21:58:42 +03:00
return fd + 1 ;
return 0 ;
}
/* based on selinux's flush_unauthorized_files */
void aa_inherit_files ( const struct cred * cred , struct files_struct * files )
{
2017-06-09 18:14:28 +03:00
struct aa_label * label = aa_get_newest_cred_label ( cred ) ;
2017-06-09 21:58:42 +03:00
struct file * devnull = NULL ;
unsigned int n ;
2017-06-09 18:14:28 +03:00
revalidate_tty ( label ) ;
2017-06-09 21:58:42 +03:00
/* Revalidate access to inherited open files. */
2017-06-09 18:14:28 +03:00
n = iterate_fd ( files , 0 , match_file , label ) ;
2017-06-09 21:58:42 +03:00
if ( ! n ) /* none found? */
goto out ;
devnull = dentry_open ( & aa_null , O_RDWR , cred ) ;
if ( IS_ERR ( devnull ) )
devnull = NULL ;
/* replace all the matching ones with this */
do {
replace_fd ( n - 1 , devnull , 0 ) ;
2017-06-09 18:14:28 +03:00
} while ( ( n = iterate_fd ( files , n , match_file , label ) ) ! = 0 ) ;
2017-06-09 21:58:42 +03:00
if ( devnull )
fput ( devnull ) ;
out :
2017-06-09 18:14:28 +03:00
aa_put_label ( label ) ;
2017-06-09 21:58:42 +03:00
}