summaryrefslogblamecommitdiff
path: root/kernel/user_namespace.c
blob: ed08836558e038bd287ab14be81eca78482ac6e0 (plain) (tree)
1
2
3
4
5
6
7
8
9






                                                                  
                         
                          
                       
                                 
                          
                       
                             
 

                                                       
  





                                                                              
   
                                    
 
                                                             
                                      









                                                                    
 
                                                          
                
                               


                             
                                   
                                                
                         
                                                    
                               

         
                                                                        
                               


                            




                                                          







                                                                            




                                                                               
 

                                                                           

                          
                 

 
                                    
 
                                            

                                                                
                            
                                            
                            
 
                            




                                                                                    
                                        

                           


                                                          
                                                                   
                                                          











                                                                                    
                                        




                                                          
                                                                   
                                                          






                                                  






                                                                
/*
 *  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/export.h>
#include <linux/nsproxy.h>
#include <linux/slab.h>
#include <linux/user_namespace.h>
#include <linux/highuid.h>
#include <linux/cred.h>
#include <linux/securebits.h>

static struct kmem_cache *user_ns_cachep __read_mostly;

/*
 * 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.
 */
int create_user_ns(struct cred *new)
{
	struct user_namespace *ns, *parent_ns = new->user_ns;
	struct user_struct *root_user;
	kuid_t owner = make_kuid(new->user_ns, new->euid);
	kgid_t group = make_kgid(new->user_ns, new->egid);

	/* The creator needs a mapping in the parent user namespace
	 * or else we won't be able to reasonably tell userspace who
	 * created a user_namespace.
	 */
	if (!kuid_has_mapping(parent_ns, owner) ||
	    !kgid_has_mapping(parent_ns, group))
		return -EPERM;

	ns = kmem_cache_alloc(user_ns_cachep, GFP_KERNEL);
	if (!ns)
		return -ENOMEM;

	kref_init(&ns->kref);

	/* Alloc new root user.  */
	root_user = alloc_uid(make_kuid(ns, 0));
	if (!root_user) {
		kmem_cache_free(user_ns_cachep, ns);
		return -ENOMEM;
	}

	/* set the new root user in the credentials under preparation */
	ns->parent = parent_ns;
	ns->owner = owner;
	ns->group = group;
	free_uid(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);
	/* Start with the same capabilities as init but useless for doing
	 * anything as the capabilities are bound to the new user namespace.
	 */
	new->securebits = SECUREBITS_DEFAULT;
	new->cap_inheritable = CAP_EMPTY_SET;
	new->cap_permitted = CAP_FULL_SET;
	new->cap_effective = CAP_FULL_SET;
	new->cap_bset = CAP_FULL_SET;
#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 */

	/* Leave the new->user_ns reference with the new user namespace. */
	/* Leave the reference to our user_ns with the new cred. */
	new->user_ns = ns;

	return 0;
}

void free_user_ns(struct kref *kref)
{
	struct user_namespace *parent, *ns =
		container_of(kref, struct user_namespace, kref);

	parent = ns->parent;
	kmem_cache_free(user_ns_cachep, ns);
	put_user_ns(parent);
}
EXPORT_SYMBOL(free_user_ns);

uid_t user_ns_map_uid(struct user_namespace *to, const struct cred *cred, uid_t uid)
{
	struct user_namespace *tmp;

	if (likely(to == cred->user_ns))
		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->parent ) {
		if (uid_eq(cred->user->uid, tmp->owner)) {
			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;

	if (likely(to == cred->user_ns))
		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->parent ) {
		if (uid_eq(cred->user->uid, tmp->owner)) {
			return (gid_t)0;
		}
	}

	/* No useful relationship so no mapping */
	return overflowgid;
}

static __init int user_namespaces_init(void)
{
	user_ns_cachep = KMEM_CACHE(user_namespace, SLAB_PANIC);
	return 0;
}
module_init(user_namespaces_init);