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 .
*/
# include <linux/module.h>
# 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>
2008-10-16 01:38:45 +04:00
# include <linux/cred.h>
2007-07-16 10:40:59 +04:00
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
{
struct user_namespace * ns ;
2008-10-16 01:38:45 +04:00
struct user_struct * root_user ;
2007-07-16 10:41:01 +04:00
int n ;
ns = kmalloc ( sizeof ( struct user_namespace ) , GFP_KERNEL ) ;
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 ) {
2007-07-16 10:41:01 +04:00
kfree ( 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
2008-10-16 01:38:45 +04:00
/* alloc_uid() incremented the userns refcount. Just set it to 1 */
kref_set ( & ns - > kref , 1 ) ;
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 ) ;
2007-07-16 10:40:59 +04:00
kfree ( ns ) ;
}
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 ) ;