2016-07-30 21:58:49 +03:00
/*
* 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/stat.h>
# include <linux/sysctl.h>
# include <linux/slab.h>
2016-08-08 21:54:50 +03:00
# include <linux/hash.h>
2016-07-30 21:58:49 +03:00
# include <linux/user_namespace.h>
2016-08-08 21:54:50 +03:00
# define UCOUNTS_HASHTABLE_BITS 10
static struct hlist_head ucounts_hashtable [ ( 1 < < UCOUNTS_HASHTABLE_BITS ) ] ;
static DEFINE_SPINLOCK ( ucounts_lock ) ;
# define ucounts_hashfn(ns, uid) \
hash_long ( ( unsigned long ) __kuid_val ( uid ) + ( unsigned long ) ( ns ) , \
UCOUNTS_HASHTABLE_BITS )
# define ucounts_hashentry(ns, uid) \
( ucounts_hashtable + ucounts_hashfn ( ns , uid ) )
2016-07-30 21:58:49 +03:00
# ifdef CONFIG_SYSCTL
static struct ctl_table_set *
set_lookup ( struct ctl_table_root * root )
{
return & current_user_ns ( ) - > set ;
}
static int set_is_seen ( struct ctl_table_set * set )
{
return & current_user_ns ( ) - > set = = set ;
}
static int set_permissions ( struct ctl_table_header * head ,
struct ctl_table * table )
{
struct user_namespace * user_ns =
container_of ( head - > set , struct user_namespace , set ) ;
int mode ;
/* Allow users with CAP_SYS_RESOURCE unrestrained access */
if ( ns_capable ( user_ns , CAP_SYS_RESOURCE ) )
mode = ( table - > mode & S_IRWXU ) > > 6 ;
else
/* Allow all others at most read-only access */
mode = table - > mode & S_IROTH ;
return ( mode < < 6 ) | ( mode < < 3 ) | mode ;
}
static struct ctl_table_root set_root = {
. lookup = set_lookup ,
. permissions = set_permissions ,
} ;
2016-08-08 21:41:24 +03:00
static int zero = 0 ;
static int int_max = INT_MAX ;
2016-08-08 21:54:50 +03:00
static struct ctl_table user_table [ ] = {
2016-08-08 21:41:24 +03:00
{
. procname = " max_user_namespaces " ,
. data = & init_user_ns . max_user_namespaces ,
. maxlen = sizeof ( init_user_ns . max_user_namespaces ) ,
. mode = 0644 ,
. proc_handler = proc_dointvec_minmax ,
. extra1 = & zero ,
. extra2 = & int_max ,
} ,
2016-07-30 21:58:49 +03:00
{ }
} ;
# endif /* CONFIG_SYSCTL */
bool setup_userns_sysctls ( struct user_namespace * ns )
{
# ifdef CONFIG_SYSCTL
struct ctl_table * tbl ;
setup_sysctl_set ( & ns - > set , & set_root , set_is_seen ) ;
2016-08-08 21:54:50 +03:00
tbl = kmemdup ( user_table , sizeof ( user_table ) , GFP_KERNEL ) ;
2016-07-30 21:58:49 +03:00
if ( tbl ) {
2016-08-08 21:41:24 +03:00
tbl [ 0 ] . data = & ns - > max_user_namespaces ;
2016-08-08 21:54:50 +03:00
ns - > sysctls = __register_sysctl_table ( & ns - > set , " user " , tbl ) ;
2016-07-30 21:58:49 +03:00
}
if ( ! ns - > sysctls ) {
kfree ( tbl ) ;
retire_sysctl_set ( & ns - > set ) ;
return false ;
}
# endif
return true ;
}
void retire_userns_sysctls ( struct user_namespace * ns )
{
# ifdef CONFIG_SYSCTL
struct ctl_table * tbl ;
tbl = ns - > sysctls - > ctl_table_arg ;
unregister_sysctl_table ( ns - > sysctls ) ;
retire_sysctl_set ( & ns - > set ) ;
kfree ( tbl ) ;
# endif
}
2016-08-08 21:54:50 +03:00
static struct ucounts * find_ucounts ( struct user_namespace * ns , kuid_t uid , struct hlist_head * hashent )
{
struct ucounts * ucounts ;
hlist_for_each_entry ( ucounts , hashent , node ) {
if ( uid_eq ( ucounts - > uid , uid ) & & ( ucounts - > ns = = ns ) )
return ucounts ;
}
return NULL ;
}
static struct ucounts * get_ucounts ( struct user_namespace * ns , kuid_t uid )
{
struct hlist_head * hashent = ucounts_hashentry ( ns , uid ) ;
struct ucounts * ucounts , * new ;
spin_lock ( & ucounts_lock ) ;
ucounts = find_ucounts ( ns , uid , hashent ) ;
if ( ! ucounts ) {
spin_unlock ( & ucounts_lock ) ;
new = kzalloc ( sizeof ( * new ) , GFP_KERNEL ) ;
if ( ! new )
return NULL ;
new - > ns = ns ;
new - > uid = uid ;
atomic_set ( & new - > count , 0 ) ;
spin_lock ( & ucounts_lock ) ;
ucounts = find_ucounts ( ns , uid , hashent ) ;
if ( ucounts ) {
kfree ( new ) ;
} else {
hlist_add_head ( & new - > node , hashent ) ;
ucounts = new ;
}
}
if ( ! atomic_add_unless ( & ucounts - > count , 1 , INT_MAX ) )
ucounts = NULL ;
spin_unlock ( & ucounts_lock ) ;
return ucounts ;
}
static void put_ucounts ( struct ucounts * ucounts )
{
if ( atomic_dec_and_test ( & ucounts - > count ) ) {
spin_lock ( & ucounts_lock ) ;
hlist_del_init ( & ucounts - > node ) ;
spin_unlock ( & ucounts_lock ) ;
kfree ( ucounts ) ;
}
}
2016-08-08 21:41:24 +03:00
static inline bool atomic_inc_below ( atomic_t * v , int u )
{
int c , old ;
c = atomic_read ( v ) ;
for ( ; ; ) {
if ( unlikely ( c > = u ) )
return false ;
old = atomic_cmpxchg ( v , c , c + 1 ) ;
if ( likely ( old = = c ) )
return true ;
c = old ;
}
}
2016-08-08 21:54:50 +03:00
struct ucounts * inc_user_namespaces ( struct user_namespace * ns , kuid_t uid )
2016-08-08 21:41:24 +03:00
{
2016-08-08 21:54:50 +03:00
struct ucounts * ucounts , * iter , * bad ;
struct user_namespace * tns ;
ucounts = get_ucounts ( ns , uid ) ;
for ( iter = ucounts ; iter ; iter = tns - > ucounts ) {
int max ;
tns = iter - > ns ;
max = READ_ONCE ( tns - > max_user_namespaces ) ;
if ( ! atomic_inc_below ( & iter - > user_namespaces , max ) )
2016-08-08 21:41:24 +03:00
goto fail ;
}
2016-08-08 21:54:50 +03:00
return ucounts ;
2016-08-08 21:41:24 +03:00
fail :
2016-08-08 21:54:50 +03:00
bad = iter ;
for ( iter = ucounts ; iter ! = bad ; iter = iter - > ns - > ucounts )
atomic_dec ( & iter - > user_namespaces ) ;
2016-08-08 21:41:24 +03:00
2016-08-08 21:54:50 +03:00
put_ucounts ( ucounts ) ;
return NULL ;
2016-08-08 21:41:24 +03:00
}
2016-08-08 21:54:50 +03:00
void dec_user_namespaces ( struct ucounts * ucounts )
2016-08-08 21:41:24 +03:00
{
2016-08-08 21:54:50 +03:00
struct ucounts * iter ;
for ( iter = ucounts ; iter ; iter = iter - > ns - > ucounts ) {
int dec = atomic_dec_if_positive ( & iter - > user_namespaces ) ;
2016-08-08 21:41:24 +03:00
WARN_ON_ONCE ( dec < 0 ) ;
}
2016-08-08 21:54:50 +03:00
put_ucounts ( ucounts ) ;
2016-08-08 21:41:24 +03:00
}
2016-08-08 21:54:50 +03:00
2016-07-30 21:58:49 +03:00
static __init int user_namespace_sysctl_init ( void )
{
# ifdef CONFIG_SYSCTL
2016-08-08 21:54:50 +03:00
static struct ctl_table_header * user_header ;
2016-07-30 21:58:49 +03:00
static struct ctl_table empty [ 1 ] ;
/*
2016-08-08 21:54:50 +03:00
* It is necessary to register the user directory in the
2016-07-30 21:58:49 +03:00
* default set so that registrations in the child sets work
* properly .
*/
2016-08-08 21:54:50 +03:00
user_header = register_sysctl ( " user " , empty ) ;
BUG_ON ( ! user_header ) ;
2016-07-30 21:58:49 +03:00
BUG_ON ( ! setup_userns_sysctls ( & init_user_ns ) ) ;
# endif
return 0 ;
}
subsys_initcall ( user_namespace_sysctl_init ) ;