]> nv-tegra.nvidia Code Review - linux-3.10.git/blobdiff - security/keys/keyring.c
Merge branch 'master' into next
[linux-3.10.git] / security / keys / keyring.c
index c9a5de197487879d1e8ae6900b079a423d75ba57..3f425a65906f1ef9f8432907b45d94c86cab0368 100644 (file)
@@ -1,6 +1,6 @@
-/* keyring.c: keyring handling
+/* Keyring handling
  *
- * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
 #include <linux/init.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
+#include <linux/security.h>
 #include <linux/seq_file.h>
 #include <linux/err.h>
-#include <asm/uaccess.h>
+#include <keys/keyring-type.h>
+#include <linux/uaccess.h>
 #include "internal.h"
 
+#define rcu_dereference_locked_keyring(keyring)                                \
+       (rcu_dereference_protected(                                     \
+               (keyring)->payload.subscriptions,                       \
+               rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem)))
+
 /*
  * when plumbing the depths of the key tree, this sets a hard limit set on how
  * deep we're willing to go
@@ -37,7 +44,7 @@ static inline unsigned keyring_hash(const char *desc)
        unsigned bucket = 0;
 
        for (; *desc; desc++)
-               bucket += (unsigned char) *desc;
+               bucket += (unsigned char)*desc;
 
        return bucket & (KEYRING_NAME_HASH_SIZE - 1);
 }
@@ -47,8 +54,8 @@ static inline unsigned keyring_hash(const char *desc)
  */
 static int keyring_instantiate(struct key *keyring,
                               const void *data, size_t datalen);
-static int keyring_duplicate(struct key *keyring, const struct key *source);
 static int keyring_match(const struct key *keyring, const void *criterion);
+static void keyring_revoke(struct key *keyring);
 static void keyring_destroy(struct key *keyring);
 static void keyring_describe(const struct key *keyring, struct seq_file *m);
 static long keyring_read(const struct key *keyring,
@@ -58,25 +65,27 @@ struct key_type key_type_keyring = {
        .name           = "keyring",
        .def_datalen    = sizeof(struct keyring_list),
        .instantiate    = keyring_instantiate,
-       .duplicate      = keyring_duplicate,
        .match          = keyring_match,
+       .revoke         = keyring_revoke,
        .destroy        = keyring_destroy,
        .describe       = keyring_describe,
        .read           = keyring_read,
 };
 
+EXPORT_SYMBOL(key_type_keyring);
+
 /*
  * semaphore to serialise link/link calls to prevent two link calls in parallel
  * introducing a cycle
  */
-DECLARE_RWSEM(keyring_serialise_link_sem);
+static DECLARE_RWSEM(keyring_serialise_link_sem);
 
 /*****************************************************************************/
 /*
  * publish the name of a keyring so that it can be found by name (if it has
  * one)
  */
-void keyring_publish_name(struct key *keyring)
+static void keyring_publish_name(struct key *keyring)
 {
        int bucket;
 
@@ -117,68 +126,6 @@ static int keyring_instantiate(struct key *keyring,
 
 } /* end keyring_instantiate() */
 
-/*****************************************************************************/
-/*
- * duplicate the list of subscribed keys from a source keyring into this one
- */
-static int keyring_duplicate(struct key *keyring, const struct key *source)
-{
-       struct keyring_list *sklist, *klist;
-       unsigned max;
-       size_t size;
-       int loop, ret;
-
-       const unsigned limit =
-               (PAGE_SIZE - sizeof(*klist)) / sizeof(struct key);
-
-       ret = 0;
-
-       /* find out how many keys are currently linked */
-       rcu_read_lock();
-       sklist = rcu_dereference(source->payload.subscriptions);
-       max = 0;
-       if (sklist)
-               max = sklist->nkeys;
-       rcu_read_unlock();
-
-       /* allocate a new payload and stuff load with key links */
-       if (max > 0) {
-               BUG_ON(max > limit);
-
-               max = (max + 3) & ~3;
-               if (max > limit)
-                       max = limit;
-
-               ret = -ENOMEM;
-               size = sizeof(*klist) + sizeof(struct key) * max;
-               klist = kmalloc(size, GFP_KERNEL);
-               if (!klist)
-                       goto error;
-
-               /* set links */
-               rcu_read_lock();
-               sklist = rcu_dereference(source->payload.subscriptions);
-
-               klist->maxkeys = max;
-               klist->nkeys = sklist->nkeys;
-               memcpy(klist->keys,
-                      sklist->keys,
-                      sklist->nkeys * sizeof(struct key));
-
-               for (loop = klist->nkeys - 1; loop >= 0; loop--)
-                       atomic_inc(&klist->keys[loop]->usage);
-
-               rcu_read_unlock();
-
-               rcu_assign_pointer(keyring->payload.subscriptions, klist);
-               ret = 0;
-       }
-
- error:
-       return ret;
-
-} /* end keyring_duplicate() */
-
 /*****************************************************************************/
 /*
  * match keyrings on their name
@@ -201,11 +148,17 @@ static void keyring_destroy(struct key *keyring)
 
        if (keyring->description) {
                write_lock(&keyring_name_lock);
-               list_del(&keyring->type_data.link);
+
+               if (keyring->type_data.link.next != NULL &&
+                   !list_empty(&keyring->type_data.link))
+                       list_del(&keyring->type_data.link);
+
                write_unlock(&keyring_name_lock);
        }
 
-       klist = rcu_dereference(keyring->payload.subscriptions);
+       klist = rcu_dereference_check(keyring->payload.subscriptions,
+                                     rcu_read_lock_held() ||
+                                     atomic_read(&keyring->usage) == 0);
        if (klist) {
                for (loop = klist->nkeys - 1; loop >= 0; loop--)
                        key_put(klist->keys[loop]);
@@ -222,12 +175,10 @@ static void keyring_describe(const struct key *keyring, struct seq_file *m)
 {
        struct keyring_list *klist;
 
-       if (keyring->description) {
+       if (keyring->description)
                seq_puts(m, keyring->description);
-       }
-       else {
+       else
                seq_puts(m, "[anon]");
-       }
 
        rcu_read_lock();
        klist = rcu_dereference(keyring->payload.subscriptions);
@@ -253,8 +204,7 @@ static long keyring_read(const struct key *keyring,
        int loop, ret;
 
        ret = 0;
-       klist = rcu_dereference(keyring->payload.subscriptions);
-
+       klist = rcu_dereference_locked_keyring(keyring);
        if (klist) {
                /* calculate how much data we could return */
                qty = klist->nkeys * sizeof(key_serial_t);
@@ -289,7 +239,7 @@ static long keyring_read(const struct key *keyring,
                ret = qty;
        }
 
- error:
+error:
        return ret;
 
 } /* end keyring_read() */
@@ -299,16 +249,19 @@ static long keyring_read(const struct key *keyring,
  * allocate a keyring and link into the destination keyring
  */
 struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
-                         int not_in_quota, struct key *dest)
+                         const struct cred *cred, unsigned long flags,
+                         struct key *dest)
 {
        struct key *keyring;
        int ret;
 
        keyring = key_alloc(&key_type_keyring, description,
-                           uid, gid, KEY_USR_ALL, not_in_quota);
+                           uid, gid, cred,
+                           (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
+                           flags);
 
        if (!IS_ERR(keyring)) {
-               ret = key_instantiate_and_link(keyring, NULL, 0, dest);
+               ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL);
                if (ret < 0) {
                        key_put(keyring);
                        keyring = ERR_PTR(ret);
@@ -326,14 +279,16 @@ struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
  * - we only find keys on which we have search permission
  * - we use the supplied match function to see if the description (or other
  *   feature of interest) matches
- * - we readlock the keyrings as we search down the tree
+ * - we rely on RCU to prevent the keyring lists from disappearing on us
  * - we return -EAGAIN if we didn't find any matching key
  * - we return -ENOKEY if we only found negative matching keys
+ * - we propagate the possession attribute from the keyring ref to the key ref
  */
-struct key *keyring_search_aux(struct key *keyring,
-                              struct key_type *type,
-                              const void *description,
-                              key_match_func_t match)
+key_ref_t keyring_search_aux(key_ref_t keyring_ref,
+                            const struct cred *cred,
+                            struct key_type *type,
+                            const void *description,
+                            key_match_func_t match)
 {
        struct {
                struct keyring_list *keylist;
@@ -342,29 +297,61 @@ struct key *keyring_search_aux(struct key *keyring,
 
        struct keyring_list *keylist;
        struct timespec now;
-       struct key *key;
+       unsigned long possessed, kflags;
+       struct key *keyring, *key;
+       key_ref_t key_ref;
        long err;
        int sp, kix;
 
+       keyring = key_ref_to_ptr(keyring_ref);
+       possessed = is_key_possessed(keyring_ref);
        key_check(keyring);
 
-       rcu_read_lock();
-
        /* top keyring must have search permission to begin the search */
-       key = ERR_PTR(-EACCES);
-       if (!key_permission(keyring, KEY_SEARCH))
+       err = key_task_permission(keyring_ref, cred, KEY_SEARCH);
+       if (err < 0) {
+               key_ref = ERR_PTR(err);
                goto error;
+       }
 
-       key = ERR_PTR(-ENOTDIR);
+       key_ref = ERR_PTR(-ENOTDIR);
        if (keyring->type != &key_type_keyring)
                goto error;
 
+       rcu_read_lock();
+
        now = current_kernel_time();
        err = -EAGAIN;
        sp = 0;
 
+       /* firstly we should check to see if this top-level keyring is what we
+        * are looking for */
+       key_ref = ERR_PTR(-EAGAIN);
+       kflags = keyring->flags;
+       if (keyring->type == type && match(keyring, description)) {
+               key = keyring;
+
+               /* check it isn't negative and hasn't expired or been
+                * revoked */
+               if (kflags & (1 << KEY_FLAG_REVOKED))
+                       goto error_2;
+               if (key->expiry && now.tv_sec >= key->expiry)
+                       goto error_2;
+               key_ref = ERR_PTR(-ENOKEY);
+               if (kflags & (1 << KEY_FLAG_NEGATIVE))
+                       goto error_2;
+               goto found;
+       }
+
+       /* otherwise, the top keyring must not be revoked, expired, or
+        * negatively instantiated if we are to search it */
+       key_ref = ERR_PTR(-EAGAIN);
+       if (kflags & ((1 << KEY_FLAG_REVOKED) | (1 << KEY_FLAG_NEGATIVE)) ||
+           (keyring->expiry && now.tv_sec >= keyring->expiry))
+               goto error_2;
+
        /* start processing a new keyring */
- descend:
+descend:
        if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
                goto not_this_keyring;
 
@@ -375,13 +362,14 @@ struct key *keyring_search_aux(struct key *keyring,
        /* iterate through the keys in this keyring first */
        for (kix = 0; kix < keylist->nkeys; kix++) {
                key = keylist->keys[kix];
+               kflags = key->flags;
 
                /* ignore keys not of this type */
                if (key->type != type)
                        continue;
 
                /* skip revoked keys and expired keys */
-               if (test_bit(KEY_FLAG_REVOKED, &key->flags))
+               if (kflags & (1 << KEY_FLAG_REVOKED))
                        continue;
 
                if (key->expiry && now.tv_sec >= key->expiry)
@@ -392,11 +380,12 @@ struct key *keyring_search_aux(struct key *keyring,
                        continue;
 
                /* key must have search permissions */
-               if (!key_permission(key, KEY_SEARCH))
+               if (key_task_permission(make_key_ref(key, possessed),
+                                       cred, KEY_SEARCH) < 0)
                        continue;
 
-               /* we set a different error code if we find a negative key */
-               if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) {
+               /* we set a different error code if we pass a negative key */
+               if (kflags & (1 << KEY_FLAG_NEGATIVE)) {
                        err = -ENOKEY;
                        continue;
                }
@@ -406,7 +395,7 @@ struct key *keyring_search_aux(struct key *keyring,
 
        /* search through the keyrings nested in this one */
        kix = 0;
- ascend:
+ascend:
        for (; kix < keylist->nkeys; kix++) {
                key = keylist->keys[kix];
                if (key->type != &key_type_keyring)
@@ -418,7 +407,8 @@ struct key *keyring_search_aux(struct key *keyring,
                if (sp >= KEYRING_SEARCH_MAX_DEPTH)
                        continue;
 
-               if (!key_permission(key, KEY_SEARCH))
+               if (key_task_permission(make_key_ref(key, possessed),
+                                       cred, KEY_SEARCH) < 0)
                        continue;
 
                /* stack the current position */
@@ -433,7 +423,7 @@ struct key *keyring_search_aux(struct key *keyring,
 
        /* the keyring we're looking at was disqualified or didn't contain a
         * matching key */
- not_this_keyring:
+not_this_keyring:
        if (sp > 0) {
                /* resume the processing of a keyring higher up in the tree */
                sp--;
@@ -442,16 +432,18 @@ struct key *keyring_search_aux(struct key *keyring,
                goto ascend;
        }
 
-       key = ERR_PTR(err);
-       goto error;
+       key_ref = ERR_PTR(err);
+       goto error_2;
 
        /* we found a viable match */
- found:
+found:
        atomic_inc(&key->usage);
        key_check(key);
- error:
+       key_ref = make_key_ref(key, possessed);
+error_2:
        rcu_read_unlock();
-       return key;
+error:
+       return key_ref;
 
 } /* end keyring_search_aux() */
 
@@ -464,11 +456,15 @@ struct key *keyring_search_aux(struct key *keyring,
  * - we return -EAGAIN if we didn't find any matching key
  * - we return -ENOKEY if we only found negative matching keys
  */
-struct key *keyring_search(struct key *keyring,
-                          struct key_type *type,
-                          const char *description)
+key_ref_t keyring_search(key_ref_t keyring,
+                        struct key_type *type,
+                        const char *description)
 {
-       return keyring_search_aux(keyring, type, description, type->match);
+       if (!type->match)
+               return ERR_PTR(-ENOKEY);
+
+       return keyring_search_aux(keyring, current->cred,
+                                 type, description, type->match);
 
 } /* end keyring_search() */
 
@@ -478,16 +474,21 @@ EXPORT_SYMBOL(keyring_search);
 /*
  * search the given keyring only (no recursion)
  * - keyring must be locked by caller
+ * - caller must guarantee that the keyring is a keyring
  */
-struct key *__keyring_search_one(struct key *keyring,
-                                const struct key_type *ktype,
-                                const char *description,
-                                key_perm_t perm)
+key_ref_t __keyring_search_one(key_ref_t keyring_ref,
+                              const struct key_type *ktype,
+                              const char *description,
+                              key_perm_t perm)
 {
        struct keyring_list *klist;
-       struct key *key;
+       unsigned long possessed;
+       struct key *keyring, *key;
        int loop;
 
+       keyring = key_ref_to_ptr(keyring_ref);
+       possessed = is_key_possessed(keyring_ref);
+
        rcu_read_lock();
 
        klist = rcu_dereference(keyring->payload.subscriptions);
@@ -496,22 +497,23 @@ struct key *__keyring_search_one(struct key *keyring,
                        key = klist->keys[loop];
 
                        if (key->type == ktype &&
-                           key->type->match(key, description) &&
-                           key_permission(key, perm) &&
+                           (!key->type->match ||
+                            key->type->match(key, description)) &&
+                           key_permission(make_key_ref(key, possessed),
+                                          perm) == 0 &&
                            !test_bit(KEY_FLAG_REVOKED, &key->flags)
                            )
                                goto found;
                }
        }
 
-       key = ERR_PTR(-ENOKEY);
-       goto error;
+       rcu_read_unlock();
+       return ERR_PTR(-ENOKEY);
 
- found:
+found:
        atomic_inc(&key->usage);
- error:
        rcu_read_unlock();
-       return key;
+       return make_key_ref(key, possessed);
 
 } /* end __keyring_search_one() */
 
@@ -519,17 +521,15 @@ struct key *__keyring_search_one(struct key *keyring,
 /*
  * find a keyring with the specified name
  * - all named keyrings are searched
- * - only find keyrings with search permission for the process
- * - only find keyrings with a serial number greater than the one specified
+ * - normally only finds keyrings with search permission for the current process
  */
-struct key *find_keyring_by_name(const char *name, key_serial_t bound)
+struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
 {
        struct key *keyring;
        int bucket;
 
-       keyring = ERR_PTR(-EINVAL);
        if (!name)
-               goto error;
+               return ERR_PTR(-EINVAL);
 
        bucket = keyring_hash(name);
 
@@ -542,31 +542,32 @@ struct key *find_keyring_by_name(const char *name, key_serial_t bound)
                                    &keyring_name_hash[bucket],
                                    type_data.link
                                    ) {
+                       if (keyring->user->user_ns != current_user_ns())
+                               continue;
+
                        if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
                                continue;
 
                        if (strcmp(keyring->description, name) != 0)
                                continue;
 
-                       if (!key_permission(keyring, KEY_SEARCH))
+                       if (!skip_perm_check &&
+                           key_permission(make_key_ref(keyring, 0),
+                                          KEY_SEARCH) < 0)
                                continue;
 
-                       /* found a potential candidate, but we still need to
-                        * check the serial number */
-                       if (keyring->serial <= bound)
+                       /* we've got a match but we might end up racing with
+                        * key_cleanup() if the keyring is currently 'dead'
+                        * (ie. it has a zero usage count) */
+                       if (!atomic_inc_not_zero(&keyring->usage))
                                continue;
-
-                       /* we've got a match */
-                       atomic_inc(&keyring->usage);
-                       read_unlock(&keyring_name_lock);
-                       goto error;
+                       goto out;
                }
        }
 
-       read_unlock(&keyring_name_lock);
        keyring = ERR_PTR(-ENOKEY);
-
- error:
+out:
+       read_unlock(&keyring_name_lock);
        return keyring;
 
 } /* end find_keyring_by_name() */
@@ -599,7 +600,7 @@ static int keyring_detect_cycle(struct key *A, struct key *B)
        sp = 0;
 
        /* start processing a new keyring */
- descend:
+descend:
        if (test_bit(KEY_FLAG_REVOKED, &subtree->flags))
                goto not_this_keyring;
 
@@ -608,7 +609,7 @@ static int keyring_detect_cycle(struct key *A, struct key *B)
                goto not_this_keyring;
        kix = 0;
 
- ascend:
+ascend:
        /* iterate through the remaining keys in this keyring */
        for (; kix < keylist->nkeys; kix++) {
                key = keylist->keys[kix];
@@ -634,7 +635,7 @@ static int keyring_detect_cycle(struct key *A, struct key *B)
 
        /* the keyring we're looking at was disqualified or didn't contain a
         * matching key */
- not_this_keyring:
+not_this_keyring:
        if (sp > 0) {
                /* resume the checking of a keyring higher up in the tree */
                sp--;
@@ -645,15 +646,15 @@ static int keyring_detect_cycle(struct key *A, struct key *B)
 
        ret = 0; /* no cycles detected */
 
- error:
+error:
        rcu_read_unlock();
        return ret;
 
- too_deep:
+too_deep:
        ret = -ELOOP;
        goto error;
 
- cycle_detected:
+cycle_detected:
        ret = -EDEADLK;
        goto error;
 
@@ -672,17 +673,33 @@ static void keyring_link_rcu_disposal(struct rcu_head *rcu)
 
 } /* end keyring_link_rcu_disposal() */
 
+/*****************************************************************************/
+/*
+ * dispose of a keyring list after the RCU grace period, freeing the unlinked
+ * key
+ */
+static void keyring_unlink_rcu_disposal(struct rcu_head *rcu)
+{
+       struct keyring_list *klist =
+               container_of(rcu, struct keyring_list, rcu);
+
+       key_put(klist->keys[klist->delkey]);
+       kfree(klist);
+
+} /* end keyring_unlink_rcu_disposal() */
+
 /*****************************************************************************/
 /*
  * link a key into to a keyring
  * - must be called with the keyring's semaphore write-locked
+ * - discard already extant link to matching key if there is one
  */
 int __key_link(struct key *keyring, struct key *key)
 {
        struct keyring_list *klist, *nklist;
        unsigned max;
        size_t size;
-       int ret;
+       int loop, ret;
 
        ret = -EKEYREVOKED;
        if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
@@ -692,26 +709,64 @@ int __key_link(struct key *keyring, struct key *key)
        if (keyring->type != &key_type_keyring)
                goto error;
 
-       /* serialise link/link calls to prevent parallel calls causing a
-        * cycle when applied to two keyring in opposite orders */
-       down_write(&keyring_serialise_link_sem);
-
-       /* check that we aren't going to create a cycle adding one keyring to
-        * another */
+       /* do some special keyring->keyring link checks */
        if (key->type == &key_type_keyring) {
+               /* serialise link/link calls to prevent parallel calls causing
+                * a cycle when applied to two keyring in opposite orders */
+               down_write(&keyring_serialise_link_sem);
+
+               /* check that we aren't going to create a cycle adding one
+                * keyring to another */
                ret = keyring_detect_cycle(keyring, key);
                if (ret < 0)
                        goto error2;
        }
 
+       /* see if there's a matching key we can displace */
+       klist = rcu_dereference_locked_keyring(keyring);
+       if (klist && klist->nkeys > 0) {
+               struct key_type *type = key->type;
+
+               for (loop = klist->nkeys - 1; loop >= 0; loop--) {
+                       if (klist->keys[loop]->type == type &&
+                           strcmp(klist->keys[loop]->description,
+                                  key->description) == 0
+                           ) {
+                               /* found a match - replace with new key */
+                               size = sizeof(struct key *) * klist->maxkeys;
+                               size += sizeof(*klist);
+                               BUG_ON(size > PAGE_SIZE);
+
+                               ret = -ENOMEM;
+                               nklist = kmemdup(klist, size, GFP_KERNEL);
+                               if (!nklist)
+                                       goto error2;
+
+                               /* replace matched key */
+                               atomic_inc(&key->usage);
+                               nklist->keys[loop] = key;
+
+                               rcu_assign_pointer(
+                                       keyring->payload.subscriptions,
+                                       nklist);
+
+                               /* dispose of the old keyring list and the
+                                * displaced key */
+                               klist->delkey = loop;
+                               call_rcu(&klist->rcu,
+                                        keyring_unlink_rcu_disposal);
+
+                               goto done;
+                       }
+               }
+       }
+
        /* check that we aren't going to overrun the user's quota */
        ret = key_payload_reserve(keyring,
                                  keyring->datalen + KEYQUOTA_LINK_BYTES);
        if (ret < 0)
                goto error2;
 
-       klist = keyring->payload.subscriptions;
-
        if (klist && klist->nkeys < klist->maxkeys) {
                /* there's sufficient slack space to add directly */
                atomic_inc(&key->usage);
@@ -720,10 +775,7 @@ int __key_link(struct key *keyring, struct key *key)
                smp_wmb();
                klist->nkeys++;
                smp_wmb();
-
-               ret = 0;
-       }
-       else {
+       } else {
                /* grow the key list */
                max = 4;
                if (klist)
@@ -732,7 +784,7 @@ int __key_link(struct key *keyring, struct key *key)
                ret = -ENFILE;
                if (max > 65535)
                        goto error3;
-               size = sizeof(*klist) + sizeof(*key) * max;
+               size = sizeof(*klist) + sizeof(struct key *) * max;
                if (size > PAGE_SIZE)
                        goto error3;
 
@@ -759,16 +811,17 @@ int __key_link(struct key *keyring, struct key *key)
                /* dispose of the old keyring list */
                if (klist)
                        call_rcu(&klist->rcu, keyring_link_rcu_disposal);
-
-               ret = 0;
        }
 
- error2:
-       up_write(&keyring_serialise_link_sem);
- error:
+done:
+       ret = 0;
+error2:
+       if (key->type == &key_type_keyring)
+               up_write(&keyring_serialise_link_sem);
+error:
        return ret;
 
- error3:
+error3:
        /* undo the quota changes */
        key_payload_reserve(keyring,
                            keyring->datalen - KEYQUOTA_LINK_BYTES);
@@ -797,21 +850,6 @@ int key_link(struct key *keyring, struct key *key)
 
 EXPORT_SYMBOL(key_link);
 
-/*****************************************************************************/
-/*
- * dispose of a keyring list after the RCU grace period, freeing the unlinked
- * key
- */
-static void keyring_unlink_rcu_disposal(struct rcu_head *rcu)
-{
-       struct keyring_list *klist =
-               container_of(rcu, struct keyring_list, rcu);
-
-       key_put(klist->keys[klist->delkey]);
-       kfree(klist);
-
-} /* end keyring_unlink_rcu_disposal() */
-
 /*****************************************************************************/
 /*
  * unlink the first link to a key from a keyring
@@ -830,7 +868,7 @@ int key_unlink(struct key *keyring, struct key *key)
 
        down_write(&keyring->sem);
 
-       klist = keyring->payload.subscriptions;
+       klist = rcu_dereference_locked_keyring(keyring);
        if (klist) {
                /* search the keyring for the key */
                for (loop = 0; loop < klist->nkeys; loop++)
@@ -844,7 +882,8 @@ int key_unlink(struct key *keyring, struct key *key)
 
 key_is_present:
        /* we need to copy the key list for RCU purposes */
-       nklist = kmalloc(sizeof(*klist) + sizeof(*key) * klist->maxkeys,
+       nklist = kmalloc(sizeof(*klist) +
+                        sizeof(struct key *) * klist->maxkeys,
                         GFP_KERNEL);
        if (!nklist)
                goto nomem;
@@ -854,12 +893,12 @@ key_is_present:
        if (loop > 0)
                memcpy(&nklist->keys[0],
                       &klist->keys[0],
-                      loop * sizeof(klist->keys[0]));
+                      loop * sizeof(struct key *));
 
        if (loop < nklist->nkeys)
                memcpy(&nklist->keys[loop],
                       &klist->keys[loop + 1],
-                      (nklist->nkeys - loop) * sizeof(klist->keys[0]));
+                      (nklist->nkeys - loop) * sizeof(struct key *));
 
        /* adjust the user's quota */
        key_payload_reserve(keyring,
@@ -920,7 +959,7 @@ int keyring_clear(struct key *keyring)
                /* detach the pointer block with the locks held */
                down_write(&keyring->sem);
 
-               klist = keyring->payload.subscriptions;
+               klist = rcu_dereference_locked_keyring(keyring);
                if (klist) {
                        /* adjust the quota */
                        key_payload_reserve(keyring,
@@ -944,3 +983,123 @@ int keyring_clear(struct key *keyring)
 } /* end keyring_clear() */
 
 EXPORT_SYMBOL(keyring_clear);
+
+/*****************************************************************************/
+/*
+ * dispose of the links from a revoked keyring
+ * - called with the key sem write-locked
+ */
+static void keyring_revoke(struct key *keyring)
+{
+       struct keyring_list *klist;
+
+       klist = rcu_dereference_locked_keyring(keyring);
+
+       /* adjust the quota */
+       key_payload_reserve(keyring, 0);
+
+       if (klist) {
+               rcu_assign_pointer(keyring->payload.subscriptions, NULL);
+               call_rcu(&klist->rcu, keyring_clear_rcu_disposal);
+       }
+
+} /* end keyring_revoke() */
+
+/*
+ * Determine whether a key is dead
+ */
+static bool key_is_dead(struct key *key, time_t limit)
+{
+       return test_bit(KEY_FLAG_DEAD, &key->flags) ||
+               (key->expiry > 0 && key->expiry <= limit);
+}
+
+/*
+ * Collect garbage from the contents of a keyring
+ */
+void keyring_gc(struct key *keyring, time_t limit)
+{
+       struct keyring_list *klist, *new;
+       struct key *key;
+       int loop, keep, max;
+
+       kenter("{%x,%s}", key_serial(keyring), keyring->description);
+
+       down_write(&keyring->sem);
+
+       klist = rcu_dereference_locked_keyring(keyring);
+       if (!klist)
+               goto no_klist;
+
+       /* work out how many subscriptions we're keeping */
+       keep = 0;
+       for (loop = klist->nkeys - 1; loop >= 0; loop--)
+               if (!key_is_dead(klist->keys[loop], limit))
+                       keep++;
+
+       if (keep == klist->nkeys)
+               goto just_return;
+
+       /* allocate a new keyring payload */
+       max = roundup(keep, 4);
+       new = kmalloc(sizeof(struct keyring_list) + max * sizeof(struct key *),
+                     GFP_KERNEL);
+       if (!new)
+               goto nomem;
+       new->maxkeys = max;
+       new->nkeys = 0;
+       new->delkey = 0;
+
+       /* install the live keys
+        * - must take care as expired keys may be updated back to life
+        */
+       keep = 0;
+       for (loop = klist->nkeys - 1; loop >= 0; loop--) {
+               key = klist->keys[loop];
+               if (!key_is_dead(key, limit)) {
+                       if (keep >= max)
+                               goto discard_new;
+                       new->keys[keep++] = key_get(key);
+               }
+       }
+       new->nkeys = keep;
+
+       /* adjust the quota */
+       key_payload_reserve(keyring,
+                           sizeof(struct keyring_list) +
+                           KEYQUOTA_LINK_BYTES * keep);
+
+       if (keep == 0) {
+               rcu_assign_pointer(keyring->payload.subscriptions, NULL);
+               kfree(new);
+       } else {
+               rcu_assign_pointer(keyring->payload.subscriptions, new);
+       }
+
+       up_write(&keyring->sem);
+
+       call_rcu(&klist->rcu, keyring_clear_rcu_disposal);
+       kleave(" [yes]");
+       return;
+
+discard_new:
+       new->nkeys = keep;
+       keyring_clear_rcu_disposal(&new->rcu);
+       up_write(&keyring->sem);
+       kleave(" [discard]");
+       return;
+
+just_return:
+       up_write(&keyring->sem);
+       kleave(" [no dead]");
+       return;
+
+no_klist:
+       up_write(&keyring->sem);
+       kleave(" [no_klist]");
+       return;
+
+nomem:
+       up_write(&keyring->sem);
+       kleave(" [oom]");
+}