2010-07-30 01:48:07 +04:00
/*
* AppArmor security module
*
* This file contains AppArmor LSM hooks .
*
* Copyright ( C ) 1998 - 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 .
*/
2015-05-03 01:10:46 +03:00
# include <linux/lsm_hooks.h>
2010-07-30 01:48:07 +04:00
# include <linux/moduleparam.h>
# include <linux/mm.h>
# include <linux/mman.h>
# include <linux/mount.h>
# include <linux/namei.h>
# include <linux/ptrace.h>
# include <linux/ctype.h>
# include <linux/sysctl.h>
# include <linux/audit.h>
userns: security: make capabilities relative to the user namespace
- Introduce ns_capable to test for a capability in a non-default
user namespace.
- Teach cap_capable to handle capabilities in a non-default
user namespace.
The motivation is to get to the unprivileged creation of new
namespaces. It looks like this gets us 90% of the way there, with
only potential uid confusion issues left.
I still need to handle getting all caps after creation but otherwise I
think I have a good starter patch that achieves all of your goals.
Changelog:
11/05/2010: [serge] add apparmor
12/14/2010: [serge] fix capabilities to created user namespaces
Without this, if user serge creates a user_ns, he won't have
capabilities to the user_ns he created. THis is because we
were first checking whether his effective caps had the caps
he needed and returning -EPERM if not, and THEN checking whether
he was the creator. Reverse those checks.
12/16/2010: [serge] security_real_capable needs ns argument in !security case
01/11/2011: [serge] add task_ns_capable helper
01/11/2011: [serge] add nsown_capable() helper per Bastian Blank suggestion
02/16/2011: [serge] fix a logic bug: the root user is always creator of
init_user_ns, but should not always have capabilities to
it! Fix the check in cap_capable().
02/21/2011: Add the required user_ns parameter to security_capable,
fixing a compile failure.
02/23/2011: Convert some macros to functions as per akpm comments. Some
couldn't be converted because we can't easily forward-declare
them (they are inline if !SECURITY, extern if SECURITY). Add
a current_user_ns function so we can use it in capability.h
without #including cred.h. Move all forward declarations
together to the top of the #ifdef __KERNEL__ section, and use
kernel-doc format.
02/23/2011: Per dhowells, clean up comment in cap_capable().
02/23/2011: Per akpm, remove unreachable 'return -EPERM' in cap_capable.
(Original written and signed off by Eric; latest, modified version
acked by him)
[akpm@linux-foundation.org: fix build]
[akpm@linux-foundation.org: export current_user_ns() for ecryptfs]
[serge.hallyn@canonical.com: remove unneeded extra argument in selinux's task_has_capability]
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: Serge E. Hallyn <serge.hallyn@canonical.com>
Acked-by: "Eric W. Biederman" <ebiederm@xmission.com>
Acked-by: Daniel Lezcano <daniel.lezcano@free.fr>
Acked-by: David Howells <dhowells@redhat.com>
Cc: James Morris <jmorris@namei.org>
Signed-off-by: Serge E. Hallyn <serge.hallyn@canonical.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-03-24 02:43:17 +03:00
# include <linux/user_namespace.h>
2010-07-30 01:48:07 +04:00
# include <net/sock.h>
# include "include/apparmor.h"
# include "include/apparmorfs.h"
# include "include/audit.h"
# include "include/capability.h"
# include "include/context.h"
# include "include/file.h"
# include "include/ipc.h"
# include "include/path.h"
# include "include/policy.h"
# include "include/procattr.h"
/* Flag indicating whether initialization completed */
int apparmor_initialized __initdata ;
/*
* LSM hook functions
*/
/*
* free the associated aa_task_cxt and put its profiles
*/
static void apparmor_cred_free ( struct cred * cred )
{
2013-02-27 15:43:40 +04:00
aa_free_task_context ( cred_cxt ( cred ) ) ;
cred_cxt ( cred ) = NULL ;
2010-07-30 01:48:07 +04:00
}
/*
* allocate the apparmor part of blank credentials
*/
static int apparmor_cred_alloc_blank ( struct cred * cred , gfp_t gfp )
{
/* freed by apparmor_cred_free */
struct aa_task_cxt * cxt = aa_alloc_task_context ( gfp ) ;
if ( ! cxt )
return - ENOMEM ;
2013-02-27 15:43:40 +04:00
cred_cxt ( cred ) = cxt ;
2010-07-30 01:48:07 +04:00
return 0 ;
}
/*
* prepare new aa_task_cxt for modification by prepare_cred block
*/
static int apparmor_cred_prepare ( struct cred * new , const struct cred * old ,
gfp_t gfp )
{
/* freed by apparmor_cred_free */
struct aa_task_cxt * cxt = aa_alloc_task_context ( gfp ) ;
if ( ! cxt )
return - ENOMEM ;
2013-02-27 15:43:40 +04:00
aa_dup_task_context ( cxt , cred_cxt ( old ) ) ;
cred_cxt ( new ) = cxt ;
2010-07-30 01:48:07 +04:00
return 0 ;
}
/*
* transfer the apparmor data to a blank set of creds
*/
static void apparmor_cred_transfer ( struct cred * new , const struct cred * old )
{
2013-02-27 15:43:40 +04:00
const struct aa_task_cxt * old_cxt = cred_cxt ( old ) ;
struct aa_task_cxt * new_cxt = cred_cxt ( new ) ;
2010-07-30 01:48:07 +04:00
aa_dup_task_context ( new_cxt , old_cxt ) ;
}
static int apparmor_ptrace_access_check ( struct task_struct * child ,
unsigned int mode )
{
return aa_ptrace ( current , child , mode ) ;
}
static int apparmor_ptrace_traceme ( struct task_struct * parent )
{
return aa_ptrace ( parent , current , PTRACE_MODE_ATTACH ) ;
}
/* Derived from security/commoncap.c:cap_capget */
static int apparmor_capget ( struct task_struct * target , kernel_cap_t * effective ,
kernel_cap_t * inheritable , kernel_cap_t * permitted )
{
struct aa_profile * profile ;
const struct cred * cred ;
rcu_read_lock ( ) ;
cred = __task_cred ( target ) ;
profile = aa_cred_profile ( cred ) ;
2015-05-03 01:11:42 +03:00
/*
* cap_capget is stacked ahead of this and will
* initialize effective and permitted .
*/
2011-06-25 19:57:07 +04:00
if ( ! unconfined ( profile ) & & ! COMPLAIN_MODE ( profile ) ) {
2010-07-30 01:48:07 +04:00
* effective = cap_intersect ( * effective , profile - > caps . allow ) ;
* permitted = cap_intersect ( * permitted , profile - > caps . allow ) ;
}
rcu_read_unlock ( ) ;
return 0 ;
}
2012-01-03 21:25:14 +04:00
static int apparmor_capable ( const struct cred * cred , struct user_namespace * ns ,
int cap , int audit )
2010-07-30 01:48:07 +04:00
{
struct aa_profile * profile ;
2015-05-03 01:11:42 +03:00
int error = 0 ;
profile = aa_cred_profile ( cred ) ;
if ( ! unconfined ( profile ) )
error = aa_capable ( profile , cap , audit ) ;
2010-07-30 01:48:07 +04:00
return error ;
}
/**
* common_perm - basic common permission check wrapper fn for paths
* @ op : operation being checked
* @ path : path to check permission of ( NOT NULL )
* @ mask : requested permissions mask
* @ cond : conditional info for the permission request ( NOT NULL )
*
* Returns : % 0 else error code if error or permission denied
*/
static int common_perm ( int op , struct path * path , u32 mask ,
struct path_cond * cond )
{
struct aa_profile * profile ;
int error = 0 ;
profile = __aa_current_profile ( ) ;
if ( ! unconfined ( profile ) )
error = aa_path_perm ( op , profile , path , 0 , mask , cond ) ;
return error ;
}
/**
* common_perm_dir_dentry - common permission wrapper when path is dir , dentry
* @ op : operation being checked
* @ dir : directory of the dentry ( NOT NULL )
* @ dentry : dentry to check ( NOT NULL )
* @ mask : requested permissions mask
* @ cond : conditional info for the permission request ( NOT NULL )
*
* Returns : % 0 else error code if error or permission denied
*/
static int common_perm_dir_dentry ( int op , struct path * dir ,
struct dentry * dentry , u32 mask ,
struct path_cond * cond )
{
struct path path = { dir - > mnt , dentry } ;
return common_perm ( op , & path , mask , cond ) ;
}
/**
* common_perm_mnt_dentry - common permission wrapper when mnt , dentry
* @ op : operation being checked
* @ mnt : mount point of dentry ( NOT NULL )
* @ dentry : dentry to check ( NOT NULL )
* @ mask : requested permissions mask
*
* Returns : % 0 else error code if error or permission denied
*/
static int common_perm_mnt_dentry ( int op , struct vfsmount * mnt ,
struct dentry * dentry , u32 mask )
{
struct path path = { mnt , dentry } ;
2015-03-18 01:26:22 +03:00
struct path_cond cond = { d_backing_inode ( dentry ) - > i_uid ,
d_backing_inode ( dentry ) - > i_mode
2010-07-30 01:48:07 +04:00
} ;
return common_perm ( op , & path , mask , & cond ) ;
}
/**
* common_perm_rm - common permission wrapper for operations doing rm
* @ op : operation being checked
* @ dir : directory that the dentry is in ( NOT NULL )
* @ dentry : dentry being rm ' d ( NOT NULL )
* @ mask : requested permission mask
*
* Returns : % 0 else error code if error or permission denied
*/
static int common_perm_rm ( int op , struct path * dir ,
struct dentry * dentry , u32 mask )
{
2015-03-18 01:26:22 +03:00
struct inode * inode = d_backing_inode ( dentry ) ;
2010-07-30 01:48:07 +04:00
struct path_cond cond = { } ;
2015-01-29 15:02:31 +03:00
if ( ! inode | | ! dir - > mnt | | ! mediated_filesystem ( dentry ) )
2010-07-30 01:48:07 +04:00
return 0 ;
cond . uid = inode - > i_uid ;
cond . mode = inode - > i_mode ;
return common_perm_dir_dentry ( op , dir , dentry , mask , & cond ) ;
}
/**
* common_perm_create - common permission wrapper for operations doing create
* @ op : operation being checked
* @ dir : directory that dentry will be created in ( NOT NULL )
* @ dentry : dentry to create ( NOT NULL )
* @ mask : request permission mask
* @ mode : created file mode
*
* Returns : % 0 else error code if error or permission denied
*/
static int common_perm_create ( int op , struct path * dir , struct dentry * dentry ,
u32 mask , umode_t mode )
{
struct path_cond cond = { current_fsuid ( ) , mode } ;
2015-01-29 15:02:31 +03:00
if ( ! dir - > mnt | | ! mediated_filesystem ( dir - > dentry ) )
2010-07-30 01:48:07 +04:00
return 0 ;
return common_perm_dir_dentry ( op , dir , dentry , mask , & cond ) ;
}
static int apparmor_path_unlink ( struct path * dir , struct dentry * dentry )
{
return common_perm_rm ( OP_UNLINK , dir , dentry , AA_MAY_DELETE ) ;
}
static int apparmor_path_mkdir ( struct path * dir , struct dentry * dentry ,
2011-11-21 23:56:21 +04:00
umode_t mode )
2010-07-30 01:48:07 +04:00
{
return common_perm_create ( OP_MKDIR , dir , dentry , AA_MAY_CREATE ,
S_IFDIR ) ;
}
static int apparmor_path_rmdir ( struct path * dir , struct dentry * dentry )
{
return common_perm_rm ( OP_RMDIR , dir , dentry , AA_MAY_DELETE ) ;
}
static int apparmor_path_mknod ( struct path * dir , struct dentry * dentry ,
2011-11-21 23:58:38 +04:00
umode_t mode , unsigned int dev )
2010-07-30 01:48:07 +04:00
{
return common_perm_create ( OP_MKNOD , dir , dentry , AA_MAY_CREATE , mode ) ;
}
2010-07-30 03:02:04 +04:00
static int apparmor_path_truncate ( struct path * path )
2010-07-30 01:48:07 +04:00
{
2015-03-18 01:26:22 +03:00
struct path_cond cond = { d_backing_inode ( path - > dentry ) - > i_uid ,
d_backing_inode ( path - > dentry ) - > i_mode
2010-07-30 01:48:07 +04:00
} ;
2015-01-29 15:02:31 +03:00
if ( ! path - > mnt | | ! mediated_filesystem ( path - > dentry ) )
2010-07-30 01:48:07 +04:00
return 0 ;
return common_perm ( OP_TRUNC , path , MAY_WRITE | AA_MAY_META_WRITE ,
& cond ) ;
}
static int apparmor_path_symlink ( struct path * dir , struct dentry * dentry ,
const char * old_name )
{
return common_perm_create ( OP_SYMLINK , dir , dentry , AA_MAY_CREATE ,
S_IFLNK ) ;
}
static int apparmor_path_link ( struct dentry * old_dentry , struct path * new_dir ,
struct dentry * new_dentry )
{
struct aa_profile * profile ;
int error = 0 ;
2015-01-29 15:02:31 +03:00
if ( ! mediated_filesystem ( old_dentry ) )
2010-07-30 01:48:07 +04:00
return 0 ;
profile = aa_current_profile ( ) ;
if ( ! unconfined ( profile ) )
error = aa_path_link ( profile , old_dentry , new_dir , new_dentry ) ;
return error ;
}
static int apparmor_path_rename ( struct path * old_dir , struct dentry * old_dentry ,
struct path * new_dir , struct dentry * new_dentry )
{
struct aa_profile * profile ;
int error = 0 ;
2015-01-29 15:02:31 +03:00
if ( ! mediated_filesystem ( old_dentry ) )
2010-07-30 01:48:07 +04:00
return 0 ;
profile = aa_current_profile ( ) ;
if ( ! unconfined ( profile ) ) {
struct path old_path = { old_dir - > mnt , old_dentry } ;
struct path new_path = { new_dir - > mnt , new_dentry } ;
2015-03-18 01:26:22 +03:00
struct path_cond cond = { d_backing_inode ( old_dentry ) - > i_uid ,
d_backing_inode ( old_dentry ) - > i_mode
2010-07-30 01:48:07 +04:00
} ;
error = aa_path_perm ( OP_RENAME_SRC , profile , & old_path , 0 ,
MAY_READ | AA_MAY_META_READ | MAY_WRITE |
AA_MAY_META_WRITE | AA_MAY_DELETE ,
& cond ) ;
if ( ! error )
error = aa_path_perm ( OP_RENAME_DEST , profile , & new_path ,
0 , MAY_WRITE | AA_MAY_META_WRITE |
AA_MAY_CREATE , & cond ) ;
}
return error ;
}
2011-12-08 19:51:53 +04:00
static int apparmor_path_chmod ( struct path * path , umode_t mode )
2010-07-30 01:48:07 +04:00
{
2015-01-29 15:02:31 +03:00
if ( ! mediated_filesystem ( path - > dentry ) )
2010-07-30 01:48:07 +04:00
return 0 ;
2011-12-08 19:51:53 +04:00
return common_perm_mnt_dentry ( OP_CHMOD , path - > mnt , path - > dentry , AA_MAY_CHMOD ) ;
2010-07-30 01:48:07 +04:00
}
2012-06-02 02:14:19 +04:00
static int apparmor_path_chown ( struct path * path , kuid_t uid , kgid_t gid )
2010-07-30 01:48:07 +04:00
{
2015-03-18 01:26:22 +03:00
struct path_cond cond = { d_backing_inode ( path - > dentry ) - > i_uid ,
d_backing_inode ( path - > dentry ) - > i_mode
2010-07-30 01:48:07 +04:00
} ;
2015-01-29 15:02:31 +03:00
if ( ! mediated_filesystem ( path - > dentry ) )
2010-07-30 01:48:07 +04:00
return 0 ;
return common_perm ( OP_CHOWN , path , AA_MAY_CHOWN , & cond ) ;
}
2015-03-09 02:28:30 +03:00
static int apparmor_inode_getattr ( const struct path * path )
2010-07-30 01:48:07 +04:00
{
2015-03-09 02:28:30 +03:00
if ( ! mediated_filesystem ( path - > dentry ) )
2010-07-30 01:48:07 +04:00
return 0 ;
2015-03-09 02:28:30 +03:00
return common_perm_mnt_dentry ( OP_GETATTR , path - > mnt , path - > dentry ,
2010-07-30 01:48:07 +04:00
AA_MAY_META_READ ) ;
}
2012-04-04 21:45:40 +04:00
static int apparmor_file_open ( struct file * file , const struct cred * cred )
2010-07-30 01:48:07 +04:00
{
struct aa_file_cxt * fcxt = file - > f_security ;
struct aa_profile * profile ;
int error = 0 ;
2015-01-29 15:02:31 +03:00
if ( ! mediated_filesystem ( file - > f_path . dentry ) )
2010-07-30 01:48:07 +04:00
return 0 ;
/* If in exec, permission is handled by bprm hooks.
* Cache permissions granted by the previous exec check , with
* implicit read and executable mmap which are required to
* actually execute the image .
*/
if ( current - > in_execve ) {
fcxt - > allow = MAY_EXEC | MAY_READ | AA_EXEC_MMAP ;
return 0 ;
}
profile = aa_cred_profile ( cred ) ;
if ( ! unconfined ( profile ) ) {
2013-01-24 02:07:38 +04:00
struct inode * inode = file_inode ( file ) ;
2010-07-30 01:48:07 +04:00
struct path_cond cond = { inode - > i_uid , inode - > i_mode } ;
error = aa_path_perm ( OP_OPEN , profile , & file - > f_path , 0 ,
aa_map_file_to_perms ( file ) , & cond ) ;
/* todo cache full allowed permissions set and state */
fcxt - > allow = aa_map_file_to_perms ( file ) ;
}
return error ;
}
static int apparmor_file_alloc_security ( struct file * file )
{
/* freed by apparmor_file_free_security */
file - > f_security = aa_alloc_file_context ( GFP_KERNEL ) ;
if ( ! file - > f_security )
return - ENOMEM ;
return 0 ;
}
static void apparmor_file_free_security ( struct file * file )
{
struct aa_file_cxt * cxt = file - > f_security ;
aa_free_file_context ( cxt ) ;
}
static int common_file_perm ( int op , struct file * file , u32 mask )
{
struct aa_file_cxt * fcxt = file - > f_security ;
struct aa_profile * profile , * fprofile = aa_cred_profile ( file - > f_cred ) ;
int error = 0 ;
BUG_ON ( ! fprofile ) ;
if ( ! file - > f_path . mnt | |
2015-01-29 15:02:31 +03:00
! mediated_filesystem ( file - > f_path . dentry ) )
2010-07-30 01:48:07 +04:00
return 0 ;
profile = __aa_current_profile ( ) ;
/* 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 ( fprofile ) is to handle file
* delegation from unconfined tasks
*/
if ( ! unconfined ( profile ) & & ! unconfined ( fprofile ) & &
( ( fprofile ! = profile ) | | ( mask & ~ fcxt - > allow ) ) )
error = aa_file_perm ( op , profile , file , mask ) ;
return error ;
}
static int apparmor_file_permission ( struct file * file , int mask )
{
return common_file_perm ( OP_FPERM , file , mask ) ;
}
static int apparmor_file_lock ( struct file * file , unsigned int cmd )
{
u32 mask = AA_MAY_LOCK ;
if ( cmd = = F_WRLCK )
mask | = MAY_WRITE ;
return common_file_perm ( OP_FLOCK , file , mask ) ;
}
static int common_mmap ( int op , struct file * file , unsigned long prot ,
unsigned long flags )
{
int mask = 0 ;
if ( ! file | | ! file - > f_security )
return 0 ;
if ( prot & PROT_READ )
mask | = MAY_READ ;
/*
* Private mappings don ' t require write perms since they don ' t
* write back to the files
*/
if ( ( prot & PROT_WRITE ) & & ! ( flags & MAP_PRIVATE ) )
mask | = MAY_WRITE ;
if ( prot & PROT_EXEC )
mask | = AA_EXEC_MMAP ;
return common_file_perm ( op , file , mask ) ;
}
2012-05-30 21:30:51 +04:00
static int apparmor_mmap_file ( struct file * file , unsigned long reqprot ,
unsigned long prot , unsigned long flags )
2010-07-30 01:48:07 +04:00
{
return common_mmap ( OP_FMMAP , file , prot , flags ) ;
}
static int apparmor_file_mprotect ( struct vm_area_struct * vma ,
unsigned long reqprot , unsigned long prot )
{
return common_mmap ( OP_FMPROT , vma - > vm_file , prot ,
! ( vma - > vm_flags & VM_SHARED ) ? MAP_PRIVATE : 0 ) ;
}
static int apparmor_getprocattr ( struct task_struct * task , char * name ,
char * * value )
{
int error = - ENOENT ;
/* released below */
const struct cred * cred = get_task_cred ( task ) ;
2013-02-27 15:43:40 +04:00
struct aa_task_cxt * cxt = cred_cxt ( cred ) ;
2013-07-11 08:07:43 +04:00
struct aa_profile * profile = NULL ;
2010-07-30 01:48:07 +04:00
if ( strcmp ( name , " current " ) = = 0 )
2013-07-11 08:07:43 +04:00
profile = aa_get_newest_profile ( cxt - > profile ) ;
2010-07-30 01:48:07 +04:00
else if ( strcmp ( name , " prev " ) = = 0 & & cxt - > previous )
2013-07-11 08:07:43 +04:00
profile = aa_get_newest_profile ( cxt - > previous ) ;
2010-07-30 01:48:07 +04:00
else if ( strcmp ( name , " exec " ) = = 0 & & cxt - > onexec )
2013-07-11 08:07:43 +04:00
profile = aa_get_newest_profile ( cxt - > onexec ) ;
2010-07-30 01:48:07 +04:00
else
error = - EINVAL ;
2013-07-11 08:07:43 +04:00
if ( profile )
error = aa_getprocattr ( profile , value ) ;
aa_put_profile ( profile ) ;
2010-07-30 01:48:07 +04:00
put_cred ( cred ) ;
return error ;
}
static int apparmor_setprocattr ( struct task_struct * task , char * name ,
void * value , size_t size )
{
2013-02-27 15:44:40 +04:00
struct common_audit_data sa ;
struct apparmor_audit_data aad = { 0 , } ;
2010-07-30 01:48:07 +04:00
char * command , * args = value ;
size_t arg_size ;
int error ;
if ( size = = 0 )
return - EINVAL ;
/* args points to a PAGE_SIZE buffer, AppArmor requires that
* the buffer must be null terminated or have size < = PAGE_SIZE - 1
* so that AppArmor can null terminate them
*/
if ( args [ size - 1 ] ! = ' \0 ' ) {
if ( size = = PAGE_SIZE )
return - EINVAL ;
args [ size ] = ' \0 ' ;
}
/* task can only write its own attributes */
if ( current ! = task )
return - EACCES ;
args = value ;
args = strim ( args ) ;
command = strsep ( & args , " " ) ;
if ( ! args )
return - EINVAL ;
args = skip_spaces ( args ) ;
if ( ! * args )
return - EINVAL ;
arg_size = size - ( args - ( char * ) value ) ;
if ( strcmp ( name , " current " ) = = 0 ) {
if ( strcmp ( command , " changehat " ) = = 0 ) {
error = aa_setprocattr_changehat ( args , arg_size ,
! AA_DO_TEST ) ;
} else if ( strcmp ( command , " permhat " ) = = 0 ) {
error = aa_setprocattr_changehat ( args , arg_size ,
AA_DO_TEST ) ;
} else if ( strcmp ( command , " changeprofile " ) = = 0 ) {
error = aa_setprocattr_changeprofile ( args , ! AA_ONEXEC ,
! AA_DO_TEST ) ;
} else if ( strcmp ( command , " permprofile " ) = = 0 ) {
error = aa_setprocattr_changeprofile ( args , ! AA_ONEXEC ,
AA_DO_TEST ) ;
2013-02-27 15:44:40 +04:00
} else
goto fail ;
2010-07-30 01:48:07 +04:00
} else if ( strcmp ( name , " exec " ) = = 0 ) {
2013-02-27 15:44:40 +04:00
if ( strcmp ( command , " exec " ) = = 0 )
error = aa_setprocattr_changeprofile ( args , AA_ONEXEC ,
! AA_DO_TEST ) ;
else
goto fail ;
} else
2010-07-30 01:48:07 +04:00
/* only support the "current" and "exec" process attributes */
return - EINVAL ;
2013-02-27 15:44:40 +04:00
2010-07-30 01:48:07 +04:00
if ( ! error )
error = size ;
return error ;
2013-02-27 15:44:40 +04:00
fail :
sa . type = LSM_AUDIT_DATA_NONE ;
sa . aad = & aad ;
aad . profile = aa_current_profile ( ) ;
aad . op = OP_SETPROCATTR ;
aad . info = name ;
aad . error = - EINVAL ;
aa_audit_msg ( AUDIT_APPARMOR_DENIED , & sa , NULL ) ;
return - EINVAL ;
2010-07-30 01:48:07 +04:00
}
2010-08-11 13:28:02 +04:00
static int apparmor_task_setrlimit ( struct task_struct * task ,
unsigned int resource , struct rlimit * new_rlim )
2010-07-30 01:48:07 +04:00
{
2011-06-09 02:07:47 +04:00
struct aa_profile * profile = __aa_current_profile ( ) ;
2010-07-30 01:48:07 +04:00
int error = 0 ;
if ( ! unconfined ( profile ) )
2010-09-06 21:10:20 +04:00
error = aa_task_setrlimit ( profile , task , resource , new_rlim ) ;
2010-07-30 01:48:07 +04:00
return error ;
}
2015-05-03 01:11:42 +03:00
static struct security_hook_list apparmor_hooks [ ] = {
2015-05-03 01:11:36 +03:00
LSM_HOOK_INIT ( ptrace_access_check , apparmor_ptrace_access_check ) ,
LSM_HOOK_INIT ( ptrace_traceme , apparmor_ptrace_traceme ) ,
LSM_HOOK_INIT ( capget , apparmor_capget ) ,
LSM_HOOK_INIT ( capable , apparmor_capable ) ,
LSM_HOOK_INIT ( path_link , apparmor_path_link ) ,
LSM_HOOK_INIT ( path_unlink , apparmor_path_unlink ) ,
LSM_HOOK_INIT ( path_symlink , apparmor_path_symlink ) ,
LSM_HOOK_INIT ( path_mkdir , apparmor_path_mkdir ) ,
LSM_HOOK_INIT ( path_rmdir , apparmor_path_rmdir ) ,
LSM_HOOK_INIT ( path_mknod , apparmor_path_mknod ) ,
LSM_HOOK_INIT ( path_rename , apparmor_path_rename ) ,
LSM_HOOK_INIT ( path_chmod , apparmor_path_chmod ) ,
LSM_HOOK_INIT ( path_chown , apparmor_path_chown ) ,
LSM_HOOK_INIT ( path_truncate , apparmor_path_truncate ) ,
LSM_HOOK_INIT ( inode_getattr , apparmor_inode_getattr ) ,
LSM_HOOK_INIT ( file_open , apparmor_file_open ) ,
LSM_HOOK_INIT ( file_permission , apparmor_file_permission ) ,
LSM_HOOK_INIT ( file_alloc_security , apparmor_file_alloc_security ) ,
LSM_HOOK_INIT ( file_free_security , apparmor_file_free_security ) ,
LSM_HOOK_INIT ( mmap_file , apparmor_mmap_file ) ,
LSM_HOOK_INIT ( file_mprotect , apparmor_file_mprotect ) ,
LSM_HOOK_INIT ( file_lock , apparmor_file_lock ) ,
LSM_HOOK_INIT ( getprocattr , apparmor_getprocattr ) ,
LSM_HOOK_INIT ( setprocattr , apparmor_setprocattr ) ,
LSM_HOOK_INIT ( cred_alloc_blank , apparmor_cred_alloc_blank ) ,
LSM_HOOK_INIT ( cred_free , apparmor_cred_free ) ,
LSM_HOOK_INIT ( cred_prepare , apparmor_cred_prepare ) ,
LSM_HOOK_INIT ( cred_transfer , apparmor_cred_transfer ) ,
LSM_HOOK_INIT ( bprm_set_creds , apparmor_bprm_set_creds ) ,
LSM_HOOK_INIT ( bprm_committing_creds , apparmor_bprm_committing_creds ) ,
LSM_HOOK_INIT ( bprm_committed_creds , apparmor_bprm_committed_creds ) ,
LSM_HOOK_INIT ( bprm_secureexec , apparmor_bprm_secureexec ) ,
LSM_HOOK_INIT ( task_setrlimit , apparmor_task_setrlimit ) ,
2010-07-30 01:48:07 +04:00
} ;
/*
* AppArmor sysfs module parameters
*/
2010-08-02 06:00:43 +04:00
static int param_set_aabool ( const char * val , const struct kernel_param * kp ) ;
static int param_get_aabool ( char * buffer , const struct kernel_param * kp ) ;
2011-12-15 07:11:32 +04:00
# define param_check_aabool param_check_bool
2015-05-27 04:39:38 +03:00
static const struct kernel_param_ops param_ops_aabool = {
2014-08-27 00:51:23 +04:00
. flags = KERNEL_PARAM_OPS_FL_NOARG ,
2010-08-02 06:00:43 +04:00
. set = param_set_aabool ,
. get = param_get_aabool
} ;
2010-07-30 01:48:07 +04:00
2010-08-02 06:00:43 +04:00
static int param_set_aauint ( const char * val , const struct kernel_param * kp ) ;
static int param_get_aauint ( char * buffer , const struct kernel_param * kp ) ;
2011-12-15 07:11:32 +04:00
# define param_check_aauint param_check_uint
2015-05-27 04:39:38 +03:00
static const struct kernel_param_ops param_ops_aauint = {
2010-08-02 06:00:43 +04:00
. set = param_set_aauint ,
. get = param_get_aauint
} ;
2010-07-30 01:48:07 +04:00
2010-08-02 06:00:43 +04:00
static int param_set_aalockpolicy ( const char * val , const struct kernel_param * kp ) ;
static int param_get_aalockpolicy ( char * buffer , const struct kernel_param * kp ) ;
2011-12-15 07:11:32 +04:00
# define param_check_aalockpolicy param_check_bool
2015-05-27 04:39:38 +03:00
static const struct kernel_param_ops param_ops_aalockpolicy = {
2014-08-27 00:51:23 +04:00
. flags = KERNEL_PARAM_OPS_FL_NOARG ,
2010-08-02 06:00:43 +04:00
. set = param_set_aalockpolicy ,
. get = param_get_aalockpolicy
} ;
2010-07-30 01:48:07 +04:00
static int param_set_audit ( const char * val , struct kernel_param * kp ) ;
static int param_get_audit ( char * buffer , struct kernel_param * kp ) ;
static int param_set_mode ( const char * val , struct kernel_param * kp ) ;
static int param_get_mode ( char * buffer , struct kernel_param * kp ) ;
/* Flag values, also controllable via /sys/module/apparmor/parameters
* We define special types as we want to do additional mediation .
*/
/* AppArmor global enforcement switch - complain, enforce, kill */
enum profile_mode aa_g_profile_mode = APPARMOR_ENFORCE ;
module_param_call ( mode , param_set_mode , param_get_mode ,
& aa_g_profile_mode , S_IRUSR | S_IWUSR ) ;
/* Debug mode */
2012-01-13 03:02:20 +04:00
bool aa_g_debug ;
2010-07-30 01:48:07 +04:00
module_param_named ( debug , aa_g_debug , aabool , S_IRUSR | S_IWUSR ) ;
/* Audit mode */
enum audit_mode aa_g_audit ;
module_param_call ( audit , param_set_audit , param_get_audit ,
& aa_g_audit , S_IRUSR | S_IWUSR ) ;
/* Determines if audit header is included in audited messages. This
* provides more context if the audit daemon is not running
*/
2012-01-13 03:02:20 +04:00
bool aa_g_audit_header = 1 ;
2010-07-30 01:48:07 +04:00
module_param_named ( audit_header , aa_g_audit_header , aabool ,
S_IRUSR | S_IWUSR ) ;
/* lock out loading/removal of policy
* TODO : add in at boot loading of policy , which is the only way to
* load policy , if lock_policy is set
*/
2012-01-13 03:02:20 +04:00
bool aa_g_lock_policy ;
2010-07-30 01:48:07 +04:00
module_param_named ( lock_policy , aa_g_lock_policy , aalockpolicy ,
S_IRUSR | S_IWUSR ) ;
/* Syscall logging mode */
2012-01-13 03:02:20 +04:00
bool aa_g_logsyscall ;
2010-07-30 01:48:07 +04:00
module_param_named ( logsyscall , aa_g_logsyscall , aabool , S_IRUSR | S_IWUSR ) ;
/* Maximum pathname length before accesses will start getting rejected */
unsigned int aa_g_path_max = 2 * PATH_MAX ;
module_param_named ( path_max , aa_g_path_max , aauint , S_IRUSR | S_IWUSR ) ;
/* Determines how paranoid loading of policy is and how much verification
* on the loaded policy is done .
*/
2012-01-13 03:02:20 +04:00
bool aa_g_paranoid_load = 1 ;
2010-07-30 01:48:07 +04:00
module_param_named ( paranoid_load , aa_g_paranoid_load , aabool ,
S_IRUSR | S_IWUSR ) ;
/* Boot time disable flag */
2012-01-13 03:02:20 +04:00
static bool apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE ;
2013-07-11 08:03:43 +04:00
module_param_named ( enabled , apparmor_enabled , bool , S_IRUGO ) ;
2010-07-30 01:48:07 +04:00
static int __init apparmor_enabled_setup ( char * str )
{
unsigned long enabled ;
2014-02-05 10:13:14 +04:00
int error = kstrtoul ( str , 0 , & enabled ) ;
2010-07-30 01:48:07 +04:00
if ( ! error )
apparmor_enabled = enabled ? 1 : 0 ;
return 1 ;
}
__setup ( " apparmor= " , apparmor_enabled_setup ) ;
/* set global flag turning off the ability to load policy */
2010-08-02 06:00:43 +04:00
static int param_set_aalockpolicy ( const char * val , const struct kernel_param * kp )
2010-07-30 01:48:07 +04:00
{
if ( ! capable ( CAP_MAC_ADMIN ) )
return - EPERM ;
if ( aa_g_lock_policy )
return - EACCES ;
return param_set_bool ( val , kp ) ;
}
2010-08-02 06:00:43 +04:00
static int param_get_aalockpolicy ( char * buffer , const struct kernel_param * kp )
2010-07-30 01:48:07 +04:00
{
if ( ! capable ( CAP_MAC_ADMIN ) )
return - EPERM ;
return param_get_bool ( buffer , kp ) ;
}
2010-08-02 06:00:43 +04:00
static int param_set_aabool ( const char * val , const struct kernel_param * kp )
2010-07-30 01:48:07 +04:00
{
if ( ! capable ( CAP_MAC_ADMIN ) )
return - EPERM ;
return param_set_bool ( val , kp ) ;
}
2010-08-02 06:00:43 +04:00
static int param_get_aabool ( char * buffer , const struct kernel_param * kp )
2010-07-30 01:48:07 +04:00
{
if ( ! capable ( CAP_MAC_ADMIN ) )
return - EPERM ;
return param_get_bool ( buffer , kp ) ;
}
2010-08-02 06:00:43 +04:00
static int param_set_aauint ( const char * val , const struct kernel_param * kp )
2010-07-30 01:48:07 +04:00
{
if ( ! capable ( CAP_MAC_ADMIN ) )
return - EPERM ;
return param_set_uint ( val , kp ) ;
}
2010-08-02 06:00:43 +04:00
static int param_get_aauint ( char * buffer , const struct kernel_param * kp )
2010-07-30 01:48:07 +04:00
{
if ( ! capable ( CAP_MAC_ADMIN ) )
return - EPERM ;
return param_get_uint ( buffer , kp ) ;
}
static int param_get_audit ( char * buffer , struct kernel_param * kp )
{
if ( ! capable ( CAP_MAC_ADMIN ) )
return - EPERM ;
if ( ! apparmor_enabled )
return - EINVAL ;
return sprintf ( buffer , " %s " , audit_mode_names [ aa_g_audit ] ) ;
}
static int param_set_audit ( const char * val , struct kernel_param * kp )
{
int i ;
if ( ! capable ( CAP_MAC_ADMIN ) )
return - EPERM ;
if ( ! apparmor_enabled )
return - EINVAL ;
if ( ! val )
return - EINVAL ;
for ( i = 0 ; i < AUDIT_MAX_INDEX ; i + + ) {
if ( strcmp ( val , audit_mode_names [ i ] ) = = 0 ) {
aa_g_audit = i ;
return 0 ;
}
}
return - EINVAL ;
}
static int param_get_mode ( char * buffer , struct kernel_param * kp )
{
if ( ! capable ( CAP_MAC_ADMIN ) )
return - EPERM ;
if ( ! apparmor_enabled )
return - EINVAL ;
2013-07-11 08:13:43 +04:00
return sprintf ( buffer , " %s " , aa_profile_mode_names [ aa_g_profile_mode ] ) ;
2010-07-30 01:48:07 +04:00
}
static int param_set_mode ( const char * val , struct kernel_param * kp )
{
int i ;
if ( ! capable ( CAP_MAC_ADMIN ) )
return - EPERM ;
if ( ! apparmor_enabled )
return - EINVAL ;
if ( ! val )
return - EINVAL ;
2013-07-11 08:13:43 +04:00
for ( i = 0 ; i < APPARMOR_MODE_NAMES_MAX_INDEX ; i + + ) {
if ( strcmp ( val , aa_profile_mode_names [ i ] ) = = 0 ) {
2010-07-30 01:48:07 +04:00
aa_g_profile_mode = i ;
return 0 ;
}
}
return - EINVAL ;
}
/*
* AppArmor init functions
*/
/**
* set_init_cxt - set a task context and profile on the first task .
*
* TODO : allow setting an alternate profile than unconfined
*/
static int __init set_init_cxt ( void )
{
struct cred * cred = ( struct cred * ) current - > real_cred ;
struct aa_task_cxt * cxt ;
cxt = aa_alloc_task_context ( GFP_KERNEL ) ;
if ( ! cxt )
return - ENOMEM ;
cxt - > profile = aa_get_profile ( root_ns - > unconfined ) ;
2013-02-27 15:43:40 +04:00
cred_cxt ( cred ) = cxt ;
2010-07-30 01:48:07 +04:00
return 0 ;
}
static int __init apparmor_init ( void )
{
int error ;
2015-05-03 01:11:42 +03:00
if ( ! apparmor_enabled | | ! security_module_enable ( " apparmor " ) ) {
2010-07-30 01:48:07 +04:00
aa_info_message ( " AppArmor disabled by boot time parameter " ) ;
apparmor_enabled = 0 ;
return 0 ;
}
error = aa_alloc_root_ns ( ) ;
if ( error ) {
AA_ERROR ( " Unable to allocate default profile namespace \n " ) ;
goto alloc_out ;
}
error = set_init_cxt ( ) ;
if ( error ) {
AA_ERROR ( " Failed to set context on init task \n " ) ;
2015-05-03 01:11:42 +03:00
aa_free_root_ns ( ) ;
goto alloc_out ;
2010-07-30 01:48:07 +04:00
}
2015-05-03 01:11:42 +03:00
security_add_hooks ( apparmor_hooks , ARRAY_SIZE ( apparmor_hooks ) ) ;
2010-07-30 01:48:07 +04:00
/* Report that AppArmor successfully initialized */
apparmor_initialized = 1 ;
if ( aa_g_profile_mode = = APPARMOR_COMPLAIN )
aa_info_message ( " AppArmor initialized: complain mode enabled " ) ;
else if ( aa_g_profile_mode = = APPARMOR_KILL )
aa_info_message ( " AppArmor initialized: kill mode enabled " ) ;
else
aa_info_message ( " AppArmor initialized " ) ;
return error ;
alloc_out :
aa_destroy_aafs ( ) ;
apparmor_enabled = 0 ;
return error ;
}
security_initcall ( apparmor_init ) ;