[funini.com] -> [kei@sodan] -> Kernel Reading

root/security/keys/keyring.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. keyring_hash
  2. keyring_publish_name
  3. keyring_instantiate
  4. keyring_match
  5. keyring_destroy
  6. keyring_describe
  7. keyring_read
  8. keyring_alloc
  9. keyring_search_aux
  10. keyring_search
  11. __keyring_search_one
  12. find_keyring_by_name
  13. keyring_detect_cycle
  14. keyring_link_rcu_disposal
  15. keyring_unlink_rcu_disposal
  16. __key_link
  17. key_link
  18. key_unlink
  19. keyring_clear_rcu_disposal
  20. keyring_clear
  21. keyring_revoke

/* Keyring handling
 *
 * 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
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */

#include <linux/module.h>
#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 "internal.h"

/*
 * when plumbing the depths of the key tree, this sets a hard limit set on how
 * deep we're willing to go
 */
#define KEYRING_SEARCH_MAX_DEPTH 6

/*
 * we keep all named keyrings in a hash to speed looking them up
 */
#define KEYRING_NAME_HASH_SIZE  (1 << 5)

static struct list_head keyring_name_hash[KEYRING_NAME_HASH_SIZE];
static DEFINE_RWLOCK(keyring_name_lock);

static inline unsigned keyring_hash(const char *desc)
{
        unsigned bucket = 0;

        for (; *desc; desc++)
                bucket += (unsigned char) *desc;

        return bucket & (KEYRING_NAME_HASH_SIZE - 1);
}

/*
 * the keyring type definition
 */
static int keyring_instantiate(struct key *keyring,
                               const void *data, size_t datalen);
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,
                         char __user *buffer, size_t buflen);

struct key_type key_type_keyring = {
        .name           = "keyring",
        .def_datalen    = sizeof(struct keyring_list),
        .instantiate    = keyring_instantiate,
        .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
 */
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)
 */
static void keyring_publish_name(struct key *keyring)
{
        int bucket;

        if (keyring->description) {
                bucket = keyring_hash(keyring->description);

                write_lock(&keyring_name_lock);

                if (!keyring_name_hash[bucket].next)
                        INIT_LIST_HEAD(&keyring_name_hash[bucket]);

                list_add_tail(&keyring->type_data.link,
                              &keyring_name_hash[bucket]);

                write_unlock(&keyring_name_lock);
        }

} /* end keyring_publish_name() */

/*****************************************************************************/
/*
 * initialise a keyring
 * - we object if we were given any data
 */
static int keyring_instantiate(struct key *keyring,
                               const void *data, size_t datalen)
{
        int ret;

        ret = -EINVAL;
        if (datalen == 0) {
                /* make the keyring available by name if it has one */
                keyring_publish_name(keyring);
                ret = 0;
        }

        return ret;

} /* end keyring_instantiate() */

/*****************************************************************************/
/*
 * match keyrings on their name
 */
static int keyring_match(const struct key *keyring, const void *description)
{
        return keyring->description &&
                strcmp(keyring->description, description) == 0;

} /* end keyring_match() */

/*****************************************************************************/
/*
 * dispose of the data dangling from the corpse of a keyring
 */
static void keyring_destroy(struct key *keyring)
{
        struct keyring_list *klist;
        int loop;

        if (keyring->description) {
                write_lock(&keyring_name_lock);

                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);
        if (klist) {
                for (loop = klist->nkeys - 1; loop >= 0; loop--)
                        key_put(klist->keys[loop]);
                kfree(klist);
        }

} /* end keyring_destroy() */

/*****************************************************************************/
/*
 * describe the keyring
 */
static void keyring_describe(const struct key *keyring, struct seq_file *m)
{
        struct keyring_list *klist;

        if (keyring->description) {
                seq_puts(m, keyring->description);
        }
        else {
                seq_puts(m, "[anon]");
        }

        rcu_read_lock();
        klist = rcu_dereference(keyring->payload.subscriptions);
        if (klist)
                seq_printf(m, ": %u/%u", klist->nkeys, klist->maxkeys);
        else
                seq_puts(m, ": empty");
        rcu_read_unlock();

} /* end keyring_describe() */

/*****************************************************************************/
/*
 * read a list of key IDs from the keyring's contents
 * - the keyring's semaphore is read-locked
 */
static long keyring_read(const struct key *keyring,
                         char __user *buffer, size_t buflen)
{
        struct keyring_list *klist;
        struct key *key;
        size_t qty, tmp;
        int loop, ret;

        ret = 0;
        klist = rcu_dereference(keyring->payload.subscriptions);

        if (klist) {
                /* calculate how much data we could return */
                qty = klist->nkeys * sizeof(key_serial_t);

                if (buffer && buflen > 0) {
                        if (buflen > qty)
                                buflen = qty;

                        /* copy the IDs of the subscribed keys into the
                         * buffer */
                        ret = -EFAULT;

                        for (loop = 0; loop < klist->nkeys; loop++) {
                                key = klist->keys[loop];

                                tmp = sizeof(key_serial_t);
                                if (tmp > buflen)
                                        tmp = buflen;

                                if (copy_to_user(buffer,
                                                 &key->serial,
                                                 tmp) != 0)
                                        goto error;

                                buflen -= tmp;
                                if (buflen == 0)
                                        break;
                                buffer += tmp;
                        }
                }

                ret = qty;
        }

 error:
        return ret;

} /* end keyring_read() */

/*****************************************************************************/
/*
 * allocate a keyring and link into the destination keyring
 */
struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
                          struct task_struct *ctx, unsigned long flags,
                          struct key *dest)
{
        struct key *keyring;
        int ret;

        keyring = key_alloc(&key_type_keyring, description,
                            uid, gid, ctx,
                            (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
                            flags);

        if (!IS_ERR(keyring)) {
                ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL);
                if (ret < 0) {
                        key_put(keyring);
                        keyring = ERR_PTR(ret);
                }
        }

        return keyring;

} /* end keyring_alloc() */

/*****************************************************************************/
/*
 * search the supplied keyring tree for a key that matches the criterion
 * - perform a breadth-then-depth search up to the prescribed limit
 * - 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 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
 */
key_ref_t keyring_search_aux(key_ref_t keyring_ref,
                             struct task_struct *context,
                             struct key_type *type,
                             const void *description,
                             key_match_func_t match)
{
        struct {
                struct keyring_list *keylist;
                int kix;
        } stack[KEYRING_SEARCH_MAX_DEPTH];

        struct keyring_list *keylist;
        struct timespec now;
        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);

        /* top keyring must have search permission to begin the search */
        err = key_task_permission(keyring_ref, context, KEY_SEARCH);
        if (err < 0) {
                key_ref = ERR_PTR(err);
                goto error;
        }

        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:
        if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
                goto not_this_keyring;

        keylist = rcu_dereference(keyring->payload.subscriptions);
        if (!keylist)
                goto not_this_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 (kflags & (1 << KEY_FLAG_REVOKED))
                        continue;

                if (key->expiry && now.tv_sec >= key->expiry)
                        continue;

                /* keys that don't match */
                if (!match(key, description))
                        continue;

                /* key must have search permissions */
                if (key_task_permission(make_key_ref(key, possessed),
                                        context, KEY_SEARCH) < 0)
                        continue;

                /* we set a different error code if we pass a negative key */
                if (kflags & (1 << KEY_FLAG_NEGATIVE)) {
                        err = -ENOKEY;
                        continue;
                }

                goto found;
        }

        /* search through the keyrings nested in this one */
        kix = 0;
ascend:
        for (; kix < keylist->nkeys; kix++) {
                key = keylist->keys[kix];
                if (key->type != &key_type_keyring)
                        continue;

                /* recursively search nested keyrings
                 * - only search keyrings for which we have search permission
                 */
                if (sp >= KEYRING_SEARCH_MAX_DEPTH)
                        continue;

                if (key_task_permission(make_key_ref(key, possessed),
                                        context, KEY_SEARCH) < 0)
                        continue;

                /* stack the current position */
                stack[sp].keylist = keylist;
                stack[sp].kix = kix;
                sp++;

                /* begin again with the new keyring */
                keyring = key;
                goto descend;
        }

        /* the keyring we're looking at was disqualified or didn't contain a
         * matching key */
not_this_keyring:
        if (sp > 0) {
                /* resume the processing of a keyring higher up in the tree */
                sp--;
                keylist = stack[sp].keylist;
                kix = stack[sp].kix + 1;
                goto ascend;
        }

        key_ref = ERR_PTR(err);
        goto error_2;

        /* we found a viable match */
found:
        atomic_inc(&key->usage);
        key_check(key);
        key_ref = make_key_ref(key, possessed);
error_2:
        rcu_read_unlock();
error:
        return key_ref;

} /* end keyring_search_aux() */

/*****************************************************************************/
/*
 * search the supplied keyring tree for a key that matches the criterion
 * - perform a breadth-then-depth search up to the prescribed limit
 * - we only find keys on which we have search permission
 * - we readlock the keyrings as we search down the tree
 * - we return -EAGAIN if we didn't find any matching key
 * - we return -ENOKEY if we only found negative matching keys
 */
key_ref_t keyring_search(key_ref_t keyring,
                         struct key_type *type,
                         const char *description)
{
        if (!type->match)
                return ERR_PTR(-ENOKEY);

        return keyring_search_aux(keyring, current,
                                  type, description, type->match);

} /* end keyring_search() */

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
 */
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;
        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);
        if (klist) {
                for (loop = 0; loop < klist->nkeys; loop++) {
                        key = klist->keys[loop];

                        if (key->type == ktype &&
                            (!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;
                }
        }

        rcu_read_unlock();
        return ERR_PTR(-ENOKEY);

 found:
        atomic_inc(&key->usage);
        rcu_read_unlock();
        return make_key_ref(key, possessed);

} /* end __keyring_search_one() */

/*****************************************************************************/
/*
 * find a keyring with the specified name
 * - all named keyrings are searched
 * - normally only finds keyrings with search permission for the current process
 */
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;

        bucket = keyring_hash(name);

        read_lock(&keyring_name_lock);

        if (keyring_name_hash[bucket].next) {
                /* search this hash bucket for a keyring with a matching name
                 * that's readable and that hasn't been revoked */
                list_for_each_entry(keyring,
                                    &keyring_name_hash[bucket],
                                    type_data.link
                                    ) {
                        if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
                                continue;

                        if (strcmp(keyring->description, name) != 0)
                                continue;

                        if (!skip_perm_check &&
                            key_permission(make_key_ref(keyring, 0),
                                           KEY_SEARCH) < 0)
                                continue;

                        /* we've got a match */
                        atomic_inc(&keyring->usage);
                        read_unlock(&keyring_name_lock);
                        goto error;
                }
        }

        read_unlock(&keyring_name_lock);
        keyring = ERR_PTR(-ENOKEY);

 error:
        return keyring;

} /* end find_keyring_by_name() */

/*****************************************************************************/
/*
 * see if a cycle will will be created by inserting acyclic tree B in acyclic
 * tree A at the topmost level (ie: as a direct child of A)
 * - since we are adding B to A at the top level, checking for cycles should
 *   just be a matter of seeing if node A is somewhere in tree B
 */
static int keyring_detect_cycle(struct key *A, struct key *B)
{
        struct {
                struct keyring_list *keylist;
                int kix;
        } stack[KEYRING_SEARCH_MAX_DEPTH];

        struct keyring_list *keylist;
        struct key *subtree, *key;
        int sp, kix, ret;

        rcu_read_lock();

        ret = -EDEADLK;
        if (A == B)
                goto cycle_detected;

        subtree = B;
        sp = 0;

        /* start processing a new keyring */
 descend:
        if (test_bit(KEY_FLAG_REVOKED, &subtree->flags))
                goto not_this_keyring;

        keylist = rcu_dereference(subtree->payload.subscriptions);
        if (!keylist)
                goto not_this_keyring;
        kix = 0;

 ascend:
        /* iterate through the remaining keys in this keyring */
        for (; kix < keylist->nkeys; kix++) {
                key = keylist->keys[kix];

                if (key == A)
                        goto cycle_detected;

                /* recursively check nested keyrings */
                if (key->type == &key_type_keyring) {
                        if (sp >= KEYRING_SEARCH_MAX_DEPTH)
                                goto too_deep;

                        /* stack the current position */
                        stack[sp].keylist = keylist;
                        stack[sp].kix = kix;
                        sp++;

                        /* begin again with the new keyring */
                        subtree = key;
                        goto descend;
                }
        }

        /* the keyring we're looking at was disqualified or didn't contain a
         * matching key */
 not_this_keyring:
        if (sp > 0) {
                /* resume the checking of a keyring higher up in the tree */
                sp--;
                keylist = stack[sp].keylist;
                kix = stack[sp].kix + 1;
                goto ascend;
        }

        ret = 0; /* no cycles detected */

 error:
        rcu_read_unlock();
        return ret;

 too_deep:
        ret = -ELOOP;
        goto error;

 cycle_detected:
        ret = -EDEADLK;
        goto error;

} /* end keyring_detect_cycle() */

/*****************************************************************************/
/*
 * dispose of a keyring list after the RCU grace period
 */
static void keyring_link_rcu_disposal(struct rcu_head *rcu)
{
        struct keyring_list *klist =
                container_of(rcu, struct keyring_list, rcu);

        kfree(klist);

} /* 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 loop, ret;

        ret = -EKEYREVOKED;
        if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
                goto error;

        ret = -ENOTDIR;
        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 */
        if (key->type == &key_type_keyring) {
                ret = keyring_detect_cycle(keyring, key);
                if (ret < 0)
                        goto error2;
        }

        /* see if there's a matching key we can displace */
        klist = keyring->payload.subscriptions;

        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);

                klist->keys[klist->nkeys] = key;
                smp_wmb();
                klist->nkeys++;
                smp_wmb();
        }
        else {
                /* grow the key list */
                max = 4;
                if (klist)
                        max += klist->maxkeys;

                ret = -ENFILE;
                if (max > 65535)
                        goto error3;
                size = sizeof(*klist) + sizeof(struct key *) * max;
                if (size > PAGE_SIZE)
                        goto error3;

                ret = -ENOMEM;
                nklist = kmalloc(size, GFP_KERNEL);
                if (!nklist)
                        goto error3;
                nklist->maxkeys = max;
                nklist->nkeys = 0;

                if (klist) {
                        nklist->nkeys = klist->nkeys;
                        memcpy(nklist->keys,
                               klist->keys,
                               sizeof(struct key *) * klist->nkeys);
                }

                /* add the key into the new space */
                atomic_inc(&key->usage);
                nklist->keys[nklist->nkeys++] = key;

                rcu_assign_pointer(keyring->payload.subscriptions, nklist);

                /* dispose of the old keyring list */
                if (klist)
                        call_rcu(&klist->rcu, keyring_link_rcu_disposal);
        }

done:
        ret = 0;
error2:
        up_write(&keyring_serialise_link_sem);
error:
        return ret;

error3:
        /* undo the quota changes */
        key_payload_reserve(keyring,
                            keyring->datalen - KEYQUOTA_LINK_BYTES);
        goto error2;

} /* end __key_link() */

/*****************************************************************************/
/*
 * link a key to a keyring
 */
int key_link(struct key *keyring, struct key *key)
{
        int ret;

        key_check(keyring);
        key_check(key);

        down_write(&keyring->sem);
        ret = __key_link(keyring, key);
        up_write(&keyring->sem);

        return ret;

} /* end key_link() */

EXPORT_SYMBOL(key_link);

/*****************************************************************************/
/*
 * unlink the first link to a key from a keyring
 */
int key_unlink(struct key *keyring, struct key *key)
{
        struct keyring_list *klist, *nklist;
        int loop, ret;

        key_check(keyring);
        key_check(key);

        ret = -ENOTDIR;
        if (keyring->type != &key_type_keyring)
                goto error;

        down_write(&keyring->sem);

        klist = keyring->payload.subscriptions;
        if (klist) {
                /* search the keyring for the key */
                for (loop = 0; loop < klist->nkeys; loop++)
                        if (klist->keys[loop] == key)
                                goto key_is_present;
        }

        up_write(&keyring->sem);
        ret = -ENOENT;
        goto error;

key_is_present:
        /* we need to copy the key list for RCU purposes */
        nklist = kmalloc(sizeof(*klist) +
                         sizeof(struct key *) * klist->maxkeys,
                         GFP_KERNEL);
        if (!nklist)
                goto nomem;
        nklist->maxkeys = klist->maxkeys;
        nklist->nkeys = klist->nkeys - 1;

        if (loop > 0)
                memcpy(&nklist->keys[0],
                       &klist->keys[0],
                       loop * sizeof(struct key *));

        if (loop < nklist->nkeys)
                memcpy(&nklist->keys[loop],
                       &klist->keys[loop + 1],
                       (nklist->nkeys - loop) * sizeof(struct key *));

        /* adjust the user's quota */
        key_payload_reserve(keyring,
                            keyring->datalen - KEYQUOTA_LINK_BYTES);

        rcu_assign_pointer(keyring->payload.subscriptions, nklist);

        up_write(&keyring->sem);

        /* schedule for later cleanup */
        klist->delkey = loop;
        call_rcu(&klist->rcu, keyring_unlink_rcu_disposal);

        ret = 0;

error:
        return ret;
nomem:
        ret = -ENOMEM;
        up_write(&keyring->sem);
        goto error;

} /* end key_unlink() */

EXPORT_SYMBOL(key_unlink);

/*****************************************************************************/
/*
 * dispose of a keyring list after the RCU grace period, releasing the keys it
 * links to
 */
static void keyring_clear_rcu_disposal(struct rcu_head *rcu)
{
        struct keyring_list *klist;
        int loop;

        klist = container_of(rcu, struct keyring_list, rcu);

        for (loop = klist->nkeys - 1; loop >= 0; loop--)
                key_put(klist->keys[loop]);

        kfree(klist);

} /* end keyring_clear_rcu_disposal() */

/*****************************************************************************/
/*
 * clear the specified process keyring
 * - implements keyctl(KEYCTL_CLEAR)
 */
int keyring_clear(struct key *keyring)
{
        struct keyring_list *klist;
        int ret;

        ret = -ENOTDIR;
        if (keyring->type == &key_type_keyring) {
                /* detach the pointer block with the locks held */
                down_write(&keyring->sem);

                klist = keyring->payload.subscriptions;
                if (klist) {
                        /* adjust the quota */
                        key_payload_reserve(keyring,
                                            sizeof(struct keyring_list));

                        rcu_assign_pointer(keyring->payload.subscriptions,
                                           NULL);
                }

                up_write(&keyring->sem);

                /* free the keys after the locks have been dropped */
                if (klist)
                        call_rcu(&klist->rcu, keyring_clear_rcu_disposal);

                ret = 0;
        }

        return ret;

} /* 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 = keyring->payload.subscriptions;

        /* 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() */

/* [<][>][^][v][top][bottom][index][help] */

[funini.com] -> [kei@sodan] -> Kernel Reading