Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris...
[~shefty/rdma-dev.git] / security / keys / process_keys.c
index 86468f385fc8ef1ac0422afb117114f8d81b6ba4..58dfe089094793030f56fdb895f6621e403e4157 100644 (file)
@@ -45,10 +45,12 @@ int install_user_keyrings(void)
        struct user_struct *user;
        const struct cred *cred;
        struct key *uid_keyring, *session_keyring;
+       key_perm_t user_keyring_perm;
        char buf[20];
        int ret;
        uid_t uid;
 
+       user_keyring_perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL;
        cred = current_cred();
        user = cred->user;
        uid = from_kuid(cred->user_ns, user->uid);
@@ -73,8 +75,8 @@ int install_user_keyrings(void)
                uid_keyring = find_keyring_by_name(buf, true);
                if (IS_ERR(uid_keyring)) {
                        uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID,
-                                                   cred, KEY_ALLOC_IN_QUOTA,
-                                                   NULL);
+                                                   cred, user_keyring_perm,
+                                                   KEY_ALLOC_IN_QUOTA, NULL);
                        if (IS_ERR(uid_keyring)) {
                                ret = PTR_ERR(uid_keyring);
                                goto error;
@@ -89,7 +91,8 @@ int install_user_keyrings(void)
                if (IS_ERR(session_keyring)) {
                        session_keyring =
                                keyring_alloc(buf, user->uid, INVALID_GID,
-                                             cred, KEY_ALLOC_IN_QUOTA, NULL);
+                                             cred, user_keyring_perm,
+                                             KEY_ALLOC_IN_QUOTA, NULL);
                        if (IS_ERR(session_keyring)) {
                                ret = PTR_ERR(session_keyring);
                                goto error_release;
@@ -130,6 +133,7 @@ int install_thread_keyring_to_cred(struct cred *new)
        struct key *keyring;
 
        keyring = keyring_alloc("_tid", new->uid, new->gid, new,
+                               KEY_POS_ALL | KEY_USR_VIEW,
                                KEY_ALLOC_QUOTA_OVERRUN, NULL);
        if (IS_ERR(keyring))
                return PTR_ERR(keyring);
@@ -170,27 +174,18 @@ static int install_thread_keyring(void)
 int install_process_keyring_to_cred(struct cred *new)
 {
        struct key *keyring;
-       int ret;
 
-       if (new->tgcred->process_keyring)
+       if (new->process_keyring)
                return -EEXIST;
 
-       keyring = keyring_alloc("_pid", new->uid, new->gid,
-                               new, KEY_ALLOC_QUOTA_OVERRUN, NULL);
+       keyring = keyring_alloc("_pid", new->uid, new->gid, new,
+                               KEY_POS_ALL | KEY_USR_VIEW,
+                               KEY_ALLOC_QUOTA_OVERRUN, NULL);
        if (IS_ERR(keyring))
                return PTR_ERR(keyring);
 
-       spin_lock_irq(&new->tgcred->lock);
-       if (!new->tgcred->process_keyring) {
-               new->tgcred->process_keyring = keyring;
-               keyring = NULL;
-               ret = 0;
-       } else {
-               ret = -EEXIST;
-       }
-       spin_unlock_irq(&new->tgcred->lock);
-       key_put(keyring);
-       return ret;
+       new->process_keyring = keyring;
+       return 0;
 }
 
 /*
@@ -231,11 +226,12 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
        /* create an empty session keyring */
        if (!keyring) {
                flags = KEY_ALLOC_QUOTA_OVERRUN;
-               if (cred->tgcred->session_keyring)
+               if (cred->session_keyring)
                        flags = KEY_ALLOC_IN_QUOTA;
 
-               keyring = keyring_alloc("_ses", cred->uid, cred->gid,
-                                       cred, flags, NULL);
+               keyring = keyring_alloc("_ses", cred->uid, cred->gid, cred,
+                                       KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,
+                                       flags, NULL);
                if (IS_ERR(keyring))
                        return PTR_ERR(keyring);
        } else {
@@ -243,17 +239,11 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
        }
 
        /* install the keyring */
-       spin_lock_irq(&cred->tgcred->lock);
-       old = cred->tgcred->session_keyring;
-       rcu_assign_pointer(cred->tgcred->session_keyring, keyring);
-       spin_unlock_irq(&cred->tgcred->lock);
-
-       /* we're using RCU on the pointer, but there's no point synchronising
-        * on it if it didn't previously point to anything */
-       if (old) {
-               synchronize_rcu();
+       old = cred->session_keyring;
+       rcu_assign_pointer(cred->session_keyring, keyring);
+
+       if (old)
                key_put(old);
-       }
 
        return 0;
 }
@@ -368,9 +358,9 @@ key_ref_t search_my_process_keyrings(struct key_type *type,
        }
 
        /* search the process keyring second */
-       if (cred->tgcred->process_keyring) {
+       if (cred->process_keyring) {
                key_ref = keyring_search_aux(
-                       make_key_ref(cred->tgcred->process_keyring, 1),
+                       make_key_ref(cred->process_keyring, 1),
                        cred, type, description, match, no_state_check);
                if (!IS_ERR(key_ref))
                        goto found;
@@ -389,12 +379,10 @@ key_ref_t search_my_process_keyrings(struct key_type *type,
        }
 
        /* search the session keyring */
-       if (cred->tgcred->session_keyring) {
+       if (cred->session_keyring) {
                rcu_read_lock();
                key_ref = keyring_search_aux(
-                       make_key_ref(rcu_dereference(
-                                            cred->tgcred->session_keyring),
-                                    1),
+                       make_key_ref(rcu_dereference(cred->session_keyring), 1),
                        cred, type, description, match, no_state_check);
                rcu_read_unlock();
 
@@ -564,7 +552,7 @@ try_again:
                break;
 
        case KEY_SPEC_PROCESS_KEYRING:
-               if (!cred->tgcred->process_keyring) {
+               if (!cred->process_keyring) {
                        if (!(lflags & KEY_LOOKUP_CREATE))
                                goto error;
 
@@ -576,13 +564,13 @@ try_again:
                        goto reget_creds;
                }
 
-               key = cred->tgcred->process_keyring;
+               key = cred->process_keyring;
                atomic_inc(&key->usage);
                key_ref = make_key_ref(key, 1);
                break;
 
        case KEY_SPEC_SESSION_KEYRING:
-               if (!cred->tgcred->session_keyring) {
+               if (!cred->session_keyring) {
                        /* always install a session keyring upon access if one
                         * doesn't exist yet */
                        ret = install_user_keyrings();
@@ -597,7 +585,7 @@ try_again:
                        if (ret < 0)
                                goto error;
                        goto reget_creds;
-               } else if (cred->tgcred->session_keyring ==
+               } else if (cred->session_keyring ==
                           cred->user->session_keyring &&
                           lflags & KEY_LOOKUP_CREATE) {
                        ret = join_session_keyring(NULL);
@@ -607,7 +595,7 @@ try_again:
                }
 
                rcu_read_lock();
-               key = rcu_dereference(cred->tgcred->session_keyring);
+               key = rcu_dereference(cred->session_keyring);
                atomic_inc(&key->usage);
                rcu_read_unlock();
                key_ref = make_key_ref(key, 1);
@@ -767,12 +755,6 @@ long join_session_keyring(const char *name)
        struct key *keyring;
        long ret, serial;
 
-       /* only permit this if there's a single thread in the thread group -
-        * this avoids us having to adjust the creds on all threads and risking
-        * ENOMEM */
-       if (!current_is_single_threaded())
-               return -EMLINK;
-
        new = prepare_creds();
        if (!new)
                return -ENOMEM;
@@ -784,7 +766,7 @@ long join_session_keyring(const char *name)
                if (ret < 0)
                        goto error;
 
-               serial = new->tgcred->session_keyring->serial;
+               serial = new->session_keyring->serial;
                ret = commit_creds(new);
                if (ret == 0)
                        ret = serial;
@@ -798,8 +780,10 @@ long join_session_keyring(const char *name)
        keyring = find_keyring_by_name(name, false);
        if (PTR_ERR(keyring) == -ENOKEY) {
                /* not found - try and create a new one */
-               keyring = keyring_alloc(name, old->uid, old->gid, old,
-                                       KEY_ALLOC_IN_QUOTA, NULL);
+               keyring = keyring_alloc(
+                       name, old->uid, old->gid, old,
+                       KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_LINK,
+                       KEY_ALLOC_IN_QUOTA, NULL);
                if (IS_ERR(keyring)) {
                        ret = PTR_ERR(keyring);
                        goto error2;
@@ -807,6 +791,9 @@ long join_session_keyring(const char *name)
        } else if (IS_ERR(keyring)) {
                ret = PTR_ERR(keyring);
                goto error2;
+       } else if (keyring == new->session_keyring) {
+               ret = 0;
+               goto error2;
        }
 
        /* we've got a keyring - now to install it */
@@ -863,8 +850,7 @@ void key_change_session_keyring(struct callback_head *twork)
 
        new->jit_keyring        = old->jit_keyring;
        new->thread_keyring     = key_get(old->thread_keyring);
-       new->tgcred->tgid       = old->tgcred->tgid;
-       new->tgcred->process_keyring = key_get(old->tgcred->process_keyring);
+       new->process_keyring    = key_get(old->process_keyring);
 
        security_transfer_creds(new, old);