1279 lines · 32.2 KB · C
RawDownload
/* 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 <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))) #define rcu_deref_link_locked(klist, index, keyring)			\	(rcu_dereference_protected(					\		(klist)->keys[index],					\		rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem))) #define MAX_KEYRING_LINKS						\	min_t(size_t, USHRT_MAX - 1,					\	      ((PAGE_SIZE - sizeof(struct keyring_list)) / sizeof(struct key *))) #define KEY_LINK_FIXQUOTA 1UL /* * 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 key type definition.  Keyrings are simply keys of this type and * can be treated as ordinary keys in addition to having their own special * operations. */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);	}} /* * Initialise a keyring. * * Returns 0 on success, -EINVAL if 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;} /* * Match keyrings on their name */static int keyring_match(const struct key *keyring, const void *description){	return keyring->description &&		strcmp(keyring->description, description) == 0;} /* * Clean up a keyring when it is destroyed.  Unpublish its name if it had one * and dispose of its data. * * The garbage collector detects the final key_put(), removes the keyring from * the serial number tree and then does RCU synchronisation before coming here, * so we shouldn't need to worry about code poking around here with the RCU * readlock held by this time. */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_access_pointer(keyring->payload.subscriptions);	if (klist) {		for (loop = klist->nkeys - 1; loop >= 0; loop--)			key_put(rcu_access_pointer(klist->keys[loop]));		kfree(klist);	}} /* * Describe a keyring for /proc. */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]"); 	if (key_is_instantiated(keyring)) {		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();	}} /* * Read a list of key IDs from the keyring's contents in binary form * * The keyring's semaphore is read-locked by the caller. */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_locked_keyring(keyring);	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 = rcu_deref_link_locked(klist, loop,							    keyring); 				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;} /* * Allocate a keyring and link into the destination keyring. */struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,			  const struct cred *cred, unsigned long flags,			  struct key *dest){	struct key *keyring;	int ret; 	keyring = key_alloc(&key_type_keyring, description,			    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, NULL);		if (ret < 0) {			key_put(keyring);			keyring = ERR_PTR(ret);		}	} 	return keyring;} /** * keyring_search_aux - Search a keyring tree for a key matching some criteria * @keyring_ref: A pointer to the keyring with possession indicator. * @cred: The credentials to use for permissions checks. * @type: The type of key to search for. * @description: Parameter for @match. * @match: Function to rule on whether or not a key is the one required. * @no_state_check: Don't check if a matching key is bad * * Search the supplied keyring tree for a key that matches the criteria given. * The root keyring and any linked keyrings must grant Search permission to the * caller to be searchable and keys can only be found if they too grant Search * to the caller. The possession flag on the root keyring pointer controls use * of the possessor bits in permissions checking of the entire tree.  In * addition, the LSM gets to forbid keyring searches and key matches. * * The search is performed as a breadth-then-depth search up to the prescribed * limit (KEYRING_SEARCH_MAX_DEPTH). * * Keys are matched to the type provided and are then filtered by the match * function, which is given the description to use in any way it sees fit.  The * match function may use any attributes of a key that it wishes to to * determine the match.  Normally the match function from the key type would be * used. * * RCU is used to prevent the keyring key lists from disappearing without the * need to take lots of locks. * * Returns a pointer to the found key and increments the key usage count if * successful; -EAGAIN if no matching keys were found, or if expired or revoked * keys were found; -ENOKEY if only negative keys were found; -ENOTDIR if the * specified keyring wasn't a keyring. * * In the case of a successful return, the possession attribute from * @keyring_ref is propagated to the returned key reference. */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,			     bool no_state_check){	struct {		/* Need a separate keylist pointer for RCU purposes */		struct key *keyring;		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, nkeys, 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, cred, 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;		if (no_state_check)			goto found; 		/* 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(key->type_data.reject_error);		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_INVALIDATED) |		      (1 << KEY_FLAG_REVOKED) |		      (1 << KEY_FLAG_NEGATIVE)) ||	    (keyring->expiry && now.tv_sec >= keyring->expiry))		goto error_2; 	/* start processing a new keyring */descend:	kflags = keyring->flags;	if (kflags & ((1 << KEY_FLAG_INVALIDATED) |		      (1 << KEY_FLAG_REVOKED)))		goto not_this_keyring; 	keylist = rcu_dereference(keyring->payload.subscriptions);	if (!keylist)		goto not_this_keyring; 	/* iterate through the keys in this keyring first */	nkeys = keylist->nkeys;	smp_rmb();	for (kix = 0; kix < nkeys; kix++) {		key = rcu_dereference(keylist->keys[kix]);		kflags = key->flags; 		/* ignore keys not of this type */		if (key->type != type)			continue; 		/* skip invalidated, revoked and expired keys */		if (!no_state_check) {			if (kflags & ((1 << KEY_FLAG_INVALIDATED) |				      (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),					cred, KEY_SEARCH) < 0)			continue; 		if (no_state_check)			goto found; 		/* we set a different error code if we pass a negative key */		if (kflags & (1 << KEY_FLAG_NEGATIVE)) {			err = key->type_data.reject_error;			continue;		} 		goto found;	} 	/* search through the keyrings nested in this one */	kix = 0;ascend:	nkeys = keylist->nkeys;	smp_rmb();	for (; kix < nkeys; kix++) {		key = rcu_dereference(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),					cred, KEY_SEARCH) < 0)			continue; 		/* stack the current position */		stack[sp].keyring = keyring;		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--;		keyring = stack[sp].keyring;		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->last_used_at = now.tv_sec;	keyring->last_used_at = now.tv_sec;	while (sp > 0)		stack[--sp].keyring->last_used_at = now.tv_sec;	key_check(key);	key_ref = make_key_ref(key, possessed);error_2:	rcu_read_unlock();

Lines 1–500 of 1279.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t