2007-07-16 10:40:59 +04: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 .
*/
2011-05-23 22:51:41 +04:00
# include <linux/export.h>
2007-07-16 10:40:59 +04:00
# include <linux/nsproxy.h>
2008-04-29 11:59:25 +04:00
# include <linux/slab.h>
2007-07-16 10:40:59 +04:00
# include <linux/user_namespace.h>
2010-06-13 07:28:03 +04:00
# include <linux/highuid.h>
2008-10-16 01:38:45 +04:00
# include <linux/cred.h>
2007-07-16 10:40:59 +04:00
2011-01-13 04:00:46 +03:00
static struct kmem_cache * user_ns_cachep __read_mostly ;
2007-07-16 10:41:01 +04:00
/*
2008-10-16 01:38:45 +04:00
* Create a new user namespace , deriving the creator from the user in the
* passed credentials , and replacing that user with the new root user for the
* new namespace .
*
* This is called by copy_creds ( ) , which will finish setting the target task ' s
* credentials .
2007-07-16 10:41:01 +04:00
*/
2008-10-16 01:38:45 +04:00
int create_user_ns ( struct cred * new )
2007-07-16 10:41:01 +04:00
{
2011-11-17 09:52:53 +04:00
struct user_namespace * ns , * parent_ns = new - > user_ns ;
2008-10-16 01:38:45 +04:00
struct user_struct * root_user ;
2007-07-16 10:41:01 +04:00
int n ;
2011-01-13 04:00:46 +03:00
ns = kmem_cache_alloc ( user_ns_cachep , GFP_KERNEL ) ;
2007-07-16 10:41:01 +04:00
if ( ! ns )
2008-10-16 01:38:45 +04:00
return - ENOMEM ;
2007-07-16 10:41:01 +04:00
kref_init ( & ns - > kref ) ;
for ( n = 0 ; n < UIDHASH_SZ ; + + n )
2007-09-19 09:46:44 +04:00
INIT_HLIST_HEAD ( ns - > uidhash_table + n ) ;
2007-07-16 10:41:01 +04:00
2008-10-16 01:38:45 +04:00
/* Alloc new root user. */
root_user = alloc_uid ( ns , 0 ) ;
if ( ! root_user ) {
2011-01-13 04:00:46 +03:00
kmem_cache_free ( user_ns_cachep , ns ) ;
2008-10-16 01:38:45 +04:00
return - ENOMEM ;
2007-07-16 10:41:01 +04:00
}
2008-10-16 01:38:45 +04:00
/* set the new root user in the credentials under preparation */
ns - > creator = new - > user ;
new - > user = root_user ;
new - > uid = new - > euid = new - > suid = new - > fsuid = 0 ;
new - > gid = new - > egid = new - > sgid = new - > fsgid = 0 ;
put_group_info ( new - > group_info ) ;
new - > group_info = get_group_info ( & init_groups ) ;
# ifdef CONFIG_KEYS
key_put ( new - > request_key_auth ) ;
new - > request_key_auth = NULL ;
# endif
/* tgcred will be cleared in our caller bc CLONE_THREAD won't be set */
2007-07-16 10:41:01 +04:00
2011-11-17 09:52:53 +04:00
/* Leave the reference to our user_ns with the new cred */
new - > user_ns = ns ;
put_user_ns ( parent_ns ) ;
2007-07-16 10:41:01 +04:00
2008-10-16 01:38:45 +04:00
return 0 ;
2007-07-16 10:40:59 +04:00
}
2009-02-28 01:03:03 +03:00
/*
* Deferred destructor for a user namespace . This is required because
* free_user_ns ( ) may be called with uidhash_lock held , but we need to call
* back to free_uid ( ) which will want to take the lock again .
*/
static void free_user_ns_work ( struct work_struct * work )
2007-07-16 10:40:59 +04:00
{
2009-02-28 01:03:03 +03:00
struct user_namespace * ns =
container_of ( work , struct user_namespace , destroyer ) ;
2008-10-16 01:38:45 +04:00
free_uid ( ns - > creator ) ;
2011-01-13 04:00:46 +03:00
kmem_cache_free ( user_ns_cachep , ns ) ;
2007-07-16 10:40:59 +04:00
}
2009-02-28 01:03:03 +03:00
void free_user_ns ( struct kref * kref )
{
struct user_namespace * ns =
container_of ( kref , struct user_namespace , kref ) ;
INIT_WORK ( & ns - > destroyer , free_user_ns_work ) ;
schedule_work ( & ns - > destroyer ) ;
}
2008-04-29 11:59:52 +04:00
EXPORT_SYMBOL ( free_user_ns ) ;
2010-06-13 07:28:03 +04:00
uid_t user_ns_map_uid ( struct user_namespace * to , const struct cred * cred , uid_t uid )
{
struct user_namespace * tmp ;
2011-11-17 11:15:31 +04:00
if ( likely ( to = = cred - > user_ns ) )
2010-06-13 07:28:03 +04:00
return uid ;
/* Is cred->user the creator of the target user_ns
* or the creator of one of it ' s parents ?
*/
for ( tmp = to ; tmp ! = & init_user_ns ;
tmp = tmp - > creator - > user_ns ) {
if ( cred - > user = = tmp - > creator ) {
return ( uid_t ) 0 ;
}
}
/* No useful relationship so no mapping */
return overflowuid ;
}
gid_t user_ns_map_gid ( struct user_namespace * to , const struct cred * cred , gid_t gid )
{
struct user_namespace * tmp ;
2011-11-17 11:15:31 +04:00
if ( likely ( to = = cred - > user_ns ) )
2010-06-13 07:28:03 +04:00
return gid ;
/* Is cred->user the creator of the target user_ns
* or the creator of one of it ' s parents ?
*/
for ( tmp = to ; tmp ! = & init_user_ns ;
tmp = tmp - > creator - > user_ns ) {
if ( cred - > user = = tmp - > creator ) {
return ( gid_t ) 0 ;
}
}
/* No useful relationship so no mapping */
return overflowgid ;
}
2011-01-13 04:00:46 +03:00
static __init int user_namespaces_init ( void )
{
user_ns_cachep = KMEM_CACHE ( user_namespace , SLAB_PANIC ) ;
return 0 ;
}
module_init ( user_namespaces_init ) ;