2019-06-01 11:08:55 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2017-01-16 11:42:15 +03:00
/*
* AppArmor security module
*
* This file contains AppArmor policy manipulation functions
*
* Copyright ( C ) 1998 - 2008 Novell / SUSE
* Copyright 2009 - 2017 Canonical Ltd .
*
* AppArmor policy namespaces , allow for different sets of policies
* to be loaded for tasks within the namespace .
*/
# include <linux/list.h>
# include <linux/mutex.h>
# include <linux/slab.h>
# include <linux/string.h>
# include "include/apparmor.h"
2017-10-11 11:04:48 +03:00
# include "include/cred.h"
2017-01-16 11:42:15 +03:00
# include "include/policy_ns.h"
2017-06-09 18:14:28 +03:00
# include "include/label.h"
2017-01-16 11:42:15 +03:00
# include "include/policy.h"
/* root profile namespace */
2017-01-16 11:42:16 +03:00
struct aa_ns * root_ns ;
2017-01-16 11:42:15 +03:00
const char * aa_hidden_ns_name = " --- " ;
/**
* aa_ns_visible - test if @ view is visible from @ curr
* @ curr : namespace to treat as the parent ( NOT NULL )
* @ view : namespace to test if visible from @ curr ( NOT NULL )
2017-01-16 11:42:25 +03:00
* @ subns : whether view of a subns is allowed
2017-01-16 11:42:15 +03:00
*
* Returns : true if @ view is visible from @ curr else false
*/
2017-01-16 11:42:25 +03:00
bool aa_ns_visible ( struct aa_ns * curr , struct aa_ns * view , bool subns )
2017-01-16 11:42:15 +03:00
{
if ( curr = = view )
return true ;
2017-01-16 11:42:25 +03:00
if ( ! subns )
return false ;
2017-01-16 11:42:15 +03:00
for ( ; view ; view = view - > parent ) {
if ( view - > parent = = curr )
return true ;
}
2017-01-16 11:42:25 +03:00
2017-01-16 11:42:15 +03:00
return false ;
}
/**
* aa_na_name - Find the ns name to display for @ view from @ curr
* @ curr - current namespace ( NOT NULL )
* @ view - namespace attempting to view ( NOT NULL )
2017-01-16 11:42:25 +03:00
* @ subns - are subns visible
2017-01-16 11:42:15 +03:00
*
* Returns : name of @ view visible from @ curr
*/
2017-01-16 11:42:25 +03:00
const char * aa_ns_name ( struct aa_ns * curr , struct aa_ns * view , bool subns )
2017-01-16 11:42:15 +03:00
{
/* if view == curr then the namespace name isn't displayed */
if ( curr = = view )
return " " ;
2017-01-16 11:42:25 +03:00
if ( aa_ns_visible ( curr , view , subns ) ) {
2017-01-16 11:42:15 +03:00
/* at this point if a ns is visible it is in a view ns
* thus the curr ns . hname is a prefix of its name .
* Only output the virtualized portion of the name
* Add + 2 to skip over // separating curr hname prefix
* from the visible tail of the views hname
*/
return view - > base . hname + strlen ( curr - > base . hname ) + 2 ;
}
return aa_hidden_ns_name ;
}
/**
2017-01-16 11:42:16 +03:00
* alloc_ns - allocate , initialize and return a new namespace
2017-01-16 11:42:15 +03:00
* @ prefix : parent namespace name ( MAYBE NULL )
* @ name : a preallocated name ( NOT NULL )
*
* Returns : refcounted namespace or NULL on failure .
*/
2017-01-16 11:42:16 +03:00
static struct aa_ns * alloc_ns ( const char * prefix , const char * name )
2017-01-16 11:42:15 +03:00
{
2017-01-16 11:42:16 +03:00
struct aa_ns * ns ;
2017-01-16 11:42:15 +03:00
ns = kzalloc ( sizeof ( * ns ) , GFP_KERNEL ) ;
AA_DEBUG ( " %s(%p) \n " , __func__ , ns ) ;
if ( ! ns )
return NULL ;
2017-01-16 11:42:31 +03:00
if ( ! aa_policy_init ( & ns - > base , prefix , name , GFP_KERNEL ) )
2017-01-16 11:42:15 +03:00
goto fail_ns ;
INIT_LIST_HEAD ( & ns - > sub_ns ) ;
2017-05-09 10:08:41 +03:00
INIT_LIST_HEAD ( & ns - > rawdata_list ) ;
2017-01-16 11:42:15 +03:00
mutex_init ( & ns - > lock ) ;
2017-05-27 02:27:58 +03:00
init_waitqueue_head ( & ns - > wait ) ;
2017-01-16 11:42:15 +03:00
2017-01-16 11:42:16 +03:00
/* released by aa_free_ns() */
2017-06-09 18:14:28 +03:00
ns - > unconfined = aa_alloc_profile ( " unconfined " , NULL , GFP_KERNEL ) ;
2017-01-16 11:42:15 +03:00
if ( ! ns - > unconfined )
goto fail_unconfined ;
2017-06-09 18:14:28 +03:00
ns - > unconfined - > label . flags | = FLAG_IX_ON_NAME_ERROR |
FLAG_IMMUTIBLE | FLAG_NS_COUNT | FLAG_UNCONFINED ;
2017-01-16 11:42:15 +03:00
ns - > unconfined - > mode = APPARMOR_UNCONFINED ;
2017-08-16 15:48:06 +03:00
ns - > unconfined - > file . dfa = aa_get_dfa ( nulldfa ) ;
ns - > unconfined - > policy . dfa = aa_get_dfa ( nulldfa ) ;
2017-01-16 11:42:15 +03:00
/* ns and ns->unconfined share ns->unconfined refcount */
ns - > unconfined - > ns = ns ;
atomic_set ( & ns - > uniq_null , 0 ) ;
2017-06-09 18:14:28 +03:00
aa_labelset_init ( & ns - > labels ) ;
2017-01-16 11:42:15 +03:00
return ns ;
fail_unconfined :
kzfree ( ns - > base . hname ) ;
fail_ns :
kzfree ( ns ) ;
return NULL ;
}
/**
2017-01-16 11:42:16 +03:00
* aa_free_ns - free a profile namespace
2017-01-16 11:42:15 +03:00
* @ ns : the namespace to free ( MAYBE NULL )
*
* Requires : All references to the namespace must have been put , if the
* namespace was referenced by a profile confining a task ,
*/
2017-01-16 11:42:16 +03:00
void aa_free_ns ( struct aa_ns * ns )
2017-01-16 11:42:15 +03:00
{
if ( ! ns )
return ;
aa_policy_destroy ( & ns - > base ) ;
2017-06-09 18:14:28 +03:00
aa_labelset_destroy ( & ns - > labels ) ;
2017-01-16 11:42:16 +03:00
aa_put_ns ( ns - > parent ) ;
2017-01-16 11:42:15 +03:00
ns - > unconfined - > ns = NULL ;
aa_free_profile ( ns - > unconfined ) ;
kzfree ( ns ) ;
}
/**
2017-01-16 11:42:22 +03:00
* aa_findn_ns - look up a profile namespace on the namespace list
2017-01-16 11:42:15 +03:00
* @ root : namespace to search in ( NOT NULL )
* @ name : name of namespace to find ( NOT NULL )
2017-01-16 11:42:22 +03:00
* @ n : length of @ name
2017-01-16 11:42:15 +03:00
*
* Returns : a refcounted namespace on the list , or NULL if no namespace
* called @ name exists .
*
* refcount released by caller
*/
2017-01-16 11:42:22 +03:00
struct aa_ns * aa_findn_ns ( struct aa_ns * root , const char * name , size_t n )
2017-01-16 11:42:15 +03:00
{
2017-01-16 11:42:16 +03:00
struct aa_ns * ns = NULL ;
2017-01-16 11:42:15 +03:00
rcu_read_lock ( ) ;
2017-01-16 11:42:22 +03:00
ns = aa_get_ns ( __aa_findn_ns ( & root - > sub_ns , name , n ) ) ;
2017-01-16 11:42:15 +03:00
rcu_read_unlock ( ) ;
return ns ;
}
2017-01-16 11:42:22 +03:00
/**
* aa_find_ns - look up a profile namespace on the namespace list
* @ root : namespace to search in ( NOT NULL )
* @ name : name of namespace to find ( NOT NULL )
*
* Returns : a refcounted namespace on the list , or NULL if no namespace
* called @ name exists .
*
* refcount released by caller
*/
struct aa_ns * aa_find_ns ( struct aa_ns * root , const char * name )
{
return aa_findn_ns ( root , name , strlen ( name ) ) ;
}
2017-06-03 03:44:27 +03:00
/**
* __aa_lookupn_ns - lookup the namespace matching @ hname
* @ base : base list to start looking up profile name from ( NOT NULL )
* @ hname : hierarchical ns name ( NOT NULL )
* @ n : length of @ hname
*
* Requires : rcu_read_lock be held
*
* Returns : unrefcounted ns pointer or NULL if not found
*
* Do a relative name lookup , recursing through profile tree .
*/
struct aa_ns * __aa_lookupn_ns ( struct aa_ns * view , const char * hname , size_t n )
{
struct aa_ns * ns = view ;
const char * split ;
for ( split = strnstr ( hname , " // " , n ) ; split ;
split = strnstr ( hname , " // " , n ) ) {
ns = __aa_findn_ns ( & ns - > sub_ns , hname , split - hname ) ;
if ( ! ns )
return NULL ;
n - = split + 2 - hname ;
hname = split + 2 ;
}
if ( n )
return __aa_findn_ns ( & ns - > sub_ns , hname , n ) ;
return NULL ;
}
/**
* aa_lookupn_ns - look up a policy namespace relative to @ view
* @ view : namespace to search in ( NOT NULL )
* @ name : name of namespace to find ( NOT NULL )
* @ n : length of @ name
*
* Returns : a refcounted namespace on the list , or NULL if no namespace
* called @ name exists .
*
* refcount released by caller
*/
struct aa_ns * aa_lookupn_ns ( struct aa_ns * view , const char * name , size_t n )
{
struct aa_ns * ns = NULL ;
rcu_read_lock ( ) ;
ns = aa_get_ns ( __aa_lookupn_ns ( view , name , n ) ) ;
rcu_read_unlock ( ) ;
return ns ;
}
2017-01-16 11:42:34 +03:00
static struct aa_ns * __aa_create_ns ( struct aa_ns * parent , const char * name ,
struct dentry * dir )
{
struct aa_ns * ns ;
int error ;
AA_BUG ( ! parent ) ;
AA_BUG ( ! name ) ;
AA_BUG ( ! mutex_is_locked ( & parent - > lock ) ) ;
ns = alloc_ns ( parent - > base . hname , name ) ;
if ( ! ns )
2018-08-02 11:38:23 +03:00
return ERR_PTR ( - ENOMEM ) ;
2017-11-21 10:24:09 +03:00
ns - > level = parent - > level + 1 ;
mutex_lock_nested ( & ns - > lock , ns - > level ) ;
2017-05-25 16:31:46 +03:00
error = __aafs_ns_mkdir ( ns , ns_subns_dir ( parent ) , name , dir ) ;
2017-01-16 11:42:34 +03:00
if ( error ) {
AA_ERROR ( " Failed to create interface for ns %s \n " ,
ns - > base . name ) ;
mutex_unlock ( & ns - > lock ) ;
aa_free_ns ( ns ) ;
return ERR_PTR ( error ) ;
}
ns - > parent = aa_get_ns ( parent ) ;
list_add_rcu ( & ns - > base . list , & parent - > sub_ns ) ;
/* add list ref */
aa_get_ns ( ns ) ;
mutex_unlock ( & ns - > lock ) ;
return ns ;
}
2017-01-16 11:42:15 +03:00
/**
2017-01-16 11:42:34 +03:00
* aa_create_ns - create an ns , fail if it already exists
* @ parent : the parent of the namespace being created
* @ name : the name of the namespace
* @ dir : if not null the dir to put the ns entries in
2017-01-16 11:42:15 +03:00
*
2017-01-16 11:42:34 +03:00
* Returns : the a refcounted ns that has been add or an ERR_PTR
2017-01-16 11:42:15 +03:00
*/
2017-01-16 11:42:34 +03:00
struct aa_ns * __aa_find_or_create_ns ( struct aa_ns * parent , const char * name ,
struct dentry * dir )
2017-01-16 11:42:15 +03:00
{
2017-01-16 11:42:34 +03:00
struct aa_ns * ns ;
2017-01-16 11:42:15 +03:00
2017-01-16 11:42:34 +03:00
AA_BUG ( ! mutex_is_locked ( & parent - > lock ) ) ;
2017-01-16 11:42:15 +03:00
2017-01-16 11:42:34 +03:00
/* try and find the specified ns */
/* released by caller */
ns = aa_get_ns ( __aa_find_ns ( & parent - > sub_ns , name ) ) ;
if ( ! ns )
ns = __aa_create_ns ( parent , name , dir ) ;
else
ns = ERR_PTR ( - EEXIST ) ;
2017-01-16 11:42:15 +03:00
2017-01-16 11:42:34 +03:00
/* return ref */
return ns ;
}
2017-01-16 11:42:15 +03:00
2017-01-16 11:42:34 +03:00
/**
* aa_prepare_ns - find an existing or create a new namespace of @ name
* @ parent : ns to treat as parent
* @ name : the namespace to find or add ( NOT NULL )
*
* Returns : refcounted namespace or PTR_ERR if failed to create one
*/
struct aa_ns * aa_prepare_ns ( struct aa_ns * parent , const char * name )
{
struct aa_ns * ns ;
2017-11-21 10:24:09 +03:00
mutex_lock_nested ( & parent - > lock , parent - > level ) ;
2017-01-16 11:42:15 +03:00
/* try and find the specified ns and if it doesn't exist create it */
/* released by caller */
2017-01-16 11:42:34 +03:00
ns = aa_get_ns ( __aa_find_ns ( & parent - > sub_ns , name ) ) ;
if ( ! ns )
ns = __aa_create_ns ( parent , name , NULL ) ;
mutex_unlock ( & parent - > lock ) ;
2017-01-16 11:42:15 +03:00
/* return ref */
return ns ;
}
static void __ns_list_release ( struct list_head * head ) ;
/**
2017-01-16 11:42:16 +03:00
* destroy_ns - remove everything contained by @ ns
2017-01-16 11:42:24 +03:00
* @ ns : namespace to have it contents removed ( NOT NULL )
2017-01-16 11:42:15 +03:00
*/
2017-01-16 11:42:16 +03:00
static void destroy_ns ( struct aa_ns * ns )
2017-01-16 11:42:15 +03:00
{
if ( ! ns )
return ;
2017-11-21 10:24:09 +03:00
mutex_lock_nested ( & ns - > lock , ns - > level ) ;
2017-01-16 11:42:15 +03:00
/* release all profiles in this namespace */
__aa_profile_list_release ( & ns - > base . profiles ) ;
/* release all sub namespaces */
__ns_list_release ( & ns - > sub_ns ) ;
2017-06-09 18:14:28 +03:00
if ( ns - > parent ) {
unsigned long flags ;
write_lock_irqsave ( & ns - > labels . lock , flags ) ;
__aa_proxy_redirect ( ns_unconfined ( ns ) ,
ns_unconfined ( ns - > parent ) ) ;
write_unlock_irqrestore ( & ns - > labels . lock , flags ) ;
}
2017-05-25 16:23:42 +03:00
__aafs_ns_rmdir ( ns ) ;
2017-01-16 11:42:15 +03:00
mutex_unlock ( & ns - > lock ) ;
}
/**
2017-01-16 11:42:16 +03:00
* __aa_remove_ns - remove a namespace and all its children
2017-01-16 11:42:15 +03:00
* @ ns : namespace to be removed ( NOT NULL )
*
* Requires : ns - > parent - > lock be held and ns removed from parent .
*/
2017-01-16 11:42:16 +03:00
void __aa_remove_ns ( struct aa_ns * ns )
2017-01-16 11:42:15 +03:00
{
/* remove ns from namespace list */
list_del_rcu ( & ns - > base . list ) ;
2017-01-16 11:42:16 +03:00
destroy_ns ( ns ) ;
aa_put_ns ( ns ) ;
2017-01-16 11:42:15 +03:00
}
/**
* __ns_list_release - remove all profile namespaces on the list put refs
* @ head : list of profile namespaces ( NOT NULL )
*
* Requires : namespace lock be held
*/
static void __ns_list_release ( struct list_head * head )
{
2017-01-16 11:42:16 +03:00
struct aa_ns * ns , * tmp ;
2017-01-16 11:42:15 +03:00
list_for_each_entry_safe ( ns , tmp , head , base . list )
2017-01-16 11:42:16 +03:00
__aa_remove_ns ( ns ) ;
2017-01-16 11:42:15 +03:00
}
/**
2017-01-16 11:42:24 +03:00
* aa_alloc_root_ns - allocate the root profile namespace
2017-01-16 11:42:15 +03:00
*
* Returns : % 0 on success else error
*
*/
int __init aa_alloc_root_ns ( void )
{
/* released by aa_free_root_ns - used as list ref*/
2017-01-16 11:42:16 +03:00
root_ns = alloc_ns ( NULL , " root " ) ;
2017-01-16 11:42:15 +03:00
if ( ! root_ns )
return - ENOMEM ;
return 0 ;
}
/**
* aa_free_root_ns - free the root profile namespace
*/
void __init aa_free_root_ns ( void )
{
2017-01-16 11:42:16 +03:00
struct aa_ns * ns = root_ns ;
2017-01-16 11:42:15 +03:00
root_ns = NULL ;
2017-01-16 11:42:16 +03:00
destroy_ns ( ns ) ;
aa_put_ns ( ns ) ;
2017-01-16 11:42:15 +03:00
}