swh:1:snp:173f8deb0c56c557784b4fd217e7608ac6197844
Raw File
Tip revision: 88d7bd8cb9eb8d64bf7997600b0d64f7834047c5 authored by Linus Torvalds on 07 May 2005, 05:20:31 UTC
Linux v2.6.12-rc4
Tip revision: 88d7bd8
keyring.c
/* keyring.c: keyring handling
 *
 * Copyright (C) 2004 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/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_duplicate(struct key *keyring, const struct key *source);
static int keyring_match(const struct key *keyring, const void *criterion);
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,
	.duplicate	= keyring_duplicate,
	.match		= keyring_match,
	.destroy	= keyring_destroy,
	.describe	= keyring_describe,
	.read		= keyring_read,
};

/*
 * semaphore to serialise link/link calls to prevent two link calls in parallel
 * introducing a cycle
 */
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)
{
	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() */

/*****************************************************************************/
/*
 * 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;
	sklist = source->payload.subscriptions;

	if (sklist && sklist->nkeys > 0) {
		max = sklist->nkeys;
		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;

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

		keyring->payload.subscriptions = klist;
		ret = 0;
	}

 error:
	return ret;

} /* end keyring_duplicate() */

/*****************************************************************************/
/*
 * 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);
		list_del(&keyring->type_data.link);
		write_unlock(&keyring_name_lock);
	}

	klist = 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]");
	}

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

} /* end keyring_describe() */

/*****************************************************************************/
/*
 * read a list of key IDs from the keyring's contents
 */
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 = 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,
			  int not_in_quota, struct key *dest)
{
	struct key *keyring;
	int ret;

	keyring = key_alloc(&key_type_keyring, description,
			    uid, gid, KEY_USR_ALL, not_in_quota);

	if (!IS_ERR(keyring)) {
		ret = key_instantiate_and_link(keyring, NULL, 0, dest);
		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 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
 */
struct key *keyring_search_aux(struct key *keyring,
			       struct key_type *type,
			       const void *description,
			       key_match_func_t match)
{
	struct {
		struct key *keyring;
		int kix;
	} stack[KEYRING_SEARCH_MAX_DEPTH];

	struct keyring_list *keylist;
	struct timespec now;
	struct key *key;
	long err;
	int sp, psp, kix;

	key_check(keyring);

	/* top keyring must have search permission to begin the search */
	key = ERR_PTR(-EACCES);
	if (!key_permission(keyring, KEY_SEARCH))
		goto error;

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

	now = current_kernel_time();
	err = -EAGAIN;
	sp = 0;

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

	keylist = 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];

		/* ignore keys not of this type */
		if (key->type != type)
			continue;

		/* skip revoked keys and expired keys */
		if (key->flags & 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_permission(key, KEY_SEARCH))
			continue;

		/* we set a different error code if we find a negative key */
		if (key->flags & KEY_FLAG_NEGATIVE) {
			err = -ENOKEY;
			continue;
		}

		goto found;
	}

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

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

		if (!key_permission(key, KEY_SEARCH))
			goto next;

		/* evade loops in the keyring tree */
		for (psp = 0; psp < sp; psp++)
			if (stack[psp].keyring == keyring)
				goto next;

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

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

	next:
		kix++;
	}

	/* the keyring we're looking at was disqualified or didn't contain a
	 * matching key */
 not_this_keyring:
	read_unlock(&keyring->lock);

	if (sp > 0) {
		/* resume the processing of a keyring higher up in the tree */
		sp--;
		keyring = stack[sp].keyring;
		keylist = keyring->payload.subscriptions;
		kix = stack[sp].kix + 1;
		goto ascend;
	}

	key = ERR_PTR(err);
	goto error;

	/* we found a viable match */
 found:
	atomic_inc(&key->usage);
	read_unlock(&keyring->lock);

	/* unwind the keyring stack */
	while (sp > 0) {
		sp--;
		read_unlock(&stack[sp].keyring->lock);
	}

	key_check(key);
 error:
	return key;

} /* 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
 */
struct key *keyring_search(struct key *keyring,
			   struct key_type *type,
			   const char *description)
{
	return keyring_search_aux(keyring, type, description, type->match);

} /* end keyring_search() */

EXPORT_SYMBOL(keyring_search);

/*****************************************************************************/
/*
 * search the given keyring only (no recursion)
 * - keyring must be locked by caller
 */
struct key *__keyring_search_one(struct key *keyring,
				 const struct key_type *ktype,
				 const char *description,
				 key_perm_t perm)
{
	struct keyring_list *klist;
	struct key *key;
	int loop;

	klist = keyring->payload.subscriptions;
	if (klist) {
		for (loop = 0; loop < klist->nkeys; loop++) {
			key = klist->keys[loop];

			if (key->type == ktype &&
			    key->type->match(key, description) &&
			    key_permission(key, perm) &&
			    !(key->flags & KEY_FLAG_REVOKED)
			    )
				goto found;
		}
	}

	key = ERR_PTR(-ENOKEY);
	goto error;

 found:
	atomic_inc(&key->usage);
 error:
	return key;

} /* end __keyring_search_one() */

/*****************************************************************************/
/*
 * 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
 */
struct key *find_keyring_by_name(const char *name, key_serial_t bound)
{
	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 (keyring->flags & KEY_FLAG_REVOKED)
				continue;

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

			if (!key_permission(keyring, KEY_SEARCH))
				continue;

			/* found a potential candidate, but we still need to
			 * check the serial number */
			if (keyring->serial <= bound)
				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 key *subtree;
		int kix;
	} stack[KEYRING_SEARCH_MAX_DEPTH];

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

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

	subtree = B;
	sp = 0;

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

	keylist = 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].subtree = subtree;
			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:
	read_unlock(&subtree->lock);

	if (sp > 0) {
		/* resume the checking of a keyring higher up in the tree */
		sp--;
		subtree = stack[sp].subtree;
		keylist = subtree->payload.subscriptions;
		kix = stack[sp].kix + 1;
		goto ascend;
	}

	ret = 0; /* no cycles detected */

 error:
	return ret;

 too_deep:
	ret = -ELOOP;
	goto error_unwind;
 cycle_detected:
	ret = -EDEADLK;
 error_unwind:
	read_unlock(&subtree->lock);

	/* unwind the keyring stack */
	while (sp > 0) {
		sp--;
		read_unlock(&stack[sp].subtree->lock);
	}

	goto error;

} /* end keyring_detect_cycle() */

/*****************************************************************************/
/*
 * link a key into to a keyring
 * - must be called with the keyring's semaphore held
 */
int __key_link(struct key *keyring, struct key *key)
{
	struct keyring_list *klist, *nklist;
	unsigned max;
	size_t size;
	int ret;

	ret = -EKEYREVOKED;
	if (keyring->flags & KEY_FLAG_REVOKED)
		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;
	}

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

		write_lock(&keyring->lock);
		klist->keys[klist->nkeys++] = key;
		write_unlock(&keyring->lock);

		ret = 0;
	}
	else {
		/* grow the key list */
		max = 4;
		if (klist)
			max += klist->maxkeys;

		ret = -ENFILE;
		size = sizeof(*klist) + sizeof(*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);

		write_lock(&keyring->lock);
		keyring->payload.subscriptions = nklist;
		nklist->keys[nklist->nkeys++] = key;
		write_unlock(&keyring->lock);

		/* dispose of the old keyring list */
		kfree(klist);

		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;
	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:
	/* adjust the user's quota */
	key_payload_reserve(keyring,
			    keyring->datalen - KEYQUOTA_LINK_BYTES);

	/* shuffle down the key pointers
	 * - it might be worth shrinking the allocated memory, but that runs
	 *   the risk of ENOMEM as we would have to copy
	 */
	write_lock(&keyring->lock);

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

	write_unlock(&keyring->lock);

	up_write(&keyring->sem);
	key_put(key);
	ret = 0;

 error:
	return ret;

} /* end key_unlink() */

EXPORT_SYMBOL(key_unlink);

/*****************************************************************************/
/*
 * clear the specified process keyring
 * - implements keyctl(KEYCTL_CLEAR)
 */
int keyring_clear(struct key *keyring)
{
	struct keyring_list *klist;
	int loop, 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));

			write_lock(&keyring->lock);
			keyring->payload.subscriptions = NULL;
			write_unlock(&keyring->lock);
		}

		up_write(&keyring->sem);

		/* free the keys after the locks have been dropped */
		if (klist) {
			for (loop = klist->nkeys - 1; loop >= 0; loop--)
				key_put(klist->keys[loop]);

			kfree(klist);
		}

		ret = 0;
	}

	return ret;

} /* end keyring_clear() */

EXPORT_SYMBOL(keyring_clear);
back to top