Skip to content

Commit

Permalink
userns: Implent proc namespace operations
Browse files Browse the repository at this point in the history
This allows entering a user namespace, and the ability
to store a reference to a user namespace with a bind
mount.

Addition of missing userns_ns_put in userns_install
from Gao feng <[email protected]>

Acked-by: Serge Hallyn <[email protected]>
Signed-off-by: "Eric W. Biederman" <[email protected]>
  • Loading branch information
ebiederm committed Nov 20, 2012
1 parent 4c44aaa commit cde1975
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 17 deletions.
4 changes: 4 additions & 0 deletions fs/proc/namespaces.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <net/net_namespace.h>
#include <linux/ipc_namespace.h>
#include <linux/pid_namespace.h>
#include <linux/user_namespace.h>
#include "internal.h"


Expand All @@ -26,6 +27,9 @@ static const struct proc_ns_operations *ns_entries[] = {
#endif
#ifdef CONFIG_PID_NS
&pidns_operations,
#endif
#ifdef CONFIG_USER_NS
&userns_operations,
#endif
&mntns_operations,
};
Expand Down
1 change: 1 addition & 0 deletions include/linux/proc_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ extern const struct proc_ns_operations netns_operations;
extern const struct proc_ns_operations utsns_operations;
extern const struct proc_ns_operations ipcns_operations;
extern const struct proc_ns_operations pidns_operations;
extern const struct proc_ns_operations userns_operations;
extern const struct proc_ns_operations mntns_operations;

union proc_op {
Expand Down
90 changes: 73 additions & 17 deletions kernel/user_namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <linux/nsproxy.h>
#include <linux/slab.h>
#include <linux/user_namespace.h>
#include <linux/proc_fs.h>
#include <linux/highuid.h>
#include <linux/cred.h>
#include <linux/securebits.h>
Expand All @@ -26,6 +27,24 @@ static struct kmem_cache *user_ns_cachep __read_mostly;
static bool new_idmap_permitted(struct user_namespace *ns, int cap_setid,
struct uid_gid_map *map);

static void set_cred_user_ns(struct cred *cred, struct user_namespace *user_ns)
{
/* Start with the same capabilities as init but useless for doing
* anything as the capabilities are bound to the new user namespace.
*/
cred->securebits = SECUREBITS_DEFAULT;
cred->cap_inheritable = CAP_EMPTY_SET;
cred->cap_permitted = CAP_FULL_SET;
cred->cap_effective = CAP_FULL_SET;
cred->cap_bset = CAP_FULL_SET;
#ifdef CONFIG_KEYS
key_put(cred->request_key_auth);
cred->request_key_auth = NULL;
#endif
/* tgcred will be cleared in our caller bc CLONE_THREAD won't be set */
cred->user_ns = user_ns;
}

/*
* 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
Expand Down Expand Up @@ -53,27 +72,12 @@ int create_user_ns(struct cred *new)
return -ENOMEM;

kref_init(&ns->kref);
/* Leave the new->user_ns reference with the new user namespace. */
ns->parent = parent_ns;
ns->owner = owner;
ns->group = group;

/* 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;
set_cred_user_ns(new, ns);

return 0;
}
Expand Down Expand Up @@ -737,6 +741,58 @@ static bool new_idmap_permitted(struct user_namespace *ns, int cap_setid,
return false;
}

static void *userns_get(struct task_struct *task)
{
struct user_namespace *user_ns;

rcu_read_lock();
user_ns = get_user_ns(__task_cred(task)->user_ns);
rcu_read_unlock();

return user_ns;
}

static void userns_put(void *ns)
{
put_user_ns(ns);
}

static int userns_install(struct nsproxy *nsproxy, void *ns)
{
struct user_namespace *user_ns = ns;
struct cred *cred;

/* Don't allow gaining capabilities by reentering
* the same user namespace.
*/
if (user_ns == current_user_ns())
return -EINVAL;

/* Threaded many not enter a different user namespace */
if (atomic_read(&current->mm->mm_users) > 1)
return -EINVAL;

if (!ns_capable(user_ns, CAP_SYS_ADMIN))
return -EPERM;

cred = prepare_creds();
if (!cred)
return -ENOMEM;

put_user_ns(cred->user_ns);
set_cred_user_ns(cred, get_user_ns(user_ns));

return commit_creds(cred);
}

const struct proc_ns_operations userns_operations = {
.name = "user",
.type = CLONE_NEWUSER,
.get = userns_get,
.put = userns_put,
.install = userns_install,
};

static __init int user_namespaces_init(void)
{
user_ns_cachep = KMEM_CACHE(user_namespace, SLAB_PANIC);
Expand Down

0 comments on commit cde1975

Please sign in to comment.