cred: Refcount the user_ns pointed to by the cred.
authorEric W. Biederman <ebiederm@xmission.com>
Thu, 17 Nov 2011 05:52:53 +0000 (21:52 -0800)
committerEric W. Biederman <ebiederm@xmission.com>
Sat, 7 Apr 2012 23:55:52 +0000 (16:55 -0700)
struct user_struct will shortly loose it's user_ns reference
so make the cred user_ns reference a proper reference complete
with reference counting.

Acked-by: Serge Hallyn <serge.hallyn@canonical.com>
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
include/linux/cred.h
kernel/cred.c
kernel/user_namespace.c
security/keys/process_keys.c

index d12c4e475c15914d7a9a0193f198b870b1365b5c..2c60ec802678b6437e169fc83546feb1fe1c298a 100644 (file)
@@ -146,7 +146,7 @@ struct cred {
        void            *security;      /* subjective LSM security */
 #endif
        struct user_struct *user;       /* real user ID subscription */
-       struct user_namespace *user_ns; /* cached user->user_ns */
+       struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
        struct group_info *group_info;  /* supplementary groups for euid/fsgid */
        struct rcu_head rcu;            /* RCU deletion hook */
 };
index 97b36eeca4c90b51cf9fa0abcd81af72e1dfb715..7a0d80669886be609b0bace5e1cb474711606356 100644 (file)
@@ -148,6 +148,7 @@ static void put_cred_rcu(struct rcu_head *rcu)
        if (cred->group_info)
                put_group_info(cred->group_info);
        free_uid(cred->user);
+       put_user_ns(cred->user_ns);
        kmem_cache_free(cred_jar, cred);
 }
 
@@ -303,6 +304,7 @@ struct cred *prepare_creds(void)
        set_cred_subscribers(new, 0);
        get_group_info(new->group_info);
        get_uid(new->user);
+       get_user_ns(new->user_ns);
 
 #ifdef CONFIG_KEYS
        key_get(new->thread_keyring);
@@ -412,11 +414,6 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
                        goto error_put;
        }
 
-       /* cache user_ns in cred.  Doesn't need a refcount because it will
-        * stay pinned by cred->user
-        */
-       new->user_ns = new->user->user_ns;
-
 #ifdef CONFIG_KEYS
        /* new threads get their own thread keyrings if their parent already
         * had one */
@@ -676,6 +673,7 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
        atomic_set(&new->usage, 1);
        set_cred_subscribers(new, 0);
        get_uid(new->user);
+       get_user_ns(new->user_ns);
        get_group_info(new->group_info);
 
 #ifdef CONFIG_KEYS
index f084083a0fd3f9e119016baf52c3c2b3ce53c58a..58bb8781a7784e40b660bb091a103bbd365c296c 100644 (file)
@@ -24,7 +24,7 @@ static struct kmem_cache *user_ns_cachep __read_mostly;
  */
 int create_user_ns(struct cred *new)
 {
-       struct user_namespace *ns;
+       struct user_namespace *ns, *parent_ns = new->user_ns;
        struct user_struct *root_user;
        int n;
 
@@ -57,8 +57,10 @@ int create_user_ns(struct cred *new)
 #endif
        /* tgcred will be cleared in our caller bc CLONE_THREAD won't be set */
 
-       /* root_user holds a reference to ns, our reference can be dropped */
-       put_user_ns(ns);
+       /* Leave the reference to our user_ns with the new cred */
+       new->user_ns = ns;
+
+       put_user_ns(parent_ns);
 
        return 0;
 }
index 70febff06da914ffce830aaad683650070fc8bdb..447fb7618ff38d4bda9d7d3282243c31be8f8c5c 100644 (file)
@@ -858,7 +858,7 @@ void key_replace_session_keyring(void)
        new-> sgid      = old-> sgid;
        new->fsgid      = old->fsgid;
        new->user       = get_uid(old->user);
-       new->user_ns    = new->user_ns;
+       new->user_ns    = get_user_ns(new->user_ns);
        new->group_info = get_group_info(old->group_info);
 
        new->securebits = old->securebits;